Skip to content

Commit e602cb2

Browse files
authored
Fix Incorrect Notification Opened Events (#357)
* Fix Incorrect Notification Opened Events • The SDK was previously incorrectly sending 'notification opened' HTTP requests when notifications were received • Fixed the issue so that this event is only triggered when the user opens a notification • Added a test to ensure it doesn't occur in the future • In some tests, the SDK was previously checking to make sure the last HTTP request was of a certain type, which caused some race condition issues. Changed it so that it will now check to see if the request was executed recently (since some other event, ie. registering the user, may occur but this shouldn't break the test) * Fix Notification Opened Issue • Changes the SDK so that it will correctly send a notification opened HTTP request even if the display type == None and the notification has content. • However, this change also makes it so that the initialization handleNotificationOpened completion block does not get called
1 parent be5ca2a commit e602cb2

File tree

7 files changed

+70
-22
lines changed

7 files changed

+70
-22
lines changed

iOS_SDK/OneSignalSDK/Source/OneSignal.m

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,7 @@ + (void)setLastnonActiveMessageId:(NSString*)value { _lastnonActiveMessageId = v
14041404
// - 2. Notification received
14051405
// - 2A. iOS 9 - Notification received while app is in focus.
14061406
// - 2B. iOS 10 - Notification received/displayed while app is in focus.
1407-
+ (void)notificationOpened:(NSDictionary*)messageDict isActive:(BOOL)isActive {
1407+
+ (void)notificationReceived:(NSDictionary*)messageDict isActive:(BOOL)isActive wasOpened:(BOOL)opened {
14081408

14091409
if (!app_id)
14101410
return;
@@ -1439,18 +1439,16 @@ + (void)notificationOpened:(NSDictionary*)messageDict isActive:(BOOL)isActive {
14391439
// App is active and a notification was received without inApp display. Display type is none or notification
14401440
// Call Received Block
14411441
[OneSignalHelper handleNotificationReceived:self.inFocusDisplayType];
1442-
1443-
// Notify backend that user opened the notification
1444-
NSString *messageId = [customDict objectForKey:@"i"];
1445-
[OneSignal submitNotificationOpened:messageId];
14461442
} else {
14471443
// Prevent duplicate calls
14481444
let newId = [self checkForProcessedDups:customDict lastMessageId:_lastnonActiveMessageId];
14491445
if ([@"dup" isEqualToString:newId])
14501446
return;
14511447
if (newId)
14521448
_lastnonActiveMessageId = newId;
1453-
1449+
}
1450+
1451+
if (opened) {
14541452
//app was in background / not running and opened due to a tap on a notification or an action check what type
14551453
NSString* actionSelected = NULL;
14561454
OSNotificationActionType type = OSNotificationActionTypeOpened;
@@ -1464,8 +1462,7 @@ + (void)notificationOpened:(NSDictionary*)messageDict isActive:(BOOL)isActive {
14641462
}
14651463

14661464
// Call Action Block
1467-
[OneSignalHelper handleNotificationAction:type actionID:actionSelected displayType:OSNotificationDisplayTypeNotification];
1468-
[OneSignal handleNotificationOpened:messageDict isActive:isActive actionType:type displayType:OSNotificationDisplayTypeNotification];
1465+
[OneSignal handleNotificationOpened:messageDict isActive:isActive actionType:type displayType:OneSignal.inFocusDisplayType];
14691466
}
14701467
}
14711468

@@ -1504,7 +1501,11 @@ + (void)handleNotificationOpened:(NSDictionary*)messageDict
15041501

15051502
//Call Action Block
15061503
[OneSignalHelper lastMessageReceived:messageDict];
1507-
[OneSignalHelper handleNotificationAction:actionType actionID:actionID displayType:displayType];
1504+
1505+
//ensures that if the app is open and display type == none, the handleNotificationAction block does not get called
1506+
if (displayType != OSNotificationDisplayTypeNone || (displayType == OSNotificationDisplayTypeNone && !isActive)) {
1507+
[OneSignalHelper handleNotificationAction:actionType actionID:actionID displayType:displayType];
1508+
}
15081509
}
15091510

15101511
+ (BOOL)shouldPromptToShowURL {
@@ -1669,9 +1670,13 @@ + (BOOL)remoteSilentNotification:(UIApplication*)application UserInfo:(NSDiction
16691670
// Method was called due to a tap on a notification - Fire open notification
16701671
else if (application.applicationState != UIApplicationStateBackground) {
16711672
[OneSignalHelper lastMessageReceived:userInfo];
1673+
16721674
if (application.applicationState == UIApplicationStateActive)
16731675
[OneSignalHelper handleNotificationReceived:OSNotificationDisplayTypeNotification];
1674-
[OneSignal notificationOpened:userInfo isActive:NO];
1676+
1677+
if (![OneSignalHelper isRemoteSilentNotification:userInfo])
1678+
[OneSignal notificationReceived:userInfo isActive:NO wasOpened:YES];
1679+
16751680
return startedBackgroundJob;
16761681
}
16771682
// content-available notification received in the background - Fire handleNotificationReceived block in app
@@ -1697,7 +1702,7 @@ + (void)processLocalActionBasedNotification:(UILocalNotification*) notification
16971702
return;
16981703

16991704
let isActive = [[UIApplication sharedApplication] applicationState] == UIApplicationStateActive;
1700-
[OneSignal notificationOpened:userInfo isActive:isActive];
1705+
[OneSignal notificationReceived:userInfo isActive:isActive wasOpened:YES];
17011706

17021707
// Notification Tapped or notification Action Tapped
17031708
if (!isActive)

iOS_SDK/OneSignalSDK/Source/UIApplicationDelegate+OneSignal.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ + (void) didRegisterForRemoteNotifications:(UIApplication*)app deviceToken:(NSDa
4040
+ (void) handleDidFailRegisterForRemoteNotification:(NSError*)error;
4141
+ (void) updateNotificationTypes:(int)notificationTypes;
4242
+ (NSString*) app_id;
43-
+ (void) notificationOpened:(NSDictionary*)messageDict isActive:(BOOL)isActive;
43+
+ (void)notificationReceived:(NSDictionary*)messageDict isActive:(BOOL)isActive wasOpened:(BOOL)opened;
4444
+ (BOOL) remoteSilentNotification:(UIApplication*)application UserInfo:(NSDictionary*)userInfo completionHandler:(void (^)(UIBackgroundFetchResult))completionHandler;
4545
+ (void) processLocalActionBasedNotification:(UILocalNotification*) notification identifier:(NSString*)identifier;
4646
+ (void) onesignal_Log:(ONE_S_LOG_LEVEL)logLevel message:(NSString*) message;
@@ -182,7 +182,7 @@ - (void)oneSignalReceivedRemoteNotification:(UIApplication*)application userInfo
182182
[OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"oneSignalReceivedRemoteNotification:userInfo:"];
183183

184184
if ([OneSignal app_id])
185-
[OneSignal notificationOpened:userInfo isActive:[application applicationState] == UIApplicationStateActive];
185+
[OneSignal notificationReceived:userInfo isActive:[application applicationState] == UIApplicationStateActive wasOpened:true];
186186

187187
if ([self respondsToSelector:@selector(oneSignalReceivedRemoteNotification:userInfo:)])
188188
[self oneSignalReceivedRemoteNotification:application userInfo:userInfo];
@@ -201,7 +201,7 @@ - (void) oneSignalRemoteSilentNotification:(UIApplication*)application UserInfo:
201201

202202
if ([OneSignal app_id]) {
203203
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive && userInfo[@"aps"][@"alert"])
204-
[OneSignal notificationOpened:userInfo isActive:YES];
204+
[OneSignal notificationReceived:userInfo isActive:YES wasOpened:NO];
205205
else
206206
startedBackgroundJob = [OneSignal remoteSilentNotification:application UserInfo:userInfo completionHandler:callExistingSelector ? nil : completionHandler];
207207
}

iOS_SDK/OneSignalSDK/Source/UNUserNotificationCenter+OneSignal.m

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
4747

4848
@interface OneSignal (UN_extra)
49-
+ (void)notificationOpened:(NSDictionary*)messageDict isActive:(BOOL)isActive;
49+
+ (void)notificationReceived:(NSDictionary*)messageDict isActive:(BOOL)isActive wasOpened:(BOOL)opened;
5050
@end
5151

5252
// This class hooks into the following iSO 10 UNUserNotificationCenterDelegate selectors:
@@ -159,9 +159,10 @@ - (void)onesignalUserNotificationCenter:(UNUserNotificationCenter *)center
159159
default: break;
160160
}
161161

162-
if ([OneSignal app_id]) {
163-
[OneSignal notificationOpened:notification.request.content.userInfo isActive:YES];
164-
}
162+
let notShown = OneSignal.inFocusDisplayType == OSNotificationDisplayTypeNone && notification.request.content.body != nil;
163+
164+
if ([OneSignal app_id])
165+
[OneSignal notificationReceived:notification.request.content.userInfo isActive:YES wasOpened:notShown];
165166

166167
// Call orginal selector if one was set.
167168
if ([self respondsToSelector:@selector(onesignalUserNotificationCenter:willPresentNotification:withCompletionHandler:)])
@@ -239,7 +240,8 @@ + (void) processiOS10Open:(UNNotificationResponse*)response {
239240
let userInfo = [OneSignalHelper formatApsPayloadIntoStandard:response.notification.request.content.userInfo
240241
identifier:response.actionIdentifier];
241242

242-
[OneSignal notificationOpened:userInfo isActive:isActive];
243+
[OneSignal notificationReceived:userInfo isActive:isActive wasOpened:YES];
244+
243245
}
244246

245247
// Calls depercated pre-iOS 10 selector if one is set on the AppDelegate.

iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,6 @@ - (void)testEmailSubscriptionObserver {
415415

416416
//when the user is logged in with email, on_focus requests should be duplicated for the email player id as well
417417
- (void)testOnFocusEmailRequest {
418-
419418
[UnitTestCommonMethods runBackgroundThreads];
420419

421420
[self setupEmailTest];
@@ -435,7 +434,7 @@ - (void)testOnFocusEmailRequest {
435434

436435
[UnitTestCommonMethods runBackgroundThreads];
437436

438-
XCTAssertTrue([OneSignalClientOverrider.lastHTTPRequestType isEqualToString:NSStringFromClass([OSRequestOnFocus class])]);
437+
XCTAssertTrue([OneSignalClientOverrider hasExecutedRequestOfType:[OSRequestOnFocus class]]);
439438
XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1);
440439

441440
[OneSignalClientOverrider reset:self];
@@ -469,7 +468,7 @@ - (void)testOnFocusEmailRequest {
469468
[UnitTestCommonMethods runBackgroundThreads];
470469

471470
// on_focus should fire off two requests, one for the email player ID and one for push player ID
472-
XCTAssertTrue([OneSignalClientOverrider.lastHTTPRequestType isEqualToString:NSStringFromClass([OSRequestOnFocus class])]);
471+
XCTAssertTrue([OneSignalClientOverrider hasExecutedRequestOfType:[OSRequestOnFocus class]]);
473472
XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2);
474473

475474
[OneSignalClientOverrider setRequiresEmailAuth:false];

iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@
4141
+(void)runBackgroundThreads;
4242
+(NSString *)lastHTTPRequestType;
4343
+(void)setRequiresEmailAuth:(BOOL)required;
44+
+(BOOL)hasExecutedRequestOfType:(Class)type;
4445
@end
4546

iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ @implementation OneSignalClientOverrider
4848
static dispatch_queue_t executionQueue;
4949
static NSString *lastHTTPRequestType;
5050
static BOOL requiresEmailAuth = false;
51+
static NSMutableArray<NSString *> *executedRequestTypes;
5152

5253
+ (void)load {
5354
serialMockMainLooper = dispatch_queue_create("com.onesignal.unittest", DISPATCH_QUEUE_SERIAL);
@@ -59,6 +60,7 @@ + (void)load {
5960

6061
executionQueue = dispatch_queue_create("com.onesignal.execution", NULL);
6162

63+
executedRequestTypes = [[NSMutableArray alloc] init];
6264
}
6365

6466
- (void)overrideExecuteSimultaneousRequests:(NSDictionary<NSString *, OneSignalRequest *> *)requests withSuccess:(OSMultipleSuccessBlock)successBlock onFailure:(OSMultipleFailureBlock)failureBlock {
@@ -69,6 +71,8 @@ - (void)overrideExecuteSimultaneousRequests:(NSDictionary<NSString *, OneSignalR
6971
__block NSMutableDictionary<NSString *, NSDictionary *> *results = [NSMutableDictionary new];
7072

7173
for (NSString *key in requests.allKeys) {
74+
[executedRequestTypes addObject:NSStringFromClass([requests[key] class])];
75+
7276
[OneSignalClient.sharedClient executeRequest:requests[key] onSuccess:^(NSDictionary *result) {
7377
results[key] = result;
7478
dispatch_semaphore_signal(semaphore);
@@ -90,6 +94,8 @@ - (void)overrideExecuteSimultaneousRequests:(NSDictionary<NSString *, OneSignalR
9094
}
9195

9296
- (void)overrideExecuteRequest:(OneSignalRequest *)request onSuccess:(OSResultSuccessBlock)successBlock onFailure:(OSFailureBlock)failureBlock {
97+
[executedRequestTypes addObject:NSStringFromClass([request class])];
98+
9399
if (executeInstantaneously) {
94100
[OneSignalClientOverrider finishExecutingRequest:request onSuccess:successBlock onFailure:failureBlock];
95101
} else {
@@ -127,6 +133,14 @@ + (void)finishExecutingRequest:(OneSignalRequest *)request onSuccess:(OSResultSu
127133
}
128134
}
129135

136+
+(BOOL)hasExecutedRequestOfType:(Class)type {
137+
for (id requestType in executedRequestTypes)
138+
if ([requestType isEqualToString:NSStringFromClass(type)])
139+
return true;
140+
141+
return false;
142+
}
143+
130144
+(dispatch_queue_t)getHTTPQueue {
131145
return executionQueue;
132146
}
@@ -145,6 +159,7 @@ +(void)reset:(XCTestCase*)testInstance {
145159
networkRequestCount = 0;
146160
lastUrl = nil;
147161
lastHTTPRequest = nil;
162+
[executedRequestTypes removeAllObjects];
148163
}
149164

150165
+(void)setLastHTTPRequest:(NSDictionary*)value {

iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,14 +1244,40 @@ - (void)receivedCallbackWithButtonsWithUserInfo:(NSDictionary *)userInfo {
12441244
UNUserNotificationCenter *notifCenter = [UNUserNotificationCenter currentNotificationCenter];
12451245
let notifCenterDelegate = notifCenter.delegate;
12461246

1247+
UIApplicationOverrider.currentUIApplicationState = UIApplicationStateInactive;
1248+
12471249
//iOS 10 calls UNUserNotificationCenterDelegate method directly when a notification is received while the app is in focus.
12481250
[notifCenterDelegate userNotificationCenter:notifCenter
12491251
willPresentNotification:[notifResponse notification]
12501252
withCompletionHandler:^(UNNotificationPresentationOptions options) {}];
12511253

1254+
[UnitTestCommonMethods runBackgroundThreads];
1255+
12521256
XCTAssertEqual(recievedWasFire, true);
12531257
}
12541258

1259+
/*
1260+
There was a bug where receiving notifications would cause OSRequestSubmitNotificationOpened
1261+
to fire, even though the notification had not been opened
1262+
*/
1263+
- (void)testReceiveNotificationDoesNotSubmitOpenedRequest {
1264+
[OneSignalClientOverrider reset:self];
1265+
1266+
let newFormat = @{@"aps": @{@"content_available": @1},
1267+
@"os_data": @{
1268+
@"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba",
1269+
@"buttons": @{
1270+
@"m": @"alert body only",
1271+
@"o": @[@{@"i": @"id1", @"n": @"text1"}]
1272+
}
1273+
}
1274+
};
1275+
1276+
[self receivedCallbackWithButtonsWithUserInfo:newFormat];
1277+
1278+
XCTAssertFalse([OneSignalClientOverrider hasExecutedRequestOfType:[OSRequestSubmitNotificationOpened class]]);
1279+
}
1280+
12551281
- (void)testReceivedCallbackWithButtonsWithNewFormat {
12561282
let newFormat = @{@"aps": @{@"content_available": @1},
12571283
@"os_data": @{

0 commit comments

Comments
 (0)