8

Minor update to our app was released, this version had also changed its App ID from a unique ID to our Team ID by Apple support.

According to Apple, changing to the use of Team ID should only reset keychain access, we don't use the keychain so it shouldn't have any effect on our app.

But since the update was released, some of our users on production have been experiencing corruption of their app data. This happens only when they respond to a local notification using an action button from the Notification Center or lock screen. It may happen on all iOS versions supported.

Users experiencing this have another symptom, they receive two local notifications instead of just one, but the app only see one notification. Further more, these notifications still won't go away after disabling notifications and cancellation using [[UIApplication sharedApplication] cancelAllLocalNotifications].

These duplicate notification were scheduled before the app was updated from the App Store, but after update app lost control over them for some users. This issue is described in detail in this question.

The biggest clue may lay in crash reports received from Apple -

Incident Identifier: ED0E9C.............74B38C
CrashReporter Key:   ae05b............dbc46
Hardware Model:      iPhone4,1
Process:             MY_APP [444]
Path:                /private/var/mobile/Containers/Bundle/Application/65324..................8616/MY_APP.app/MY_APP
Identifier:          com.mycompany.myapp
Version:             X.X
Code Type:           ARM (Native)
Parent Process:      launchd [1]

Date/Time:           2015-10-27 21:45:24.24 -0500
Launch Time:         2015-10-27 21:45:20.20 -0500
OS Version:          iOS 9.1 (13B143)
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                  0x244b3676 __exceptionPreprocess + 122 (NSException.m:162)
1   libobjc.A.dylib                 0x3582ee12 objc_exception_throw + 34 (objc-exception.mm:531)
2   CoreFoundation                  0x244b354c +[NSException raise:format:arguments:] + 100 (NSException.m:131)
3   Foundation                      0x25240bc4 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 88 (NSException.m:152)
4   UIKit                           0x28840754 -[UIApplication _runWithMainScene:transitionContext:completion:] + 2928 (UIApplication.m:3299)
5   UIKit                           0x28853a48 __84-[UIApplication _handleApplicationActivationWithScene:transitionContext:completion:]_block_invoke3218 + 32 (UIApplication.m:11920)
6   UIKit                           0x2883d71e -[UIApplication workspaceDidEndTransaction:] + 130 (UIApplication.m:2648)
7   FrontBoardServices              0x2c52dca2 -[FBSSerialQueue _performNext] + 226 (FBSSerialQueue.m:157)
8   FrontBoardServices              0x2c52df94 -[FBSSerialQueue _performNextFromRunLoopSource] + 44 (FBSSerialQueue.m:204)
9   CoreFoundation                  0x24476bfa __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 10 (CFRunLoop.c:1761)
10  CoreFoundation                  0x244767e8 __CFRunLoopDoSources0 + 448 (CFRunLoop.c:1807)
11  CoreFoundation                  0x24474b56 __CFRunLoopRun + 790 (CFRunLoop.c:2536)
12  CoreFoundation                  0x243c8114 CFRunLoopRunSpecific + 516 (CFRunLoop.c:2814)
13  CoreFoundation                  0x243c7f00 CFRunLoopRunInMode + 104 (CFRunLoop.c:2844)
14  UIKit                           0x28610208 -[UIApplication _run] + 520 (UIApplication.m:2489)
15  UIKit                           0x2860af10 UIApplicationMain + 140 (UIApplication.m:3665)
16  MY_APP                          0xc4972 main + 22 (main.m:14)
17  libdyld.dylib                   0x35f9d86e tlv_get_addr + 42 (threadLocalHelpers.s:310)

It has a stacktrace exactly the same as in this question, which happens when users respond to a push notification. Crash during app load may explain the corrupted app data.

As seen in the stacktrace this crash is not caused by our code, we haven't changed anything in the code, it has been working well before the App ID change and it happens to ~2% of our users.

Here's the code that handles action buttons of notifications -

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {

    @try {
        if (notification) {
            NSDate *alarmTime = notification.userInfo ? [notification.userInfo objectForKey:@"time"] : nil;
            [logic saveAlarmTime:alarmTime takenAt:[NSDate date]];
        }
    }
    @catch (NSException *exception) {
        NSLog(@"Exception: %@", [exception description]);
    }
    @finally {
        completionHandler();
    }
}

What causes this crash?

(I know that it's thrown in -[UIApplication _runWithMainScene:transitionContext:completion:] + 2928 (UIApplication.m:3299), question is what's in there that causes this to crash.)

Community
  • 1
  • 1
