Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove +[UIApplication load], Remove -[UIApplication setDelegate:] swizzle #259

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion OneSignal.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "OneSignal"
s.version = "2.8.2"
s.version = "2.11.0"
s.summary = "OneSignal push notification library for mobile apps."
s.homepage = "https://onesignal.com"
s.license = { :type => 'MIT', :file => 'LICENSE' }
Expand Down
Binary file not shown.
51 changes: 19 additions & 32 deletions iOS_SDK/OneSignalSDK/Source/OneSignal.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@

#import "OSObservable.h"

#import "OneSignalExtensionBadgeHandler.h"

#import <stdlib.h>
#import <stdio.h>
#import <sys/types.h>
Expand Down Expand Up @@ -405,6 +403,9 @@ + (id)initWithLaunchOptions:(NSDictionary*)launchOptions appId:(NSString*)appId

[OneSignalHelper notificationBlocks: receivedCallback : actionCallback];

// Set up our UIApplicationDelegate + UNUserNotificationCenter swizzles
[self swizzleSelectors];

if ([OneSignalHelper isIOSVersionGreaterOrEqual:8])
registeredWithApple = self.currentPermissionState.accepted;
else
Expand Down Expand Up @@ -1910,8 +1911,6 @@ + (void)emailChangedWithNewEmailPlayerId:(NSString * _Nullable)emailPlayerId {
}];
}

@end

// Swizzles UIApplication class to swizzling the following:
// - UIApplication
// - setDelegate:
Expand All @@ -1922,15 +1921,12 @@ + (void)emailChangedWithNewEmailPlayerId:(NSString * _Nullable)emailPlayerId {
// - For iOS 10 only, swizzle all UNUserNotificationCenterDelegate selectors on the passed in class.
// - This may or may not be set so we set our own now in registerAsUNNotificationCenterDelegate to an empty class.
//
// Note1: Do NOT move this category to it's own file. This is required so when the app developer calls OneSignal.initWithLaunchOptions this load+
// will fire along with it. This is due to how iOS loads .m files into memory instead of classes.
// Note2: Do NOT directly add swizzled selectors to this category as if this class is loaded into the runtime twice unexpected results will occur.
// The oneSignalLoadedTagSelector: selector is used a flag to prevent double swizzling if this library is loaded twice.
@implementation UIApplication (OneSignal)
// Note1: Do NOT directly add swizzled selectors to this category as if this class is loaded into the runtime twice unexpected results will occur.
// The oneSignalLoadedTagSelector: selector is used a flag to warn the developer that the UIApplicationDelegate selectors have already been swizzled.
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
+ (void)load {
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"UIApplication(OneSignal) LOADED!"];
+ (void)swizzleSelectors {
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"%s called", __PRETTY_FUNCTION__]];

// Prevent Xcode storyboard rendering process from crashing with custom IBDesignable Views
// https://github.com/OneSignal/OneSignal-iOS-SDK/issues/160
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
Expand All @@ -1939,33 +1935,24 @@ + (void)load {

if (SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(@"7.0"))
return;

// Double loading of class detection.
BOOL existing = injectSelector([OneSignalAppDelegate class], @selector(oneSignalLoadedTagSelector:), self, @selector(oneSignalLoadedTagSelector:));
BOOL existing = injectSelector([OneSignalAppDelegate class], @selector(oneSignalLoadedTagSelector:), [UIApplication class], @selector(oneSignalLoadedTagSelector:));
if (existing) {
[OneSignal onesignal_Log:ONE_S_LL_WARN message:@"Already swizzled UIApplication.setDelegate. Make sure the OneSignal library wasn't loaded into the runtime twice!"];
[OneSignal onesignal_Log:ONE_S_LL_WARN message:@"Already swizzled UIApplicationDelegate selectors."];
return;
}

// Swizzle - UIApplication delegate
injectToProperClass(@selector(setOneSignalDelegate:), @selector(setDelegate:), @[], [OneSignalAppDelegate class], [UIApplication class]);

injectToProperClass(@selector(onesignalSetApplicationIconBadgeNumber:), @selector(setApplicationIconBadgeNumber:), @[], [OneSignalAppDelegate class], [UIApplication class]);

[self setupUNUserNotificationCenterDelegate];
}

/*
In order for the badge count to be consistent even in situations where the developer manually sets the badge number,
We swizzle the 'setApplicationIconBadgeNumber()' to intercept these calls so we always know the latest count
*/
- (void)onesignalSetApplicationIconBadgeNumber:(NSInteger)badge {
[OneSignalExtensionBadgeHandler updateCachedBadgeValue:badge];

[self onesignalSetApplicationIconBadgeNumber:badge];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Swizzle - UIApplication delegate
[OneSignalAppDelegate swizzleSelectors];

[self setupUNUserNotificationCenterDelegate];
});
}

