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)
}
}
}
Ich habe das gleiche Problem gerade jetzt. Irgendwelche Lösungen? –
@ MJQZ1347 Ich habe dieses Problem auch haben Sie jemals eine Lösung gefunden? –