iOS 11 crashing with bundleProxy != nil error on using UNUserNotificationCenter
Asked Answered
V

4

14

The following line of code is where our app has suddenly started to crash on iOS 11 / 11.0.1 / 11.0.2 / 11.1.1 / 11.2.2 for some users:

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];

We've got this in didFinishLaunchingWithOptions. The crash report says:

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]

It's coming from iOS clearly. Any one else experiencing the same error? Any idea what's going on?

Valvule answered 5/10, 2017 at 22:0 Comment(3)
Did you figure it out?Smite
Nope, still seeing mysterious crashesValvule
@SamJarman I think soValvule
V
6

I'm not sure if this is going to work for everyone, however I've figured it out for my use case. I had created a framework that the iOS app used. This framework was using UNUserNotificationCenter to setup alerts. It seems for some reason, the 'bundle' does not get correctly initialised when this code is used from inside a framework. Sometimes it works, sometimes it doesn't. This bundleProxy, by the sound of things, is some kind of a proxy that the notification framework relies on. Since the code is executing from inside of a framework, perhaps this bundle is not found at runtime and the system returns nil. I've had this issue in general when trying to load resources from the Framework where the bundle location isn't correct.

Any way, the solution for me was to store a reference to [UNUserNotificationCenter currentNotificationCenter] in the app's delegate at launch, and then pass this to any method that wanted to use it. By the time the app has finished launching, this proxy seems to load correctly if the calling code is the app's binary itself. This seems to have fixed it for me.

Valvule answered 29/3, 2018 at 15:27 Comment(7)
I'm running into the same problem with bundleProxy != nil Assertion failing. The call that's causing it is UNUserNotificationCenter.current() in the AppDelegate.didFinishLaunching, which seems to be what you suggest. Can you post your code that works?Leer
No, but as explained, make sure your code isn’t running inside of a framework. If it is, you need to pass a reference from your app’s delegate.Valvule
It is in a framework, and in the app's delegate is where I was trying to get UNUserNotificationCenter.current(), but that's the call failing.Leer
There is no AppDelegate in the project. I'm getting this error while unit testing a framework/SDK project.Disassembly
@Disassembly then create and initialize it in the unit test setup methods, probably the class setup methodValvule
@Valvule can you please share the code. if you know anyDisassembly
@Disassembly Did you had any chance to find a solution ?. I am also facfing the same issue for my frameworkSpinozism
S
3

Why does [UNUserNotificationCenter currentNotificationCenter] crash sometimes?

According to the crash stack trace, the bundleIdentifier is nil in the UNUserNotificationCenter's private init Method and an exception is raised. I don't know why.

Unfortunately, the method is called in a dispatch_once context, so we can't reproduce this crash easily. First,I tried to use NSBundle's Method: NSBundle.mainBundle.bundleIdentifier, but it failed. I guess the system did not use this Method to get bundleIdentifier, so I tried to use UNUserNotificationCenter's private init Method initWithBundleIdentifier:(String),it worked and tried to pass nil to this method, which caused a crash 100% of the time!!!! So we can use this method when file loads, and return nil if bundleIdentifier==nil, I hope this helps you.

see picture to know how it work

---------------- code -----------------

    /* 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
Starcrossed answered 18/10, 2018 at 11:17 Comment(1)
This solution is great, but it doesn't solve the problem completely. After using this method, some of my users will still occasionally encounter crashes. The crash issue is Invalid parameter not satisfying: bundleProxy != nilBrevet
F
0

OneSignal experienced this as well. Their fix was to inspect the current process name and if it contained IBDesignable in it to return early. It worked for me as well.

GitHub issue

Fix commit

Florez answered 23/4, 2019 at 3:27 Comment(0)
W
0

For those who are here for using PacketTunnelProvider NetworkExtension, it could happen if you call it from the stopTunnelWithReason. Instead get the currentNotificationCenter before and save it.

Wowser answered 27/4, 2021 at 5:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.