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.
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)
}
}
}
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.