Home > Software design >  Firebase fetching causes crash for some users
Firebase fetching causes crash for some users

Time:01-19

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()
                    }
                )}
            }
  • Related