swiftxcodeuikitcrash-reports

Unknown cause of crash, Xcode points to unknown function: Array._getElement(_:wasNativeTypeChecked:matchingSubscriptCheck:) + 12 (AppDelegate.swift:0)


I am investigating a crash reported through Xcode crash logs for my app that's live on the app store. In Xcode it points to line #5 with that has the line Array.getElement(:wasNativeTypeChecked:matchingSubscriptCheck:) + 12 (AppDelegate.swift:0). I can't find any information on this function. Because of the lines around it, I have investigated the App Delegate, but nothing is popping out to me as an issue. I am at a loss with how to dive into solving this bug. The main thing I gather is that it occurs when the app receives a remote notification.

Any thoughts on how to investigate this issue or what could be happening would be appreciated.

Here is the crash log

<!-- language: none -->
Incident Identifier: 3406EE83-04DB-4B61-B2E5-758F04A63614
Hardware Model:      iPhone13,4
Process:             FitSW [5443]
Path:                /private/var/containers/Bundle/Application/4ECB1953-2547-4511-BA9B-D1BDFA9B8155/FitSW.app/FitSW
Identifier:          FitSW
Version:             3.97 (1)
AppStoreTools:       14C17
AppVariant:          1:iPhone13,4:15
Code Type:           ARM-64 (Native)
Role:                Foreground
Parent Process:      launchd [1]
Coalition:           FitSW [1058]

Date/Time:           2023-01-13 19:13:15.6975 +0300
Launch Time:         2023-01-13 18:36:32.1730 +0300
OS Version:          iPhone OS 15.1.1 (19B81)
Release Type:        User
Baseband Version:    2.11.04
Report Version:      104

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  0

Last Exception Backtrace:
0   CoreFoundation                  0x18112504c __exceptionPreprocess + 220 (NSException.m:200)
1   libobjc.A.dylib                 0x199799f54 objc_exception_throw + 60 (objc-exception.mm:565)
2   CoreFoundation                  0x1811acc98 -[__NSArray0 objectAtIndex:] + 112 (CFArray.c:103)
3   libswiftCore.dylib              0x185ada7ac _CocoaArrayWrapper.subscript.getter + 36 (CocoaArray.swift:59)
4   FitSW                           0x102d545b4 specialized _ArrayBuffer._getElementSlowPath(_:) + 208
5   FitSW                           0x102b451d8 specialized Array._getElement(_:wasNativeTypeChecked:matchingSubscriptCheck:) + 12 (AppDelegate.swift:0)
6   FitSW                           0x102b451d8 specialized Array.subscript.getter + 12 (AppDelegate.swift:210)
7   FitSW                           0x102b451d8 specialized AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:) + 4840
8   FitSW                           0x102b428d8 specialized AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:) + 16 (<compiler-generated>:0)
9   FitSW                           0x102b428d8 @objc AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:) + 144
10  GoogleUtilities                 0x104b2f504 -[GULAppDelegateSwizzler application:donor_didReceiveRemoteNotification:fetchCompletionHandler:] + 432 (GULAppDelegateSwizzler.m:915)
11  UIKitCore                       0x183772464 -[UIApplication _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion:] + 8016 (UIApplication.m:10900)
12  UIKitCore                       0x183712d50 -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:] + 496 (UIScene.m:1492)
13  UIKitCore                       0x183756524 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:] + 288 (UIScene.m:1774)
14  UIKitCore                       0x1836e0370 -[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:] + 492 (UIApplicationSceneClientAgent.m:80)
15  FrontBoardServices              0x192c00100 -[FBSScene updater:didUpdateSettings:withDiff:transitionContext:completion:] + 528 (FBSScene.m:549)
16  FrontBoardServices              0x192c18d4c __94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke_2 + 152 (FBSWorkspaceScenesClient.m:581)
17  FrontBoardServices              0x192bfd6b4 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 240 (FBSWorkspace.m:352)
18  FrontBoardServices              0x192c03b10 __94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke + 396 (FBSWorkspaceScenesClient.m:580)
19  libdispatch.dylib               0x180d97660 _dispatch_client_callout + 20 (object.m:560)
20  libdispatch.dylib               0x180d9b118 _dispatch_block_invoke_direct + 264 (queue.c:489)
21  FrontBoardServices              0x192bfef94 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 48 (FBSSerialQueue.m:157)
22  FrontBoardServices              0x192bfe3d4 -[FBSSerialQueue _targetQueue_performNextIfPossible] + 220 (FBSSerialQueue.m:181)
23  FrontBoardServices              0x192c029e4 -[FBSSerialQueue _performNextFromRunLoopSource] + 28 (FBSSerialQueue.m:194)
24  CoreFoundation                  0x181147020 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (CFRunLoop.c:1972)
25  CoreFoundation                  0x181157ce0 __CFRunLoopDoSource0 + 208 (CFRunLoop.c:2016)
26  CoreFoundation                  0x181091fe8 __CFRunLoopDoSources0 + 268 (CFRunLoop.c:2053)
27  CoreFoundation                  0x1810977f4 __CFRunLoopRun + 820 (CFRunLoop.c:2951)
28  CoreFoundation                  0x1810ab3b8 CFRunLoopRunSpecific + 600 (CFRunLoop.c:3268)
29  GraphicsServices                0x19ca3b38c GSEventRunModal + 164 (GSEvent.c:2200)
30  UIKitCore                       0x183a4b6a8 -[UIApplication _run] + 1100 (UIApplication.m:3493)
31  UIKitCore                       0x1837ca7f4 UIApplicationMain + 2092 (UIApplication.m:5046)
32  FitSW                           0x1025a0afc main + 68 (AppDelegate.swift:22)
33  dyld                            0x10348da24 start + 520 (dyldMain.cpp:876)

