Skip to content

Commit 01f5d04

Browse files
authored
SDK-72 push types (#394)
* Send push types on notifications registration * Unit tests for push types
1 parent c70d7e7 commit 01f5d04

File tree

2 files changed

+197
-37
lines changed

2 files changed

+197
-37
lines changed

Example/Tests/Classes/LPPushNotificationsManagerTest.m

Lines changed: 135 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ + (void)setUp
3939
- (void)setUp
4040
{
4141
[super setUp];
42-
// Automatically sets up AppId and AccessKey for development mode.
43-
[LeanplumHelper setup_development_test];
4442
}
4543

4644
- (void)tearDown
@@ -49,16 +47,48 @@ - (void)tearDown
4947
[LeanplumHelper clean_up];
5048
}
5149

52-
- (void)test_push_token
50+
- (LPPushNotificationsManager*)mockManager
5351
{
54-
XCTAssertTrue([LeanplumHelper start_production_test]);
55-
56-
// Partial mock Action Manager.
5752
LPPushNotificationsManager *manager = [LPPushNotificationsManager sharedManager];
5853
id pushNotificationsManagerMock = OCMPartialMock(manager);
5954
OCMStub([LPPushNotificationsManager sharedManager]).andReturn(pushNotificationsManagerMock);
6055
OCMStub([pushNotificationsManagerMock respondsToSelector:
6156
@selector(leanplum_application:didRegisterForRemoteNotificationsWithDeviceToken:)]).andReturn(NO);
57+
return manager;
58+
}
59+
60+
- (NSString*)formatToken:(NSData*)token
61+
{
62+
NSString *formattedToken = [[LPNotificationsManager shared] hexadecimalStringFromData:token];
63+
formattedToken = [[[formattedToken stringByReplacingOccurrencesOfString:@"<" withString:@""]
64+
stringByReplacingOccurrencesOfString:@">" withString:@""]
65+
stringByReplacingOccurrencesOfString:@" " withString:@""];
66+
return formattedToken;
67+
}
68+
69+
- (void)removePushTypes
70+
{
71+
NSString *settingsKey = [[LPPushNotificationsManager sharedManager] leanplum_createUserNotificationSettingsKey];
72+
[[NSUserDefaults standardUserDefaults] removeObjectForKey:settingsKey];
73+
[[NSUserDefaults standardUserDefaults] synchronize];
74+
}
75+
76+
- (void)mockUserNotificationSettings:(UIUserNotificationType)type withCategoryId:(NSString *)categoryId
77+
{
78+
id mockApplication = OCMClassMock([UIApplication class]);
79+
OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
80+
UIMutableUserNotificationCategory *cat = [[UIMutableUserNotificationCategory alloc] init];
81+
cat.identifier = categoryId;
82+
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:type categories:[NSSet setWithObject:cat]];
83+
OCMStub([mockApplication currentUserNotificationSettings]).andReturn(settings);
84+
}
85+
86+
- (void)test_push_token
87+
{
88+
XCTAssertTrue([LeanplumHelper start_production_test]);
89+
90+
// Partial mock Action Manager.
91+
LPPushNotificationsManager *manager = [self mockManager];
6292

6393
// Remove Push Token.
6494
[manager removePushToken];
@@ -67,14 +97,10 @@ - (void)test_push_token
6797
UIApplication *app = [UIApplication sharedApplication];
6898
XCTestExpectation *expectNewToken = [self expectationWithDescription:@"expectNewToken"];
6999
NSData *token = [@"sample" dataUsingEncoding:NSUTF8StringEncoding];
70-
NSString *formattedToken = [[LPNotificationsManager shared] hexadecimalStringFromData:token];
71-
formattedToken = [[[formattedToken stringByReplacingOccurrencesOfString:@"<" withString:@""]
72-
stringByReplacingOccurrencesOfString:@">" withString:@""]
73-
stringByReplacingOccurrencesOfString:@" " withString:@""];
74100
[LPRequestSender validate_request:^BOOL(NSString *method, NSString *apiMethod,
75101
NSDictionary *params) {
76102
XCTAssertTrue([apiMethod isEqual:@"setDeviceAttributes"]);
77-
XCTAssertTrue([params[@"iosPushToken"] isEqual:formattedToken]);
103+
XCTAssertTrue([params[@"iosPushToken"] isEqual:[self formatToken:token]]);
78104
[expectNewToken fulfill];
79105
return YES;
80106
}];
@@ -91,15 +117,11 @@ - (void)test_push_token
91117

92118
// Test push token is sent if the token changes.
93119
token = [@"sample2" dataUsingEncoding:NSUTF8StringEncoding];
94-
formattedToken = [[LPNotificationsManager shared] hexadecimalStringFromData:token];
95-
formattedToken = [[[formattedToken stringByReplacingOccurrencesOfString:@"<" withString:@""]
96-
stringByReplacingOccurrencesOfString:@">" withString:@""]
97-
stringByReplacingOccurrencesOfString:@" " withString:@""];
98120
XCTestExpectation *expectUpdatedToken = [self expectationWithDescription:@"expectUpdatedToken"];
99121
[LPRequestSender validate_request:^BOOL(NSString *method, NSString *apiMethod,
100122
NSDictionary *params) {
101123
XCTAssertTrue([apiMethod isEqual:@"setDeviceAttributes"]);
102-
XCTAssertTrue([params[@"iosPushToken"] isEqual:formattedToken]);
124+
XCTAssertTrue([params[LP_PARAM_DEVICE_PUSH_TOKEN] isEqual:[self formatToken:token]]);
103125
[expectUpdatedToken fulfill];
104126
return YES;
105127
}];
@@ -108,6 +130,103 @@ - (void)test_push_token
108130
[self waitForExpectationsWithTimeout:2 handler:nil];
109131
}
110132