+(void)setupUNUserNotificationCenterDelegate {
+ (void)setupUNUserNotificationCenterDelegate {
// Swizzle - UNUserNotificationCenter delegate - iOS 10+
if (!NSClassFromString(@"UNUserNotificationCenter"))
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#define UIApplicationDelegate_OneSignal_h
@interface OneSignalAppDelegate : NSObject

+ (void)swizzleSelectors;
+ (void)sizzlePreiOS10MethodsPhase1;
+ (void)sizzlePreiOS10MethodsPhase2;

Expand Down
25 changes: 18 additions & 7 deletions iOS_SDK/OneSignalSDK/Source/UIApplicationDelegate+OneSignal.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#import "OneSignalLocation.h"
#import "OneSignalSelectorHelpers.h"
#import "OneSignalHelper.h"
#import "OneSignalExtensionBadgeHandler.h"

@interface OneSignal (UN_extra)
+ (void) didRegisterForRemoteNotifications:(UIApplication*)app deviceToken:(NSData*)inDeviceToken;
Expand Down Expand Up @@ -69,19 +70,22 @@ +(Class)delegateClass {



- (void) setOneSignalDelegate:(id<UIApplicationDelegate>)delegate {
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"ONESIGNAL setOneSignalDelegate CALLED: %@", delegate]];
+ (void)swizzleSelectors {
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"%s called", __PRETTY_FUNCTION__]];

if (delegateClass) {
[self setOneSignalDelegate:delegate];
return;
}

Class newClass = [OneSignalAppDelegate class];

delegateClass = getClassWithProtocolInHierarchy([delegate class], @protocol(UIApplicationDelegate));
delegateClass = getClassWithProtocolInHierarchy([[UIApplication sharedApplication].delegate class], @protocol(UIApplicationDelegate));
delegateSubclasses = ClassGetSubclasses(delegateClass);

injectToProperClass(@selector(onesignalSetApplicationIconBadgeNumber:),
@selector(setApplicationIconBadgeNumber:), delegateSubclasses, newClass, delegateClass);


// Need to keep this one for iOS 10 for content-available notifiations when the app is not in focus
// iOS 10 doesn't fire a selector on UNUserNotificationCenter in this cases most likely becuase
// UNNotificationServiceExtension (mutable-content) and UNNotificationContentExtension (with category) replaced it.
Expand All @@ -94,7 +98,6 @@ - (void) setOneSignalDelegate:(id<UIApplicationDelegate>)delegate {
@selector(application:didFailToRegisterForRemoteNotificationsWithError:), delegateSubclasses, newClass, delegateClass);

if (NSClassFromString(@"CoronaAppDelegate")) {
[self setOneSignalDelegate:delegate];
return;
}

Expand All @@ -116,8 +119,6 @@ - (void) setOneSignalDelegate:(id<UIApplicationDelegate>)delegate {
// Used to track how long the app has been closed
injectToProperClass(@selector(oneSignalApplicationWillTerminate:),
@selector(applicationWillTerminate:), delegateSubclasses, newClass, delegateClass);

[self setOneSignalDelegate:delegate];
}

+ (void)sizzlePreiOS10MethodsPhase1 {
Expand Down Expand Up @@ -146,6 +147,16 @@ + (void)sizzlePreiOS10MethodsPhase2 {
}


/*
In order for the badge count to be consistent even in situations where the developer manually sets the badge number,
We swizzle the 'setApplicationIconBadgeNumber()' to intercept these calls so we always know the latest count
*/
- (void)onesignalSetApplicationIconBadgeNumber:(NSInteger)badge {
[OneSignalExtensionBadgeHandler updateCachedBadgeValue:badge];

[self onesignalSetApplicationIconBadgeNumber:badge];
}

- (void)oneSignalDidRegisterForRemoteNotifications:(UIApplication*)app deviceToken:(NSData*)inDeviceToken {
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"oneSignalDidRegisterForRemoteNotifications:deviceToken:"];

Expand Down
33 changes: 22 additions & 11 deletions iOS_SDK/OneSignalSDK/Source/UNUserNotificationCenter+OneSignal.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ @implementation OneSignalUNUserNotificationCenter
static NSArray* delegateUNSubclasses = nil;

+ (void)swizzleSelectors {
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"%s called", __PRETTY_FUNCTION__]];

injectToProperClass(@selector(setOneSignalUNDelegate:), @selector(setDelegate:), @[], [OneSignalUNUserNotificationCenter class], [UNUserNotificationCenter class]);
[OneSignalUNUserNotificationCenter swizzleUNUserNotificationCenterDelegateSelectors];

// Overrides to work around 10.2.1 bug where getNotificationSettingsWithCompletionHandler: reports as declined if called before
// requestAuthorizationWithOptions:'s completionHandler fires when the user accepts notifications.
Expand All @@ -81,6 +84,23 @@ + (void)swizzleSelectors {
[OneSignalUNUserNotificationCenter class], [UNUserNotificationCenter class]);
}

