When I try to fetch user data from Firebase a crash occurs for some users, I can't reproduce this crash myself but I do have the following crash log:
0 Ski Tracker 0x77bf0 closure #1 in HistoryPresenter.downloadHistory(completionHandler:) 4340857840 (HistoryPresenter.swift:4340857840)
1 Ski Tracker 0x86c8 closure #1 in FetchFromDatabase.fetchUserHistoryFromDatabase(uid:completionHandler:) 4340401864 (<compiler-generated>:4340401864)
2 Ski Tracker 0x8604 thunk for @escaping @callee_guaranteed (@guaranteed FIRDataSnapshot) -> () 4340401668 (<compiler-generated>:4340401668)
3 FirebaseDatabase 0x1df28 __92-[FIRDatabaseQuery observeSingleEventOfType:andPreviousSiblingKeyWithBlock:withCancelBlock:]_block_invoke 120
4 FirebaseDatabase 0xbf94 __43-[FChildEventRegistration fireEvent:queue:]_block_invoke.11 80
5 libdispatch.dylib 0x24b4 _dispatch_call_block_and_release 32
6 libdispatch.dylib 0x3fdc _dispatch_client_callout 20
7 libdispatch.dylib 0x127f4 _dispatch_main_queue_drain 928
8 libdispatch.dylib 0x12444 _dispatch_main_queue_callback_4CF 44
9 CoreFoundation 0x9a6f8 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ 16
10 CoreFoundation 0x7c058 __CFRunLoopRun 2036
11 CoreFoundation 0x80ed4 CFRunLoopRunSpecific 612
12 GraphicsServices 0x1368 GSEventRunModal 164
13 UIKitCore 0x3a23d0 -[UIApplication _run] 888
14 UIKitCore 0x3a2034 UIApplicationMain 340
15 libswiftUIKit.dylib 0x35308 UIApplicationMain(_:_:_:_:) 104
16 Ski Tracker 0x7160 main 4340396384 (FriendView.swift:4340396384)
17 ??? 0x1f6938960 (Missing)
If I understand the crash log correctly the code which is causing the crash is within the fetchUserHistoryFromDatabase function:
func fetchUserHistoryFromDatabase(uid : String, completionHandler: @escaping([String : Any]?) -> Void ) {
ref?.child("users").child(uid).child("runData").observeSingleEvent(of: .value, with: { snapshot in
guard let result = snapshot.value as? [String:Any] else {
print("Error no rundata")
completionHandler(nil)
return
}
completionHandler(result)
})
}
This function is called from downloadHistory where potential nil values are handled:
private func downloadHistory(completionHandler: @escaping () -> Void) {
if let id = Auth.auth().currentUser?.uid {
FetchFromDatabase().fetchUserHistoryFromDatabase(uid : id, completionHandler: { [weak self] dict in
if dict != nil {
for run in dict! {
self?.determineTimeStamp(run : run)
}
if !(self!.tempDict.isEmpty) {
let sortedDict = self?.tempDict.keys.sorted(by: { $0 > $1 } )
self?.convertDictToArray(sortedDict: sortedDict!)
}
}
completionHandler()
}
)}
}
Any help here is greatly appreciated.
CodePudding user response:
Remove the force unwrapping from your code. Every !
is an invitation for a crash.
private func downloadHistory(completionHandler: @escaping () -> Void) {
if let id = Auth.auth().currentUser?.uid {
FetchFromDatabase().fetchUserHistoryFromDatabase(uid : id, completionHandler: { [weak self] dict in
guard let self = self else {
completion()
return
}
if let dict = dict {
for run in dict {
self.determineTimeStamp(run : run)
}
if !self.tempDict.isEmpty {
let sortedDict = self.tempDict.keys.sorted(by: { $0 > $1 } )
self.convertDictToArray(sortedDict: sortedDict)
}
}
completionHandler()
}
)}
}
CodePudding user response:
I notice a self!
there dangerous, because a user could leave the calling context of the function and since the closure has a capture list of weak self, it should return nil but you are forcing it
try this
private func downloadHistory(completionHandler: @escaping () -> Void) {
if let id = Auth.auth().currentUser?.uid {
FetchFromDatabase().fetchUserHistoryFromDatabase(uid : id, completionHandler: { [weak self] dict in
guard let self = self else { completionHandler()
return }
if let safeDict = dict {
for run in dict {
self.determineTimeStamp(run : run)
}
if (self.tempDict.isEmpty) {
let sortedDict = self.tempDict.keys.sorted(by: { $0 > $1 } )
self.convertDictToArray(sortedDict: sortedDict)
}
}
completionHandler()
}
)}
}