Skip to content

Commit 1d53c50

Browse files
authored
Merge pull request #145 from OneSignal/infocus_swizzling_fixes
Fixes for local notification AppDelegate selectors not firing
2 parents 7723e9a + 543b490 commit 1d53c50

File tree

3 files changed

+62
-83
lines changed

3 files changed

+62
-83
lines changed

iOS_SDK/OneSignal/OneSignal.h

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,6 @@
5252
#define XC8_AVAILABLE 1
5353
#import <UserNotifications/UserNotifications.h>
5454

55-
@protocol OSUserNotificationCenterDelegate <NSObject>
56-
@optional
57-
- (void)userNotificationCenter:(id)center willPresentNotification:(id)notification withCompletionHandler:(void (^)(NSUInteger options))completionHandler __deprecated_msg("Can use your own delegate as normal.");
58-
- (void)userNotificationCenter:(id)center didReceiveNotificationResponse:(id)response withCompletionHandler:(void (^)())completionHandler __deprecated_msg("Can use your own delegate as normal.");
59-
@end
60-
6155
#endif
6256

6357
/* The action type associated to an OSNotificationAction object */
@@ -80,9 +74,9 @@ typedef NS_ENUM(NSUInteger, OSNotificationDisplayType) {
8074

8175

8276

83-
/* iOS 10+
77+
/*
8478
Used as value type for `kOSSettingsKeyInFocusDisplayOption`
85-
for setting the display option of a notification received while the app was in focus
79+
for setting the display option of a notification received while the app was in focus.
8680
*/
8781
typedef OSNotificationDisplayType OSInFocusDisplayOption;
8882

@@ -239,7 +233,7 @@ typedef NS_ENUM(NSUInteger, ONE_S_LOG_LEVEL) {
239233
+ (id)initWithLaunchOptions:(NSDictionary*)launchOptions appId:(NSString*)appId handleNotificationReceived:(OSHandleNotificationReceivedBlock)erceivedCallback handleNotificationAction:(OSHandleNotificationActionBlock)actionCallback settings:(NSDictionary*)settings;
240234

241235
+ (NSString*)app_id;
242-
236+
243237
// Only use if you passed FALSE to autoRegister
244238
+ (void)registerForPushNotifications;
245239

@@ -280,10 +274,4 @@ typedef NS_ENUM(NSUInteger, ONE_S_LOG_LEVEL) {
280274
// Optional method that sends us the user's email as an anonymized hash so that we can better target and personalize notifications sent to that user across their devices.
281275
+ (void)syncHashedEmail:(NSString*)email;
282276

283-
// - iOS 10 features currently only available on XCode 8 & iOS 10.0+
284-
#if XC8_AVAILABLE
285-
+ (void)setNotificationCenterDelegate:(id<OSUserNotificationCenterDelegate>)delegate __deprecated_msg("Can use your own delegate as normal.");
286-
+ (id<OSUserNotificationCenterDelegate>)notificationCenterDelegate __deprecated_msg("Can use your own delegate as normal.");
287-
#endif
288-
289277
@end

iOS_SDK/OneSignal/OneSignal.m

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ + (void)notificationOpened:(NSDictionary*)messageDict isActive:(BOOL)isActive {
860860

861861
// App is active and a notification was received without inApp display. Display type is none or notification
862862
// Call Received Block
863-
[OneSignalHelper handleNotificationReceived:[[[NSUserDefaults standardUserDefaults] objectForKey:@"ONESIGNAL_ALERT_OPTION"] intValue]];
863+
[OneSignalHelper handleNotificationReceived:iaaoption];
864864

865865
// Notify backend that user opened the notifiation
866866
NSString* messageId = [customDict objectForKey:@"i"];
@@ -1092,23 +1092,6 @@ + (void)processLocalActionBasedNotification:(UILocalNotification*) notification
10921092

10931093
}
10941094

1095-
#if XC8_AVAILABLE
1096-
static id<OSUserNotificationCenterDelegate> notificationCenterDelegate;
1097-
1098-
+ (void) setNotificationCenterDelegate:(id<OSUserNotificationCenterDelegate>)delegate {
1099-
if (!NSClassFromString(@"UNNotification")) {
1100-
onesignal_Log(ONE_S_LL_ERROR, @"Cannot assign delegate. Please make sure you are running on iOS 10+.");
1101-
return;
1102-
}
1103-
notificationCenterDelegate = delegate;
1104-
}
1105-
1106-
+ (id<OSUserNotificationCenterDelegate>)notificationCenterDelegate {
1107-
return notificationCenterDelegate;
1108-
}
1109-
1110-
#endif
1111-
11121095
+ (void)syncHashedEmail:(NSString *)email {
11131096

11141097
if(mUserId == nil) {

iOS_SDK/OneSignal/UNUserNotificationCenter+OneSignal.m

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@ + (void)notificationOpened:(NSDictionary*)messageDict isActive:(BOOL)isActive;
5050
// - userNotificationCenter:willPresentNotification:withCompletionHandler:
5151
// - Reads kOSSettingsKeyInFocusDisplayOption to respect it's setting.
5252
// - userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
53-
// - Used to process opening a notifications.
54-
// - The presents of this selector tells iOS to no longer fire `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
55-
// We call this to maintain existing behavior.
53+
// - Used to process opening notifications.
54+
//
55+
// NOTE: On iOS 10, when a UNUserNotificationCenterDelegate is set, UIApplicationDelegate notification selectors no longer fire.
56+
// However, this class maintains firing of UIApplicationDelegate selectors if the app did not setup it's own UNUserNotificationCenterDelegate.
57+
// This ensures we don't produce any side effects to standard iOS API selectors.
58+
// The `callLegacyAppDeletegateSelector` selector below takes care of this backwards compatibility handling.
5659

5760
@implementation swizzleUNUserNotif
5861

@@ -81,18 +84,13 @@ - (void) setOneSignalUNDelegate:(id)delegate {
8184
}
8285

8386
// Apple's docs - Called when a notification is delivered to a foreground app.
87+
// NOTE: iOS behavior - Calling completionHandler with 0 means userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: does not trigger.
88+
// - callLegacyAppDeletegateSelector is called from here due to this case.
8489
- (void)onesignalUserNotificationCenter:(UNUserNotificationCenter *)center
8590
willPresentNotification:(UNNotification *)notification
8691
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
8792
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"onesignalUserNotificationCenter:willPresentNotification:withCompletionHandler: Fired!"];
8893

89-
// Depercated - [OneSignal notificationCenterDelegate] - Now handled by swizzling.
90-
// Proxy to user if listening to delegate and overrides the method.
91-
if ([[OneSignal notificationCenterDelegate] respondsToSelector:@selector(userNotificationCenter:willPresentNotification:withCompletionHandler:)]) {
92-
[[OneSignal notificationCenterDelegate] userNotificationCenter:center willPresentNotification:notification withCompletionHandler:completionHandler];
93-
return;
94-
}
95-
9694
// Set the completionHandler options based on the ONESIGNAL_ALERT_OPTION value.
9795
if (![[NSUserDefaults standardUserDefaults] objectForKey:@"ONESIGNAL_ALERT_OPTION"]) {
9896
[[NSUserDefaults standardUserDefaults] setObject:@(OSNotificationDisplayTypeInAppAlert) forKey:@"ONESIGNAL_ALERT_OPTION"];
@@ -108,11 +106,19 @@ - (void)onesignalUserNotificationCenter:(UNUserNotificationCenter *)center
108106
default: break;
109107
}
110108

111-
// Call notificationOpened if no alert (MSB not set)
112109
[OneSignal notificationOpened:notification.request.content.userInfo isActive:YES];
113110

111+
// Call orginal selector if one was set.
114112
if ([self respondsToSelector:@selector(onesignalUserNotificationCenter:willPresentNotification:withCompletionHandler:)])
115113
[self onesignalUserNotificationCenter:center willPresentNotification:notification withCompletionHandler:completionHandler];
114+
// Or call a legacy AppDelegate selector
115+
else {
116+
[swizzleUNUserNotif callLegacyAppDeletegateSelector:notification
117+
isTextReply:false
118+
actionIdentifier:nil
119+
userText:nil
120+
withCompletionHandler:^() {}];
121+
}
116122

117123
// Calling completionHandler for the following reasons:
118124
// App dev may have not implented userNotificationCenter:willPresentNotification.
@@ -129,15 +135,20 @@ - (void)onesignalUserNotificationCenter:(UNUserNotificationCenter *)center
129135

130136
[swizzleUNUserNotif processiOS10Open:response];
131137

132-
// For depercated OSUserNotificationCenterDelegate
133-
[swizzleUNUserNotif tunnelToDelegate:center response:response handler:completionHandler];
134-
135138
// Call orginal selector if one was set.
136139
if ([self respondsToSelector:@selector(onesignalUserNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:)])
137140
[self onesignalUserNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
138-
// Or call a legacy selector AppDelegate selector
139-
else if (![swizzleUNUserNotif isDismissEvent:response]) // iOS 9 did not have a dismiss event
140-
[swizzleUNUserNotif callLegacyAppDeletegateSelector:response withCompletionHandler:completionHandler];
141+
// Or call a legacy AppDelegate selector
142+
// - If not a dismiss event as their isn't a iOS 9 selector for it.
143+
else if (![swizzleUNUserNotif isDismissEvent:response]) {
144+
BOOL isTextReply = [response isKindOfClass:NSClassFromString(@"UNTextInputNotificationResponse")];
145+
NSString* userText = isTextReply ? [response valueForKey:@"userText"] : nil;
146+
[swizzleUNUserNotif callLegacyAppDeletegateSelector:response.notification
147+
isTextReply:isTextReply
148+
actionIdentifier:response.actionIdentifier
149+
userText:userText
150+
withCompletionHandler:completionHandler];
151+
}
141152
else
142153
completionHandler();
143154
}
@@ -163,63 +174,60 @@ + (void) processiOS10Open:(UNNotificationResponse *)response {
163174
[OneSignal notificationOpened:userInfo isActive:isActive];
164175
}
165176

166-
// Depercated - [OneSignal notificationCenterDelegate] - Now handled by swizzling.
167-
+ (BOOL)tunnelToDelegate:(id)center response:(id)response handler:(void (^)())handler {
168-
if ([[OneSignal notificationCenterDelegate] respondsToSelector:@selector(userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:)]) {
169-
[[OneSignal notificationCenterDelegate] userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:handler];
170-
return true;
171-
}
172-
173-
return false;
174-
}
175-
176177
// Calls depercated pre-iOS 10 selector if one is set on the AppDelegate.
177178
// Even though they are deperated in iOS 10 they should still be called in iOS 10
179+
// As long as they didn't setup their own UNUserNotificationCenterDelegate
178180
// - application:didReceiveLocalNotification:
179181
// - application:didReceiveRemoteNotification:fetchCompletionHandler:
180182
// - application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler:
181183
// - application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:
182184
// - application:handleActionWithIdentifier:forLocalNotification:completionHandler:
183185
// - application:handleActionWithIdentifier:forRemoteNotification:completionHandler:
184-
+ (void)callLegacyAppDeletegateSelector:(UNNotificationResponse *)response
186+
+ (void)callLegacyAppDeletegateSelector:(UNNotification *)notification
187+
isTextReply:(BOOL)isTextReply
188+
actionIdentifier:(NSString*)actionIdentifier
189+
userText:(NSString*)userText
185190
withCompletionHandler:(void(^)())completionHandler {
191+
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"callLegacyAppDeletegateSelector:withCompletionHandler: Fired!"];
192+
186193
UIApplication *sharedApp = [UIApplication sharedApplication];
187194

188-
BOOL isTextReply = [response isKindOfClass:NSClassFromString(@"UNTextInputNotificationResponse")];
189-
BOOL isLegacyLocalNotif = [response.notification.request.trigger isKindOfClass:NSClassFromString(@"UNLegacyNotificationTrigger")];
190-
BOOL isCustomAction = ![@"com.apple.UNNotificationDefaultActionIdentifier" isEqualToString:response.actionIdentifier];
191-
BOOL isRemote = [response.notification.request.trigger isKindOfClass:NSClassFromString(@"UNPushNotificationTrigger")];
195+
// trigger is nil when UIApplication.presentLocalNotificationNow: is used.
196+
// However it will be UNLegacyNotificationTrigger when UIApplication.scheduleLocalNotification: is used
197+
BOOL isLegacyLocalNotif = !notification.request.trigger || [notification.request.trigger isKindOfClass:NSClassFromString(@"UNLegacyNotificationTrigger")];
198+
BOOL isCustomAction = actionIdentifier && ![@"com.apple.UNNotificationDefaultActionIdentifier" isEqualToString:actionIdentifier];
199+
BOOL isRemote = [notification.request.trigger isKindOfClass:NSClassFromString(@"UNPushNotificationTrigger")];
192200

193201
if (isLegacyLocalNotif) {
194202
UILocalNotification *localNotif = [NSClassFromString(@"UIConcreteLocalNotification") alloc];
195-
localNotif.alertBody = response.notification.request.content.body;
196-
localNotif.alertTitle = response.notification.request.content.title;
197-
localNotif.applicationIconBadgeNumber = [response.notification.request.content.badge integerValue];
198-
NSString* soundName = [response.notification.request.content.sound valueForKey:@"_toneFileName"];
203+
localNotif.alertBody = notification.request.content.body;
204+
localNotif.alertTitle = notification.request.content.title;
205+
localNotif.applicationIconBadgeNumber = [notification.request.content.badge integerValue];
206+
NSString* soundName = [notification.request.content.sound valueForKey:@"_toneFileName"];
199207
if (!soundName)
200208
soundName = @"UILocalNotificationDefaultSoundName";
201209
localNotif.soundName = soundName;
202-
localNotif.alertLaunchImage = response.notification.request.content.launchImageName;
203-
localNotif.userInfo = response.notification.request.content.userInfo;
204-
localNotif.category = response.notification.request.content.categoryIdentifier;
210+
localNotif.alertLaunchImage = notification.request.content.launchImageName;
211+
localNotif.userInfo = notification.request.content.userInfo;
212+
localNotif.category = notification.request.content.categoryIdentifier;
205213
localNotif.hasAction = true; // Defaults to true, UNLocalNotification doesn't seem to have a flag for this.
206-
localNotif.fireDate = response.notification.date;
207-
localNotif.timeZone = [response.notification.request.trigger valueForKey:@"_timeZone"];
208-
localNotif.repeatInterval = (NSCalendarUnit)[response.notification.request.trigger valueForKey:@"_repeatInterval"];
209-
localNotif.repeatCalendar = [response.notification.request.trigger valueForKey:@"_repeatCalendar"];
214+
localNotif.fireDate = notification.date;
215+
localNotif.timeZone = [notification.request.trigger valueForKey:@"_timeZone"];
216+
localNotif.repeatInterval = (NSCalendarUnit)[notification.request.trigger valueForKey:@"_repeatInterval"];
217+
localNotif.repeatCalendar = [notification.request.trigger valueForKey:@"_repeatCalendar"];
210218
// localNotif.region =
211219
// localNotif.regionTriggersOnce =
212220

213221
if (isTextReply &&
214222
[sharedApp.delegate respondsToSelector:@selector(application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler:)]) {
215-
NSDictionary* dict = @{UIUserNotificationActionResponseTypedTextKey: [response valueForKey:@"userText"]};
216-
[sharedApp.delegate application:sharedApp handleActionWithIdentifier:response.actionIdentifier forLocalNotification:localNotif withResponseInfo:dict completionHandler:^() {
223+
NSDictionary* dict = @{UIUserNotificationActionResponseTypedTextKey: userText};
224+
[sharedApp.delegate application:sharedApp handleActionWithIdentifier:actionIdentifier forLocalNotification:localNotif withResponseInfo:dict completionHandler:^() {
217225
completionHandler();
218226
}];
219227
}
220228
else if (isCustomAction &&
221229
[sharedApp.delegate respondsToSelector:@selector(application:handleActionWithIdentifier:forLocalNotification:completionHandler:)])
222-
[sharedApp.delegate application:sharedApp handleActionWithIdentifier:response.actionIdentifier forLocalNotification:localNotif completionHandler:^() {
230+
[sharedApp.delegate application:sharedApp handleActionWithIdentifier:actionIdentifier forLocalNotification:localNotif completionHandler:^() {
223231
completionHandler();
224232
}];
225233
else if ([sharedApp.delegate respondsToSelector:@selector(application:didReceiveLocalNotification:)]) {
@@ -230,18 +238,18 @@ + (void)callLegacyAppDeletegateSelector:(UNNotificationResponse *)response
230238
completionHandler();
231239
}
232240
else if (isRemote) {
233-
NSDictionary* remoteUserInfo = response.notification.request.content.userInfo;
241+
NSDictionary* remoteUserInfo = notification.request.content.userInfo;
234242

235243
if (isTextReply &&
236244
[sharedApp.delegate respondsToSelector:@selector(application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:)]) {
237-
NSDictionary* responseInfo = @{UIUserNotificationActionResponseTypedTextKey: [response valueForKey:@"userText"]};
238-
[sharedApp.delegate application:sharedApp handleActionWithIdentifier:response.actionIdentifier forRemoteNotification:remoteUserInfo withResponseInfo:responseInfo completionHandler:^() {
245+
NSDictionary* responseInfo = @{UIUserNotificationActionResponseTypedTextKey: userText};
246+
[sharedApp.delegate application:sharedApp handleActionWithIdentifier:actionIdentifier forRemoteNotification:remoteUserInfo withResponseInfo:responseInfo completionHandler:^() {
239247
completionHandler();
240248
}];
241249
}
242250
else if (isCustomAction &&
243251
[sharedApp.delegate respondsToSelector:@selector(application:handleActionWithIdentifier:forRemoteNotification:completionHandler:)])
244-
[sharedApp.delegate application:sharedApp handleActionWithIdentifier:response.actionIdentifier forRemoteNotification:remoteUserInfo completionHandler:^() {
252+
[sharedApp.delegate application:sharedApp handleActionWithIdentifier:actionIdentifier forRemoteNotification:remoteUserInfo completionHandler:^() {
245253
completionHandler();
246254
}];
247255
else if ([sharedApp.delegate respondsToSelector:@selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]) {

0 commit comments

Comments
 (0)