Skip to content

Commit

Permalink
- Remove +[UIApplication load] in OneSignal class implementation in
Browse files Browse the repository at this point in the history
  order to prevent swizzling before the library is instantiated (IF it
is instantiated at all)
- Remove `-[UIApplication setDelegate:]` swizzle in favor of manually
  calling `+[OneSignal swizzleSelectors]` during init, which kicks off
all required method swizzling. This method also wraps the sensitive code in a
`dispatch_once` block in order to prevent multiple swizzles from
happening if init is called more than once

- Force swizzle before all tests execute to accomodate previous commit
- Add some pretty prints to make debugging easier

- Attempts to swizzle the current UNUserNotificationCenterDelegate
  during +[UNUserNotificationCenter swizzleSelectors] just in case the
delegate has already been set by the time this method is called

- Fixes potential memory leak in Reachability
- Missing -[UIViewController viewDidLoad] super call

- Update binary
  • Loading branch information
alexhillc committed Aug 15, 2017
1 parent 01a340b commit 64cae9b
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 45 deletions.
Binary file not shown.
50 changes: 25 additions & 25 deletions iOS_SDK/OneSignalSDK/Source/OneSignal.m
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,9 @@ + (id)initWithLaunchOptions:(NSDictionary*)launchOptions appId:(NSString*)appId
return self;
}

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

if ([OneSignalHelper isIOSVersionGreaterOrEqual:8])
registeredWithApple = self.currentPermissionState.accepted;
else
Expand Down Expand Up @@ -1569,9 +1572,6 @@ + (UNMutableNotificationContent*)serviceExtensionTimeWillExpireRequest:(UNNotifi
return replacementContent;
}


@end

// Swizzles UIApplication class to swizzling the following:
// - UIApplication
// - setDelegate:
Expand All @@ -1582,15 +1582,12 @@ + (UNMutableNotificationContent*)serviceExtensionTimeWillExpireRequest:(UNNotifi
// - 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 @@ -1599,25 +1596,28 @@ + (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]);

// Swizzle - UNUserNotificationCenter delegate - iOS 10+
if (!NSClassFromString(@"UNUserNotificationCenter"))
return;

[OneSignalUNUserNotificationCenter swizzleSelectors];

// Set our own delegate if one hasn't been set already from something else.
[OneSignalHelper registerAsUNNotificationCenterDelegate];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Swizzle - UIApplication delegate
[OneSignalAppDelegate swizzleSelectors];

// Swizzle - UNUserNotificationCenter delegate - iOS 10+
if (!NSClassFromString(@"UNUserNotificationCenter"))
return;

[OneSignalUNUserNotificationCenter swizzleSelectors];

// Set our own delegate if one hasn't been set already from something else.
[OneSignalHelper registerAsUNNotificationCenterDelegate];
});
}

@end
Expand Down
3 changes: 3 additions & 0 deletions iOS_SDK/OneSignalSDK/Source/OneSignalReachability.m
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ + (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress
returnValue->_reachabilityRef = reachability;
returnValue->_alwaysReturnLocalWiFiStatus = NO;
}

CFRelease(reachability);
}

return returnValue;
}

Expand Down
2 changes: 2 additions & 0 deletions iOS_SDK/OneSignalSDK/Source/OneSignalWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ @implementation OneSignalWebView
UIViewController *viewControllerForPresentation;

-(void)viewDidLoad {
[super viewDidLoad];

_webView = [[UIWebView alloc] initWithFrame:self.view.frame];
_webView.delegate = self;
[self.view addSubview:_webView];
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
10 changes: 3 additions & 7 deletions iOS_SDK/OneSignalSDK/Source/UIApplicationDelegate+OneSignal.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,16 @@ +(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);

// Need to keep this one for iOS 10 for content-available notifiations when the app is not in focus
Expand All @@ -94,7 +93,6 @@ - (void) setOneSignalDelegate:(id<UIApplicationDelegate>)delegate {
@selector(application:didFailToRegisterForRemoteNotificationsWithError:), delegateSubclasses, newClass, delegateClass);

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

Expand All @@ -116,8 +114,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
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
3 changes: 1 addition & 2 deletions iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -720,8 +720,7 @@ - (void)beforeAllTest {

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

Expand Down

0 comments on commit 64cae9b

Please sign in to comment.