2016-06-12 20 views
6

Pracuję nad aplikacją do czatu, na której użytkownicy powinni otrzymywać powiadomienia o nowych wiadomościach od swoich kontaktów. To powiadomienie powinno również zawierać liczbę nieprzeczytanych wiadomości. Ponieważ zarówno nadawca, jak i odbiorca mogą aktualizować te informacje, preferowane jest ustawienie runTransaction. Niestety czasami to nie działa. Czuje się "zablokowany", po czym znów zaczyna działać. Węzeł privateChats (patrz poniżej) jest zawsze aktualizowany najnowszym komunikatem, ale nie węzłem openChatMessages.Firebase/iOS: runTransactions czasami nie działa

Czy tak się stanie, jeśli wiele wiadomości zostanie wysłanych w krótkim czasie, tzn. runTransactions jest wykonywany zbyt często dla tego samego ref?

Moja struktura danych:

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 

Mój kod:

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:

przeciwieństwie do moich oczekiwań z mojej pierwszej odpowiedzi Błąd nadal występuje. Zakładam, że ma coś wspólnego z połączeniem? Na przykład. kiedy nie ma dobrego połączenia, czasami wykonanie transakcji zajmuje więcej czasu? Ale czasami zdarza się to również, gdy siedzę tuż obok routera. Pozostałe węzły są zapisywane, ale nie te z transakcją. Po ponownym uruchomieniu aplikacji w takich sytuacjach zaczyna działać ponownie. Sądzę więc, że coś jest nie tak pod maską.

Bardzo doceniam rozwiązania tego problemu. Aplikacja czatu, w której odbiornik czasami nie otrzymuje powiadomienia o nowych wiadomościach, nie jest już gotowa.

Jestem również w porządku z obejściami: Czy transakcje są rzeczywiście potrzebne, gdy chcesz zwiększyć licznik? Mogę zaktualizować inne dane, takie jak text, senderId lub timestamp z setValue, ale to mogłoby doprowadzić do uszkodzenia danych, gdy obaj użytkownicy próbują ustawić wartość podwęzłów w tym samym czasie, prawda?

Oto mój najnowszy kod:

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

Mam teraz ten sam problem. Jakieś rozwiązania? –

+0

@ MJQZ1347 Mam również ten problem, czy kiedykolwiek znalazłeś rozwiązanie? –

Odpowiedz

4

Ok, widocznie nie jest to błąd w Firebase SDK. Oddzwanianie pod numer updateChildValues nie jest czasami wykonywane, mimo że aktualizacja się powiodła. Usunąłem completionBlock i teraz działa bezbłędnie.

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

EDIT: Zobacz zaktualizowane pytanie, problem nadal występuje.

+0

Nadal występuje w wersji 3.5.1, bardzo denerwujące – Markus

Powiązane problemy