// Take the UNUserNotificationCenterDelegate and swizzle in our own hooks.
+ (void)swizzleUNUserNotificationCenterDelegateSelectors {
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"%s called", __PRETTY_FUNCTION__]];

Class newDelegateUNClass = getClassWithProtocolInHierarchy([[UNUserNotificationCenter currentNotificationCenter].delegate class], @protocol(UNUserNotificationCenterDelegate));
if (delegateUNClass != newDelegateUNClass) {
delegateUNClass = newDelegateUNClass;
delegateUNSubclasses = ClassGetSubclasses(delegateUNClass);

injectToProperClass(@selector(onesignalUserNotificationCenter:willPresentNotification:withCompletionHandler:),
@selector(userNotificationCenter:willPresentNotification:withCompletionHandler:), delegateUNSubclasses, [OneSignalUNUserNotificationCenter class], delegateUNClass);

injectToProperClass(@selector(onesignalUserNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:),
@selector(userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:), delegateUNSubclasses, [OneSignalUNUserNotificationCenter class], delegateUNClass);
}
}

static BOOL useiOS10_2_workaround = true;
+ (void)setUseiOS10_2_workaround:(BOOL)enable {
useiOS10_2_workaround = enable;
Expand Down Expand Up @@ -120,18 +140,9 @@ - (void)onesignalGetNotificationSettingsWithCompletionHandler:(void(^)(UNNotific
// - Selector will be called once if developer does not set a UNUserNotificationCenter delegate.
// - Selector will be called a 2nd time if the developer does set one.
- (void) setOneSignalUNDelegate:(id)delegate {
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"OneSignalUNUserNotificationCenter setOneSignalUNDelegate Fired!"];

delegateUNClass = getClassWithProtocolInHierarchy([delegate class], @protocol(UNUserNotificationCenterDelegate));
delegateUNSubclasses = ClassGetSubclasses(delegateUNClass);

injectToProperClass(@selector(onesignalUserNotificationCenter:willPresentNotification:withCompletionHandler:),
@selector(userNotificationCenter:willPresentNotification:withCompletionHandler:), delegateUNSubclasses, [OneSignalUNUserNotificationCenter class], delegateUNClass);

injectToProperClass(@selector(onesignalUserNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:),
@selector(userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:), delegateUNSubclasses, [OneSignalUNUserNotificationCenter class], delegateUNClass);

[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"%s fired!", __PRETTY_FUNCTION__]];
[self setOneSignalUNDelegate:delegate];
[OneSignalUNUserNotificationCenter swizzleUNUserNotificationCenterDelegateSelectors];
}

// Apple's docs - Called when a notification is delivered to a foreground app.
Expand Down
46 changes: 46 additions & 0 deletions iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,52 @@ @interface UnitTests : XCTestCase

@implementation UnitTests

- (void)beforeAllTest {
static var setupUIApplicationDelegate = false;
if (setupUIApplicationDelegate)
return;

// Normally this just loops internally, overwrote _run to work around this.
UIApplicationMain(0, nil, nil, NSStringFromClass([UnitTestAppDelegate class]));

setupUIApplicationDelegate = true;

// InstallUncaughtExceptionHandler();

// Force swizzle in all methods for tests.
OneSignalHelperOverrider.mockIOSVersion = 8;
[OneSignalAppDelegate swizzleSelectors];
OneSignalHelperOverrider.mockIOSVersion = 10;
}

- (void)clearStateForAppRestart {
NSLog(@"======= APP RESTART ======\n\n");

NSDateOverrider.timeOffset = 0;
[OneSignalClientOverrider reset:self];
[UNUserNotificationCenterOverrider reset:self];
[UIApplicationOverrider reset];
[OneSignalTrackFirebaseAnalyticsOverrider reset];

NSLocaleOverrider.preferredLanguagesArray = @[@"en-US"];

[OneSignalHelper performSelector:NSSelectorFromString(@"resetLocals")];

[OneSignal setValue:nil forKeyPath:@"lastAppActiveMessageId"];
[OneSignal setValue:nil forKeyPath:@"lastnonActiveMessageId"];
[OneSignal setValue:@0 forKeyPath:@"mSubscriptionStatus"];

[OneSignalTracker performSelector:NSSelectorFromString(@"resetLocals")];

[NSObjectOverrider reset];

[OneSignal performSelector:NSSelectorFromString(@"clearStatics")];

[UIAlertViewOverrider reset];

[OneSignal setLogLevel:ONE_S_LL_VERBOSE visualLevel:ONE_S_LL_NONE];
}

// Called before each test.
- (void)setUp {
[super setUp];
Expand Down