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
Post a Comment