Thread 0 name:
Thread 0 Crashed:
0   libsystem_kernel.dylib          0x00000001b80ca964 __pthread_kill + 8 (:-1)
1   libsystem_pthread.dylib         0x00000001f1cb4378 pthread_kill + 268 (pthread.c:1610)
2   libsystem_c.dylib               0x000000018bf62f50 abort + 164 (abort.c:118)
3   libc++abi.dylib                 0x00000001998a1bc4 abort_message + 132 (abort_message.cpp:78)
4   libc++abi.dylib                 0x0000000199892fd8 demangling_terminate_handler() + 332 (cxa_default_handlers.cpp:71)
5   libobjc.A.dylib                 0x00000001997a0064 _objc_terminate() + 144 (objc-exception.mm:701)
6   libc++abi.dylib                 0x00000001998a0f58 std::__terminate(void (*)()) + 20 (cxa_handlers.cpp:59)
7   libc++abi.dylib                 0x00000001998a0ef4 std::terminate() + 64 (cxa_handlers.cpp:88)
8   libdispatch.dylib               0x0000000180d97674 _dispatch_client_callout + 40 (object.m:563)
9   libdispatch.dylib               0x0000000180d9b118 _dispatch_block_invoke_direct + 264 (queue.c:489)
10  FrontBoardServices              0x0000000192bfef94 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 48 (FBSSerialQueue.m:157)
11  FrontBoardServices              0x0000000192bfe3d4 -[FBSSerialQueue _targetQueue_performNextIfPossible] + 220 (FBSSerialQueue.m:181)
12  FrontBoardServices              0x0000000192c029e4 -[FBSSerialQueue _performNextFromRunLoopSource] + 28 (FBSSerialQueue.m:194)
13  CoreFoundation                  0x0000000181147020 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (CFRunLoop.c:1972)
14  CoreFoundation                  0x0000000181157ce0 __CFRunLoopDoSource0 + 208 (CFRunLoop.c:2016)
15  CoreFoundation                  0x0000000181091fe8 __CFRunLoopDoSources0 + 268 (CFRunLoop.c:2053)
16  CoreFoundation                  0x00000001810977f4 __CFRunLoopRun + 820 (CFRunLoop.c:2951)
17  CoreFoundation                  0x00000001810ab3b8 CFRunLoopRunSpecific + 600 (CFRunLoop.c:3268)
18  GraphicsServices                0x000000019ca3b38c GSEventRunModal + 164 (GSEvent.c:2200)
19  UIKitCore                       0x0000000183a4b6a8 -[UIApplication _run] + 1100 (UIApplication.m:3493)
20  UIKitCore                       0x00000001837ca7f4 UIApplicationMain + 2092 (UIApplication.m:5046)
21  FitSW                           0x00000001025a0afc main + 68 (AppDelegate.swift:22)
22  dyld                            0x000000010348da24 start + 520 (dyldMain.cpp:876)

