Кнопка действия iOS UILocalNotification дает сбой и повреждает данные приложения

Было выпущено незначительное обновление нашего приложения, эта версия также изменила идентификатор приложения с уникального идентификатора на идентификатор нашей команды службой поддержки Apple.

Согласно Apple, переход на использование Team ID должен только сбросить доступ к связке ключей, мы не используем связку ключей, поэтому это не должно влиять на наше приложение.

Но с тех пор как обновление было выпущено, некоторые наши рабочие пользователи столкнулись с повреждением данных своих приложений. Это происходит только тогда, когда они реагируют на локальное уведомление с помощью кнопки действия в Центре уведомлений или на экране блокировки. Это может произойти на всех поддерживаемых версиях iOS.

У пользователей, сталкивающихся с этим, есть еще один симптом: они получают два локальных уведомления вместо одного, но приложение видит только одно уведомление. Кроме того, эти уведомления по-прежнему не исчезнут после отключения уведомлений и отмены с помощью [[UIApplication sharedApplication] cancelAllLocalNotifications].

Эти повторяющиеся уведомления были запланированы до того, как приложение было обновлено из App Store, но после обновления приложение потеряло контроль над ними для некоторых пользователей. Эта проблема подробно описана в этом вопрос.

Самая большая подсказка может заключаться в отчетах о сбоях, полученных от 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)

Трассировка стека точно такая же, как в этом вопросе, что происходит, когда пользователи отвечают на push-уведомление. Сбой во время загрузки приложения может объяснить повреждение данных приложения.

Как видно из трассировки стека, этот сбой не вызван нашим кодом, мы ничего не меняли в коде, он работал хорошо до изменения идентификатора приложения, и это происходит примерно с 2% наших пользователей.

Вот код, который обрабатывает кнопки действий уведомлений:

- (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();
    }
}

Что вызывает этот сбой?

(Я знаю, что он брошен в -[UIApplication _runWithMainScene:transitionContext:completion:] + 2928 (UIApplication.m:3299), вопрос в том, что там вызывает сбой.)


person Kof    schedule 28.10.2015    source источник
comment
может это вопрос быть полезным? Существует та же трассировка стека для сбоя, что и в вашем вопросе. Возможно ли, что ваше приложение перенаправляет пользователя на какой-то контроллер представления после того, как он откроет приложение через push-уведомление, но открытие этого контроллера выполняется с помощью [window addSubview:viewController.view]; вместо [window setRootViewController:viewController];? Это не решает проблему с двумя уведомлениями, но, возможно, это обе ошибки/обновления Xcode 7.   -  person medvedNick    schedule 03.11.2015
comment
Просто предложение. Проверьте все Observer для уведомления. Наверняка вызов removeObserver: в dealloc этих объектов Observer. И stackoverflow.com /вопросы/26371462/   -  person tuledev    schedule 03.11.2015
comment
@anhtu это не такое уведомление, мы говорим о локальных уведомлениях, наблюдатели в этом отношении не использовались.   -  person Kof    schedule 04.11.2015


Ответы (3)


Возможно, это исключение вообще не связано с реакцией на уведомление с помощью кнопки действия. Эта трассировка стека указывает на проблему при загрузке приложения, но имеет несколько возможных причин. Все, что приводит к сбою приложения во время загрузки, приведет к этому.

Например, это может произойти, когда приложение загружается, а его окну не назначен корневой контроллер представления. Также может произойти, если он был добавлен с использованием addSubview вместо setRootViewController, как показано здесь.

Вам нужно будет проверить у пользователей, которые столкнулись с этим сбоем, что они делали, действительно ли они ответили только на локальную кнопку действия уведомления или они столкнулись с сбоем при открытии приложения.

person gala    schedule 04.11.2015

Причиной повреждения данных была опция "Защита данных". Его можно включить в Xcode или на портале разработчиков iOS в разделе «Идентификаторы» -> «Идентификаторы приложений».

Вариант защиты данных Xcode

Он имеет три состояния: «Полная защита», «Защищено, если не открыто» и «Защищено до аутентификации первого пользователя». Пока данные защищены, даже приложение не может получить к ним доступ.

Портал разработчиков iOS

Например, если была выбрана «Полная защита», как в нашем случае, и запущен фоновый процесс, такой как кнопка действия уведомления, Core Data не будет иметь доступа к базе данных из-за ошибки разрешения.

Я до сих пор не уверен, почему он испортил данные, иногда ошибка «Разрешение определено» не отображалось, но все равно не удавалось прочитать файл SQLite. Возможно, это также было написано в файле, но поскольку шифрование было включено, оно испортило файл.

Решение состоит в том, чтобы выбрать «Защищено до аутентификации первого пользователя» или отключить защиту данных.

Связанный вопрос SO: ошибки/исключения CoreData, когда приложение работает в фоновом режиме

PDF-файл Apple по безопасности iOS

person Kof    schedule 07.11.2015

Эта строка вызывает сбой:

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

Если команда изменяет приложение, уникальный идентификатор этого приложения также изменяется, поскольку оно является его частью. Локальные уведомления используют уникальный идентификатор приложения. Так что это действительно ошибка Apple, поскольку iOS должна распознавать одно уведомление, для которого больше нет соответствующего приложения. И это также ваш сбой, поскольку вы запустились, но не получили объект, с которым обычно работали.

Слепое предположение, так как я не знаю вашего кода. При ответе на локальное уведомление перед продолжением проверьте наличие объекта и всех связанных с ним данных. Вы знаете, что-то вроде:

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

(намного лучше, чем исключения, имхо...)

Что касается уведомлений, которые вам больше не принадлежат - пользователи могут удалить их сами. Просто убедитесь, что вы не разбились - ничего не делать нормально. Обычно люди удаляют их сами в какой-то момент, а затем очищают свой центр уведомлений.

person Helge Becker    schedule 30.10.2015
comment
Это в основном повторяет выводы исходного вопроса, в других частях я даже не уверен, как это связано. "it is basically indeed a bug by Apple" - на основании чего? - person Kof; 30.10.2015
comment
Я вижу, ты не установил связь. 1) Это ошибка Apple, потому что есть уведомление для приложения, которое не относится к команде. 2) Это тоже ошибка в вашем коде, поскольку вы где-то не обрабатываете nil. Вот почему вы видите прыгающее утверждение. Вы полагаетесь в своем коде, что что-то есть, но это не так. Вероятно, уведомление принадлежит вашему приложению, а не вашей команде. - person Helge Becker; 31.10.2015
comment
Нет, все объекты проверяются на ноль перед использованием. Он даже не доходит до кода приложения, 0xc4972 main + 22 (main.m:14) не мой код, он только инициирует UIApplicationMain, который является классом iOS, который запускает приложение, где-то в пути происходит сбой, прежде чем добраться до кода моего приложения. - person Kof; 31.10.2015
comment
У вас есть нулевая ссылка, иначе утверждение не сработает. Где, без кода сложно сказать. фиксация гл. - person Helge Becker; 02.11.2015
comment
Ни один корневой контроллер представления не является нулевым. В любом случае рад, что вы нашли свою ошибку - person Helge Becker; 22.11.2015
comment
Да, но rootViewController не имеет ничего общего с уведомлениями, и это не ошибка Apple. - person Kof; 22.11.2015