133+
/**
134+
* Test iOS Push Types are sent when notifications are registered successfully
135+
*/
136+
- (void)test_push_types
137+
{
138+
// Partial mock Action Manager.
139+
LPPushNotificationsManager *manager = [self mockManager];
140+
141+
// Remove Push Token.
142+
[manager removePushToken];
143+
[self removePushTypes];
144+
145+
[self mockUserNotificationSettings:UIUserNotificationTypeAlert withCategoryId:@"testCategory"];
146+
147+
UIApplication *app = [UIApplication sharedApplication];
148+
XCTestExpectation *expectNewToken = [self expectationWithDescription:@"expectNewToken"];
149+
NSData *token = [@"sample" dataUsingEncoding:NSUTF8StringEncoding];
150+
[LPRequestSender validate_request:^BOOL(NSString *method, NSString *apiMethod,
151+
NSDictionary *params) {
152+
XCTAssertTrue([apiMethod isEqual:@"setDeviceAttributes"]);
153+
XCTAssertTrue([params[LP_PARAM_DEVICE_PUSH_TOKEN] isEqual:[self formatToken:token]]);
154+
XCTAssertTrue([params[LP_PARAM_DEVICE_USER_NOTIFICATION_TYPES] isEqual:@(UIUserNotificationTypeAlert)]);
155+
XCTAssertTrue([params[LP_PARAM_DEVICE_USER_NOTIFICATION_CATEGORIES] isEqual:[LPJSON stringFromJSON:@[@"testCategory"]?: @""]]);
156+
[expectNewToken fulfill];
157+
return YES;
158+
}];
159+
160+
[manager leanplum_application:app didRegisterForRemoteNotificationsWithDeviceToken:token];
161+
162+
[self mockUserNotificationSettings:UIUserNotificationTypeBadge withCategoryId:@"testCategory2"];
163+
// Test categories will be sent even if token is the same
164+
[LPRequestSender validate_request:^BOOL(NSString *method, NSString *apiMethod,
165+
NSDictionary *params) {
166+
XCTAssertTrue([apiMethod isEqual:@"setDeviceAttributes"]);
167+
XCTAssertNil(params[LP_PARAM_DEVICE_PUSH_TOKEN]);
168+
XCTAssertTrue([params[LP_PARAM_DEVICE_USER_NOTIFICATION_TYPES] isEqual:@(UIUserNotificationTypeBadge)]);
169+
XCTAssertTrue([params[LP_PARAM_DEVICE_USER_NOTIFICATION_CATEGORIES] isEqual:[LPJSON stringFromJSON:@[@"testCategory2"]?: @""]]);
170+
return YES;
171+
}];
172+
[manager leanplum_application:app didRegisterForRemoteNotificationsWithDeviceToken:token];
173+
174+
[self waitForExpectationsWithTimeout:2 handler:nil];
175+
}
176+
177+
- (void)test_push_types_foreground
178+
{
179+
[self removePushTypes];
180+
181+
LPPushNotificationsManager *manager = [LPPushNotificationsManager sharedManager];
182+
id pushNotificationsManagerMock = OCMPartialMock(manager);
183+
OCMStub([LPPushNotificationsManager sharedManager]).andReturn(pushNotificationsManagerMock);
184+
185+
// Call start to attach the observer for App Resume/Foreground
186+
// Note that multiple start calls will attach multiple observers
187+
if (!Leanplum.hasStarted){
188+
XCTAssertTrue([LeanplumHelper start_production_test]);
189+
}
190+
191+
// Mock Application Notification Settings
192+
[self mockUserNotificationSettings:UIUserNotificationTypeAlert withCategoryId:@"testCategory"];
193+
194+
XCTestExpectation *expectPushTypesSet = [self expectationWithDescription:@"expectPushTypesSet"];
195+
[LPRequestSender validate_request:^BOOL(NSString *method, NSString *apiMethod,
196+
NSDictionary *params) {
197+
if ([apiMethod isEqual:@"setDeviceAttributes"]) {
198+
// Use the mock object to verify
199+
OCMVerify([[pushNotificationsManagerMock handler] sendUserNotificationSettingsIfChanged:[OCMArg any]]);
200+
XCTAssertTrue([params[LP_PARAM_DEVICE_USER_NOTIFICATION_TYPES] isEqual:@(UIUserNotificationTypeAlert)]);
201+
XCTAssertTrue([params[LP_PARAM_DEVICE_USER_NOTIFICATION_CATEGORIES] isEqual:[LPJSON stringFromJSON:@[@"testCategory"]?: @""]]);
202+
[expectPushTypesSet fulfill];
203+
}
204+
return YES;
205+
}];
206+
207+
// Triggers sendUserNotificationSettingsIfChanged and resumeSession
208+
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillEnterForegroundNotification
209+
object:nil];
210+
211+
// Verify no request is made if the settings are the same
212+
[LPRequestSender validate_request:^BOOL(NSString *method, NSString *apiMethod,
213+
NSDictionary *params) {
214+
if ([apiMethod isEqual:@"setDeviceAttributes"]) {
215+
XCTAssertTrue(NO);
216+
}
217+
return YES;
218+
}];
219+
220+
// Triggers sendUserNotificationSettingsIfChanged and resumeSession
221+
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillEnterForegroundNotification
222+
object:nil];
223+
// Use the mock object to verify
224+
OCMVerify([[pushNotificationsManagerMock handler] sendUserNotificationSettingsIfChanged:[OCMArg any]]);
225+
226+
[pushNotificationsManagerMock stopMocking];
227+
[self waitForExpectationsWithTimeout:2 handler:nil];
228+
}
229+
111230
- (void)test_update_and_remove_push_token
112231
{
113232
LPPushNotificationsManager *manager = [LPPushNotificationsManager sharedManager];

Leanplum-SDK/Classes/Notifications/Push/LPPushNotificationsHandler.m

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,39 @@ @interface LPPushNotificationsHandler()
1919
@property (nonatomic, strong) NSDate *notificationHandledTime;
2020
@end
2121

22+
@interface UIUserNotificationSettings (LPUtil)
23+
@property (readonly, nonatomic) NSDictionary *dictionary;
24+
+(NSDictionary *)toRequestParams:(NSDictionary *)settings;
25+
@end
26+
27+
@implementation UIUserNotificationSettings (LPUtil)
28+
29+
- (NSDictionary *)dictionary
30+
{
31+
NSNumber *types = @([self types]);
32+
NSMutableArray *categories = [NSMutableArray array];
33+
for (UIMutableUserNotificationCategory *category in [self categories]) {
34+
if ([category identifier]) {
35+
// Skip categories that have no identifier.
36+
[categories addObject:[category identifier]];
37+
}
38+
}
39+
NSArray *sortedCategories = [categories sortedArrayUsingSelector:@selector(compare:)];
40+
NSDictionary *settings = @{LP_PARAM_DEVICE_USER_NOTIFICATION_TYPES: types,
41+
LP_PARAM_DEVICE_USER_NOTIFICATION_CATEGORIES: sortedCategories};
42+
return settings;
43+
}
44+
45+
+ (NSDictionary *)toRequestParams:(NSDictionary *)settings
46+
{
47+
NSDictionary *params = [@{
48+
LP_PARAM_DEVICE_USER_NOTIFICATION_TYPES: settings[LP_PARAM_DEVICE_USER_NOTIFICATION_TYPES],
49+
LP_PARAM_DEVICE_USER_NOTIFICATION_CATEGORIES:
50+
[LPJSON stringFromJSON:settings[LP_PARAM_DEVICE_USER_NOTIFICATION_CATEGORIES]] ?: @""} mutableCopy];
51+
return params;
52+
}
53+
@end
54+
2255
@implementation LPPushNotificationsHandler
2356

2457
-(instancetype)init
@@ -94,12 +127,23 @@ -(void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token
94127
stringByReplacingOccurrencesOfString:@">" withString:@""]
95128
stringByReplacingOccurrencesOfString:@" " withString:@""];
96129

