swift - Firebase / iOS: runTransactions sometimes doesn't work -


i working on chat app, users should notified new messages contacts. notification message should include number of unread messages. because both sender , receiver can update information runtransaction preferred. unfortunately doesn't work. feels "stuck" , starts working after while again. privatechats node (see below) gets updated latest message, not openchatmessages node.

can happen if many messages sent in short period of time, i.e. runtransactions performed same ref?

my data structure:

privatechats     $userid         $chatid             $messageid                 text                 timestamp                 senderid                 senderemail                 sendername  // node contains information open chats // last message , counter unread messages openchatmessages     $userid         $chatid             text             timestamp             senderid             senderemail             sendername             counter 

my code:

class chatviewcontroller: jsqmessagesviewcontroller {      var user: firuser!     var ref: firdatabasereference!     var chatref: firdatabasereference!     var senderopenchatref: firdatabasereference!     var receiveropenchatref: firdatabasereference!      // following variables 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 receivers openchatobject increasing counter         self.receiveropenchatref.runtransactionblock({ (currentdata: firmutabledata) -> firtransactionresult in              var openchatobject = [string: anyobject]()              // update openchatobject latest information 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 (the sender's) openchatobject setting counter 0         self.senderopenchatref.runtransactionblock({ (currentdata: firmutabledata) -> firtransactionresult in              var openchatobject = [string: anyobject]()              // update openchatobject latest information 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:

contrary expectations first answer bug still occurs. assume has connection? e.g. when there no connection takes longer run transaction? occurs when sit right next router. other nodes written to, not ones transaction. after restarting app in situations starts work again. guess there wrong under hood.

i highly appreciate solutions problem. chat app receiver not notified new messages no go.

i ok workarounds: transactions needed when want increment counter? update other data text, senderid or timestamp setvalue, lead corrupt data, when both users try set value of subnodes @ same time, wouldn't it?

here's latest 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 image 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 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 , 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 , report transaction success             currentdata.value = openchatobject             return firtransactionresult.successwithvalue(currentdata)         }) { (error, committed, snapshot) in             if let error = error {                 print(error.localizeddescription)             }     } } 

ok, apparently there bug in firebase sdk. callback of updatechildvalues doesn't executed sometimes, though update successful. removed completionblock , works flawlessly.

  self.ref.updatechildvalues(childupdates)    jsqsystemsoundplayer.jsq_playmessagesentsound()   self.finishsendingmessage()   self.updateopenchats(text) 

edit: see updated question, problem still occurs.


Comments

Popular posts from this blog

sequelize.js - Sequelize group by with association includes id -

android - Robolectric "INTERNET permission is required" -

java - Android raising EPERM (Operation not permitted) when attempting to send UDP packet after network connection -