I have created a method to fetch user messages from Firebase, however when leaving DispatchGroup app crashes leading to this error Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
I'm not sure what I'm doing wrong. Please help and explain me.
Code:
public func fetchMessages(for userId: String, completion: @escaping (_ result: Result<([Message], [String: Message]), Error>) -> Void) {
let group = DispatchGroup()
var messages = [Message]()
var messagesDict = [String: Message]()
group.enter()
database.child("user-messages").child(userId).observe(.childAdded, with: { [weak self] snapshot in
let messageId = snapshot.key
let messagesRef = self?.database.child("messages").child(messageId)
messagesRef?.observeSingleEvent(of: .value, with: { snapshot in
if let dict = snapshot.value as? [String: AnyObject] {
let message = Message(dict: dict)
if let chatPartnerId = message.chatPartnerId() {
messagesDict[chatPartnerId] = message
messages = Array(messagesDict.values)
messages.sort { message1, message2 in
guard let timestamp1 = message1.timestamp?.intValue, let timestamp2 = message2.timestamp?.intValue else { return false }
return timestamp1 > timestamp2
}
group.leave() // Crashes
}
}
}, withCancel: nil)
}, withCancel: nil)
group.notify(queue: .main) {
print("Array: \(messages)\nDict: \(messagesDict)")
}
}
It is because you are using observe option. Which probably is notifying you several times. The crash happens due to the fact that you call ‘leave’ without calling ‘enter’ before. I mean you did call ‘enter’. And you do call ‘leave’. But because you observe the completion is probably called more than once. Which will trigger another ‘leave ‘ call while you called the ‘enter’ only once.
You can easily reproduce the crash with this code
I'm not sure if you need the observe functionality - i.e listen for changes on the object.
Generally, I'd recommend using Firestore - the new (not that new) DB by Firebase. Or follow this guide for getting/setting data from Firebase Realtime Database
https://firebase.google.com/docs/database/ios/read-and-writeIf you need the "listening" i.e observe feature, I'm not sure how the usage of DispatchGroup helps with your implementation. You would generally use it when you, for example, release 2 (or more) API calls in parallel and want to gather all the information from them. You would create a DispatfchGroup, call
enteraccording to the number of calls you are releasiong, and callleaveafter you gathered the relevant information.Something like this