130+
NSMutableDictionary* deviceAttributeParams = [[NSMutableDictionary alloc] init];
97131
// Send push token if we don't have one and when the token changed.
98132
// We no longer send in start's response because saved push token will be send in start too.
99133
NSString *existingToken = [[LPPushNotificationsManager sharedManager] pushToken];
100134
if (!existingToken || ![existingToken isEqualToString:formattedToken]) {
101135
[[LPPushNotificationsManager sharedManager] updatePushToken:formattedToken];
102-
LPRequest *request = [LPRequestFactory setDeviceAttributesWithParams:@{LP_PARAM_DEVICE_PUSH_TOKEN: formattedToken}];
136+
deviceAttributeParams[LP_PARAM_DEVICE_PUSH_TOKEN] = formattedToken;
137+
}
138+
// Get the push types if changed
139+
NSDictionary* settings = [[UIApplication sharedApplication].currentUserNotificationSettings dictionary];
140+
if ([self updateUserNotificationSettings:settings]) {
141+
[deviceAttributeParams addEntriesFromDictionary:[UIUserNotificationSettings toRequestParams:settings]];
142+
}
143+
144+
// If there are changes to the push token and/or the push types, send a request
145+
if (deviceAttributeParams.count > 0) {
146+
LPRequest *request = [LPRequestFactory setDeviceAttributesWithParams:deviceAttributeParams];
103147
[[LPRequestSender sharedInstance] send:request];
104148
}
105149
LP_END_TRY
@@ -121,31 +165,28 @@ - (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notifi
121165
LP_END_TRY
122166
}
123167

