2016-06-12 9 views
6

私は自分の連絡先から新しいメッセージを受け取るべきチャットアプリを開発中です。この通知メッセージには、未読メッセージの数も含める必要があります。送信者と受信者の両方がこの情報を更新できるので、runTransactionが好ましい。残念ながらそれはうまくいきません。それは "つまらない"と感じ、しばらくしてから作業を開始します。 privateChatsノード(下記参照)は常に最新のメッセージで更新されますが、openChatMessagesノードでは更新されません。Firebase/iOS:runTransactionsが動作しない場合があります

短時間に多数のメッセージが送信された場合、つまり同じメッセージに対してrunTransactionsがあまりにも頻繁に実行されると、これが起こる可能性がありますか?ref

マイデータ構造:

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 

マイコード:

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:バグはまだ発生し、私の最初の答えから私の期待に

反して。私はそれが接続と関係があると思いますか?例えば。接続が良好でない場合、トランザクションを実行するのに時間がかかることがあります。しかし、ルータのすぐ隣に座っているときにも起こることがあります。他のノードはトランザクションに書き込まれますが、トランザクションには書き込まれません。このような状況でアプリを再起動すると、アプリは再び動作を開始します。だから私はフードの下で何かが間違っていると思う。

この問題の解決法を高く評価します。受信者が新しいメッセージについて時々通知されないチャットアプリでは、何もしません。

回避策もあります:カウンターを増やしたいときにトランザクションが実際に必要ですか? text,senderIdまたはtimestampのような他のデータをsetValueで更新できますが、両方のユーザーが同時にサブノードの値を設定しようとすると、データが破損する可能性がありますか?

ここに私の最新のコードです:

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

私は今同じ問題を抱えています。どんな解決策ですか? –

+0

@ MJQZ1347私もこの問題を抱えています。 –

答えて

4

[OK]を、明らかにFirebase SDKのバグがありますが。更新が成功したとしても、updateChildValuesのコールバックが実行されることはありません。私はcompletionBlockを削除しましたが、今は完璧に動作します。

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

EDIT: が更新質問を参照してください、それでも問題が発生します。

+0

まだ3.5.1で発生し、非常に迷惑な – Markus

関連する問題