2016-06-12 14 views
6

Ich arbeite an einer Chat-App, in der Benutzer über neue Nachrichten von ihren Kontakten benachrichtigt werden sollen. Diese Benachrichtigung sollte auch die Anzahl der ungelesenen Nachrichten enthalten. Da sowohl der Sender als auch der Empfänger diese Informationen aktualisieren können, wird runTransaction bevorzugt. Leider klappt es manchmal nicht. Es fühlt sich "fest" und beginnt nach einiger Zeit wieder zu arbeiten. Der Knoten privateChats (siehe unten) wird immer mit der letzten Nachricht aktualisiert, nicht jedoch mit dem Knoten openChatMessages.Firebase/iOS: runTransactions funktioniert manchmal nicht

Kann dies passieren, wenn viele Nachrichten in einem kurzen Zeitraum gesendet werden, d. H. runTransactions wird zu oft für denselben ref durchgeführt?

Meine Datenstruktur:

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 

Mein Code:

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:

Entgegen meinen Erwartungen von meiner ersten Antwort ist immer noch der Fehler auftritt. Ich nehme an, es hat etwas mit der Verbindung zu tun? Z.B. wenn es keine gute Verbindung gibt, dauert es manchmal länger, die Transaktion auszuführen? Aber manchmal passiert es auch, wenn ich direkt neben dem Router sitze. Die anderen Knoten werden beschrieben, aber nicht die mit der Transaktion. Nach dem Neustart der App in diesen Situationen beginnt es wieder zu arbeiten. Also ich denke, da ist etwas falsch unter der Haube.

Ich würde sehr zu schätzen Lösungen für dieses Problem. Eine Chat-App, bei der der Empfänger manchmal nicht über neue Nachrichten benachrichtigt wird, ist ein Nein.

Ich bin auch in Ordnung mit Problemumgehungen: Werden Transaktionen tatsächlich benötigt, wenn Sie einen Zähler inkrementieren möchten? Ich könnte die anderen Daten wie text, senderId oder timestamp mit setValue aktualisieren, aber das würde zu beschädigten Daten führen, wenn beide Benutzer versuchen, den Wert von Unterknoten zur gleichen Zeit zu setzen, nicht wahr?

Hier ist mein letzter Code:

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

Ich habe das gleiche Problem gerade jetzt. Irgendwelche Lösungen? –

+0

@ MJQZ1347 Ich habe dieses Problem auch haben Sie jemals eine Lösung gefunden? –

Antwort

4

Ok, anscheinend gibt es einen Fehler in der Firebase SDK. Der Rückruf von updateChildValues wird manchmal nicht ausgeführt, obwohl das Update erfolgreich war. Ich habe die completionBlock entfernt und jetzt funktioniert es einwandfrei.

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

EDIT: aktualisiert Frage Siehe, das Problem weiterhin auftritt.

+0

Tritt immer noch in 3.5.1 auf, sehr nervig – Markus

Verwandte Themen