Here is the function in question Line 6 on the crash log highlights the line that follows the MARK

 func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

        if let messageID = userInfo["google.c.a.e"] {
            print("Message ID: \(messageID)")
        }
        
        if let newMessageFlag = userInfo["newMessageFlag"] as? String {
            let newMessageFlagInt = (Int(newMessageFlag) ?? 0)
            UserDefaults.standard.set(newMessageFlag, forKey: "newMessageFlag")
            var newNotificationsInt = 0
            if let newNotifications = UserDefaults.standard.value(forKey: "newNotificationFlag") as? String {
                newNotificationsInt = Int(newNotifications) ?? 0
            }
            UIApplication.shared.applicationIconBadgeNumber = newMessageFlagInt + newNotificationsInt
            //            guard let tabBarController = window?.rootViewController as? UITabBarController else { return }
            
            let applicationState = UIApplication.shared.applicationState
            
            if applicationState != .active {
                
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    guard let tabBarController = self.window?.rootViewController as? MainStartingTabbarViewController else { return }
                    let navigationController = tabBarController.viewControllers![4] as? UINavigationController // Regardless of the other custom app versions & the user type, the more menu will be the fifth tab
                    navigationController?.tabBarItem.badgeValue = String(newMessageFlagInt + newNotificationsInt)
                    var moreTab = navigationController?.viewControllers[0] as? MoreMenuTableViewController
                    moreTab?.messagesFlagInt = newMessageFlagInt
                    tabBarController.selectedViewController = tabBarController.viewControllers![4]
                    let numberOfTabs = tabBarController.viewControllers
                    let isGroup = userInfo["is_private"] as? String == "0"
                    let roomIDInt = Int(userInfo["room_id"] as? String ?? "") ?? 0
                    moreTab?.openMessages(didSelectGroup: isGroup, isShared: false, roomID: roomIDInt)
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
                }
                
            }
            else {
                guard let tabBarController = self.window?.rootViewController as? MainStartingTabbarViewController else { return }
                if (tabBarController.selectedViewController == tabBarController.viewControllers![4]) {
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
                } else {
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateTrainerButton"), object: nil)
                }
            }
        }
        else if let newNotificationFlag = userInfo["newNotificationFlag"] as? String {
            let newNotificationFlagInt = (Int(newNotificationFlag) ?? 0)
            UserDefaults.standard.set(newNotificationFlag, forKey: "newNotificationFlag")
            var newMessagesInt = 0
            
            //MARK: - This is the line that breaks
            if let newMessages = UserDefaults.standard.value(forKey: "newMessageFlag") as? String {
                newMessagesInt = Int(newMessages) ?? 0
            }
            
            
            UIApplication.shared.applicationIconBadgeNumber = newMessagesInt + newNotificationFlagInt
            guard let tabBarController = self.window?.rootViewController as? UITabBarController else { return }
            let navigationController = tabBarController.viewControllers![4] as? UINavigationController // Regardless of the other custom app versions & the user type, the more menu will be the fifth tab
            navigationController?.tabBarItem.badgeValue = String(newMessagesInt + newNotificationFlagInt)
            let moreTab = navigationController?.viewControllers[0] as? MoreMenuTableViewController
            moreTab?.notificationsFlagInt = newNotificationFlagInt
            
            let applicationState = UIApplication.shared.applicationState
            if applicationState != .active {
                tabBarController.selectedViewController = tabBarController.viewControllers![4]
                moreTab?.openNotifications()
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
            }
            else {
                if (tabBarController.selectedViewController == tabBarController.viewControllers![4]) {
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
                } else {
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateTrainerButton"), object: nil)
                }
            }
        }
        completionHandler(UIBackgroundFetchResult.newData)
}