124-
#pragma mark - Push Notifications
125-
- (void)sendUserNotificationSettingsIfChanged:(UIUserNotificationSettings *)notificationSettings
168+
#pragma mark - Notification Settings
169+
- (BOOL)updateUserNotificationSettings:(NSDictionary *)newSettings
126170
{
127-
// Send settings.
128171
NSString *settingsKey = [[LPPushNotificationsManager sharedManager] leanplum_createUserNotificationSettingsKey];
129172
NSDictionary *existingSettings = [[NSUserDefaults standardUserDefaults] dictionaryForKey:settingsKey];
130-
NSNumber *types = @([notificationSettings types]);
131-
NSMutableArray *categories = [NSMutableArray array];
132-
for (UIMutableUserNotificationCategory *category in [notificationSettings categories]) {
133-
if ([category identifier]) {
134-
// Skip categories that have no identifier.
135-
[categories addObject:[category identifier]];
136-
}
137-
}
138-
NSArray *sortedCategories = [categories sortedArrayUsingSelector:@selector(compare:)];
139-
NSDictionary *settings = @{LP_PARAM_DEVICE_USER_NOTIFICATION_TYPES: types,
140-
LP_PARAM_DEVICE_USER_NOTIFICATION_CATEGORIES: sortedCategories};
141-
if (![existingSettings isEqualToDictionary:settings]) {
142-
[[NSUserDefaults standardUserDefaults] setObject:settings forKey:settingsKey];
173+
if (![existingSettings isEqualToDictionary:newSettings]) {
174+
[[NSUserDefaults standardUserDefaults] setObject:newSettings forKey:settingsKey];
143175
[[NSUserDefaults standardUserDefaults] synchronize];
176+
return YES;
177+
}
178+
179+
return NO;
180+
}
181+
182+
#pragma mark - Push Notifications
183+
- (void)sendUserNotificationSettingsIfChanged:(UIUserNotificationSettings *)notificationSettings
184+
{
185+
NSDictionary* settings = [notificationSettings dictionary];
186+
// Send settings.
187+
if ([self updateUserNotificationSettings:settings]) {
144188
NSString *existingToken = [[LPPushNotificationsManager sharedManager] pushToken];
145-
NSMutableDictionary *params = [@{
146-
LP_PARAM_DEVICE_USER_NOTIFICATION_TYPES: types,
147-
LP_PARAM_DEVICE_USER_NOTIFICATION_CATEGORIES:
148-
[LPJSON stringFromJSON:sortedCategories] ?: @""} mutableCopy];
189+
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:[UIUserNotificationSettings toRequestParams:settings]];
149190
if (existingToken) {
150191
params[LP_PARAM_DEVICE_PUSH_TOKEN] = existingToken;
151192
}

0 commit comments

Comments
 (0)