2016-06-12 13 views
6

Sto lavorando a un'app di chat, in cui gli utenti devono ricevere notifiche sui nuovi messaggi dai propri contatti. Questo messaggio di notifica dovrebbe includere anche il numero di messaggi non letti. Poiché sia ​​il mittente che il destinatario possono aggiornare queste informazioni, è preferibile il runTransaction. Purtroppo a volte non funziona. Si sente "bloccato" e poi riprende a lavorare dopo un po 'di tempo. Il nodo privateChats (vedi sotto) viene sempre aggiornato con il messaggio più recente, ma non con il nodo openChatMessages.Firebase/iOS: runTransactions a volte non funziona

Questo può accadere se molti messaggi vengono inviati in un breve periodo di tempo, ovvero runTransactions viene eseguito troppo spesso per lo stesso ref?

La mia struttura dei dati:

privateChats 
    $userId 
     $chatId 
      $messageId 
       text 
       timestamp 
       senderId 
       senderEmail 
       senderName 

// this node contains information about open chats 
// like last message and counter for unread messages 
openChatMessages 
    $userId 
     $chatId 
      text 
      timestamp 
      senderId 
      senderEmail 
      senderName 
      counter 

Il mio codice:

class ChatViewController: JSQMessagesViewController { 

    var user: FIRUser! 
    var ref: FIRDatabaseReference! 
    var chatRef: FIRDatabaseReference! 
    var senderOpenChatRef: FIRDatabaseReference! 
    var receiverOpenChatRef: FIRDatabaseReference! 

    // the following variables will be set before ChatViewController appears 

    var chatId: String? 
    var receivId: String? 
    var receiverEmail: String? 
    var receiverName: String? 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     self.user = FIRAuth.auth()?.currentUser! 
     self.ref = FIRDatabase.database().reference() 
     self.chatRef = self.ref.child("privateChats").child(self.user.uid).child(self.chatId!) 
     self.senderOpenChatRef = self.ref.child("openChatMessages").child(self.user.uid).child(self.chatId!) 
     self.receiverOpenChatRef = self.ref.child("openChatMessages").child(self.receiverId!).child(self.chatId!) 
    } 

    func sendMessage(text: String) { 
     var messageObject = [String: AnyObject]() 
     messageObject["text"] = text 
     messageObject["timestamp"] = FIRServerValue.timestamp() 
     messageObject["senderEmail"] = self.user.email 
     messageObject["senderName"] = self.user.displayName 
     messageObject["senderId"] = self.user.uid 

     let messageId = self.ref.child("privateChats").child(self.user.uid).child(self.chatId!).childByAutoId().key 

     let childUpdates = [ 
      "/privateChats/\(self.user.uid)/\(self.chatId!)/\(messageId)": messageObject, 
      "/privateChats/\(self.receiverId!)/\(self.chatId!)/\(messageId)": messageObject 
     ] 

     self.ref.updateChildValues(childUpdates, withCompletionBlock: { (error, ref) -> Void in 
      if error != nil { 
       print("childUpdates error:\(error)") 
       return 
      } 

      JSQSystemSoundPlayer.jsq_playMessageSentSound() 
      self.finishSendingMessage() 
      self.updateOpenChats(text) 
     }) 
    } 


    func updateOpenChats(text: String) { 

     // update the receivers openChatObject with increasing the counter 
     self.receiverOpenChatRef.runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in 

      var openChatObject = [String: AnyObject]() 

      // update openChatObject with the latest information from currentData 
      if currentData.hasChildren() { 
       openChatObject = currentData.value as! [String: AnyObject] 
      } 

      openChatObject["text"] = text 
      openChatObject["timestamp"] = FIRServerValue.timestamp() 
      openChatObject["senderEmail"] = self.user.email 
      openChatObject["senderName"] = self.user.displayName 
      openChatObject["senderId"] = self.user.uid 

      var counter = openChatObject["counter"] as? Int 
      if counter == nil { 
       counter = 1 
      } else { 
       counter = counter! + 1 
      } 
      openChatObject["counter"] = counter 

      currentData.value = openChatObject 
      return FIRTransactionResult.successWithValue(currentData) 
      }) { (error, committed, snapshot) in 
       if let error = error { 
        print("updateOpenChats: \(error.localizedDescription)") 
       } 
     } 

     // update your (the sender's) openChatObject with setting the counter to zero 
     self.senderOpenChatRef.runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in 

      var openChatObject = [String: AnyObject]() 

      // update openChatObject with the latest information from currentData 
      if currentData.hasChildren() { 
       openChatObject = currentData.value as! [String: AnyObject] 
      } 

      openChatObject["text"] = text 
      openChatObject["timestamp"] = FIRServerValue.timestamp() 
      openChatObject["senderEmail"] = self.receiverEmail 
      openChatObject["senderName"] = self.receiverName 
      openChatObject["senderId"] = self.receiverId 
      openChatObject["counter"] = 0 

      currentData.value = openChatObject 
      return FIRTransactionResult.successWithValue(currentData) 
      }) { (error, committed, snapshot) in 
       if let error = error { 
        print(error.localizedDescription) 
       } 
     } 
    } 
} 

EDIT:

Contrariamente alle mie aspettative dal mio prima risposta il bug si verifica ancora. Presumo che abbia qualcosa a che fare con la connessione? Per esempio. quando non c'è una buona connessione a volte ci vuole più tempo per eseguire la transazione? Ma a volte si verifica anche quando mi siedo proprio accanto al router. Gli altri nodi vengono scritti, ma non quelli con la transazione. Dopo aver riavviato l'app in quelle situazioni, riprende a funzionare. Quindi immagino ci sia qualcosa di sbagliato sotto il cofano.

Apprezzerei molto le soluzioni per questo problema. Un'app di chat in cui il ricevitore a volte non riceve notifiche sui nuovi messaggi è un no go.

Sono soddisfatto anche con soluzioni alternative: le transazioni sono effettivamente necessarie quando si desidera incrementare un contatore? Potrei aggiornare gli altri dati come text, senderId o timestamp con setValue, ma questo porterebbe a dati corrotti, quando entrambi gli utenti tentano di impostare il valore dei sottonodi nello stesso momento, no?

Ecco il mio ultimo codice:

func sendMessage(text: String?, video: NSURL?, image: UIImage?) { 

    var messageObject = [String: AnyObject]() 
    messageObject["text"] = text 
    messageObject["timestamp"] = FIRServerValue.timestamp() 
    messageObject["senderEmail"] = self.user.email 
    messageObject["senderName"] = self.user.displayName 
    messageObject["senderId"] = self.user.uid 

    func completeSending() { 
     let messagesRef = self.ref.child("messages").child(self.chatId!).childByAutoId() 
     messagesRef.setValue(messageObject) 

     JSQSystemSoundPlayer.jsq_playMessageSentSound() 
     if let _ = image { 
      self.updateOpenChats(" Photo") 
     } else if let text = text { 
      self.updateOpenChats(text) 
     } 

     self.finishSendingMessageAnimated(true) 
    } 

    if let image = image { // if an image is being sent 
     let data: NSData = UIImageJPEGRepresentation(image, 0.37)! 
     let fileName = "image_\(NSDate().timeIntervalSince1970).jpg" 
     let chatImagesRef = storageRef.child("chatImages/\(self.chatId!)/\(fileName)") 
     let uploadTask = chatImagesRef.putData(data, metadata: nil) { metadata, error in 
      if (error != nil) { 
       print(error) 
       return 
      } 
     } 

     uploadTask.observeStatus(.Failure) { snapshot in 
      ProgressHUD.showError("Uploading image failed.") 
     } 

     uploadTask.observeStatus(.Success) { snapshot in 
      let imageUrl = snapshot.reference 
      messageObject["imageUrl"] = String(imageUrl) 
      completeSending() 
     } 
    } else { // if it's just a text message 
     completeSending() 
    } 

} 

func updateOpenChats(text: String) { 

     self.receiverChatRef.runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in 

      var openChatObject = [String: AnyObject]() 

      if currentData.hasChildren() { 
       openChatObject = currentData.value as! [String: AnyObject] 
      } 

      openChatObject["text"] = text 
      openChatObject["timestamp"] = FIRServerValue.timestamp() 
      openChatObject["senderEmail"] = self.user.email 
      openChatObject["senderName"] = self.user.displayName 
      openChatObject["senderId"] = self.user.uid 
      openChatObject["pushId"] = Database.pushId 

      var counter = openChatObject["counter"] as? Int 
      if counter == nil { 
       counter = 1 
      } else { 
       counter = counter! + 1 
      } 
      openChatObject["counter"] = counter 

      // Set value and report transaction success 
      currentData.value = openChatObject 
      return FIRTransactionResult.successWithValue(currentData) 
     }) { (error, committed, snapshot) in 
      if let error = error { 
       print("updateOpenChats: \(error.localizedDescription)") 
      } 
     } 

     self.senderChatRef.runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in 
      var openChatObject = [String: AnyObject]() 

      if currentData.hasChildren() { 
       openChatObject = currentData.value as! [String: AnyObject] 
      } 

      openChatObject["text"] = text 
      openChatObject["timestamp"] = FIRServerValue.timestamp() 
      openChatObject["senderEmail"] = self.receiver.email 
      openChatObject["senderName"] = self.receiver.name 
      openChatObject["senderId"] = self.receiver.uid 
      openChatObject["counter"] = 0 

      // Set value and report transaction success 
      currentData.value = openChatObject 
      return FIRTransactionResult.successWithValue(currentData) 
     }) { (error, committed, snapshot) in 
      if let error = error { 
       print(error.localizedDescription) 
      } 
    } 
} 
+0

Ho lo stesso problema adesso. Qualche soluzione? –

+0

@ MJQZ1347 Anche io ho riscontrato questo problema, hai mai trovato una soluzione? –

risposta

4

Ok, a quanto pare c'è un bug nel Firebase SDK. La richiamata di updateChildValues non viene eseguita a volte, anche se l'aggiornamento ha avuto esito positivo. Ho rimosso il completionBlock e ora funziona perfettamente.

self.ref.updateChildValues(childUpdates) 
    JSQSystemSoundPlayer.jsq_playMessageSentSound() 
    self.finishSendingMessage() 
    self.updateOpenChats(text) 

EDIT: Vedi domanda aggiornato, il problema si verifica ancora.

+0

Si verifica ancora in 3.5.1, molto fastidioso – Markus

Problemi correlati