Kof
  • 23,893
  • 9
  • 56
  • 81
  • may [this question](http://stackoverflow.com/questions/30884896/application-windows-are-expected-to-have-a-root-view-controller-at-the-end-of-a) be helpful? There is the same stack trace for the crash as in your question. Is it possible that your app forwards the user to some view controller after he open the app through the push notification, but opening this controller is performed with `[window addSubview:viewController.view];` instead of `[window setRootViewController:viewController];`? This does not solves two-notification issue, but maybe these are both Xcode 7 bugs/updates – medvedNick Nov 03 '15 at 02:45
  • Just a suggestion. Check all Observer for the notification. For sure calling `removeObserver:` in `dealloc` of these Observer objects. And http://stackoverflow.com/questions/26371462/weird-crash-when-launching-app-from-notification-center – tuledev Nov 03 '15 at 04:51
  • @anhtu that's not this kind of notification, we're talking about Local Notifications, observers were not used for that matter. – Kof Nov 04 '15 at 07:50

3 Answers3

4

It is possible that this exception is not related to responding to a notification through an action button in the first place. This stack trace indicates a problem while loading the app, but has several possible causes, anything that crashes the app while loading will cause this.

For instance, it may occur when the app loads and its window doesn't have a root view controller assigned. May also happen if it was added using addSubview instead of setRootViewController, as seen here.

You'll have to verify with users that experienced this crash what they were doing, if indeed they've only responded to a local notification action button, or they have experienced the crash while opening the app.

Community
  • 1
  • 1
gala
  • 556
  • 2
  • 3
0

The cause for data corruption was the "Data protection" option. It can be enabled from Xcode, or from iOS developer portal, under Identifiers -> App IDs.

Xcode data protection option

It has three states: "Complete Protection", "Protected Unless Open" and "Protected Until First User Authentication". As long as data is protected, not even the app can access the data.

iOS Developer Portal

For example, if "Complete Protection" was selected as in our case, and a background process like notification's action button is running, Core Data won't have access the database due to permission error.

I'm still not sure why it corrupted the data, sometimes the "Permission defined" error wouldn't show but it would still fail to read the SQLite file. Maybe it also written on the file, but since encryption was ON it corrupted the file.

Solution is to choose either "Protected Until First User Authentication" or to disable data protection.

Related SO question: CoreData errors/exceptions when app work in background mode

Apple's iOS Security PDF

Community
  • 1
  • 1
Kof
  • 23,893
  • 9
  • 56
  • 81
-1

This line causes the crash:

[UIApplication _runWithMainScene:transitionContext:completion:] + 2928 (UIApplication.m:3299)

If a team changes for an app, the unique app identifier for that app changes too since it is part of it. Local notifications use the unique app identifier. So it is basically indeed a bug by Apple, since iOS should recognize the one notification that has no more the corresponding app. And that is also your crash since you get launched but you don't get the object you would normally work with.

Blind guess since I don't know your code. When responding to a local noticication check if the object and all associated data is there before continue. You know, something like:

if (object) {
    //do the stuff
} else { 
    // do nothing
}

(way better than exceptions imho...)

As for the notification that no longer belongs to you - the users can deleted them themselves. Just make sure you don't crash - doing nothing is fine. Usually ppl will deleted them by themselves at some point then they clean up their notification center.

Helge Becker
  • 3,219
  • 1
  • 20
  • 33
  • 1
    This basically repeats conclusions in the original question, on other parts I'm not even sure how it's related. `"it is basically indeed a bug by Apple"` - based on what? – Kof Oct 30 '15 at 11:47
  • I see u didn't make the connection. 1)It is an apple bug because there is a notification for the app that doesn't being to the team. 2) Is is a bug on your code too since you don't handle a nil somewhere. Thats why you see the assertion jumping in. You rely in your code that something is there, but it isn't. Probably the notification that belongs to your app, but not to your team. – Helge Becker Oct 31 '15 at 11:05
  • Nope, all objects are being checked for nil before used. It doesn't even get to the app's code, `0xc4972 main + 22 (main.m:14)` isn't my code, it only initiates `UIApplicationMain` which is iOS's class that runs the app, it crashes somewhere on the way, before getting to my app's code. – Kof Oct 31 '15 at 13:10
  • You have a nil reference, else the assertion wouldn't fire. Where, hard to tell without the code. gl fixing. – Helge Becker Nov 02 '15 at 09:32
  • No root view controler is a nil. Anyhow glad you found your bug – Helge Becker Nov 22 '15 at 11:13
  • Yes, but rootViewController has nothing to do with notifications, and it wasn't a bug by Apple. – Kof Nov 22 '15 at 16:18