Search code examples
iosswiftcore-datansfetchrequest

Crash when fetching with CoreData


My app is in production and I've been getting a few crashes on Crashlytics when trying to cast the first element of a festRequest to a certain entity. I've not been able to recreate this crash no matter how hard I try.

static func getSettings() -> NotificationSettingsMO {
  var settings: NotificationSettingsMO!

  let moc = DataController.shared.managedObjectContext

  moc.performAndWait {
    do {
      let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "NotificationSettings")
      settings = try moc.fetch(fetchRequest).first as! NotificationSettingsMO
    } catch {
    print(error)
    }
  }

  return settings

The crash happens when trying to cast the first element of the request to NotificationSettingsMO and I'm sure the entity exists since I create it when the user is created. This also only happens to a small percentage of users, but since it crashes the app I want to try and figure out what is causing it.

EDIT: I've attached the crash log

Crashed: com.apple.main-thread
0  MyApp                        0x100073360 specialized static NotificationSettingsMO.(getSettings() -> NotificationSettingsMO).(closure #1) (NotificationSettingsMO.swift:25)
1  MyApp                        0x10007309c partial apply for static NotificationSettingsMO.(getSettings() -> NotificationSettingsMO).(closure #1) (NotificationSettingsMO.swift)
2  CoreData                       0x18311d08c developerSubmittedBlockToNSManagedObjectContextPerform + 196
3  CoreData                       0x18311cf54 -[NSManagedObjectContext performBlockAndWait:] + 220
4  MyApp                        0x100072fa8 specialized static NotificationSettingsMO.getSettings() -> NotificationSettingsMO (NotificationSettingsMO.swift)
5  MyApp                        0x1000d6190 AppDelegate.getDataOnLaunch() -> () (AppDelegate.swift)
6  MyApp                        0x1000da4bc specialized AppDelegate.application(UIApplication, didFinishLaunchingWithOptions : [UIApplicationLaunchOptionsKey : Any]?) -> Bool (AppDelegate.swift:99)
7  MyApp                        0x1000d3eb8 @objc AppDelegate.application(UIApplication, didFinishLaunchingWithOptions : [UIApplicationLaunchOptionsKey : Any]?) -> Bool (AppDelegate.swift)
8  UIKit                          0x1863f29c0 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 400
9  UIKit                          0x186622184 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 2904
10 UIKit                          0x1866265f0 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1684
11 UIKit                          0x186623764 -[UIApplication workspaceDidEndTransaction:] + 168
12 FrontBoardServices             0x182bbf7ac __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 36
13 FrontBoardServices             0x182bbf618 -[FBSSerialQueue _performNext] + 168
14 FrontBoardServices             0x182bbf9c8 -[FBSSerialQueue _performNextFromRunLoopSource] + 56
15 CoreFoundation                 0x1811d509c __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
16 CoreFoundation                 0x1811d4b30 __CFRunLoopDoSources0 + 540
17 CoreFoundation                 0x1811d2830 __CFRunLoopRun + 724
18 CoreFoundation                 0x1810fcc50 CFRunLoopRunSpecific + 384
19 UIKit                          0x1863eb94c -[UIApplication _run] + 460
20 UIKit                          0x1863e6088 UIApplicationMain + 204
21 MyApp                        0x10001ee18 main (Measurement+Network.swift:26)
22 libdispatch.dylib              0x180c9a8b8 (Missing)

Solution

  • The object does not exists so it crashes. Personally I HATE force casting in swift. The whole point of optionals is to help you manage when something is nil. Putting as! in your code is asking the program to crash. I would suggest removing it and replacing it with an if let. If you are 100% sure that it will never be nil then you should still use an if let and in the else report to crash service that it happened (use Crashlytics recordError - it will show up along with your crashes).

    As for your statement:

    I'm sure the entity exists since I create it when the user is created

    to put it bluntly - you are wrong. Computers are always right and humans are always wrong.

    Here are the possible reasons why:

    • There was an error saving it to core data I suspect that like most programs when you saved the object to core data you did not check for errors - or if you did you didn't really know what to do with the error. If you were smart you would have logged it. The most common error to get is lack of space on the hard disk. If the object wasn't saved in the first place then it wouldn't be there when you fetch it.
    • The object was there but was deleted. If you have anywhere in your code that deletes this object, then consider the possibility that it was deleted before this code ran. Look for multithreading and race conditions.
    • The object wasn't save yet. If you have ANY multithreading in the app you should consider the possibility that there is a race condition.

    Any of these situations would cause crashes in the 0-3% range.