I have tried to recreate the problem by running the simulator and opening the app when it receives a remote notification without any success in getting the same error.

Edit Solution:

Thank you to @jrturton for such a helpful and insightful answer. This helped me on many different levels. I had been running through "wait for executable to run", but a little more thorough testing did uncover an error, though it still wasn't the one I posted about.

What I had overlooked was the line the crash report was telling me was the issue was on a previous version than the one I was looking at. When I went back to the version, it was in fact the line on code that you pointed out. So I put a dispatchQueue.asyncAfter in to let it load, and placed a guard statement on the count just in case it still doesn't load in time.

Problematic Code:

let moreTab = navigationController?.viewControllers[0] as? MoreMenuTableViewController

Fixed Code:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            UIApplication.shared.applicationIconBadgeNumber = newMessagesInt + newNotificationFlagInt
            guard let tabBarController = self.window?.rootViewController as? UITabBarController,
                  (tabBarController.viewControllers?.count ?? 0) >= 5,
                  let navigationController = tabBarController.viewControllers![4] as? UINavigationController,
                  navigationController.viewControllers.count >= 1 else {
                print("hit error with tabBaar or Nav controller")
                return }
            
            navigationController.tabBarItem.badgeValue = String(newMessagesInt + newNotificationFlagInt)
            let moreTab = navigationController.viewControllers[0] as? MoreMenuTableViewController
            moreTab?.notificationsFlagInt = newNotificationFlagInt
            
            let applicationState = UIApplication.shared.applicationState
            if applicationState != .active {
                tabBarController.selectedViewController = tabBarController.viewControllers![4]
                moreTab?.openNotifications()
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
            }
            else {
                if (tabBarController.selectedViewController == tabBarController.viewControllers![4]) {
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "getMessages"), object: nil)
                } else {
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateTrainerButton"), object: nil)
                }
            }
        }

Solution

  • I don't think the line you've highlighted is the crashing line. The crash log points to an array access, and the line you've highlighted isn't doing that.

    It seems far more likely that the problem is on one of the many times you're accessing tab view controller or navigation view controller child view controllers via index - those are the only array accessors I can see in that function. Lines like this:

    let moreTab = navigationController?.viewControllers[0] as? MoreMenuTableViewController
    

    This line will crash if navigationController exists, but it has no child controller.

    My guess is that this is happening when your app is being launched via the remote notification, not necessarily receiving the notification while it is open. That might mean this code is running before your app has finished launching and loading its view controllers.

    Debugging notification issues can be tricky, but you can help yourself by creating an .apns file on your desktop containing the notification payload (see https://gist.github.com/muhammetkole/22cf3cc8a7b026f2ea2661b8497b773e for an example, you'll need to fill in your own details).

    You can then drop this file on the simulator and it will act as if the notification has been received. You can test this with the app running, or in the background, while it's connected to the debugger.

    To debug the process of launching from a notification, kill the app in the simulator, then edit the scheme / info / launch to "wait for executable to run". Build and "run" the project, then drop the file on the simulator, and it will launch the app and connect the debugger.

    Hopefully one of those scenarios will allow you to reproduce the bug.