Сбой iOS 11 из-за ошибки bundleProxy != nil при использовании UNUserNotificationCenter

В следующей строке кода наше приложение внезапно начало падать на iOS 11/11.0.1/11.0.2/11.1.1/11.2.2 у некоторых пользователей:

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];

У нас есть это в didFinishLaunchingWithOptions. В отчете о сбое говорится:

Fatal Exception: NSInternalInconsistencyException
Invalid parameter not satisfying: bundleProxy != nil

Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x1869b3d38 __exceptionPreprocess
1  libobjc.A.dylib                0x185ec8528 objc_exception_throw
2  CoreFoundation                 0x1869b3c0c +[NSException raise:format:]
3  Foundation                     0x187342c24 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]
4  UserNotifications              0x18fcc973c -[UNUserNotificationCenter initWithBundleProxy:]
5  UserNotifications              0x18fcc950c __53+[UNUserNotificationCenter currentNotificationCenter]_block_invoke
6  libdispatch.dylib              0x186339048 _dispatch_client_callout
7  libdispatch.dylib              0x18633c710 dispatch_once_f$VARIANT$mp
8  UserNotifications              0x18fcc94ac +[UNUserNotificationCenter currentNotificationCenter]

Это явно из iOS. Кто-нибудь еще испытывает ту же ошибку? Есть идеи, что происходит?


person strangetimes    schedule 05.10.2017    source источник
comment
Вы поняли это?   -  person Sam Jarman    schedule 24.10.2017
comment
Нет, все еще вижу загадочные сбои   -  person strangetimes    schedule 26.10.2017
comment
@SamJarman Я так думаю   -  person strangetimes    schedule 29.03.2018


Ответы (4)


Я не уверен, что это сработает для всех, однако я понял это для своего варианта использования. Я создал структуру, которую использовало приложение для iOS. Этот фреймворк использовал UNUserNotificationCenter для настройки предупреждений. По какой-то причине кажется, что «пакет» неправильно инициализируется, когда этот код используется внутри фреймворка. Иногда это работает, иногда нет. Этот bundleProxy, судя по всему, является своего рода прокси, на который опирается фреймворк уведомлений. Поскольку код выполняется внутри фреймворка, возможно, этот пакет не найден во время выполнения, и система возвращает nil. У меня была эта проблема в целом при попытке загрузить ресурсы из Framework, где расположение пакета неверно.

В любом случае, решение для меня состояло в том, чтобы сохранить ссылку на [UNUserNotificationCenter currentNotificationCenter] в делегате приложения при запуске, а затем передать ее любому методу, который хотел ее использовать. К тому времени, когда приложение завершит запуск, этот proxy, кажется, загружается правильно, если вызывающий код является самим двоичным файлом приложения. Кажется, это исправило это для меня.

person strangetimes    schedule 29.03.2018
comment
Я столкнулся с той же проблемой с ошибкой bundleProxy != nil Assertion. Вызов, который вызывает это, - UNUserNotificationCenter.current() в AppDelegate.didFinishLaunching, что, кажется, является тем, что вы предлагаете. Можете ли вы опубликовать свой код, который работает? - person angusc; 23.01.2019
comment
Нет, но, как объяснено, убедитесь, что ваш код не работает внутри фреймворка. Если это так, вам нужно передать ссылку от делегата вашего приложения. - person strangetimes; 23.01.2019
comment
Это в рамках, и в делегате приложения я пытался получить UNUserNotificationCenter.current(), но это не удалось. - person angusc; 23.01.2019

Почему [UNUserNotificationCenter currentNotificationCenter] иногда дает сбой?

Согласно трассировке стека сбоя, bundleIdentifier равен нулю в частном методе инициализации UNUserNotificationCenter, и возникает исключение. Я не знаю почему.

К сожалению, метод вызывается в контексте dispatch_once, поэтому мы не можем легко воспроизвести этот сбой. Во-первых, я попытался использовать метод NSBundle: NSBundle.mainBundle.bundleIdentifier, но это не удалось. Я предполагаю, что система не использовала этот метод для получения bundleIdentifier, поэтому я попытался использовать частный метод инициализации UNUserNotificationCenter initWithBundleIdentifier:(String), он работал и пытался передать nil этому методу, что вызывало сбой в 100% случаев!!!! Таким образом, мы можем использовать этот метод при загрузке файла и возвращать nil, если bundleIdentifier==nil, надеюсь, это вам поможет.

посмотрите на картинку, чтобы узнать, как это работает

---------------- код ------------------

    /* UNUserNotificationCenter + Hack */
@implementation UNUserNotificationCenter (Hack)

+ (void)load {
    static dispatch_once_t _onceToken;
    dispatch_once(&_onceToken, ^{
        [self safeHook];
    });
}

+ (void)safeHook {

    /*hook UNUserNotificationCenter's systemMethod initWithBundleIdentifier:*/
    /* private method mix,hope no runtime check ????*/
    NSString * orig_initWithBundleSelectorName = [NSString stringWithFormat:@"%@%@%@",@"initWi",@"thBundleId",@"entifier:"];

    SEL orig_initWithBundleSelector = NSSelectorFromString(orig_initWithBundleSelectorName);

    if (![self instancesRespondToSelector:orig_initWithBundleSelector]) {
        return;
    }

    SEL alt_initWithBundleSelector = @selector(ht_initWithBundleIdentifier:);
    Method origMethod = class_getInstanceMethod(self, orig_initWithBundleSelector);
    Method altMethod = class_getInstanceMethod(self, @selector(ht_initWithBundleIdentifier:));

    class_addMethod(self,
                    orig_initWithBundleSelector,
                    class_getMethodImplementation(self, orig_initWithBundleSelector),
                    method_getTypeEncoding(origMethod));
    class_addMethod(self,
                    alt_initWithBundleSelector,
                    class_getMethodImplementation(self, alt_initWithBundleSelector),
                    method_getTypeEncoding(altMethod));

    method_exchangeImplementations(origMethod, altMethod);
}

- (instancetype)ht_initWithBundleIdentifier:(id)identifier {

    if (nil==identifier||NSNull.null==identifier) {
        return nil;
    }
    /* you can test, if give nil to this method ,100% crash!!!!*/
    /* [self ht_initWithBundleIdentifier:nil] 100% crash!!!!*/
    return [self ht_initWithBundleIdentifier:identifier];
}

@end
person imqiuhang    schedule 18.10.2018
comment
Это отличное решение, но оно не решает проблему полностью. После использования этого метода некоторые из моих пользователей по-прежнему будут время от времени сталкиваться со сбоями. Проблема сбоя: Invalid parameter not satisfying: bundleProxy != nil - person Daemonson Dong; 12.06.2019

OneSignal тоже столкнулся с этим. Их исправление состояло в том, чтобы проверить имя текущего процесса и, если оно содержит IBDesignable, вернуться раньше. Это сработало и для меня.

проблема GitHub

Исправить фиксацию

person Robbie Trencheny    schedule 23.04.2019

Для тех, кто здесь для использования PacketTunnelProvider NetworkExtension, это может произойти, если вы вызовете его из stopTunnelWithReason. Вместо этого получите currentNotificationCenter раньше и сохраните его.

person Sai Manoj Kumar Yadlapati    schedule 27.04.2021