From 9e687b546f2378183103a31d9a055ef53d7000f4 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Mon, 29 Jan 2018 16:54:05 -0800 Subject: [PATCH 01/36] Email SDK Changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Begins to add support for email in the iOS SDK --- .../OneSignal.xcodeproj/project.pbxproj | 12 +++ .../OneSignalSDK/Source/NSNull+OneSignal.h | 34 ++++++++ .../OneSignalSDK/Source/NSNull+OneSignal.m | 46 +++++++++++ iOS_SDK/OneSignalSDK/Source/OSSubscription.h | 1 + iOS_SDK/OneSignalSDK/Source/OSSubscription.m | 5 ++ iOS_SDK/OneSignalSDK/Source/OneSignal.h | 12 +++ iOS_SDK/OneSignalSDK/Source/OneSignal.m | 78 ++++++++++++++++++- iOS_SDK/OneSignalSDK/Source/Requests.h | 16 +++- iOS_SDK/OneSignalSDK/Source/Requests.m | 38 ++++++++- 9 files changed, 235 insertions(+), 7 deletions(-) create mode 100644 iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.h create mode 100644 iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.m diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index d9848ce8a..0a6573e08 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -132,6 +132,10 @@ CA08FC801FE99B25004C445F /* Requests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA08FC7D1FE99B25004C445F /* Requests.m */; }; CA08FC811FE99B25004C445F /* Requests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA08FC7D1FE99B25004C445F /* Requests.m */; }; CA08FC871FE99BB4004C445F /* OneSignalClientOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = CA08FC831FE99BB4004C445F /* OneSignalClientOverrider.m */; }; + CA63AF70201FF83500E340FB /* NSNull+OneSignal.h in Headers */ = {isa = PBXBuildFile; fileRef = CA63AF6E201FF83500E340FB /* NSNull+OneSignal.h */; }; + CA63AF71201FF83500E340FB /* NSNull+OneSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */; }; + CA63AF72201FF83500E340FB /* NSNull+OneSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */; }; + CA63AF73201FF83500E340FB /* NSNull+OneSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -254,6 +258,8 @@ CA08FC7D1FE99B25004C445F /* Requests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Requests.m; sourceTree = ""; }; CA08FC821FE99BB4004C445F /* OneSignalClientOverrider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalClientOverrider.h; sourceTree = ""; }; CA08FC831FE99BB4004C445F /* OneSignalClientOverrider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalClientOverrider.m; sourceTree = ""; }; + CA63AF6E201FF83500E340FB /* NSNull+OneSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNull+OneSignal.h"; sourceTree = ""; }; + CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNull+OneSignal.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -443,6 +449,8 @@ 912412091E73342200E41FD7 /* UIApplicationDelegate+OneSignal.m */, 9124120A1E73342200E41FD7 /* UNUserNotificationCenter+OneSignal.h */, 9124120B1E73342200E41FD7 /* UNUserNotificationCenter+OneSignal.m */, + CA63AF6E201FF83500E340FB /* NSNull+OneSignal.h */, + CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */, ); name = Categories; sourceTree = ""; @@ -519,6 +527,7 @@ 912412391E73342200E41FD7 /* OneSignalWebView.h in Headers */, 91C7725E1E7CCE1000D612D0 /* OneSignalInternal.h in Headers */, 9129C6BD1E89E7AB009CB6A0 /* OSSubscription.h in Headers */, + CA63AF70201FF83500E340FB /* NSNull+OneSignal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -680,6 +689,7 @@ 91F58D891E7C9A240017D24D /* OneSignalNotificationSettingsIOS7.m in Sources */, 912412221E73342200E41FD7 /* OneSignalLocation.m in Sources */, 1AF75EAE1E8567FD0097B315 /* NSString+OneSignal.m in Sources */, + CA63AF71201FF83500E340FB /* NSNull+OneSignal.m in Sources */, 454F94F51FAD2E5A00D74CCF /* OSNotificationPayload.m in Sources */, 9129C6BE1E89E7AB009CB6A0 /* OSSubscription.m in Sources */, 912412361E73342200E41FD7 /* OneSignalTrackIAP.m in Sources */, @@ -713,6 +723,7 @@ 91B6EA421E85D38F00B5CF01 /* OSObservable.m in Sources */, 91F58D8A1E7C9A240017D24D /* OneSignalNotificationSettingsIOS7.m in Sources */, 912412231E73342200E41FD7 /* OneSignalLocation.m in Sources */, + CA63AF72201FF83500E340FB /* NSNull+OneSignal.m in Sources */, 1AF75EB01E8569720097B315 /* NSString+OneSignal.m in Sources */, 9129C6BF1E89E7AB009CB6A0 /* OSSubscription.m in Sources */, 912412371E73342200E41FD7 /* OneSignalTrackIAP.m in Sources */, @@ -735,6 +746,7 @@ 91F58D851E7C88230017D24D /* OneSignalNotificationSettingsIOS10.m in Sources */, 912412241E73342200E41FD7 /* OneSignalLocation.m in Sources */, 912412491E73369800E41FD7 /* OneSignalHelper.m in Sources */, + CA63AF73201FF83500E340FB /* NSNull+OneSignal.m in Sources */, 4529DEE41FA82C6200CEAB1D /* NSURLSessionOverrider.m in Sources */, 4529DED21FA81EA800CEAB1D /* NSObjectOverrider.m in Sources */, 912412341E73342200E41FD7 /* OneSignalTracker.m in Sources */, diff --git a/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.h b/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.h new file mode 100644 index 000000000..ad055b71a --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.h @@ -0,0 +1,34 @@ +/** + * Modified MIT License + * + * Copyright 2017 OneSignal + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import + +@interface NSNull (OneSignal) + ++ (id)nullIfObjectIsNil:(id)object; + +@end diff --git a/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.m b/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.m new file mode 100644 index 000000000..45378226c --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.m @@ -0,0 +1,46 @@ +/** + * Modified MIT License + * + * Copyright 2017 OneSignal + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + + + +/* + Creating an NSDictionary can raise exceptions when working with _Nullable parameters + If the parameter is nil, inserting it will cause a crash + To resolve this, we can use this helper method to check if the object is nil, and if it is, return NSNull instead + This will not crash the application, unlike inserting nil +*/ + +#import "NSNull+OneSignal.h" + +@implementation NSNull (OneSignal) + ++ (id)nullIfObjectIsNil:(id)object { + return object != nil ? object : [NSNull null]; +} + +@end diff --git a/iOS_SDK/OneSignalSDK/Source/OSSubscription.h b/iOS_SDK/OneSignalSDK/Source/OSSubscription.h index 37116f7a1..334198373 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSSubscription.h +++ b/iOS_SDK/OneSignalSDK/Source/OSSubscription.h @@ -66,6 +66,7 @@ typedef OSObservable*, OSSubscriptionState @property (nonatomic) BOOL accpeted; +- (void)setEmailUserId:(NSString *)emailUserId; - (void)setAccepted:(BOOL)inAccpeted; - (void)persistAsFrom; - (BOOL)compare:(OSSubscriptionState*)from; diff --git a/iOS_SDK/OneSignalSDK/Source/OSSubscription.m b/iOS_SDK/OneSignalSDK/Source/OSSubscription.m index b45d023bf..01df83cdf 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSSubscription.m +++ b/iOS_SDK/OneSignalSDK/Source/OSSubscription.m @@ -127,6 +127,11 @@ - (void)setUserSubscriptionSetting:(BOOL)userSubscriptionSetting { [self.observable notifyChange:self]; } + +- (void)setEmailUserId:(NSString *)emailUserId { + _emailUserId = emailUserId; +} + - (void)setAccepted:(BOOL)inAccpeted { BOOL lastSubscribed = self.subscribed; diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.h b/iOS_SDK/OneSignalSDK/Source/OneSignal.h index 971a3e4c8..35f78de82 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.h +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.h @@ -230,6 +230,7 @@ typedef NS_ENUM(NSInteger, OSNotificationPermission) { @property (readonly, nonatomic) BOOL subscribed; // (yes only if userId, pushToken, and setSubscription exists / are true) @property (readonly, nonatomic) BOOL userSubscriptionSetting; // returns setSubscription state. +@property (readonly, nonatomic) NSString* emailUserId; // The new Email user ID @property (readonly, nonatomic) NSString* userId; // AKA OneSignal PlayerId @property (readonly, nonatomic) NSString* pushToken; // AKA Apple Device Token - (NSDictionary*)toDictionary; @@ -379,4 +380,15 @@ typedef NS_ENUM(NSUInteger, ONE_S_LOG_LEVEL) { + (UNMutableNotificationContent*)didReceiveNotificationExtensionRequest:(UNNotificationRequest*)request withMutableNotificationContent:(UNMutableNotificationContent*)replacementContent; + (UNMutableNotificationContent*)serviceExtensionTimeWillExpireRequest:(UNNotificationRequest*)request withMutableNotificationContent:(UNMutableNotificationContent*)replacementContent; +// New Email methods + +typedef void (^OSEmailFailureBlock)(NSError* error); +typedef void (^OSEmailSuccessBlock)(); +typedef void (^OSMultipleFailureBlock)(NSDictionary *errors); +typedef void (^OSMultipleSuccessBlock)(NSDictionary *results); + ++ (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _Nullable)hashToken withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock; ++ (void)setUnauthenticatedEmail:(NSString * _Nonnull)email withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock; ++ (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock; + @end diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index 56d26b958..ac551a811 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -128,6 +128,9 @@ @implementation OneSignal static BOOL shouldDelaySubscriptionUpdate = false; +static NSString *currentUserEmail; +static NSString *authHashToken; + static NSMutableArray* pendingSendTagCallbacks; static OSResultSuccessBlock pendingGetTagsSuccessBlock; static OSFailureBlock pendingGetTagsFailureBlock; @@ -904,7 +907,7 @@ + (void)updateDeviceToken:(NSString*)deviceToken onSuccess:(OSResultSuccessBlock [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Calling OneSignal PUT updated pushToken!"]; - [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId appId:self.app_id deviceToken:deviceToken notificationTypes:@([self getNotificationTypes])] onSuccess:successBlock onFailure:failureBlock]; + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId appId:self.app_id deviceToken:deviceToken notificationTypes:@([self getNotificationTypes]) withParentId:nil] onSuccess:successBlock onFailure:failureBlock]; [self fireIdsAvailableCallback]; } @@ -1491,6 +1494,79 @@ + (UNMutableNotificationContent*)serviceExtensionTimeWillExpireRequest:(UNNotifi serviceExtensionTimeWillExpireRequest:request withMutableNotificationContent:replacementContent]; } + +#pragma mark Email + ++ (void)updateEmailPlayerId:(NSString *)emailPlayerId { + if (!emailPlayerId) + return; + + [[NSUserDefaults standardUserDefaults] setObject:emailPlayerId forKey:@"GT_EMAIL_PLAYER_ID"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + if (![emailPlayerId isEqualToString:_currentSubscriptionState.emailUserId]) { + //if the ID has changed, call Edit Edvice + + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailPlayerId appId:self.app_id deviceToken:self.currentSubscriptionState.pushToken notificationTypes:@([self getNotificationTypes]) withParentId:self.currentSubscriptionState.emailUserId] onSuccess:nil onFailure:nil]; + } +} + ++ (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _Nullable)hashToken withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { + if (![OneSignalHelper isValidEmail:email]) { + failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"description" : @"Email is invalid"}]); + return; + } + + let emailId = (NSString *)[[NSUserDefaults standardUserDefaults] objectForKey:@"GT_EMAIL_PLAYER_ID"]; + + if (emailId) { + [[OneSignalClient sharedClient] executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:self.currentSubscriptionState.pushToken notificationTypes:@([self getNotificationTypes]) withParentId:nil] onSuccess:^(NSDictionary *result) { + successBlock(); + } onFailure:^(NSError *error) { + failureBlock(error); + }]; + } else { + [OneSignalClient.sharedClient executeRequest:[OSRequestCreateDevice withAppId:self.app_id withDeviceType:@11 withEmail:currentUserEmail withPlayerId:_currentSubscriptionState.userId withEmailAuthHash:authHashToken] onSuccess:^(NSDictionary *result) { + let emailPlayerId = (NSString *)result[@"id"]; + + if (emailPlayerId) { + [[NSUserDefaults standardUserDefaults] setObject:emailPlayerId forKey:@"GT_EMAIL_PLAYER_ID"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + successBlock(); + } else { + [self onesignal_Log:ONE_S_LL_ERROR message:@"Missing OneSignal Email Player ID"]; + } + } onFailure:^(NSError *error) { + failureBlock(error); + }]; + } +} + ++ (void)setUnauthenticatedEmail:(NSString * _Nonnull)email withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { + +} + ++ (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { + if (!self.currentSubscriptionState.emailUserId) { + [OneSignal onesignal_Log:ONE_S_LL_ERROR message:@"Email Player ID does not exist, cannot logout"]; + failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"description" : @"Attempted to log out of the user's email with OneSignal. The user does not currently have an email player ID and is not logged in, so it is not possible to log out of the email for this device"}]); + return; + } + + [OneSignalClient.sharedClient executeRequest:[OSRequestLogoutEmail withEmailPlayerId:self.currentSubscriptionState.emailUserId devicePlayerId:self.currentSubscriptionState.userId emailAuthHash:authHashToken] onSuccess:^(NSDictionary *result) { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"GT_EMAIL_PLAYER_ID"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + [self.currentSubscriptionState setEmailUserId:nil]; + currentUserEmail = nil; + authHashToken = nil; + + successBlock(); + } onFailure:^(NSError *error) { + failureBlock(error); + }]; +} @end // Swizzles UIApplication class to swizzling the following: diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.h b/iOS_SDK/OneSignalSDK/Source/Requests.h index 88714b4a6..0802f409b 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.h +++ b/iOS_SDK/OneSignalSDK/Source/Requests.h @@ -50,10 +50,6 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)withAppId:(NSString *)appId withJson:(NSMutableDictionary *)json; @end -@interface OSRequestUpdateDeviceToken : OneSignalRequest -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId deviceToken:(NSString *)identifier notificationTypes:(NSNumber *)notificationTypes; -@end - @interface OSRequestUpdateNotificationTypes : OneSignalRequest + (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId notificationTypes:(NSNumber *)notificationTypes; @end @@ -81,9 +77,21 @@ NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END +@interface OSRequestUpdateDeviceToken : OneSignalRequest ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nonnull)identifier notificationTypes:(NSNumber * _Nonnull)notificationTypes withParentId:(NSString * _Nullable)parentId; +@end + @interface OSRequestRegisterUser : OneSignalRequest + (instancetype _Nonnull)withData:(NSDictionary * _Nonnull)registrationData userId:(NSString * _Nullable)userId; @end +@interface OSRequestCreateDevice : OneSignalRequest ++ (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId withDeviceType:(NSNumber * _Nonnull)deviceType withEmail:(NSString * _Nullable)email withPlayerId:(NSString * _Nullable)playerId withEmailAuthHash:(NSString * _Nullable)emailAuthHash; +@end + +@interface OSRequestLogoutEmail : OneSignalRequest ++ (instancetype _Nonnull)withEmailPlayerId:(NSString * _Nonnull)emailPlayerId devicePlayerId:(NSString * _Nonnull)devicePlayerId emailAuthHash:(NSString * _Nullable)emailAuthHash; +@end + #endif /* Requests_h */ diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.m b/iOS_SDK/OneSignalSDK/Source/Requests.m index 07d5e0a1d..bc5916e58 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.m +++ b/iOS_SDK/OneSignalSDK/Source/Requests.m @@ -29,6 +29,7 @@ #import "Requests.h" #import "OneSignalRequest.h" #import "OneSignalHelper.h" +#import "NSNull+OneSignal.h" #import #import #import @@ -95,13 +96,14 @@ + (instancetype)withAppId:(NSString *)appId withJson:(NSMutableDictionary *)json @end @implementation OSRequestUpdateDeviceToken -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId deviceToken:(NSString *)identifier notificationTypes:(NSNumber *)notificationTypes { ++ (instancetype)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nonnull)identifier notificationTypes:(NSNumber * _Nonnull)notificationTypes withParentId:(NSString * _Nullable)parentId { let request = [OSRequestUpdateDeviceToken new]; request.parameters = @{ @"app_id" : appId, @"identifier" : identifier, - @"notification_types" : notificationTypes + @"notification_types" : notificationTypes, + @"parent_player_id" : [NSNull nullIfObjectIsNil:parentId] }; request.method = PUT; @@ -111,6 +113,38 @@ + (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId deviceToke } @end +@implementation OSRequestCreateDevice ++ (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId withDeviceType:(NSNumber * _Nonnull)deviceType withEmail:(NSString * _Nullable)email withPlayerId:(NSString * _Nullable)playerId withEmailAuthHash:(NSString * _Nullable)emailAuthHash { + let request = [OSRequestCreateDevice new]; + + request.parameters = @{ + @"app_id" : appId, + @"device_type" : deviceType, + @"email" : [NSNull nullIfObjectIsNil:email], + @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthHash], + @"device_player_id" : [NSNull nullIfObjectIsNil:playerId] + }; + + request.method = POST; + request.path = @"/players"; + + return request; +} +@end + +@implementation OSRequestLogoutEmail + ++ (instancetype _Nonnull)withEmailPlayerId:(NSString * _Nonnull)emailPlayerId devicePlayerId:(NSString * _Nonnull)devicePlayerId emailAuthHash:(NSString * _Nullable)emailAuthHash { + let request = [OSRequestLogoutEmail new]; + + request.parameters = @{@"device_player_id" : devicePlayerId, @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthHash]}; + request.method = POST; + request.path = [NSString stringWithFormat:@"%@/email_logout", emailPlayerId]; + + return request; +} + +@end @implementation OSRequestUpdateNotificationTypes + (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId notificationTypes:(NSNumber *)notificationTypes { From ee30145c9e7d26a1ad121cec2cc578dea0f02089 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Mon, 29 Jan 2018 18:38:01 -0800 Subject: [PATCH 02/36] Multi-Requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Changed the SDK so that for certain HTTP requests (ie. registering a user or sending location), the SDK will send two HTTP requests (separate for Push user ID and Email user ID). • The method to execute multiple HTTP requests will wait until both finish to trigger a callback. Each request is passed in with a key string. The callback returns key/value pairs where the key is associated with the same key. • Added tests covering the additional functionality --- .../OneSignal.xcodeproj/project.pbxproj | 2 +- iOS_SDK/OneSignalSDK/Source/OneSignal.m | 89 ++++++++++--- iOS_SDK/OneSignalSDK/Source/OneSignalClient.h | 3 + iOS_SDK/OneSignalSDK/Source/OneSignalClient.m | 39 ++++++ .../OneSignalSDK/Source/OneSignalLocation.m | 10 +- .../OneSignalSDK/Source/OneSignalTracker.m | 19 ++- .../Shadows/OneSignalClientOverrider.h | 2 + .../Shadows/OneSignalClientOverrider.m | 85 ++++++++---- iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m | 123 +++++++++++++----- 9 files changed, 296 insertions(+), 76 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index 0a6573e08..e35271296 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -402,8 +402,8 @@ 91F58D7B1E7C7EE30017D24D /* NotificationSettings */, 912412461E73349500E41FD7 /* Categories */, 912412451E73346700E41FD7 /* UI */, - 912411F01E73342200E41FD7 /* OneSignal.h */, 91C7725D1E7CCE1000D612D0 /* OneSignalInternal.h */, + 912411F01E73342200E41FD7 /* OneSignal.h */, 912411F11E73342200E41FD7 /* OneSignal.m */, 912411F41E73342200E41FD7 /* OneSignalHelper.h */, 912411F51E73342200E41FD7 /* OneSignalHelper.m */, diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index ac551a811..ac56e0e4a 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -278,6 +278,10 @@ + (NSString*)mUserId { return self.currentSubscriptionState.userId; } ++ (NSString *)mEmailUserId { + return self.currentSubscriptionState.emailUserId; +} + + (void)setMSDKType:(NSString*)type { mSDKType = type; } @@ -682,12 +686,31 @@ + (void) sendTagsToServer { NSArray* nowProcessingCallbacks = pendingSendTagCallbacks; pendingSendTagCallbacks = nil; - [OneSignalClient.sharedClient executeRequest:[OSRequestSendTagsToServer withUserId:self.currentSubscriptionState.userId appId:self.app_id tags:nowSendingTags networkType:[OneSignalHelper getNetType]] onSuccess:^(NSDictionary *result) { + NSMutableDictionary *requests = [NSMutableDictionary new]; + + requests[@"push"] = [OSRequestSendTagsToServer withUserId:self.currentSubscriptionState.userId appId:self.app_id tags:nowSendingTags networkType:[OneSignalHelper getNetType]]; + + if (self.currentSubscriptionState.emailUserId) + requests[@"email"] = [OSRequestSendTagsToServer withUserId:self.currentSubscriptionState.emailUserId appId:self.app_id tags:nowSendingTags networkType:[OneSignalHelper getNetType]]; + + [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:^(NSDictionary *results) { + //the tags for email & push are identical so it doesn't matter what we return in the success block + + NSDictionary *resultTags = results[@"push"]; + + if (!resultTags) + resultTags = results[@"email"]; + if (nowProcessingCallbacks) for (OSPendingCallbacks *callbackSet in nowProcessingCallbacks) if (callbackSet.successBlock) - callbackSet.successBlock(result); - } onFailure:^(NSError *error) { + callbackSet.successBlock(resultTags); + + } onFailure:^(NSDictionary *errors) { + NSError *error = errors[@"push"]; + if (!error) + error = errors[@"email"]; + if (nowProcessingCallbacks) for (OSPendingCallbacks *callbackSet in nowProcessingCallbacks) if (callbackSet.failureBlock) @@ -1058,7 +1081,15 @@ + (void)registerUserInternal { [OneSignalLocation clearLastLocation]; } - [OneSignalClient.sharedClient executeRequest:[OSRequestRegisterUser withData:dataDic userId:self.currentSubscriptionState.userId] onSuccess:^(NSDictionary *result) { + let requests = [NSMutableDictionary new]; + requests[@"push"] = [OSRequestRegisterUser withData:dataDic userId:self.currentSubscriptionState.userId]; + + if ([[NSUserDefaults standardUserDefaults] objectForKey:@"GT_EMAIL_PLAYER_ID"]) + requests[@"email"] = [OSRequestRegisterUser withData:dataDic userId:self.currentSubscriptionState.emailUserId]; + + NSLog(@"EXECUTING REQUESTS: %@", requests); + + [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:^(NSDictionary *results) { waitingForOneSReg = false; // Success, no more high priority @@ -1066,10 +1097,19 @@ + (void)registerUserInternal { [self updateLastSessionDateTime]; - if (result[@"id"]) { - self.currentSubscriptionState.userId = result[@"id"]; + //update email player ID + if (results[@"email"] && results[@"email"][@"id"]) { + self.currentSubscriptionState.emailUserId = results[@"email"][@"id"]; + [[NSUserDefaults standardUserDefaults] setObject:self.currentSubscriptionState.emailUserId forKey:@"GT_EMAIL_PLAYER_ID"]; + //NSUserDefaults Synchronize: called after the next if-statement + } + + //update push player id + if (results.count > 0 && results[@"push"][@"id"]) { + self.currentSubscriptionState.userId = results[@"push"][@"id"]; + [[NSUserDefaults standardUserDefaults] setObject:self.currentSubscriptionState.userId forKey:@"GT_PLAYER_ID"]; - [[NSUserDefaults standardUserDefaults] synchronize]; + //NSUserDefaults Synchronize: called after this if-statement if (self.currentSubscriptionState.pushToken) [self updateDeviceToken:self.currentSubscriptionState.pushToken @@ -1098,9 +1138,14 @@ + (void)registerUserInternal { } } - } onFailure:^(NSError *error) { + + [[NSUserDefaults standardUserDefaults] synchronize]; + + } onFailure:^(NSDictionary *errors) { waitingForOneSReg = false; - [OneSignal onesignal_Log:ONE_S_LL_ERROR message:[NSString stringWithFormat: @"Error registering with OneSignal: %@", error]]; + + for (NSString *key in @[@"push", @"email"]) + [OneSignal onesignal_Log:ONE_S_LL_ERROR message:[NSString stringWithFormat: @"Encountered error during %@ registration with OneSignal: %@", key, errors[key]]]; //If the failed registration is priority, force the next one to be a high priority nextRegistrationIsHighPriority = YES; @@ -1513,17 +1558,23 @@ + (void)updateEmailPlayerId:(NSString *)emailPlayerId { + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _Nullable)hashToken withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { if (![OneSignalHelper isValidEmail:email]) { - failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"description" : @"Email is invalid"}]); + if (failureBlock) + failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"description" : @"Email is invalid"}]); return; } + currentUserEmail = email; + authHashToken = hashToken; + let emailId = (NSString *)[[NSUserDefaults standardUserDefaults] objectForKey:@"GT_EMAIL_PLAYER_ID"]; if (emailId) { - [[OneSignalClient sharedClient] executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:self.currentSubscriptionState.pushToken notificationTypes:@([self getNotificationTypes]) withParentId:nil] onSuccess:^(NSDictionary *result) { - successBlock(); + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:self.currentSubscriptionState.pushToken notificationTypes:@([self getNotificationTypes]) withParentId:nil] onSuccess:^(NSDictionary *result) { + if (successBlock) + successBlock(); } onFailure:^(NSError *error) { - failureBlock(error); + if (failureBlock) + failureBlock(error); }]; } else { [OneSignalClient.sharedClient executeRequest:[OSRequestCreateDevice withAppId:self.app_id withDeviceType:@11 withEmail:currentUserEmail withPlayerId:_currentSubscriptionState.userId withEmailAuthHash:authHashToken] onSuccess:^(NSDictionary *result) { @@ -1533,12 +1584,14 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ [[NSUserDefaults standardUserDefaults] setObject:emailPlayerId forKey:@"GT_EMAIL_PLAYER_ID"]; [[NSUserDefaults standardUserDefaults] synchronize]; - successBlock(); + if (successBlock) + successBlock(); } else { [self onesignal_Log:ONE_S_LL_ERROR message:@"Missing OneSignal Email Player ID"]; } } onFailure:^(NSError *error) { - failureBlock(error); + if (failureBlock) + failureBlock(error); }]; } } @@ -1562,9 +1615,11 @@ + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withF currentUserEmail = nil; authHashToken = nil; - successBlock(); + if (successBlock) + successBlock(); } onFailure:^(NSError *error) { - failureBlock(error); + if (failureBlock) + failureBlock(error); }]; } @end diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.h b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.h index 02bf97f7f..1d0991f41 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.h +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.h @@ -36,6 +36,9 @@ + (OneSignalClient *)sharedClient; - (void)executeRequest:(OneSignalRequest *)request onSuccess:(OSResultSuccessBlock)successBlock onFailure:(OSFailureBlock)failureBlock; - (void)executeSynchronousRequest:(OneSignalRequest *)request onSuccess:(OSResultSuccessBlock)successBlock onFailure:(OSFailureBlock)failureBlock; + +// Executes multiple OneSignalRequest's simultaneously, needs a unique identifier for each request +- (void)executeSimultaneousRequests:(NSDictionary *)requests withSuccess:(OSMultipleSuccessBlock)successBlock onFailure:(OSMultipleFailureBlock)failureBlock; @end #endif diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m index f9d0266c7..802dab6b0 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m @@ -51,6 +51,45 @@ -(instancetype)init { return self; } +- (void)executeSimultaneousRequests:(NSDictionary *)requests withSuccess:(OSMultipleSuccessBlock)successBlock onFailure:(OSMultipleFailureBlock)failureBlock { + + if (requests.allKeys.count == 0) + return; + + //execute on a background thread or the semaphore will block the caller thread + dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + __block NSMutableDictionary *errors = [NSMutableDictionary new]; + __block NSMutableDictionary *results = [NSMutableDictionary new]; + + for (NSString *identifier in requests.allKeys) { + let request = requests[identifier]; + + [self executeRequest:request onSuccess:^(NSDictionary *result) { + results[identifier] = result; + dispatch_semaphore_signal(semaphore); + } onFailure:^(NSError *error) { + errors[identifier] = error; + dispatch_semaphore_signal(semaphore); + }]; + } + + for (int i = 0; i < requests.count; i++) { + dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 15 * NSEC_PER_SEC)); + } + + //requests should all be completed at this point + dispatch_async(dispatch_get_main_queue(), ^{ + if (errors.allKeys.count > 0 && failureBlock) { + failureBlock(errors); + } else if (errors.allKeys.count == 0 && successBlock) { + successBlock(results); + } + }); + }); +} + - (void)executeRequest:(OneSignalRequest *)request onSuccess:(OSResultSuccessBlock)successBlock onFailure:(OSFailureBlock)failureBlock { if (![self validRequest:request]) { [self handleMissingAppIdError:failureBlock withRequest:request]; diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m b/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m index 433e195a5..b80e6db7f 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m @@ -35,6 +35,7 @@ @interface OneSignal () void onesignal_Log(ONE_S_LOG_LEVEL logLevel, NSString* message); ++ (NSString *)mEmailUserId; + (NSString*)mUserId; @end @@ -254,7 +255,14 @@ + (void)sendLocation { initialLocationSent = YES; - [OneSignalClient.sharedClient executeRequest:[OSRequestSendLocation withUserId:[OneSignal mUserId] appId:[OneSignal app_id] location:lastLocation networkType:[OneSignalHelper getNetType] backgroundState:([UIApplication sharedApplication].applicationState != UIApplicationStateActive)] onSuccess:nil onFailure:nil]; + NSMutableDictionary *requests = [NSMutableDictionary new]; + + if ([OneSignal mEmailUserId]) + requests[@"email"] = [OSRequestSendLocation withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] location:lastLocation networkType:[OneSignalHelper getNetType] backgroundState:([UIApplication sharedApplication].applicationState != UIApplicationStateActive)]; + + requests[@"push"] = [OSRequestSendLocation withUserId:[OneSignal mUserId] appId:[OneSignal app_id] location:lastLocation networkType:[OneSignalHelper getNetType] backgroundState:([UIApplication sharedApplication].applicationState != UIApplicationStateActive)]; + + [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:nil onFailure:nil]; } } diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m b/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m index 0df368cc6..68a454606 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m @@ -39,6 +39,7 @@ + (void)registerUser; + (BOOL)sendNotificationTypesUpdate; + (BOOL)clearBadgeCount:(BOOL)fromNotifOpened; + (NSString*)mUserId; ++ (NSString *)mEmailUserId; @end @@ -119,7 +120,14 @@ + (void)onFocus:(BOOL)toBackground { // If resuming and badge was set, clear it on the server as well. if (wasBadgeSet && !toBackground) { - [OneSignalClient.sharedClient executeRequest:[OSRequestOnFocus withUserId:[OneSignal mUserId] appId:[OneSignal app_id] badgeCount:@0] onSuccess:nil onFailure:nil]; + NSMutableDictionary *requests = [NSMutableDictionary new]; + + requests[@"push"] = [OSRequestOnFocus withUserId:[OneSignal mUserId] appId:[OneSignal app_id] badgeCount:@0]; + + if ([OneSignal mEmailUserId]) + requests[@"email"] = [OSRequestOnFocus withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] badgeCount:@0]; + + [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:nil onFailure:nil]; return; } @@ -130,7 +138,14 @@ + (void)onFocus:(BOOL)toBackground { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [OneSignalTracker beginBackgroundFocusTask]; - [OneSignalClient.sharedClient executeSynchronousRequest:[OSRequestOnFocus withUserId:[OneSignal mUserId] appId:[OneSignal app_id] state:@"ping" type:@1 activeTime:@(timeToPingWith) netType:[OneSignalHelper getNetType]] onSuccess:nil onFailure:nil]; + NSMutableDictionary *requests = [NSMutableDictionary new]; + + requests[@"push"] = [OSRequestOnFocus withUserId:[OneSignal mUserId] appId:[OneSignal app_id] state:@"ping" type:@1 activeTime:@(timeToPingWith) netType:[OneSignalHelper getNetType]]; + + if ([OneSignal mEmailUserId]) + requests[@"email"] = [OSRequestOnFocus withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] state:@"ping" type:@1 activeTime:@(timeToPingWith) netType:[OneSignalHelper getNetType]]; + + [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:nil onFailure:nil]; [OneSignalTracker endBackgroundFocusTask]; }); diff --git a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.h b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.h index 7aa98d223..5c30cf21a 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.h +++ b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.h @@ -19,5 +19,7 @@ +(NSString*)lastUrl; +(void)setShouldExecuteInstantaneously:(BOOL)instant; + (dispatch_queue_t)getHTTPQueue; ++(void)runBackgroundThreads; ++(NSString *)lastHTTPRequestType; @end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m index 092e5664e..39a99611d 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m @@ -35,6 +35,7 @@ @implementation OneSignalClientOverrider static XCTestCase* currentTestInstance; static BOOL executeInstantaneously = true; static dispatch_queue_t executionQueue; +static NSString *lastHTTPRequestType; + (void)load { serialMockMainLooper = dispatch_queue_create("com.onesignal.unittest", DISPATCH_QUEUE_SERIAL); @@ -42,12 +43,40 @@ + (void)load { //with refactored networking code, need to replace the implementation of the execute request method so tests don't actually execite HTTP requests injectToProperClass(@selector(overrideExecuteRequest:onSuccess:onFailure:), @selector(executeRequest:onSuccess:onFailure:), @[], [OneSignalClientOverrider class], [OneSignalClient class]); + injectToProperClass(@selector(overrideExecuteSimultaneousRequests:withSuccess:onFailure:), @selector(executeSimultaneousRequests:withSuccess:onFailure:), @[], [OneSignalClientOverrider class], [OneSignalClient class]); executionQueue = dispatch_queue_create("com.onesignal.execution", NULL); } +- (void)overrideExecuteSimultaneousRequests:(NSDictionary *)requests withSuccess:(OSMultipleSuccessBlock)successBlock onFailure:(OSMultipleFailureBlock)failureBlock { + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + __block NSMutableDictionary *errors = [NSMutableDictionary new]; + __block NSMutableDictionary *results = [NSMutableDictionary new]; + + for (NSString *key in requests.allKeys) { + [OneSignalClient.sharedClient executeRequest:requests[key] onSuccess:^(NSDictionary *result) { + results[key] = result; + dispatch_semaphore_signal(semaphore); + } onFailure:^(NSError *error) { + errors[key] = error; + dispatch_semaphore_signal(semaphore); + }]; + } + + for (int i = 0; i < requests.count; i++) { + dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC)); + } + + if (errors.allKeys.count > 0 && failureBlock) { + failureBlock(errors); + } else if (errors.allKeys.count == 0 && successBlock) { + successBlock(results); + } +} + - (void)overrideExecuteRequest:(OneSignalRequest *)request onSuccess:(OSResultSuccessBlock)successBlock onFailure:(OSFailureBlock)failureBlock { - NSLog(@"Executing request: %@", NSStringFromClass([request class])); if (executeInstantaneously) { [OneSignalClientOverrider finishExecutingRequest:request onSuccess:successBlock onFailure:failureBlock]; } else { @@ -58,27 +87,30 @@ - (void)overrideExecuteRequest:(OneSignalRequest *)request onSuccess:(OSResultSu } + (void)finishExecutingRequest:(OneSignalRequest *)request onSuccess:(OSResultSuccessBlock)successBlock onFailure:(OSFailureBlock)failureBlock { - NSLog(@"completing HTTP request: %@", NSStringFromClass([request class])); - - NSMutableDictionary *parameters = [request.parameters mutableCopy]; - - if (!parameters[@"app_id"] && ![request.request.URL.absoluteString containsString:@"/apps/"]) - _XCTPrimitiveFail(currentTestInstance, @"All request should include an app_id"); - - networkRequestCount++; - - id url = [request.request URL]; - NSLog(@"url: %@", url); - NSLog(@"parameters: %@", parameters); - - lastUrl = [url absoluteString]; - lastHTTPRequest = parameters; - - if (successBlock) { - if ([request.request.URL.absoluteString hasPrefix:@"https://onesignal.com/api/v1/apps/"]) - successBlock(@{@"fba": @true}); - else - successBlock(@{@"id": @"1234"}); + @synchronized(lastHTTPRequest) { + NSLog(@"completing HTTP request: %@", NSStringFromClass([request class])); + + NSMutableDictionary *parameters = [request.parameters mutableCopy]; + + if (!parameters[@"app_id"] && ![request.request.URL.absoluteString containsString:@"/apps/"]) + _XCTPrimitiveFail(currentTestInstance, @"All request should include an app_id"); + + networkRequestCount++; + + id url = [request.request URL]; + NSLog(@"url: %@", url); + NSLog(@"parameters: %@", parameters); + + lastUrl = [url absoluteString]; + lastHTTPRequest = parameters; + lastHTTPRequestType = NSStringFromClass([request class]); + + if (successBlock) { + if ([request.request.URL.absoluteString hasPrefix:@"https://onesignal.com/api/v1/apps/"]) + successBlock(@{@"fba": @true}); + else + successBlock(@{@"id": @"1234"}); + } } } @@ -86,6 +118,10 @@ +(dispatch_queue_t)getHTTPQueue { return executionQueue; } ++ (NSString *)lastHTTPRequestType { + return lastHTTPRequestType; +} + +(void)setShouldExecuteInstantaneously:(BOOL)instant { executeInstantaneously = instant; } @@ -117,5 +153,10 @@ +(NSString*)lastUrl { return lastUrl; } ++(void)runBackgroundThreads { + dispatch_sync(executionQueue, ^{}); + dispatch_sync(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{}); +} + @end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m index d14837191..4ec625ae1 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m @@ -273,7 +273,7 @@ - (void)runBackgroundThreads { // the httpQueue makes sure all HTTP request mocks are sync'ed - dispatch_queue_t registerUserQueue, notifSettingsQueue, httpQueue; + dispatch_queue_t registerUserQueue, notifSettingsQueue; for(int i = 0; i < 10; i++) { [OneSignalHelperOverrider runBackgroundThreads]; @@ -285,9 +285,7 @@ - (void)runBackgroundThreads { if (registerUserQueue) dispatch_sync(registerUserQueue, ^{}); - httpQueue = [OneSignalClientOverrider getHTTPQueue]; - if (httpQueue) - dispatch_sync(httpQueue, ^{}); + [OneSignalClientOverrider runBackgroundThreads]; [UNUserNotificationCenterOverrider runBackgroundThreads]; @@ -323,7 +321,7 @@ - (UNNotificationResponse*)createBasiciOSNotificationResponseWithPayload:(NSDict - (UNNotificationResponse*)createBasiciOSNotificationResponse { id userInfo = @{@"custom": - @{@"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb"} + @{@"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"} }; return [self createBasiciOSNotificationResponseWithPayload:userInfo]; @@ -348,9 +346,12 @@ - (void)testBasicInitTest { [self initOneSignal]; [self runBackgroundThreads]; + NSLog(@"CHECKING LAST HTTP REQUEST"); + XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"0000000000000000000000000000000000000000000000000000000000000000"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @15); + NSLog(@"RAN A FEW CONDITIONALS: %@", OneSignalClientOverrider.lastHTTPRequest); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"device_model"], @"x86_64"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"device_type"], @0); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"language"], @"en-US"); @@ -360,6 +361,8 @@ - (void)testBasicInitTest { XCTAssertTrue(status.permissionStatus.hasPrompted); XCTAssertTrue(status.permissionStatus.answeredPrompt); + NSLog(@"CURRENT USER ID: %@", status.subscriptionStatus); + XCTAssertEqual(status.subscriptionStatus.subscribed, true); XCTAssertEqual(status.subscriptionStatus.userSubscriptionSetting, true); XCTAssertEqual(status.subscriptionStatus.userId, @"1234"); @@ -371,6 +374,8 @@ - (void)testBasicInitTest { XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + + } - (void)testVersionStringLength { @@ -1044,7 +1049,7 @@ - (void)testNotificationOpen { // Make sure open tracking network call was made. XCTAssertEqual(openedWasFire, true); - XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/notifications/b2f7f966-d8cc-11e4-bed1-df8f05be55bb"); + XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/notifications/b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"opened"], @1); @@ -1061,7 +1066,7 @@ - (void)testNotificationOpen { - (UNNotificationResponse*)createNotificationResponseForAnalyticsTests { id userInfo = @{@"custom": - @{@"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @{@"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"ti": @"1117f966-d8cc-11e4-bed1-df8f05be55bb", @"tn": @"Template Name" } @@ -1085,7 +1090,7 @@ - (void)testFirebaseAnalyticsNotificationOpen { @"os_notification_opened": @{ @"campaign": @"Template Name - 1117f966-d8cc-11e4-bed1-df8f05be55bb", @"medium": @"notification", - @"notification_id": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"notification_id": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"source": @"OneSignal"} }; XCTAssertEqualObjects(OneSignalTrackFirebaseAnalyticsOverrider.loggedEvents[0], event); @@ -1109,7 +1114,7 @@ - (void)testFirebaseAnalyticsInfluenceNotificationOpen { @"os_notification_received": @{ @"campaign": @"Template Name - 1117f966-d8cc-11e4-bed1-df8f05be55bb", @"medium": @"notification", - @"notification_id": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"notification_id": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"source": @"OneSignal"} }; XCTAssertEqualObjects(OneSignalTrackFirebaseAnalyticsOverrider.loggedEvents[0], received_event); @@ -1127,7 +1132,7 @@ - (void)testFirebaseAnalyticsInfluenceNotificationOpen { @"os_notification_influence_open": @{ @"campaign": @"Template Name - 1117f966-d8cc-11e4-bed1-df8f05be55bb", @"medium": @"notification", - @"notification_id": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"notification_id": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"source": @"OneSignal"} }; XCTAssertEqualObjects(OneSignalTrackFirebaseAnalyticsOverrider.loggedEvents[1], influence_open_event); @@ -1186,7 +1191,7 @@ - (void)testNotificationOpenFromButtonPress { @"m": @"alert body only", @"o": @[@{@"i": @"id1", @"n": @"text1"}], @"custom": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb" + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" } }; @@ -1201,7 +1206,7 @@ - (void)testNotificationOpenFromButtonPress { // Make sure open tracking network call was made. XCTAssertEqual(openedWasFire, true); - XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/notifications/b2f7f966-d8cc-11e4-bed1-df8f05be55bb"); + XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/notifications/b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"opened"], @1); @@ -1234,7 +1239,7 @@ - (void)testNotificationOpenFromButtonPressWithNewformat { @"alert": @"Message Body" }, @"os_data": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"buttons": @[@{@"i": @"id1", @"n": @"text1"}], }}; @@ -1249,7 +1254,7 @@ - (void)testNotificationOpenFromButtonPressWithNewformat { // Make sure open tracking network call was made. XCTAssertEqual(openedWasFire, true); - XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/notifications/b2f7f966-d8cc-11e4-bed1-df8f05be55bb"); + XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/notifications/b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"opened"], @1); @@ -1338,7 +1343,7 @@ - (void)testOpeningWithAdditionalData { [self runBackgroundThreads]; id userInfo = @{@"custom": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"a": @{ @"foo": @"bar" } }}; @@ -1397,7 +1402,7 @@ - (void)receivedCallbackWithButtonsWithUserInfo:(NSDictionary *)userInfo { - (void)testReceivedCallbackWithButtonsWithNewFormat { let newFormat = @{@"aps": @{@"content_available": @1}, @"os_data": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"buttons": @{ @"m": @"alert body only", @"o": @[@{@"i": @"id1", @"n": @"text1"}] @@ -1459,7 +1464,7 @@ - (void)testGeneratingLocalNotificationWithButtonsiOS8_osdata_format { @"o": @[@{@"i": @"id1", @"n": @"text1"}] } }, - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb" + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" }; [self fireDidReceiveRemoteNotification:userInfo]; @@ -1475,7 +1480,7 @@ - (void)testGeneratingLocalNotificationWithButtonsiOS8 { @"m": @"alert body only", @"o": @[@{@"i": @"id1", @"n": @"text1"}], @"custom": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb" + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" } }; @@ -1502,33 +1507,32 @@ - (void)testSendTags { XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key2"], @"value2"); XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); - // More advanced test with callbacks. - __block BOOL didRunSuccess1, didRunSuccess2, didRunSuccess3; + let expectation = [self expectationWithDescription:@"wait_tags"]; + expectation.expectedFulfillmentCount = 3; [OneSignal sendTag:@"key10" value:@"value10" onSuccess:^(NSDictionary *result) { - didRunSuccess1 = true; + [expectation fulfill]; } onFailure:^(NSError *error) {}]; [OneSignal sendTags:@{@"key11": @"value11", @"key12": @"value12"} onSuccess:^(NSDictionary *result) { - didRunSuccess2 = true; + [expectation fulfill]; } onFailure:^(NSError *error) {}]; [OneSignal sendTag:@"key13" value:@"value13" onSuccess:^(NSDictionary *result) { - didRunSuccess3 = true; + [expectation fulfill]; } onFailure:^(NSError *error) {}]; [self runBackgroundThreads]; [NSObjectOverrider runPendingSelectors]; [self runBackgroundThreads]; + [self waitForExpectations:@[expectation] timeout:0.1]; + XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key10"], @"value10"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key11"], @"value11"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key12"], @"value12"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key13"], @"value13"); XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); - XCTAssertEqual(didRunSuccess1, true); - XCTAssertEqual(didRunSuccess2, true); - XCTAssertEqual(didRunSuccess3, true); } - (void)testDeleteTags { @@ -1778,7 +1782,7 @@ - (void)testDidReceiveNotificationExtensionRequestDontOverrideCategory @"alert": @"Message Body" }, @"os_data": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"buttons": @[@{@"i": @"id1", @"n": @"text1"}], @"att": @{ @"id": @"http://domain.com/file.jpg" } }}; @@ -1809,7 +1813,7 @@ - (void) testDidReceiveNotificationExtensionRequestDontOverrideCateogory { @"alert": @"Message Body" }, @"os_data": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"buttons": @[@{@"i": @"id1", @"n": @"text1"}], @"att": @{ @"id": @"http://domain.com/file.jpg" } }}; @@ -1834,7 +1838,7 @@ - (void) testDidReceiveNotificationExtensionRequestLocalFile { @"alert": @"Message Body" }, @"os_data": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"att": @{ @"id": @"file.jpg" } }}; @@ -1854,7 +1858,7 @@ - (void) testServiceExtensionTimeWillExpireRequest { @"alert": @"Message Body" }, @"os_data": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"buttons": @[@{@"i": @"id1", @"n": @"text1"}], @"att": @{ @"id": @"http://domain.com/file.jpg" } }}; @@ -1871,9 +1875,9 @@ - (void) testServiceExtensionTimeWillExpireRequest { } -(void)testBuildOSRequest { - let request = [OSRequestSendTagsToServer withUserId:@"12345" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55bb" tags:@{@"tag1" : @"test1", @"tag2" : @"test2"} networkType:[OneSignalHelper getNetType]]; + let request = [OSRequestSendTagsToServer withUserId:@"12345" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" tags:@{@"tag1" : @"test1", @"tag2" : @"test2"} networkType:[OneSignalHelper getNetType]]; - XCTAssert([request.parameters[@"app_id"] isEqualToString:@"b2f7f966-d8cc-11e4-bed1-df8f05be55bb"]); + XCTAssert([request.parameters[@"app_id"] isEqualToString:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]); XCTAssert([request.parameters[@"tags"][@"tag1"] isEqualToString:@"test1"]); XCTAssert([request.path isEqualToString:@"players/12345"]); @@ -1891,7 +1895,7 @@ -(void)testInvalidJSONTags { let invalidJson = @{@{@"invalid1" : @"invalid2"} : @"test"}; //Keys are required to be strings, this would crash the app if not handled appropriately - let request = [OSRequestSendTagsToServer withUserId:@"12345" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55bb" tags:invalidJson networkType:[OneSignalHelper getNetType]]; + let request = [OSRequestSendTagsToServer withUserId:@"12345" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" tags:invalidJson networkType:[OneSignalHelper getNetType]]; let urlRequest = request.request; @@ -1952,8 +1956,61 @@ -(void)testDelayedSubscriptionUpdate { [self runBackgroundThreads]; XCTAssertTrue(observer->last.to.subscribed); +} + +- (void)testEmailValidation { + XCTAssertFalse([OneSignalHelper isValidEmail:@"test@test"]); + + XCTAssertTrue([OneSignalHelper isValidEmail:@"john.doe233@unlv.nevada.edu"]); + + XCTAssertFalse([OneSignalHelper isValidEmail:@"testing123@22."]); +} + + + +- (void)testSetEmail { + [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" + handleNotificationAction:nil + settings:@{kOSSettingsKeyAutoPrompt: @false}]; + OSSubscriptionStateTestObserver* observer = [OSSubscriptionStateTestObserver new]; + [OneSignal addSubscriptionObserver:observer]; + + [self runBackgroundThreads]; + + [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; + + [self runBackgroundThreads]; + + XCTAssertTrue([@"OSRequestCreateDevice" isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"device_type"], @11); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email"], @"test@test.com"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"], @"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c"); + + [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; + + [self runBackgroundThreads]; + + XCTAssertTrue([@"OSRequestUpdateDeviceToken" isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); +} + +- (void)testMultipleRequests { + let first = [OSRequestGetTags withUserId:@"test1" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]; + let second = [OSRequestGetTags withUserId:@"test2" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]; + + let expectation = [self expectationWithDescription:@"multiple_requests"]; + + expectation.expectedFulfillmentCount = 1; + + [OneSignalClient.sharedClient executeSimultaneousRequests:@{@"first" : first, @"second" : second} withSuccess:^(NSDictionary *results) { + [expectation fulfill]; + } onFailure:^(NSDictionary *errors) { + + }]; + [self waitForExpectations:@[expectation] timeout:0.5]; } @end From 086b32e2d0fdf0a46293e3b62ce61b3bc5d9a191 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Mon, 29 Jan 2018 19:05:06 -0800 Subject: [PATCH 03/36] Email Logout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Included app_id as a parameter for the email logout request • Added a test for email logout • Fixed an issue where setEmail: did not correctly set the current subscription state email user ID --- iOS_SDK/OneSignalSDK/Source/OneSignal.m | 6 +++--- iOS_SDK/OneSignalSDK/Source/Requests.h | 2 +- iOS_SDK/OneSignalSDK/Source/Requests.m | 9 +++++++-- iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m | 12 ++++++++++++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index ac56e0e4a..feb5be296 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -1087,8 +1087,6 @@ + (void)registerUserInternal { if ([[NSUserDefaults standardUserDefaults] objectForKey:@"GT_EMAIL_PLAYER_ID"]) requests[@"email"] = [OSRequestRegisterUser withData:dataDic userId:self.currentSubscriptionState.emailUserId]; - NSLog(@"EXECUTING REQUESTS: %@", requests); - [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:^(NSDictionary *results) { waitingForOneSReg = false; @@ -1584,6 +1582,8 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ [[NSUserDefaults standardUserDefaults] setObject:emailPlayerId forKey:@"GT_EMAIL_PLAYER_ID"]; [[NSUserDefaults standardUserDefaults] synchronize]; + self.currentSubscriptionState.emailUserId = emailPlayerId; + if (successBlock) successBlock(); } else { @@ -1607,7 +1607,7 @@ + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withF return; } - [OneSignalClient.sharedClient executeRequest:[OSRequestLogoutEmail withEmailPlayerId:self.currentSubscriptionState.emailUserId devicePlayerId:self.currentSubscriptionState.userId emailAuthHash:authHashToken] onSuccess:^(NSDictionary *result) { + [OneSignalClient.sharedClient executeRequest:[OSRequestLogoutEmail withAppId: self.app_id emailPlayerId:self.currentSubscriptionState.emailUserId devicePlayerId:self.currentSubscriptionState.userId emailAuthHash:authHashToken] onSuccess:^(NSDictionary *result) { [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"GT_EMAIL_PLAYER_ID"]; [[NSUserDefaults standardUserDefaults] synchronize]; diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.h b/iOS_SDK/OneSignalSDK/Source/Requests.h index 0802f409b..d052c9673 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.h +++ b/iOS_SDK/OneSignalSDK/Source/Requests.h @@ -90,7 +90,7 @@ NS_ASSUME_NONNULL_END @end @interface OSRequestLogoutEmail : OneSignalRequest -+ (instancetype _Nonnull)withEmailPlayerId:(NSString * _Nonnull)emailPlayerId devicePlayerId:(NSString * _Nonnull)devicePlayerId emailAuthHash:(NSString * _Nullable)emailAuthHash; ++ (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId emailPlayerId:(NSString * _Nonnull)emailPlayerId devicePlayerId:(NSString * _Nonnull)devicePlayerId emailAuthHash:(NSString * _Nullable)emailAuthHash; @end #endif /* Requests_h */ diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.m b/iOS_SDK/OneSignalSDK/Source/Requests.m index bc5916e58..bb62c0daf 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.m +++ b/iOS_SDK/OneSignalSDK/Source/Requests.m @@ -134,10 +134,15 @@ + (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId withDeviceType:(NS @implementation OSRequestLogoutEmail -+ (instancetype _Nonnull)withEmailPlayerId:(NSString * _Nonnull)emailPlayerId devicePlayerId:(NSString * _Nonnull)devicePlayerId emailAuthHash:(NSString * _Nullable)emailAuthHash { ++ (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId emailPlayerId:(NSString * _Nonnull)emailPlayerId devicePlayerId:(NSString * _Nonnull)devicePlayerId emailAuthHash:(NSString * _Nullable)emailAuthHash { let request = [OSRequestLogoutEmail new]; - request.parameters = @{@"device_player_id" : devicePlayerId, @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthHash]}; + request.parameters = @{ + @"device_player_id" : devicePlayerId, + @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthHash], + @"app_id" : appId + }; + request.method = POST; request.path = [NSString stringWithFormat:@"%@/email_logout", emailPlayerId]; diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m index 4ec625ae1..d57f2d78a 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m @@ -1994,6 +1994,18 @@ - (void)testSetEmail { XCTAssertTrue([@"OSRequestUpdateDeviceToken" isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); + + //test email logout + let expectation = [self expectationWithDescription:@"email_logout"]; + expectation.expectedFulfillmentCount = 1; + + [OneSignal logoutEmailWithSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail("Failed with error: %@", error); + }]; + + [self waitForExpectations:@[expectation] timeout:0.1]; } - (void)testMultipleRequests { From 992566e354ae9109b9309cd54a5709f613115232 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Tue, 30 Jan 2018 10:56:34 -0800 Subject: [PATCH 04/36] Faster Tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Got rid of a thread synchronization line that wasn't necessary and slowed down unit tests by nearly 2x. --- .../OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m | 1 - 1 file changed, 1 deletion(-) diff --git a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m index 39a99611d..c9a891796 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m @@ -155,7 +155,6 @@ +(NSString*)lastUrl { +(void)runBackgroundThreads { dispatch_sync(executionQueue, ^{}); - dispatch_sync(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{}); } @end From a617ed848d6db869dd0a35d2e169902d07c1ae4c Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Tue, 30 Jan 2018 14:32:55 -0800 Subject: [PATCH 05/36] Splitting/Refactoring Tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Began (but did not finish) the work of splitting up unit tests so that they are no longer all in one giant file/class and are a bit more modular • Fixed the Create/Edit device REST requests in the application which were using incorrect 'email' vs. 'identifier' parameter banes --- .../OneSignal.xcodeproj/project.pbxproj | 10 + iOS_SDK/OneSignalSDK/Source/OneSignal.m | 4 +- iOS_SDK/OneSignalSDK/Source/Requests.m | 18 +- iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m | 101 +++++++ .../UnitTests/UnitTestCommonMethods.h | 34 +++ .../UnitTests/UnitTestCommonMethods.m | 76 +++++ iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m | 283 +++++------------- 7 files changed, 309 insertions(+), 217 deletions(-) create mode 100644 iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m create mode 100644 iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h create mode 100644 iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index e35271296..12559c0cc 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -136,6 +136,8 @@ CA63AF71201FF83500E340FB /* NSNull+OneSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */; }; CA63AF72201FF83500E340FB /* NSNull+OneSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */; }; CA63AF73201FF83500E340FB /* NSNull+OneSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */; }; + CA63AF8420211F7400E340FB /* EmailTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF8320211F7400E340FB /* EmailTests.m */; }; + CA63AF8720211FF800E340FB /* UnitTestCommonMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF8620211FF800E340FB /* UnitTestCommonMethods.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -260,6 +262,9 @@ CA08FC831FE99BB4004C445F /* OneSignalClientOverrider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalClientOverrider.m; sourceTree = ""; }; CA63AF6E201FF83500E340FB /* NSNull+OneSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNull+OneSignal.h"; sourceTree = ""; }; CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNull+OneSignal.m"; sourceTree = ""; }; + CA63AF8320211F7400E340FB /* EmailTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EmailTests.m; sourceTree = ""; }; + CA63AF8520211FF800E340FB /* UnitTestCommonMethods.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UnitTestCommonMethods.h; sourceTree = ""; }; + CA63AF8620211FF800E340FB /* UnitTestCommonMethods.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnitTestCommonMethods.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -383,6 +388,9 @@ children = ( 4529DECD1FA81DE000CEAB1D /* Shadows */, 911E2CBC1E398AB3003112A4 /* UnitTests.m */, + CA63AF8320211F7400E340FB /* EmailTests.m */, + CA63AF8520211FF800E340FB /* UnitTestCommonMethods.h */, + CA63AF8620211FF800E340FB /* UnitTestCommonMethods.m */, 911E2CBE1E398AB3003112A4 /* Info.plist */, 91F60F7B1E80E49A00706E60 /* UncaughtExceptionHandler.h */, 91F60F7C1E80E4E400706E60 /* UncaughtExceptionHandler.m */, @@ -740,6 +748,7 @@ 91F60F7D1E80E4E400706E60 /* UncaughtExceptionHandler.m in Sources */, 912412201E73342200E41FD7 /* OneSignalJailbreakDetection.m in Sources */, 912412381E73342200E41FD7 /* OneSignalTrackIAP.m in Sources */, + CA63AF8720211FF800E340FB /* UnitTestCommonMethods.m in Sources */, 4529DED81FA8253D00CEAB1D /* NSUserDefaultsOverrider.m in Sources */, 4529DEED1FA83C5D00CEAB1D /* OneSignalHelperOverrider.m in Sources */, 912412301E73342200E41FD7 /* OneSignalSelectorHelpers.m in Sources */, @@ -757,6 +766,7 @@ CA08FC7B1FE99B13004C445F /* OneSignalRequest.m in Sources */, 4529DED51FA823B900CEAB1D /* TestHelperFunctions.m in Sources */, 911E2CBD1E398AB3003112A4 /* UnitTests.m in Sources */, + CA63AF8420211F7400E340FB /* EmailTests.m in Sources */, 91B6EA431E85D38F00B5CF01 /* OSObservable.m in Sources */, 4529DEDE1FA828E500CEAB1D /* NSDateOverrider.m in Sources */, CA08FC871FE99BB4004C445F /* OneSignalClientOverrider.m in Sources */, diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index feb5be296..5692261be 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -1550,7 +1550,7 @@ + (void)updateEmailPlayerId:(NSString *)emailPlayerId { if (![emailPlayerId isEqualToString:_currentSubscriptionState.emailUserId]) { //if the ID has changed, call Edit Edvice - [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailPlayerId appId:self.app_id deviceToken:self.currentSubscriptionState.pushToken notificationTypes:@([self getNotificationTypes]) withParentId:self.currentSubscriptionState.emailUserId] onSuccess:nil onFailure:nil]; + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailPlayerId appId:self.app_id deviceToken:currentUserEmail notificationTypes:@([self getNotificationTypes]) withParentId:self.currentSubscriptionState.emailUserId] onSuccess:nil onFailure:nil]; } } @@ -1567,7 +1567,7 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ let emailId = (NSString *)[[NSUserDefaults standardUserDefaults] objectForKey:@"GT_EMAIL_PLAYER_ID"]; if (emailId) { - [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:self.currentSubscriptionState.pushToken notificationTypes:@([self getNotificationTypes]) withParentId:nil] onSuccess:^(NSDictionary *result) { + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:email notificationTypes:@([self getNotificationTypes]) withParentId:nil] onSuccess:^(NSDictionary *result) { if (successBlock) successBlock(); } onFailure:^(NSError *error) { diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.m b/iOS_SDK/OneSignalSDK/Source/Requests.m index bb62c0daf..5a311e752 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.m +++ b/iOS_SDK/OneSignalSDK/Source/Requests.m @@ -97,14 +97,16 @@ + (instancetype)withAppId:(NSString *)appId withJson:(NSMutableDictionary *)json @implementation OSRequestUpdateDeviceToken + (instancetype)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nonnull)identifier notificationTypes:(NSNumber * _Nonnull)notificationTypes withParentId:(NSString * _Nullable)parentId { + NSLog(@"USER ID: %@, APP ID: %@, DEVICE TOKEN: %@, NOTIFICATION TYPES: %@, PARENT ID: %@", userId, appId, identifier, notificationTypes, parentId); + let request = [OSRequestUpdateDeviceToken new]; request.parameters = @{ - @"app_id" : appId, - @"identifier" : identifier, - @"notification_types" : notificationTypes, - @"parent_player_id" : [NSNull nullIfObjectIsNil:parentId] - }; + @"app_id" : appId, + @"identifier" : identifier, + @"notification_types" : notificationTypes, + @"parent_player_id" : [NSNull nullIfObjectIsNil:parentId] + }; request.method = PUT; request.path = [NSString stringWithFormat:@"players/%@", userId]; @@ -120,7 +122,7 @@ + (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId withDeviceType:(NS request.parameters = @{ @"app_id" : appId, @"device_type" : deviceType, - @"email" : [NSNull nullIfObjectIsNil:email], + @"identifier" : [NSNull nullIfObjectIsNil:email], @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthHash], @"device_player_id" : [NSNull nullIfObjectIsNil:playerId] }; @@ -135,10 +137,12 @@ + (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId withDeviceType:(NS @implementation OSRequestLogoutEmail + (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId emailPlayerId:(NSString * _Nonnull)emailPlayerId devicePlayerId:(NSString * _Nonnull)devicePlayerId emailAuthHash:(NSString * _Nullable)emailAuthHash { + + let request = [OSRequestLogoutEmail new]; request.parameters = @{ - @"device_player_id" : devicePlayerId, + @"device_player_id" : [NSNull nullIfObjectIsNil:devicePlayerId], @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthHash], @"app_id" : appId }; diff --git a/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m new file mode 100644 index 000000000..32e76f2cf --- /dev/null +++ b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m @@ -0,0 +1,101 @@ +// +// EmailTests.m +// UnitTests +// +// Created by Brad Hesse on 1/30/18. +// Copyright © 2018 Hiptic. All rights reserved. +// + +#import +#import "OneSignal.h" +#import "OneSignalHelper.h" +#import "Requests.h" +#import "OneSignalClient.h" +#import "OneSignalClientOverrider.h" +#import "UnitTestCommonMethods.h" +#import "OSSubscription.h" + + + +@interface EmailTests : XCTestCase + +@end + +@implementation EmailTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testEmailValidation { + XCTAssertFalse([OneSignalHelper isValidEmail:@"test@test"]); + + XCTAssertTrue([OneSignalHelper isValidEmail:@"john.doe233@unlv.nevada.edu"]); + + XCTAssertFalse([OneSignalHelper isValidEmail:@"testing123@22."]); +} + +- (void)testSetEmail { + [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" + handleNotificationAction:nil + settings:@{kOSSettingsKeyAutoPrompt: @false}]; + + OSSubscriptionStateTestObserver* observer = [OSSubscriptionStateTestObserver new]; + [OneSignal addSubscriptionObserver:observer]; + + [UnitTestCommonMethods runBackgroundThreads]; + + [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; + + [UnitTestCommonMethods runBackgroundThreads]; + NSLog(@"EMAIL AFTER IS: %@", OneSignalClientOverrider.lastHTTPRequest[@"email"]); + XCTAssertTrue([@"OSRequestCreateDevice" isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"device_type"], @11); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"test@test.com"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"], @"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c"); + + [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; + + [UnitTestCommonMethods runBackgroundThreads]; + + XCTAssertTrue([@"OSRequestUpdateDeviceToken" isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); + + //test email logout + let expectation = [self expectationWithDescription:@"email_logout"]; + expectation.expectedFulfillmentCount = 1; + + [OneSignal logoutEmailWithSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail("Failed with error: %@", error); + }]; + + [self waitForExpectations:@[expectation] timeout:0.1]; +} + +- (void)testMultipleRequests { + let first = [OSRequestGetTags withUserId:@"test1" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]; + let second = [OSRequestGetTags withUserId:@"test2" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]; + + let expectation = [self expectationWithDescription:@"multiple_requests"]; + + expectation.expectedFulfillmentCount = 1; + + [OneSignalClient.sharedClient executeSimultaneousRequests:@{@"first" : first, @"second" : second} withSuccess:^(NSDictionary *results) { + [expectation fulfill]; + } onFailure:^(NSDictionary *errors) { + XCTFail("Failed with error: %@", errors); + }]; + + [self waitForExpectations:@[expectation] timeout:0.5]; +} + +@end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h new file mode 100644 index 000000000..f5094ffe7 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h @@ -0,0 +1,34 @@ +// +// UnitTestCommonMethods.h +// UnitTests +// +// Created by Brad Hesse on 1/30/18. +// Copyright © 2018 Hiptic. All rights reserved. +// + +#import +#import "OneSignal.h" + +@interface UnitTestCommonMethods : NSObject + ++ (void)runBackgroundThreads; + +@end + +// START - Start Observers + +@interface OSPermissionStateTestObserver : NSObject { + @package OSPermissionStateChanges* last; + @package int fireCount; +} +- (void)onOSPermissionChanged:(OSPermissionStateChanges*)stateChanges; +@end + +@interface OSSubscriptionStateTestObserver : NSObject { + @package OSSubscriptionStateChanges* last; + @package int fireCount; +} +- (void)onOSSubscriptionChanged:(OSSubscriptionStateChanges*)stateChanges; +@end + +// END - Observers diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m new file mode 100644 index 000000000..11828b1fb --- /dev/null +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m @@ -0,0 +1,76 @@ +// +// UnitTestCommonMethods.m +// UnitTests +// +// Created by Brad Hesse on 1/30/18. +// Copyright © 2018 Hiptic. All rights reserved. +// + +#import "UnitTestCommonMethods.h" +#import "OneSignalClientOverrider.h" +#import "UIApplicationOverrider.h" +#import "UNUserNotificationCenterOverrider.h" +#import "OneSignalHelperOverrider.h" +#import "OneSignal.h" +#import "OneSignalNotificationSettingsIOS10.h" + + +@interface OneSignal (UN_extra) ++ (dispatch_queue_t) getRegisterQueue; +@end + +@implementation UnitTestCommonMethods + +// Runs any blocks passed to dispatch_async() ++ (void)runBackgroundThreads { + NSLog(@"START runBackgroundThreads"); + + [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; + + // the httpQueue makes sure all HTTP request mocks are sync'ed + + dispatch_queue_t registerUserQueue, notifSettingsQueue; + for(int i = 0; i < 10; i++) { + [OneSignalHelperOverrider runBackgroundThreads]; + + notifSettingsQueue = [OneSignalNotificationSettingsIOS10 getQueue]; + if (notifSettingsQueue) + dispatch_sync(notifSettingsQueue, ^{}); + + registerUserQueue = [OneSignal getRegisterQueue]; + if (registerUserQueue) + dispatch_sync(registerUserQueue, ^{}); + + [OneSignalClientOverrider runBackgroundThreads]; + + [UNUserNotificationCenterOverrider runBackgroundThreads]; + + dispatch_barrier_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{}); + + [UIApplicationOverrider runBackgroundThreads]; + } + + NSLog(@"END runBackgroundThreads"); +} + +@end + + +@implementation OSPermissionStateTestObserver + +- (void)onOSPermissionChanged:(OSPermissionStateChanges*)stateChanges { + NSLog(@"UnitTest:onOSPermissionChanged :\n%@", stateChanges); + last = stateChanges; + fireCount++; +} +@end + + + +@implementation OSSubscriptionStateTestObserver +- (void)onOSSubscriptionChanged:(OSSubscriptionStateChanges*)stateChanges { + NSLog(@"UnitTest:onOSSubscriptionChanged:\n%@", stateChanges); + last = stateChanges; + fireCount++; +} +@end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m index d57f2d78a..4874e388d 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m @@ -27,6 +27,8 @@ #import +#import "UnitTestCommonMethods.h" + #import #import @@ -74,43 +76,7 @@ #import "Requests.h" #import "OneSignalClientOverrider.h" -@interface OneSignal (UN_extra) -+ (dispatch_queue_t) getRegisterQueue; -@end - -// START - Start Observers - -@interface OSPermissionStateTestObserver : NSObject -@end - -@implementation OSPermissionStateTestObserver { - @package OSPermissionStateChanges* last; - @package int fireCount; -} - -- (void)onOSPermissionChanged:(OSPermissionStateChanges*)stateChanges { - NSLog(@"UnitTest:onOSPermissionChanged :\n%@", stateChanges); - last = stateChanges; - fireCount++; -} -@end - - -@interface OSSubscriptionStateTestObserver : NSObject -@end - -@implementation OSSubscriptionStateTestObserver { - @package OSSubscriptionStateChanges* last; - @package int fireCount; -} -- (void)onOSSubscriptionChanged:(OSSubscriptionStateChanges*)stateChanges { - NSLog(@"UnitTest:onOSSubscriptionChanged:\n%@", stateChanges); - last = stateChanges; - fireCount++; -} -@end -// END - Observers @interface UnitTests : XCTestCase @@ -195,7 +161,7 @@ - (void)setUp { // Called after each test. - (void)tearDown { [super tearDown]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; } - (void)backgroundModesDisabledInXcode { @@ -265,38 +231,6 @@ - (void)resumeApp { [sharedApp.delegate applicationDidBecomeActive:sharedApp]; } -// Runs any blocks passed to dispatch_async() -- (void)runBackgroundThreads { - NSLog(@"START runBackgroundThreads"); - - [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; - - // the httpQueue makes sure all HTTP request mocks are sync'ed - - dispatch_queue_t registerUserQueue, notifSettingsQueue; - for(int i = 0; i < 10; i++) { - [OneSignalHelperOverrider runBackgroundThreads]; - - notifSettingsQueue = [OneSignalNotificationSettingsIOS10 getQueue]; - if (notifSettingsQueue) - dispatch_sync(notifSettingsQueue, ^{}); - - registerUserQueue = [OneSignal getRegisterQueue]; - if (registerUserQueue) - dispatch_sync(registerUserQueue, ^{}); - - [OneSignalClientOverrider runBackgroundThreads]; - - [UNUserNotificationCenterOverrider runBackgroundThreads]; - - dispatch_barrier_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{}); - - [UIApplicationOverrider runBackgroundThreads]; - } - - NSLog(@"END runBackgroundThreads"); -} - - (UNNotificationResponse*)createBasiciOSNotificationResponseWithPayload:(NSDictionary*)userInfo { // Mocking an iOS 10 notification // Setting response.notification.request.content.userInfo @@ -337,14 +271,14 @@ - (void)initOneSignal { -(void)initOneSignalAndThreadWait { [self initOneSignal]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; } - (void)testBasicInitTest { NSLog(@"iOS VERSION: %@", [[UIDevice currentDevice] systemVersion]); [self initOneSignal]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; NSLog(@"CHECKING LAST HTTP REQUEST"); @@ -446,7 +380,7 @@ - (void)testInitOnSimulator { [self initOneSignalAndThreadWait]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest[@"identifier"]); @@ -488,7 +422,7 @@ - (void)testCallingMethodsBeforeInit { [OneSignal setSubscription:true]; [OneSignal promptLocation]; [OneSignal promptForPushNotificationsWithUserResponse:nil]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [self initOneSignalAndThreadWait]; @@ -502,7 +436,7 @@ - (void)testCallingMethodsBeforeInit { [OneSignal setSubscription:true]; [OneSignal promptLocation]; [OneSignal promptForPushNotificationsWithUserResponse:nil]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [self initOneSignalAndThreadWait]; XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 0); @@ -531,7 +465,7 @@ - (void)sharedTestPermissionChangeObserver { [OneSignal addPermissionObserver:observer]; [self registerForPushNotifications]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.hasPrompted, false); XCTAssertEqual(observer->last.from.answeredPrompt, false); @@ -540,7 +474,7 @@ - (void)sharedTestPermissionChangeObserver { XCTAssertEqual(observer->fireCount, 1); [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.accepted, false); XCTAssertEqual(observer->last.to.answeredPrompt, true); @@ -558,7 +492,7 @@ - (void)testPermissionChangeObserverWhenAlreadyAccepted { OSPermissionStateTestObserver* observer = [OSPermissionStateTestObserver new]; [OneSignal addPermissionObserver:observer]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.hasPrompted, false); XCTAssertEqual(observer->last.from.answeredPrompt, false); @@ -581,7 +515,7 @@ - (void)testPermissionChangeObserverFireAfterAppRestart { // Added Observer should be notified of the change right away. observer = [OSPermissionStateTestObserver new]; [OneSignal addPermissionObserver:observer]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.accepted, true); XCTAssertEqual(observer->last.to.accepted, false); @@ -609,11 +543,11 @@ - (void)sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart { [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; // Restart App [self clearStateForAppRestart]; @@ -625,7 +559,7 @@ - (void)sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart { observer = [OSPermissionStateTestObserver new]; [OneSignal addPermissionObserver:observer]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertNil(observer->last); } @@ -638,15 +572,15 @@ - (void)testPermissionChangeObserverDontLoseFromChanges { [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [self registerForPushNotifications]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; OSPermissionStateTestObserver* observer = [OSPermissionStateTestObserver new]; [OneSignal addPermissionObserver:observer]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.hasPrompted, false); XCTAssertEqual(observer->last.from.answeredPrompt, false); @@ -659,7 +593,7 @@ - (void)testSubscriptionChangeObserverWhenAlreadyAccepted { OSSubscriptionStateTestObserver* observer = [OSSubscriptionStateTestObserver new]; [OneSignal addSubscriptionObserver:observer]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.subscribed, false); XCTAssertEqual(observer->last.to.subscribed, true); @@ -681,7 +615,7 @@ - (void)testSubscriptionChangeObserverFireAfterAppRestart { // Added Observer should be notified of the change right away. observer = [OSSubscriptionStateTestObserver new]; [OneSignal addSubscriptionObserver:observer]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.subscribed, true); XCTAssertEqual(observer->last.to.subscribed, false); @@ -701,14 +635,14 @@ - (void)testPermissionChangeObserverWithNativeiOS10PromptCall { [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError* error) {}]; [self backgroundApp]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->fireCount, 1); XCTAssertEqualObjects([observer->last description], @",\nto: \n>"); [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; // Make sure it doesn't fire for answeredPrompt then again right away for accepted XCTAssertEqual(observer->fireCount, 2); @@ -735,7 +669,7 @@ - (void)testTestPermissionChangeObserverWithNativeiOS10PromptCall { [self backgroundApp]; // Full bug details explained in answerNotifiationPrompt [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->fireCount, 3); @@ -753,7 +687,7 @@ - (void)testPermissionChangeObserverWithDecline { [OneSignal addPermissionObserver:observer]; [self registerForPushNotifications]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.hasPrompted, false); XCTAssertEqual(observer->last.from.answeredPrompt, false); @@ -762,7 +696,7 @@ - (void)testPermissionChangeObserverWithDecline { XCTAssertEqual(observer->fireCount, 1); [self answerNotifiationPrompt:false]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.accepted, false); XCTAssertEqual(observer->last.to.answeredPrompt, true); @@ -788,7 +722,7 @@ - (void)testPermissionAndSubscriptionChangeObserverRemove { [self registerForPushNotifications]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertNil(permissionObserver->last); XCTAssertNil(subscriptionObserver->last); @@ -805,13 +739,13 @@ - (void)testSubscriptionChangeObserverBasic { [self registerForPushNotifications]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.subscribed, false); XCTAssertEqual(observer->last.to.subscribed, true); [OneSignal setSubscription:false]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.subscribed, true); XCTAssertEqual(observer->last.to.subscribed, false); @@ -830,16 +764,16 @@ - (void)testSubscriptionChangeObserverWhenPromptNotShown { [OneSignal addSubscriptionObserver:observer]; // Triggers the 30 fallback to register device right away. - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [NSObjectOverrider runPendingSelectors]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertNil(observer->last.from.userId); XCTAssertEqualObjects(observer->last.to.userId, @"1234"); XCTAssertFalse(observer->last.to.subscribed); [OneSignal setSubscription:false]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(observer->last.from.userSubscriptionSetting); XCTAssertFalse(observer->last.to.userSubscriptionSetting); @@ -852,7 +786,7 @@ - (void)testSubscriptionChangeObserverWhenPromptNotShown { // Prompt and accept notifications [self registerForPushNotifications]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; // Shouldn't be subscribed yet as we called setSubscription:false before XCTAssertFalse(observer->last.from.subscribed); @@ -860,7 +794,7 @@ - (void)testSubscriptionChangeObserverWhenPromptNotShown { // Device should be reported a subscribed now as all condiditions are true. [OneSignal setSubscription:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertFalse(observer->last.from.subscribed); XCTAssertTrue(observer->last.to.subscribed); } @@ -874,7 +808,7 @@ - (void)testInitAcceptingNotificationsWithoutCapabilitesSet { XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @-13); XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); @@ -892,7 +826,7 @@ - (void)testPromptForPushNotificationsWithUserResponse { }]; [self backgroundApp]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(didAccept); } @@ -908,7 +842,7 @@ - (void)testPromptForPushNotificationsWithUserResponseOnIOS8 { }]; [self backgroundApp]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(didAccept); } @@ -924,7 +858,7 @@ - (void)testPromptForPushNotificationsWithUserResponseOnIOS7 { }]; [self backgroundApp]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(didAccept); } @@ -939,7 +873,7 @@ - (void)testPromptedButNeveranswerNotificationPrompt { // Triggers the 30 fallback to register device right away. [OneSignal performSelector:NSSelectorFromString(@"registerUser")]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @-19); @@ -954,7 +888,7 @@ - (void)testNotificationTypesWhenAlreadyAcceptedWithAutoPromptOffOnFristStartPre handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @7); } @@ -968,10 +902,10 @@ - (void)testNeverPromptedStatus { handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; // Triggers the 30 fallback to register device right away. [NSObjectOverrider runPendingSelectors]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @-18); @@ -987,7 +921,7 @@ - (void)testNotAcceptingNotificationsWithoutBackgroundModes { XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); [self answerNotifiationPrompt:false]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/players"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); @@ -1006,13 +940,13 @@ - (void)testIdsAvailableNotAcceptingNotifications { idsAvailable1Called = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [self registerForPushNotifications]; [self answerNotifiationPrompt:false]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(idsAvailable1Called); @@ -1025,7 +959,7 @@ - (void)testIdsAvailableNotAcceptingNotifications { idsAvailable2Called = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(idsAvailable2Called); } @@ -1039,7 +973,7 @@ - (void)testNotificationOpen { XCTAssertNil(result.action.actionID); openedWasFire = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; id notifResponse = [self createBasiciOSNotificationResponse]; UNUserNotificationCenter *notifCenter = [UNUserNotificationCenter currentNotificationCenter]; @@ -1123,7 +1057,7 @@ - (void)testFirebaseAnalyticsInfluenceNotificationOpen { [self backgroundApp]; NSDateOverrider.timeOffset = 41; [self resumeApp]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; // Since we opened the app under 2 mintues after receiving a notification // an influence_open should be sent to firebase. @@ -1163,7 +1097,7 @@ - (void)testNotificationOpenOn2ndColdStartWithoutAppId { [OneSignal initWithLaunchOptions:nil appId:nil handleNotificationAction:^(OSNotificationOpenedResult *result) { openedWasFire = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; id notifResponse = [self createBasiciOSNotificationResponse]; UNUserNotificationCenter *notifCenter = [UNUserNotificationCenter currentNotificationCenter]; @@ -1184,7 +1118,7 @@ - (void)testNotificationOpenFromButtonPress { XCTAssertEqualObjects(result.action.actionID, @"id1"); openedWasFire = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; UIApplicationOverrider.currentUIApplicationState = UIApplicationStateInactive; id userInfo = @{@"aps": @{@"content_available": @1}, @@ -1231,7 +1165,7 @@ - (void)testNotificationOpenFromButtonPressWithNewformat { XCTAssertEqualObjects(result.action.actionID, @"id1"); openedWasFire = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; UIApplicationOverrider.currentUIApplicationState = UIApplicationStateInactive; id userInfo = @{@"aps": @{ @@ -1284,7 +1218,7 @@ - (void)notificationAlertButtonsDisplayWithFormat:(NSDictionary *)userInfo { [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:receiveBlock]; [self resumeApp]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; id notifResponse = [self createBasiciOSNotificationResponseWithPayload:userInfo]; [notifResponse setValue:@"id1" forKeyPath:@"actionIdentifier"]; @@ -1340,7 +1274,7 @@ - (void)testOpeningWithAdditionalData { openedWasFire = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; id userInfo = @{@"custom": @{ @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @@ -1385,7 +1319,7 @@ - (void)receivedCallbackWithButtonsWithUserInfo:(NSDictionary *)userInfo { } handleNotificationAction:nil settings:nil]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; let notifResponse = [self createBasiciOSNotificationResponseWithPayload:userInfo]; UNUserNotificationCenter *notifCenter = [UNUserNotificationCenter currentNotificationCenter]; @@ -1499,7 +1433,7 @@ - (void)testSendTags { // Make sure all 3 sets of tags where send in 1 network call. [NSObjectOverrider runPendingSelectors]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [NSObjectOverrider runPendingSelectors]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key"], @"value"); @@ -1521,9 +1455,9 @@ - (void)testSendTags { [expectation fulfill]; } onFailure:^(NSError *error) {}]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [NSObjectOverrider runPendingSelectors]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [self waitForExpectations:@[expectation] timeout:0.1]; @@ -1548,7 +1482,7 @@ - (void)testDeleteTags { // Make sure only 1 network call is made and only key2 gets sent. [NSObjectOverrider runPendingSelectors]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [NSObjectOverrider runPendingSelectors]; XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key"]); @@ -1572,7 +1506,7 @@ - (void)testGetTags { NSLog(@"getTags onFailure HERE"); }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(fireGetTags); } @@ -1591,7 +1525,7 @@ - (void)testGetTagsBeforePlayerId { NSLog(@"getTags onFailure HERE"); }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(fireGetTags); @@ -1615,9 +1549,9 @@ - (void)testGetTagsWithNestedDelete { }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [NSObjectOverrider runPendingSelectors]; // create, ge tags, then sendTags call. @@ -1633,13 +1567,13 @@ - (void)testSendTagsBeforeRegisterComplete { NSObjectOverrider.selectorNamesForInstantOnlyForFirstRun = [@[@"sendTagsToServer"] mutableCopy]; [OneSignal sendTag:@"key" value:@"value"]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; // Do not try to send tag update yet as there isn't a player_id yet. XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 0); [self answerNotifiationPrompt:false]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; // A single POST player create call should be made with tags included. XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); @@ -1697,7 +1631,7 @@ - (void)testPermissionChangedInSettingsOutsideOfApp { [self backgroundApp]; [self setCurrentNotificationPermission:true]; [self resumeApp]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @15); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"0000000000000000000000000000000000000000000000000000000000000000"); @@ -1714,14 +1648,14 @@ - (void) testOnSessionWhenResuming { [self backgroundApp]; NSDateOverrider.timeOffset = 10; [self resumeApp]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); // Anything over 30 secounds should count as a session. [self backgroundApp]; NSDateOverrider.timeOffset = 41; [self resumeApp]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/players/1234/on_session"); XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); @@ -1737,7 +1671,7 @@ - (void)testContentAvailableDoesNotTriggerOpen { } handleNotificationAction:nil settings:nil]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; id userInfo = @{@"aps": @{@"content_available": @1}, @"custom": @{ @@ -1746,7 +1680,7 @@ - (void)testContentAvailableDoesNotTriggerOpen { }; [self fireDidReceiveRemoteNotification:userInfo]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(receivedWasFire, true); XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 0); @@ -1905,7 +1839,7 @@ -(void)testInvalidJSONTags { [OneSignal sendTags:invalidJson]; [NSObjectOverrider runPendingSelectors]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [NSObjectOverrider runPendingSelectors]; //the request should fail and the HTTP request should not contain the invalid tags @@ -1927,20 +1861,20 @@ -(void)testDelayedSubscriptionUpdate { OSSubscriptionStateTestObserver* observer = [OSSubscriptionStateTestObserver new]; [OneSignal addSubscriptionObserver:observer]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; // Triggers the 30 fallback to register device right away. - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [NSObjectOverrider runPendingSelectors]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [OneSignal setSubscription:false]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; // Prompt and accept notifications [self registerForPushNotifications]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; // Shouldn't be subscribed yet as we called setSubscription:false before XCTAssertFalse(observer->last.from.subscribed); @@ -1953,76 +1887,9 @@ -(void)testDelayedSubscriptionUpdate { [OneSignalClientOverrider setShouldExecuteInstantaneously:true]; XCTAssertFalse(observer->last.to.subscribed); - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(observer->last.to.subscribed); } -- (void)testEmailValidation { - XCTAssertFalse([OneSignalHelper isValidEmail:@"test@test"]); - - XCTAssertTrue([OneSignalHelper isValidEmail:@"john.doe233@unlv.nevada.edu"]); - - XCTAssertFalse([OneSignalHelper isValidEmail:@"testing123@22."]); -} - - - -- (void)testSetEmail { - [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" - handleNotificationAction:nil - settings:@{kOSSettingsKeyAutoPrompt: @false}]; - - OSSubscriptionStateTestObserver* observer = [OSSubscriptionStateTestObserver new]; - [OneSignal addSubscriptionObserver:observer]; - - [self runBackgroundThreads]; - - [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; - - [self runBackgroundThreads]; - - XCTAssertTrue([@"OSRequestCreateDevice" isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); - XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); - XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"device_type"], @11); - XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email"], @"test@test.com"); - XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"], @"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c"); - - [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; - - [self runBackgroundThreads]; - - XCTAssertTrue([@"OSRequestUpdateDeviceToken" isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); - XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); - - //test email logout - let expectation = [self expectationWithDescription:@"email_logout"]; - expectation.expectedFulfillmentCount = 1; - - [OneSignal logoutEmailWithSuccess:^{ - [expectation fulfill]; - } withFailure:^(NSError *error) { - XCTFail("Failed with error: %@", error); - }]; - - [self waitForExpectations:@[expectation] timeout:0.1]; -} - -- (void)testMultipleRequests { - let first = [OSRequestGetTags withUserId:@"test1" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]; - let second = [OSRequestGetTags withUserId:@"test2" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]; - - let expectation = [self expectationWithDescription:@"multiple_requests"]; - - expectation.expectedFulfillmentCount = 1; - - [OneSignalClient.sharedClient executeSimultaneousRequests:@{@"first" : first, @"second" : second} withSuccess:^(NSDictionary *results) { - [expectation fulfill]; - } onFailure:^(NSDictionary *errors) { - - }]; - - [self waitForExpectations:@[expectation] timeout:0.5]; -} - @end From 0082c5be1535bbbb6bb34dd8ba6d4cc4b0a596ff Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Wed, 31 Jan 2018 15:28:57 -0800 Subject: [PATCH 06/36] Re-Attempt Failed Network Requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • This implements a system so that the SDK will reattempt failed network requests under two possible situations: 1. The HTTP status code is 500+, indicating a server error. 2. The HTTP status code is 0, which in iOS indicates no internet connection • The re-attempt is only made one time, after waiting 15 seconds. • The success/fail callbacks are not called until the re-attempt is made. If the second attempt fails, the failure callback is called. --- .../OneSignal.xcodeproj/project.pbxproj | 12 ++++ iOS_SDK/OneSignalSDK/Source/OneSignalClient.m | 55 +++++++++++++++++-- .../OneSignalSDK/Source/OneSignalRequest.h | 1 + .../OneSignalSDK/Source/OneSignalRequest.m | 4 +- .../OneSignalSDK/Source/ReattemptRequest.h | 22 ++++++++ .../OneSignalSDK/Source/ReattemptRequest.m | 23 ++++++++ iOS_SDK/OneSignalSDK/Source/Requests.m | 1 - 7 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 iOS_SDK/OneSignalSDK/Source/ReattemptRequest.h create mode 100644 iOS_SDK/OneSignalSDK/Source/ReattemptRequest.m diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index 12559c0cc..a61903c77 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -138,6 +138,10 @@ CA63AF73201FF83500E340FB /* NSNull+OneSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */; }; CA63AF8420211F7400E340FB /* EmailTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF8320211F7400E340FB /* EmailTests.m */; }; CA63AF8720211FF800E340FB /* UnitTestCommonMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AF8620211FF800E340FB /* UnitTestCommonMethods.m */; }; + CA63AFC22022670A00E340FB /* ReattemptRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CA63AFC02022670A00E340FB /* ReattemptRequest.h */; }; + CA63AFC32022670A00E340FB /* ReattemptRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AFC12022670A00E340FB /* ReattemptRequest.m */; }; + CA63AFC42022670A00E340FB /* ReattemptRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AFC12022670A00E340FB /* ReattemptRequest.m */; }; + CA63AFC52022670A00E340FB /* ReattemptRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AFC12022670A00E340FB /* ReattemptRequest.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -265,6 +269,8 @@ CA63AF8320211F7400E340FB /* EmailTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EmailTests.m; sourceTree = ""; }; CA63AF8520211FF800E340FB /* UnitTestCommonMethods.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UnitTestCommonMethods.h; sourceTree = ""; }; CA63AF8620211FF800E340FB /* UnitTestCommonMethods.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnitTestCommonMethods.m; sourceTree = ""; }; + CA63AFC02022670A00E340FB /* ReattemptRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReattemptRequest.h; sourceTree = ""; }; + CA63AFC12022670A00E340FB /* ReattemptRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReattemptRequest.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -499,6 +505,8 @@ CA08FC771FE99B13004C445F /* OneSignalRequest.m */, CA08FC7C1FE99B25004C445F /* Requests.h */, CA08FC7D1FE99B25004C445F /* Requests.m */, + CA63AFC02022670A00E340FB /* ReattemptRequest.h */, + CA63AFC12022670A00E340FB /* ReattemptRequest.m */, ); name = API; path = Source; @@ -528,6 +536,7 @@ 912412311E73342200E41FD7 /* OneSignalTracker.h in Headers */, 9124122D1E73342200E41FD7 /* OneSignalSelectorHelpers.h in Headers */, 9124123D1E73342200E41FD7 /* UIApplicationDelegate+OneSignal.h in Headers */, + CA63AFC22022670A00E340FB /* ReattemptRequest.h in Headers */, 9124121D1E73342200E41FD7 /* OneSignalJailbreakDetection.h in Headers */, 9129C6B71E89E59B009CB6A0 /* OSPermission.h in Headers */, 912412151E73342200E41FD7 /* OneSignalHelper.h in Headers */, @@ -695,6 +704,7 @@ 91B6EA411E85D38F00B5CF01 /* OSObservable.m in Sources */, 4529DF0C1FA932AC00CEAB1D /* OneSignalTrackFirebaseAnalytics.m in Sources */, 91F58D891E7C9A240017D24D /* OneSignalNotificationSettingsIOS7.m in Sources */, + CA63AFC32022670A00E340FB /* ReattemptRequest.m in Sources */, 912412221E73342200E41FD7 /* OneSignalLocation.m in Sources */, 1AF75EAE1E8567FD0097B315 /* NSString+OneSignal.m in Sources */, CA63AF71201FF83500E340FB /* NSNull+OneSignal.m in Sources */, @@ -729,6 +739,7 @@ 912412271E73342200E41FD7 /* OneSignalMobileProvision.m in Sources */, 912412331E73342200E41FD7 /* OneSignalTracker.m in Sources */, 91B6EA421E85D38F00B5CF01 /* OSObservable.m in Sources */, + CA63AFC42022670A00E340FB /* ReattemptRequest.m in Sources */, 91F58D8A1E7C9A240017D24D /* OneSignalNotificationSettingsIOS7.m in Sources */, 912412231E73342200E41FD7 /* OneSignalLocation.m in Sources */, CA63AF72201FF83500E340FB /* NSNull+OneSignal.m in Sources */, @@ -779,6 +790,7 @@ 912412281E73342200E41FD7 /* OneSignalMobileProvision.m in Sources */, 9129C6C01E89E7AB009CB6A0 /* OSSubscription.m in Sources */, 912412141E73342200E41FD7 /* OneSignalAlertViewDelegate.m in Sources */, + CA63AFC52022670A00E340FB /* ReattemptRequest.m in Sources */, 4529DEE11FA82AB300CEAB1D /* NSBundleOverrider.m in Sources */, 912412441E73342200E41FD7 /* UNUserNotificationCenter+OneSignal.m in Sources */, 9124123C1E73342200E41FD7 /* OneSignalWebView.m in Sources */, diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m index 802dab6b0..e9b34210b 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m @@ -27,6 +27,11 @@ #import "OneSignalClient.h" #import "UIApplicationDelegate+OneSignal.h" +#import "ReattemptRequest.h" + +#define REATTEMPT_DELAY 15.0 +#define REQUEST_TIMEOUT_REQUEST 6.0 +#define REQUEST_TIMEOUT_RESOURCE 10.0 @interface OneSignalClient () @property (strong, nonatomic) NSURLSession *sharedSession; @@ -45,7 +50,11 @@ + (OneSignalClient *)sharedClient { -(instancetype)init { if (self = [super init]) { - _sharedSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; + let configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + configuration.timeoutIntervalForRequest = REQUEST_TIMEOUT_REQUEST; + configuration.timeoutIntervalForResource = REQUEST_TIMEOUT_RESOURCE; + + _sharedSession = [NSURLSession sessionWithConfiguration:configuration]; } return self; @@ -76,7 +85,7 @@ - (void)executeSimultaneousRequests:(NSDictionary= 500 || statusCode == 0) && !request.reattempted) { + let reattempt = [ReattemptRequest withRequest:request successBlock:successBlock failureBlock:failureBlock]; + + if (async) { + //retry again in 15 seconds + [OneSignal onesignal_Log:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"Re-scheduling request (%@) to be re-attempted in %i seconds due to failed HTTP request with status code %i", NSStringFromClass([request class]), (int)REATTEMPT_DELAY, (int)statusCode]]; + + [OneSignalHelper performSelector:@selector(reattemptRequest:) onMainThreadOnObject:self withObject:reattempt afterDelay:REATTEMPT_DELAY]; + } else { + //retry again immediately + [self reattemptRequest: reattempt]; + } + + return; + } + if (error == nil && statusCode == 200) { if (successBlock != nil) { if (innerJson != nil) diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.h b/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.h index fdc363383..423bde400 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.h +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.h @@ -39,6 +39,7 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE} HTTPMethod; @property (nonatomic) HTTPMethod method; @property (nonatomic, nonnull) NSString *path; @property (nonatomic, nullable) NSDictionary *parameters; +@property (nonatomic) BOOL reattempted; -(BOOL)missingAppId; //for requests that don't require an appId parameter, the subclass should override this method and return false -(NSMutableURLRequest * _Nonnull )request; @end diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m b/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m index 16dd55f31..880669a4d 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m @@ -35,7 +35,9 @@ @implementation OneSignalRequest - (id)init { - self = [super init]; + if (self = [super init]) { + self.reattempted = false; + } return self; } diff --git a/iOS_SDK/OneSignalSDK/Source/ReattemptRequest.h b/iOS_SDK/OneSignalSDK/Source/ReattemptRequest.h new file mode 100644 index 000000000..2ac3c0a6b --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/ReattemptRequest.h @@ -0,0 +1,22 @@ +// +// ReattemptRequest.h +// OneSignal +// +// Created by Brad Hesse on 1/31/18. +// Copyright © 2018 Hiptic. All rights reserved. +// + +#import +#import "OneSignal.h" +#import "OneSignalHelper.h" +#import "OneSignalRequest.h" + +@interface ReattemptRequest : NSObject + +@property (strong, nonatomic) OneSignalRequest *request; +@property (nonatomic) OSResultSuccessBlock successBlock; +@property (nonatomic) OSFailureBlock failureBlock; + ++(instancetype)withRequest:(OneSignalRequest *)request successBlock:(OSResultSuccessBlock)success failureBlock:(OSFailureBlock)failure; + +@end diff --git a/iOS_SDK/OneSignalSDK/Source/ReattemptRequest.m b/iOS_SDK/OneSignalSDK/Source/ReattemptRequest.m new file mode 100644 index 000000000..6c9efe872 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/ReattemptRequest.m @@ -0,0 +1,23 @@ +// +// ReattemptRequest.m +// OneSignal +// +// Created by Brad Hesse on 1/31/18. +// Copyright © 2018 Hiptic. All rights reserved. +// + +#import "ReattemptRequest.h" + +@implementation ReattemptRequest + ++(instancetype)withRequest:(OneSignalRequest *)request successBlock:(OSResultSuccessBlock)success failureBlock:(OSFailureBlock)failure { + let reattempt = [ReattemptRequest new]; + + reattempt.request = request; + reattempt.successBlock = success; + reattempt.failureBlock = failure; + + return reattempt; +} + +@end diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.m b/iOS_SDK/OneSignalSDK/Source/Requests.m index 5a311e752..20c7b51d5 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.m +++ b/iOS_SDK/OneSignalSDK/Source/Requests.m @@ -97,7 +97,6 @@ + (instancetype)withAppId:(NSString *)appId withJson:(NSMutableDictionary *)json @implementation OSRequestUpdateDeviceToken + (instancetype)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nonnull)identifier notificationTypes:(NSNumber * _Nonnull)notificationTypes withParentId:(NSString * _Nullable)parentId { - NSLog(@"USER ID: %@, APP ID: %@, DEVICE TOKEN: %@, NOTIFICATION TYPES: %@, PARENT ID: %@", userId, appId, identifier, notificationTypes, parentId); let request = [OSRequestUpdateDeviceToken new]; From c0b43254dbf09aaf90f50b383573ef723eb118e4 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Wed, 31 Jan 2018 15:32:25 -0800 Subject: [PATCH 07/36] Fix HTTP Request Re-Attempt Time Definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • The re-attempt define statements were incorrect due to debugging, fixed them to be the correct values. --- iOS_SDK/OneSignalSDK/Source/OneSignalClient.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m index e9b34210b..c0ba1cb9f 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m @@ -29,9 +29,9 @@ #import "UIApplicationDelegate+OneSignal.h" #import "ReattemptRequest.h" -#define REATTEMPT_DELAY 15.0 -#define REQUEST_TIMEOUT_REQUEST 6.0 -#define REQUEST_TIMEOUT_RESOURCE 10.0 +#define REATTEMPT_DELAY 30.0 +#define REQUEST_TIMEOUT_REQUEST 60.0 //for most HTTP requests +#define REQUEST_TIMEOUT_RESOURCE 100.0 //for loading a resource like an image @interface OneSignalClient () @property (strong, nonatomic) NSURLSession *sharedSession; From 2f1ff337c3b567f070d3eac98ab14e0bc5e36a69 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Wed, 31 Jan 2018 16:10:47 -0800 Subject: [PATCH 08/36] Multiple Re-Attempts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Changes the SDK so that multiple re-attempts can be made for failed HTTP requests • Currently, all failed requests (HTTP code 500+) will be attempted 3 times total. --- iOS_SDK/OneSignalSDK/Source/OneSignalClient.m | 7 ++++--- iOS_SDK/OneSignalSDK/Source/OneSignalRequest.h | 2 +- iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m index c0ba1cb9f..682ad01d8 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m @@ -32,6 +32,7 @@ #define REATTEMPT_DELAY 30.0 #define REQUEST_TIMEOUT_REQUEST 60.0 //for most HTTP requests #define REQUEST_TIMEOUT_RESOURCE 100.0 //for loading a resource like an image +#define MAX_ATTEMPT_COUNT 3 @interface OneSignalClient () @property (strong, nonatomic) NSURLSession *sharedSession; @@ -166,9 +167,9 @@ - (void)reattemptRequest:(ReattemptRequest *)reattempt { return; } - //very important to set this flag otherwise the request will continue to reattempt infinitely until it stops getting a 500+ error code. + //very important to increment this variable otherwise the request will continue to reattempt infinitely until it stops getting a 500+ error code. //we want requests to only retry one time after a delay. - reattempt.request.reattempted = true; + reattempt.request.reattemptCount++; [self executeRequest:reattempt.request onSuccess:reattempt.successBlock onFailure:reattempt.failureBlock]; } @@ -191,7 +192,7 @@ - (void)handleJSONNSURLResponse:(NSURLResponse*)response data:(NSData*)data erro } // in the event that there is no network connection, NSURLSession will return status code 0 - if ((statusCode >= 500 || statusCode == 0) && !request.reattempted) { + if ((statusCode >= 500 || statusCode == 0) && request.reattemptCount < MAX_ATTEMPT_COUNT - 1) { let reattempt = [ReattemptRequest withRequest:request successBlock:successBlock failureBlock:failureBlock]; if (async) { diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.h b/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.h index 423bde400..1c7f79346 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.h +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.h @@ -39,7 +39,7 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE} HTTPMethod; @property (nonatomic) HTTPMethod method; @property (nonatomic, nonnull) NSString *path; @property (nonatomic, nullable) NSDictionary *parameters; -@property (nonatomic) BOOL reattempted; +@property (nonatomic) int reattemptCount; -(BOOL)missingAppId; //for requests that don't require an appId parameter, the subclass should override this method and return false -(NSMutableURLRequest * _Nonnull )request; @end diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m b/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m index 880669a4d..3564f5e39 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m @@ -36,7 +36,7 @@ @implementation OneSignalRequest - (id)init { if (self = [super init]) { - self.reattempted = false; + self.reattemptCount = 0; } return self; From fc9b0b1246cf88ebcb9fee77359b621687863ab1 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Thu, 1 Feb 2018 18:05:30 -0800 Subject: [PATCH 09/36] OneSignal Email SDK Feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Built out most of the Email feature for the SDK • Improved the failed HTTP request re-attempt code to be a bit more readable • Put a lot of commonly used strings into a single CommonDefines header file • Made it so that calls to `setEmail:` will be delayed under two conditions: 1. The player_id is not set, meaning `registerUserInternal()` has not yet finished 2. No email_auth_code was passed in as a parameter to `setEmail:` and the GetIOSParams request has not yet finished. --- .../Base.lproj/Main.storyboard | 48 +++++++++-- .../OneSignalDevApp/ViewController.m | 12 +++ .../OneSignal.xcodeproj/project.pbxproj | 12 +++ .../OneSignalSDK/Source/NSNull+OneSignal.m | 2 + iOS_SDK/OneSignalSDK/Source/OSSubscription.h | 2 + iOS_SDK/OneSignalSDK/Source/OSSubscription.m | 38 ++++++--- iOS_SDK/OneSignalSDK/Source/OneSignal.m | 81 ++++++++++++++++--- iOS_SDK/OneSignalSDK/Source/OneSignalClient.m | 40 +++++---- .../Source/OneSignalCommonDefines.h | 28 +++++++ .../OneSignalNotificationSettingsIOS7.m | 3 +- .../Source/OneSignalSetEmailParameters.h | 21 +++++ .../Source/OneSignalSetEmailParameters.m | 24 ++++++ 12 files changed, 262 insertions(+), 49 deletions(-) create mode 100644 iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h create mode 100644 iOS_SDK/OneSignalSDK/Source/OneSignalSetEmailParameters.h create mode 100644 iOS_SDK/OneSignalSDK/Source/OneSignalSetEmailParameters.m diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard index f015a1da2..8588bd71f 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard @@ -1,11 +1,12 @@ - - + + - + + @@ -21,17 +22,52 @@ - + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m index 1cf994674..0fbc5abc0 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m @@ -33,6 +33,7 @@ #import @interface ViewController () +@property (weak, nonatomic) IBOutlet UITextField *textField; @end @@ -71,6 +72,17 @@ - (IBAction)sendTagButton:(id)sender { } +- (IBAction)setEmailButtonPressed:(UIButton *)sender +{ + [OneSignal setEmail:self.textField.text withEmailAuthHashToken:@"aa3e3201f8f8bfd2fcbe8a899c161b7acb5a86545196c5465bef47fd757ca356" withSuccess:^{ + NSLog(@"Successfully sent email"); + } withFailure:^(NSError *error) { + NSLog(@"Encountered error: %@", error); + }]; +} + + + - (void)promptForNotificationsWithNativeiOS10Code { id responseBlock = ^(BOOL granted, NSError* error) { NSLog(@"promptForNotificationsWithNativeiOS10Code: %d", granted); diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index a61903c77..9433173ab 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -142,6 +142,9 @@ CA63AFC32022670A00E340FB /* ReattemptRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AFC12022670A00E340FB /* ReattemptRequest.m */; }; CA63AFC42022670A00E340FB /* ReattemptRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AFC12022670A00E340FB /* ReattemptRequest.m */; }; CA63AFC52022670A00E340FB /* ReattemptRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA63AFC12022670A00E340FB /* ReattemptRequest.m */; }; + CA70E3352023D51000019273 /* OneSignalSetEmailParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = CA70E3342023D51000019273 /* OneSignalSetEmailParameters.m */; }; + CA70E3362023D51300019273 /* OneSignalSetEmailParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = CA70E3342023D51000019273 /* OneSignalSetEmailParameters.m */; }; + CA70E3372023D51300019273 /* OneSignalSetEmailParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = CA70E3342023D51000019273 /* OneSignalSetEmailParameters.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -271,6 +274,9 @@ CA63AF8620211FF800E340FB /* UnitTestCommonMethods.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnitTestCommonMethods.m; sourceTree = ""; }; CA63AFC02022670A00E340FB /* ReattemptRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReattemptRequest.h; sourceTree = ""; }; CA63AFC12022670A00E340FB /* ReattemptRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReattemptRequest.m; sourceTree = ""; }; + CA70E3332023D51000019273 /* OneSignalSetEmailParameters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalSetEmailParameters.h; sourceTree = ""; }; + CA70E3342023D51000019273 /* OneSignalSetEmailParameters.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalSetEmailParameters.m; sourceTree = ""; }; + CA70E3382023F24500019273 /* OneSignalCommonDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalCommonDefines.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -416,9 +422,12 @@ 91F58D7B1E7C7EE30017D24D /* NotificationSettings */, 912412461E73349500E41FD7 /* Categories */, 912412451E73346700E41FD7 /* UI */, + CA70E3382023F24500019273 /* OneSignalCommonDefines.h */, 91C7725D1E7CCE1000D612D0 /* OneSignalInternal.h */, 912411F01E73342200E41FD7 /* OneSignal.h */, 912411F11E73342200E41FD7 /* OneSignal.m */, + CA70E3332023D51000019273 /* OneSignalSetEmailParameters.h */, + CA70E3342023D51000019273 /* OneSignalSetEmailParameters.m */, 912411F41E73342200E41FD7 /* OneSignalHelper.h */, 912411F51E73342200E41FD7 /* OneSignalHelper.m */, 912411F81E73342200E41FD7 /* OneSignalJailbreakDetection.h */, @@ -701,6 +710,7 @@ 912412261E73342200E41FD7 /* OneSignalMobileProvision.m in Sources */, 454F94F21FAD218000D74CCF /* OneSignalNotificationServiceExtensionHandler.m in Sources */, 912412321E73342200E41FD7 /* OneSignalTracker.m in Sources */, + CA70E3352023D51000019273 /* OneSignalSetEmailParameters.m in Sources */, 91B6EA411E85D38F00B5CF01 /* OSObservable.m in Sources */, 4529DF0C1FA932AC00CEAB1D /* OneSignalTrackFirebaseAnalytics.m in Sources */, 91F58D891E7C9A240017D24D /* OneSignalNotificationSettingsIOS7.m in Sources */, @@ -736,6 +746,7 @@ 9124123B1E73342200E41FD7 /* OneSignalWebView.m in Sources */, 9124123F1E73342200E41FD7 /* UIApplicationDelegate+OneSignal.m in Sources */, 0338566C1FBBDB150002F7C1 /* OneSignalNotificationServiceExtensionHandler.m in Sources */, + CA70E3362023D51300019273 /* OneSignalSetEmailParameters.m in Sources */, 912412271E73342200E41FD7 /* OneSignalMobileProvision.m in Sources */, 912412331E73342200E41FD7 /* OneSignalTracker.m in Sources */, 91B6EA421E85D38F00B5CF01 /* OSObservable.m in Sources */, @@ -760,6 +771,7 @@ 912412201E73342200E41FD7 /* OneSignalJailbreakDetection.m in Sources */, 912412381E73342200E41FD7 /* OneSignalTrackIAP.m in Sources */, CA63AF8720211FF800E340FB /* UnitTestCommonMethods.m in Sources */, + CA70E3372023D51300019273 /* OneSignalSetEmailParameters.m in Sources */, 4529DED81FA8253D00CEAB1D /* NSUserDefaultsOverrider.m in Sources */, 4529DEED1FA83C5D00CEAB1D /* OneSignalHelperOverrider.m in Sources */, 912412301E73342200E41FD7 /* OneSignalSelectorHelpers.m in Sources */, diff --git a/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.m b/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.m index 45378226c..1cdcc12d3 100644 --- a/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.m @@ -37,6 +37,8 @@ #import "NSNull+OneSignal.h" +// Helper method to make sure nil is never inserted into a dictionary when building HTTP requests + @implementation NSNull (OneSignal) + (id)nullIfObjectIsNil:(id)object { diff --git a/iOS_SDK/OneSignalSDK/Source/OSSubscription.h b/iOS_SDK/OneSignalSDK/Source/OSSubscription.h index 334198373..24f5a0c4c 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSSubscription.h +++ b/iOS_SDK/OneSignalSDK/Source/OSSubscription.h @@ -64,6 +64,8 @@ typedef OSObservable*, OSSubscriptionState // Redefine OSSubscriptionState @interface OSSubscriptionState () +@property (strong, nonatomic) NSString *emailAuthCode; +@property (nonatomic) BOOL requiresEmailAuth; @property (nonatomic) BOOL accpeted; - (void)setEmailUserId:(NSString *)emailUserId; diff --git a/iOS_SDK/OneSignalSDK/Source/OSSubscription.m b/iOS_SDK/OneSignalSDK/Source/OSSubscription.m index 01df83cdf..ad2b3b061 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSSubscription.m +++ b/iOS_SDK/OneSignalSDK/Source/OSSubscription.m @@ -26,6 +26,7 @@ */ #import "OSSubscription.h" +#import "OneSignalCommonDefines.h" #pragma clang diagnostic push @@ -43,15 +44,19 @@ - (instancetype)initAsToWithPermision:(BOOL)permission { _accpeted = permission; NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; - _userId = [userDefaults stringForKey:@"GT_PLAYER_ID"]; - _pushToken = [userDefaults stringForKey:@"GT_DEVICE_TOKEN"]; - _userSubscriptionSetting = [userDefaults objectForKey:@"ONESIGNAL_SUBSCRIPTION"] == nil; + _requiresEmailAuth = [[userDefaults objectForKey:REQUIRE_EMAIL_AUTH] boolValue]; + _emailAuthCode = [userDefaults stringForKey:EMAIL_AUTH_CODE]; + _emailUserId = [userDefaults stringForKey:EMAIL_USERID]; + _userId = [userDefaults stringForKey:USERID]; + _pushToken = [userDefaults stringForKey:DEVICE_TOKEN]; + _userSubscriptionSetting = [userDefaults objectForKey:SUBSCRIPTION] == nil; return self; } - (BOOL)compare:(OSSubscriptionState*)from { return ![self.userId ?: @"" isEqualToString:from.userId ?: @""] || + ![self.emailUserId ?: @"" isEqualToString:from.emailUserId ?: @""] || ![self.pushToken ?: @"" isEqualToString:from.pushToken ?: @""] || self.userSubscriptionSetting != from.userSubscriptionSetting || self.accpeted != from.accpeted; @@ -60,10 +65,13 @@ - (BOOL)compare:(OSSubscriptionState*)from { - (instancetype)initAsFrom { NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; - _userId = [userDefaults stringForKey:@"GT_PLAYER_ID_LAST"]; - _pushToken = [userDefaults stringForKey:@"GT_DEVICE_TOKEN_LAST"]; - _userSubscriptionSetting = [userDefaults objectForKey:@"ONESIGNAL_SUBSCRIPTION_LAST"] == nil; - _accpeted = [userDefaults boolForKey:@"ONESIGNAL_PERMISSION_ACCEPTED_LAST"]; + _requiresEmailAuth = [[userDefaults objectForKey:REQUIRE_EMAIL_AUTH] boolValue]; + _emailAuthCode = [userDefaults stringForKey:EMAIL_AUTH_CODE]; + _emailUserId = [userDefaults stringForKey:EMAIL_USERID]; + _userId = [userDefaults stringForKey:USERID_LAST]; + _pushToken = [userDefaults stringForKey:PUSH_TOKEN]; + _userSubscriptionSetting = [userDefaults objectForKey:SUBSCRIPTION_SETTING] == nil; + _accpeted = [userDefaults boolForKey:ACCEPTED_PERMISSION]; return self; } @@ -74,11 +82,14 @@ - (void)persistAsFrom { NSString* strUserSubscriptionSetting = nil; if (!_userSubscriptionSetting) strUserSubscriptionSetting = @"no"; - [userDefaults setObject:strUserSubscriptionSetting forKey:@"ONESIGNAL_SUBSCRIPTION_LAST"]; - [userDefaults setObject:_userId forKey:@"GT_PLAYER_ID_LAST"]; - [userDefaults setObject:_pushToken forKey:@"GT_DEVICE_TOKEN_LAST"]; - [userDefaults setBool:_accpeted forKey:@"ONESIGNAL_PERMISSION_ACCEPTED_LAST"]; + [userDefaults setObject:[NSNumber numberWithBool:_requiresEmailAuth] forKey:REQUIRE_EMAIL_AUTH]; + [userDefaults setObject:_emailAuthCode forKey:EMAIL_AUTH_CODE]; + [userDefaults setObject:strUserSubscriptionSetting forKey:SUBSCRIPTION_SETTING]; + [userDefaults setObject:_emailUserId forKey:EMAIL_USERID]; + [userDefaults setObject:_userId forKey:USERID_LAST]; + [userDefaults setObject:_pushToken forKey:PUSH_TOKEN]; + [userDefaults setBool:_accpeted forKey:ACCEPTED_PERMISSION]; [userDefaults synchronize]; } @@ -87,6 +98,9 @@ - (instancetype)copyWithZone:(NSZone*)zone { OSSubscriptionState* copy = [[[self class] alloc] init]; if (copy) { + copy->_requiresEmailAuth = _requiresEmailAuth; + copy->_emailAuthCode = [_emailAuthCode copy]; + copy->_emailUserId = [_emailUserId copy]; copy->_userId = [_userId copy]; copy->_pushToken = [_pushToken copy]; copy->_userSubscriptionSetting = _userSubscriptionSetting; @@ -112,7 +126,7 @@ - (void)setPushToken:(NSString*)pushToken { BOOL changed = ![[NSString stringWithString:pushToken] isEqualToString:_pushToken]; _pushToken = pushToken; if (changed) { - [[NSUserDefaults standardUserDefaults] setObject:_pushToken forKey:@"GT_DEVICE_TOKEN"]; + [[NSUserDefaults standardUserDefaults] setObject:_pushToken forKey:DEVICE_TOKEN]; [[NSUserDefaults standardUserDefaults] synchronize]; if (self.observable) diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index 5692261be..b3962f9fa 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -63,6 +63,9 @@ #import +#import "OneSignalSetEmailParameters.h" +#import "OneSignalCommonDefines.h" + #define NOTIFICATION_TYPE_NONE 0 #define NOTIFICATION_TYPE_BADGE 1 #define NOTIFICATION_TYPE_SOUND 2 @@ -131,6 +134,14 @@ @implementation OneSignal static NSString *currentUserEmail; static NSString *authHashToken; +/* + if setEmail: was called before the device was registered (push playerID = nil), + then the call to setEmail: also gets delayed + this property stores the parameters so that once registration is complete + we can finish setEmail: +*/ +static OneSignalSetEmailParameters *delayedParameters; + static NSMutableArray* pendingSendTagCallbacks; static OSResultSuccessBlock pendingGetTagsSuccessBlock; static OSFailureBlock pendingGetTagsFailureBlock; @@ -144,6 +155,9 @@ @implementation OneSignal // Under Capabilities is "Background Modes" > "Remote notifications" enabled. static BOOL backgroundModesEnabled = false; +// indicates if the GetiOSParams request has completed +static BOOL downloadedParameters = false; + static OneSignalTrackIAP* trackIAPPurchase; static NSString* app_id; NSString* emailToSet; @@ -448,7 +462,7 @@ +(bool)initAppId:(NSString*)appId withUserDefaults:(NSUserDefaults*)userDefaults // Handle changes to the app id. This might happen on a developer's device when testing // Will also run the first time OneSignal is initialized [userDefaults setObject:app_id forKey:@"GT_APP_ID"]; - [userDefaults setObject:nil forKey:@"GT_PLAYER_ID"]; + [userDefaults setObject:nil forKey:USERID]; [userDefaults synchronize]; } @@ -465,7 +479,19 @@ +(bool)initAppId:(NSString*)appId withUserDefaults:(NSUserDefaults*)userDefaults +(void)downloadIOSParams { [OneSignalClient.sharedClient executeRequest:[OSRequestGetIosParams withUserId:self.currentSubscriptionState.userId appId:self.app_id] onSuccess:^(NSDictionary *result) { + if (result[@"require_email_auth"]) { + self.currentSubscriptionState.requiresEmailAuth = [result[@"require_email_auth"] boolValue]; + + // checks if a cell to setEmail: was delayed due to missing 'requiresEmailAuth' parameter + if (delayedParameters && self.currentSubscriptionState.userId) { + [self setEmail:delayedParameters.email withEmailAuthHashToken:delayedParameters.authToken withSuccess:delayedParameters.successBlock withFailure:delayedParameters.failureBlock]; + delayedParameters = nil; + } + } + [OneSignalTrackFirebaseAnalytics updateFromDownloadParams:result]; + + downloadedParameters = true; } onFailure:nil]; } @@ -847,7 +873,7 @@ + (void)setSubscription:(BOOL)enable { if (!enable) value = @"no"; - [[NSUserDefaults standardUserDefaults] setObject:value forKey:@"ONESIGNAL_SUBSCRIPTION"]; + [[NSUserDefaults standardUserDefaults] setObject:value forKey:SUBSCRIPTION]; [[NSUserDefaults standardUserDefaults] synchronize]; shouldDelaySubscriptionUpdate = true; @@ -1084,8 +1110,13 @@ + (void)registerUserInternal { let requests = [NSMutableDictionary new]; requests[@"push"] = [OSRequestRegisterUser withData:dataDic userId:self.currentSubscriptionState.userId]; - if ([[NSUserDefaults standardUserDefaults] objectForKey:@"GT_EMAIL_PLAYER_ID"]) - requests[@"email"] = [OSRequestRegisterUser withData:dataDic userId:self.currentSubscriptionState.emailUserId]; + if (self.currentSubscriptionState.emailUserId && self.currentSubscriptionState.emailAuthCode) { + let emailDataDic = (NSMutableDictionary *)[dataDic mutableCopy]; + emailDataDic[@"device_type"] = @11; + emailDataDic[@"email_auth_hash"] = self.currentSubscriptionState.emailAuthCode; + + requests[@"email"] = [OSRequestRegisterUser withData:emailDataDic userId:self.currentSubscriptionState.emailUserId]; + } [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:^(NSDictionary *results) { waitingForOneSReg = false; @@ -1098,7 +1129,7 @@ + (void)registerUserInternal { //update email player ID if (results[@"email"] && results[@"email"][@"id"]) { self.currentSubscriptionState.emailUserId = results[@"email"][@"id"]; - [[NSUserDefaults standardUserDefaults] setObject:self.currentSubscriptionState.emailUserId forKey:@"GT_EMAIL_PLAYER_ID"]; + [[NSUserDefaults standardUserDefaults] setObject:self.currentSubscriptionState.emailUserId forKey:EMAIL_USERID]; //NSUserDefaults Synchronize: called after the next if-statement } @@ -1106,7 +1137,13 @@ + (void)registerUserInternal { if (results.count > 0 && results[@"push"][@"id"]) { self.currentSubscriptionState.userId = results[@"push"][@"id"]; - [[NSUserDefaults standardUserDefaults] setObject:self.currentSubscriptionState.userId forKey:@"GT_PLAYER_ID"]; + if (delayedParameters) { + //a call to setEmail: was delayed because the push player_id did not exist yet + [self setEmail:delayedParameters.email withEmailAuthHashToken:delayedParameters.authToken withSuccess:delayedParameters.successBlock withFailure:delayedParameters.failureBlock]; + delayedParameters = nil; + } + + [[NSUserDefaults standardUserDefaults] setObject:self.currentSubscriptionState.userId forKey:USERID]; //NSUserDefaults Synchronize: called after this if-statement if (self.currentSubscriptionState.pushToken) @@ -1544,7 +1581,7 @@ + (void)updateEmailPlayerId:(NSString *)emailPlayerId { if (!emailPlayerId) return; - [[NSUserDefaults standardUserDefaults] setObject:emailPlayerId forKey:@"GT_EMAIL_PLAYER_ID"]; + [[NSUserDefaults standardUserDefaults] setObject:emailPlayerId forKey:EMAIL_USERID]; [[NSUserDefaults standardUserDefaults] synchronize]; if (![emailPlayerId isEqualToString:_currentSubscriptionState.emailUserId]) { @@ -1557,14 +1594,30 @@ + (void)updateEmailPlayerId:(NSString *)emailPlayerId { + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _Nullable)hashToken withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { if (![OneSignalHelper isValidEmail:email]) { if (failureBlock) - failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"description" : @"Email is invalid"}]); + failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"error" : @"Email is invalid"}]); + return; + } + + if (self.currentSubscriptionState.requiresEmailAuth && (!hashToken || hashToken.length == 0)) { + failureBlock([NSError errorWithDomain:@"com.onesignal.email" code:0 userInfo:@{@"error" : @"Email authentication (auth token) is set to REQUIRED for this application. Please provide an auth token from your backend server or change the setting in the OneSignal dashboard."}]); + return; + } + + /* + if the iOS params (with the require_email_auth setting) has not been downloaded yet, we should delay the request + however, if this method was called with an email auth code passed in, then there is no need to check this setting + and we do not need to delay the request + */ + + if (!self.currentSubscriptionState.userId || (downloadedParameters == false && hashToken == nil)) { + delayedParameters = [OneSignalSetEmailParameters withEmail:email withAuthToken:hashToken withSuccess:successBlock withFailure:failureBlock]; return; } currentUserEmail = email; authHashToken = hashToken; - let emailId = (NSString *)[[NSUserDefaults standardUserDefaults] objectForKey:@"GT_EMAIL_PLAYER_ID"]; + let emailId = (NSString *)[[NSUserDefaults standardUserDefaults] objectForKey:EMAIL_USERID]; if (emailId) { [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:email notificationTypes:@([self getNotificationTypes]) withParentId:nil] onSuccess:^(NSDictionary *result) { @@ -1575,13 +1628,15 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ failureBlock(error); }]; } else { - [OneSignalClient.sharedClient executeRequest:[OSRequestCreateDevice withAppId:self.app_id withDeviceType:@11 withEmail:currentUserEmail withPlayerId:_currentSubscriptionState.userId withEmailAuthHash:authHashToken] onSuccess:^(NSDictionary *result) { + [OneSignalClient.sharedClient executeRequest:[OSRequestCreateDevice withAppId:self.app_id withDeviceType:@11 withEmail:currentUserEmail withPlayerId:self.currentSubscriptionState.userId withEmailAuthHash:authHashToken] onSuccess:^(NSDictionary *result) { let emailPlayerId = (NSString *)result[@"id"]; if (emailPlayerId) { - [[NSUserDefaults standardUserDefaults] setObject:emailPlayerId forKey:@"GT_EMAIL_PLAYER_ID"]; + [[NSUserDefaults standardUserDefaults] setObject:hashToken forKey:EMAIL_AUTH_CODE]; + [[NSUserDefaults standardUserDefaults] setObject:emailPlayerId forKey:EMAIL_USERID]; [[NSUserDefaults standardUserDefaults] synchronize]; + self.currentSubscriptionState.emailAuthCode = hashToken; self.currentSubscriptionState.emailUserId = emailPlayerId; if (successBlock) @@ -1603,12 +1658,12 @@ + (void)setUnauthenticatedEmail:(NSString * _Nonnull)email withSuccess:(OSEmailS + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { if (!self.currentSubscriptionState.emailUserId) { [OneSignal onesignal_Log:ONE_S_LL_ERROR message:@"Email Player ID does not exist, cannot logout"]; - failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"description" : @"Attempted to log out of the user's email with OneSignal. The user does not currently have an email player ID and is not logged in, so it is not possible to log out of the email for this device"}]); + failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"error" : @"Attempted to log out of the user's email with OneSignal. The user does not currently have an email player ID and is not logged in, so it is not possible to log out of the email for this device"}]); return; } [OneSignalClient.sharedClient executeRequest:[OSRequestLogoutEmail withAppId: self.app_id emailPlayerId:self.currentSubscriptionState.emailUserId devicePlayerId:self.currentSubscriptionState.userId emailAuthHash:authHashToken] onSuccess:^(NSDictionary *result) { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"GT_EMAIL_PLAYER_ID"]; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:EMAIL_USERID]; [[NSUserDefaults standardUserDefaults] synchronize]; [self.currentSubscriptionState setEmailUserId:nil]; diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m index 682ad01d8..f1fb21aa7 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m @@ -62,7 +62,6 @@ -(instancetype)init { } - (void)executeSimultaneousRequests:(NSDictionary *)requests withSuccess:(OSMultipleSuccessBlock)successBlock onFailure:(OSMultipleFailureBlock)failureBlock { - if (requests.allKeys.count == 0) return; @@ -174,6 +173,27 @@ - (void)reattemptRequest:(ReattemptRequest *)reattempt { [self executeRequest:reattempt.request onSuccess:reattempt.successBlock onFailure:reattempt.failureBlock]; } +- (BOOL)willReattemptRequest:(int)statusCode withRequest:(OneSignalRequest *)request success:(OSResultSuccessBlock)successBlock failure:(OSFailureBlock)failureBlock asyncRequest:(BOOL)async { + // in the event that there is no network connection, NSURLSession will return status code 0 + if ((statusCode >= 500 || statusCode == 0) && request.reattemptCount < MAX_ATTEMPT_COUNT - 1) { + let reattempt = [ReattemptRequest withRequest:request successBlock:successBlock failureBlock:failureBlock]; + + if (async) { + //retry again in 15 seconds + [OneSignal onesignal_Log:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"Re-scheduling request (%@) to be re-attempted in %i seconds due to failed HTTP request with status code %i", NSStringFromClass([request class]), (int)REATTEMPT_DELAY, (int)statusCode]]; + + [OneSignalHelper performSelector:@selector(reattemptRequest:) onMainThreadOnObject:self withObject:reattempt afterDelay:REATTEMPT_DELAY]; + } else { + //retry again immediately + [self reattemptRequest: reattempt]; + } + + return true; + } + + return false; +} + - (void)handleJSONNSURLResponse:(NSURLResponse*)response data:(NSData*)data error:(NSError*)error isAsync:(BOOL)async withRequest:(OneSignalRequest *)request onSuccess:(OSResultSuccessBlock)successBlock onFailure:(OSFailureBlock)failureBlock { NSHTTPURLResponse* HTTPResponse = (NSHTTPURLResponse*)response; @@ -183,7 +203,7 @@ - (void)handleJSONNSURLResponse:(NSURLResponse*)response data:(NSData*)data erro if (data != nil && [data length] > 0) { innerJson = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError]; - [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"network response: %@", innerJson]]; + [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"network response (%@): %@", NSStringFromClass([request class]), innerJson]]; if (jsonError) { if (failureBlock != nil) failureBlock([NSError errorWithDomain:@"OneSignal Error" code:statusCode userInfo:@{@"returned" : jsonError}]); @@ -191,22 +211,8 @@ - (void)handleJSONNSURLResponse:(NSURLResponse*)response data:(NSData*)data erro } } - // in the event that there is no network connection, NSURLSession will return status code 0 - if ((statusCode >= 500 || statusCode == 0) && request.reattemptCount < MAX_ATTEMPT_COUNT - 1) { - let reattempt = [ReattemptRequest withRequest:request successBlock:successBlock failureBlock:failureBlock]; - - if (async) { - //retry again in 15 seconds - [OneSignal onesignal_Log:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"Re-scheduling request (%@) to be re-attempted in %i seconds due to failed HTTP request with status code %i", NSStringFromClass([request class]), (int)REATTEMPT_DELAY, (int)statusCode]]; - - [OneSignalHelper performSelector:@selector(reattemptRequest:) onMainThreadOnObject:self withObject:reattempt afterDelay:REATTEMPT_DELAY]; - } else { - //retry again immediately - [self reattemptRequest: reattempt]; - } - + if ([self willReattemptRequest:(int)statusCode withRequest:request success:successBlock failure:failureBlock asyncRequest:async]) return; - } if (error == nil && statusCode == 200) { if (successBlock != nil) { diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h b/iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h new file mode 100644 index 000000000..9ec383249 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h @@ -0,0 +1,28 @@ +// +// OneSignalCommonDefines.h +// OneSignal +// +// Created by Brad Hesse on 2/1/18. +// Copyright © 2018 Hiptic. All rights reserved. +// + +#ifndef OneSignalCommonDefines_h +#define OneSignalCommonDefines_h + +#define EMAIL_AUTH_CODE @"GT_EMAIL_AUTH_CODE" +#define SUBSCRIPTION_SETTING @"ONESIGNAL_SUBSCRIPTION_LAST" +#define EMAIL_USERID @"GT_EMAIL_PLAYER_ID" +#define USERID @"GT_PLAYER_ID" +#define USERID_LAST @"GT_PLAYER_ID_LAST" +#define DEVICE_TOKEN @"GT_DEVICE_TOKEN" +#define SUBSCRIPTION @"ONESIGNAL_SUBSCRIPTION" +#define PUSH_TOKEN @"GT_DEVICE_TOKEN_LAST" +#define ACCEPTED_PERMISSION @"ONESIGNAL_PERMISSION_ACCEPTED_LAST" +#define REQUIRE_EMAIL_AUTH @"GT_REQUIRE_EMAIL_AUTH" + + + + + + +#endif /* OneSignalCommonDefines_h */ diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalNotificationSettingsIOS7.m b/iOS_SDK/OneSignalSDK/Source/OneSignalNotificationSettingsIOS7.m index cefa32480..684b58432 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalNotificationSettingsIOS7.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalNotificationSettingsIOS7.m @@ -32,6 +32,7 @@ #import "OneSignal.h" #import "OneSignalInternal.h" #import "OneSignalNotificationSettingsIOS7.h" +#import "OneSignalCommonDefines.h" @implementation OneSignalNotificationSettingsIOS7 { @@ -43,7 +44,7 @@ - (void)getNotificationPermissionState:(void (^)(OSPermissionState *subcscriptio OSPermissionState* status = OneSignal.currentPermissionState; // Don't call getNotificationTypes as this will cause currentSubscriptionState to initialize before currentPermissionState - status.notificationTypes = [userDefaults stringForKey:@"GT_DEVICE_TOKEN"] == nil ? 0 : 7; + status.notificationTypes = [userDefaults stringForKey:DEVICE_TOKEN] == nil ? 0 : 7; status.accepted = status.notificationTypes > 0; status.answeredPrompt = [userDefaults boolForKey:@"OS_NOTIFICATION_PROMPT_ANSWERED"]; diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalSetEmailParameters.h b/iOS_SDK/OneSignalSDK/Source/OneSignalSetEmailParameters.h new file mode 100644 index 000000000..846ce466a --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalSetEmailParameters.h @@ -0,0 +1,21 @@ +// +// OneSignalSetEmailParameters.h +// OneSignal +// +// Created by Brad Hesse on 2/1/18. +// Copyright © 2018 Hiptic. All rights reserved. +// + +#import +#import "OneSignal.h" + +@interface OneSignalSetEmailParameters : NSObject + ++ (instancetype)withEmail:(NSString *)email withAuthToken:(NSString *)authToken withSuccess:(OSResultSuccessBlock)success withFailure:(OSFailureBlock)failure; + +@property (strong, nonatomic) NSString *email; +@property (strong, nonatomic) NSString *authToken; +@property (nonatomic) OSResultSuccessBlock successBlock; +@property (nonatomic) OSFailureBlock failureBlock; + +@end diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalSetEmailParameters.m b/iOS_SDK/OneSignalSDK/Source/OneSignalSetEmailParameters.m new file mode 100644 index 000000000..23e32e9a5 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalSetEmailParameters.m @@ -0,0 +1,24 @@ +// +// OneSignalSetEmailParameters.m +// OneSignal +// +// Created by Brad Hesse on 2/1/18. +// Copyright © 2018 Hiptic. All rights reserved. +// + +#import "OneSignalSetEmailParameters.h" + +@implementation OneSignalSetEmailParameters + ++ (instancetype)withEmail:(NSString *)email withAuthToken:(NSString *)authToken withSuccess:(OSResultSuccessBlock)success withFailure:(OSFailureBlock)failure { + OneSignalSetEmailParameters *parameters = [OneSignalSetEmailParameters new]; + + parameters.email = email; + parameters.authToken = authToken; + parameters.successBlock = success; + parameters.failureBlock = failure; + + return parameters; +} + +@end From 3705df97a109cb9b59b8d597f8ae3fbe84dcf5d4 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Fri, 2 Feb 2018 12:07:15 -0800 Subject: [PATCH 10/36] Fix Tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Fixed some issues that broke unit tests due to email. • Fixed an issue that added an extra / to the Create Device endpoint path. --- iOS_SDK/OneSignalSDK/Source/Requests.m | 2 +- iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m | 34 +++- .../UnitTests/UnitTestCommonMethods.h | 5 + .../UnitTests/UnitTestCommonMethods.m | 77 ++++++++- iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m | 151 +++++------------- 5 files changed, 156 insertions(+), 113 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.m b/iOS_SDK/OneSignalSDK/Source/Requests.m index 20c7b51d5..a8d0236e8 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.m +++ b/iOS_SDK/OneSignalSDK/Source/Requests.m @@ -127,7 +127,7 @@ + (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId withDeviceType:(NS }; request.method = POST; - request.path = @"/players"; + request.path = @"players"; return request; } diff --git a/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m index 32e76f2cf..1b36177cb 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m @@ -14,8 +14,13 @@ #import "OneSignalClientOverrider.h" #import "UnitTestCommonMethods.h" #import "OSSubscription.h" - - +#import "UIApplicationOverrider.h" +#import "NSObjectOverrider.h" +#import "OneSignalHelperOverrider.h" +#import "UNUserNotificationCenterOverrider.h" +#import "UNUserNotificationCenter+OneSignal.h" +#import "NSBundleOverrider.h" +#import "NSUserDefaultsOverrider.h" @interface EmailTests : XCTestCase @@ -26,6 +31,21 @@ @implementation EmailTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. + + OneSignalHelperOverrider.mockIOSVersion = 10; + + [OneSignalUNUserNotificationCenter setUseiOS10_2_workaround:true]; + + UNUserNotificationCenterOverrider.notifTypesOverride = 7; + UNUserNotificationCenterOverrider.authorizationStatus = [NSNumber numberWithInteger:UNAuthorizationStatusAuthorized]; + + NSBundleOverrider.nsbundleDictionary = @{@"UIBackgroundModes": @[@"remote-notification"]}; + + [NSUserDefaultsOverrider clearInternalDictionary]; + + [UnitTestCommonMethods clearStateForAppRestart]; + + [UnitTestCommonMethods beforeAllTest]; } - (void)tearDown { @@ -42,6 +62,8 @@ - (void)testEmailValidation { } - (void)testSetEmail { + + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -49,12 +71,17 @@ - (void)testSetEmail { OSSubscriptionStateTestObserver* observer = [OSSubscriptionStateTestObserver new]; [OneSignal addSubscriptionObserver:observer]; + // Triggers the 30 fallback to register device right away. [UnitTestCommonMethods runBackgroundThreads]; + [NSObjectOverrider runPendingSelectors]; + [UnitTestCommonMethods runBackgroundThreads]; + + XCTAssertEqualObjects(observer->last.to.userId, @"1234"); [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; [UnitTestCommonMethods runBackgroundThreads]; - NSLog(@"EMAIL AFTER IS: %@", OneSignalClientOverrider.lastHTTPRequest[@"email"]); + NSLog(@"EMAIL AFTER IS: %@", OneSignalClientOverrider.lastHTTPRequestType); XCTAssertTrue([@"OSRequestCreateDevice" isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"device_type"], @11); @@ -76,6 +103,7 @@ - (void)testSetEmail { [expectation fulfill]; } withFailure:^(NSError *error) { XCTFail("Failed with error: %@", error); + [expectation fulfill]; }]; [self waitForExpectations:@[expectation] timeout:0.1]; diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h index f5094ffe7..3112b9453 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h @@ -11,7 +11,12 @@ @interface UnitTestCommonMethods : NSObject ++ (void)setCurrentNotificationPermissionAsUnanswered; ++ (void)resumeApp; ++ (void)initOneSignal; + (void)runBackgroundThreads; ++ (void)beforeAllTest; ++ (void)clearStateForAppRestart; @end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m index 11828b1fb..85524b3f1 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m @@ -13,7 +13,15 @@ #import "OneSignalHelperOverrider.h" #import "OneSignal.h" #import "OneSignalNotificationSettingsIOS10.h" - +#import "UnitTestAppDelegate.h" +#import "OneSignalHelper.h" +#import "UIApplicationDelegate+OneSignal.h" +#import "NSLocaleOverrider.h" +#import "NSDateOverrider.h" +#import "OneSignalTracker.h" +#import "OneSignalTrackFirebaseAnalyticsOverrider.h" +#import "UIAlertViewOverrider.h" +#import "NSObjectOverrider.h" @interface OneSignal (UN_extra) + (dispatch_queue_t) getRegisterQueue; @@ -53,6 +61,73 @@ + (void)runBackgroundThreads { NSLog(@"END runBackgroundThreads"); } ++ (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]; +} + ++ (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 sizzlePreiOS10MethodsPhase1]; + [OneSignalAppDelegate sizzlePreiOS10MethodsPhase2]; + OneSignalHelperOverrider.mockIOSVersion = 10; +} + ++ (void)setCurrentNotificationPermissionAsUnanswered { + UNUserNotificationCenterOverrider.notifTypesOverride = 0; + UNUserNotificationCenterOverrider.authorizationStatus = [NSNumber numberWithInteger:UNAuthorizationStatusNotDetermined]; +} + + +// Helper used to simpify tests below. ++ (void)initOneSignal { + [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]; + + // iOS fires the resume event when app is cold started. + [UnitTestCommonMethods resumeApp]; +} + ++ (void)resumeApp { + UIApplicationOverrider.currentUIApplicationState = UIApplicationStateActive; + UIApplication *sharedApp = [UIApplication sharedApplication]; + [sharedApp.delegate applicationDidBecomeActive:sharedApp]; +} + @end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m index 4874e388d..61d13a31c 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m @@ -85,53 +85,6 @@ @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 sizzlePreiOS10MethodsPhase1]; - [OneSignalAppDelegate sizzlePreiOS10MethodsPhase2]; - 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]; @@ -147,9 +100,9 @@ - (void)setUp { [NSUserDefaultsOverrider clearInternalDictionary]; - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart]; - [self beforeAllTest]; + [UnitTestCommonMethods beforeAllTest]; // Uncomment to simulate slow travis-CI runs. /*float minRange = 0, maxRange = 15; @@ -168,11 +121,6 @@ - (void)backgroundModesDisabledInXcode { NSBundleOverrider.nsbundleDictionary = @{}; } -- (void)setCurrentNotificationPermissionAsUnanswered { - UNUserNotificationCenterOverrider.notifTypesOverride = 0; - UNUserNotificationCenterOverrider.authorizationStatus = [NSNumber numberWithInteger:UNAuthorizationStatusNotDetermined]; -} - - (void)setCurrentNotificationPermission:(BOOL)accepted { if (accepted) { UNUserNotificationCenterOverrider.notifTypesOverride = 7; @@ -202,7 +150,7 @@ - (void)answerNotifiationPrompt:(BOOL)accept { if (triggerDidRegisterForRemoteNotfications) [self setCurrentNotificationPermission:false]; - [self resumeApp]; + [UnitTestCommonMethods resumeApp]; [self setCurrentNotificationPermission:accept]; if (triggerDidRegisterForRemoteNotfications && NSBundleOverrider.nsbundleDictionary[@"UIBackgroundModes"]) @@ -225,12 +173,6 @@ - (void)backgroundApp { [sharedApp.delegate applicationWillResignActive:sharedApp]; } -- (void)resumeApp { - UIApplicationOverrider.currentUIApplicationState = UIApplicationStateActive; - UIApplication *sharedApp = [UIApplication sharedApplication]; - [sharedApp.delegate applicationDidBecomeActive:sharedApp]; -} - - (UNNotificationResponse*)createBasiciOSNotificationResponseWithPayload:(NSDictionary*)userInfo { // Mocking an iOS 10 notification // Setting response.notification.request.content.userInfo @@ -261,23 +203,16 @@ - (UNNotificationResponse*)createBasiciOSNotificationResponse { return [self createBasiciOSNotificationResponseWithPayload:userInfo]; } -// Helper used to simpify tests below. -- (void)initOneSignal { - [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]; - - // iOS fires the resume event when app is cold started. - [self resumeApp]; -} -(void)initOneSignalAndThreadWait { - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; [UnitTestCommonMethods runBackgroundThreads]; } - (void)testBasicInitTest { NSLog(@"iOS VERSION: %@", [[UIDevice currentDevice] systemVersion]); - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; [UnitTestCommonMethods runBackgroundThreads]; NSLog(@"CHECKING LAST HTTP REQUEST"); @@ -373,7 +308,7 @@ - (void)testInitWithEmptyPreferredLanguages { } - (void)testInitOnSimulator { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [self backgroundModesDisabledInXcode]; UIApplicationOverrider.didFailRegistarationErrorCode = 3010; @@ -406,7 +341,7 @@ - (void)testFocusSettingsOnInit { XCTAssertEqual(OneSignal.inFocusDisplayType, OSNotificationDisplayTypeNone); - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart]; // Test old very old kOSSettingsKeyInAppAlerts [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" @@ -430,7 +365,7 @@ - (void)testCallingMethodsBeforeInit { XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key"], @"value"); XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart]; [OneSignal sendTag:@"key" value:@"value"]; [OneSignal setSubscription:true]; @@ -456,7 +391,7 @@ - (void)testPermissionChangeObserverIOS7 { } - (void)sharedTestPermissionChangeObserver { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -508,7 +443,7 @@ - (void)testPermissionChangeObserverFireAfterAppRestart { [OneSignal addPermissionObserver:observer]; // User kills app, turns off notifications, then opnes it agian. - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart]; [self setCurrentNotificationPermission:false]; [self initOneSignalAndThreadWait]; @@ -535,7 +470,7 @@ - (void)testPermissionObserverDontFireIfNothingChangedAfterAppRestartiOS7 { [self sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart]; } - (void)sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; OSPermissionStateTestObserver* observer = [OSPermissionStateTestObserver new]; [OneSignal addPermissionObserver:observer]; @@ -550,7 +485,7 @@ - (void)sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart { [UnitTestCommonMethods runBackgroundThreads]; // Restart App - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil @@ -568,7 +503,7 @@ - (void)sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart { - (void)testPermissionChangeObserverDontLoseFromChanges { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -608,7 +543,7 @@ - (void)testSubscriptionChangeObserverFireAfterAppRestart { // User kills app, turns off notifications, then opnes it agian. - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart]; [self setCurrentNotificationPermission:false]; [self initOneSignalAndThreadWait]; @@ -623,7 +558,7 @@ - (void)testSubscriptionChangeObserverFireAfterAppRestart { - (void)testPermissionChangeObserverWithNativeiOS10PromptCall { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -655,7 +590,7 @@ - (void)testPermissionChangeObserverWithNativeiOS10PromptCall { - (void)testTestPermissionChangeObserverWithNativeiOS10PromptCall { [OneSignalUNUserNotificationCenter setUseiOS10_2_workaround:false]; - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -678,7 +613,7 @@ - (void)testTestPermissionChangeObserverWithNativeiOS10PromptCall { } - (void)testPermissionChangeObserverWithDecline { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -706,7 +641,7 @@ - (void)testPermissionChangeObserverWithDecline { - (void)testPermissionAndSubscriptionChangeObserverRemove { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [self backgroundModesDisabledInXcode]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil @@ -729,7 +664,7 @@ - (void)testPermissionAndSubscriptionChangeObserverRemove { } - (void)testSubscriptionChangeObserverBasic { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -755,7 +690,7 @@ - (void)testSubscriptionChangeObserverBasic { } - (void)testSubscriptionChangeObserverWhenPromptNotShown { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -802,9 +737,9 @@ - (void)testSubscriptionChangeObserverWhenPromptNotShown { - (void)testInitAcceptingNotificationsWithoutCapabilitesSet { [self backgroundModesDisabledInXcode]; UIApplicationOverrider.didFailRegistarationErrorCode = 3000; - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); [self answerNotifiationPrompt:true]; @@ -816,9 +751,9 @@ - (void)testInitAcceptingNotificationsWithoutCapabilitesSet { - (void)testPromptForPushNotificationsWithUserResponse { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; __block BOOL didAccept; [OneSignal promptForPushNotificationsWithUserResponse:^(BOOL accepted) { @@ -831,10 +766,10 @@ - (void)testPromptForPushNotificationsWithUserResponse { } - (void)testPromptForPushNotificationsWithUserResponseOnIOS8 { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; OneSignalHelperOverrider.mockIOSVersion = 8; - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; __block BOOL didAccept; [OneSignal promptForPushNotificationsWithUserResponse:^(BOOL accepted) { @@ -847,10 +782,10 @@ - (void)testPromptForPushNotificationsWithUserResponseOnIOS8 { } - (void)testPromptForPushNotificationsWithUserResponseOnIOS7 { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; OneSignalHelperOverrider.mockIOSVersion = 7; - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; __block BOOL didAccept; [OneSignal promptForPushNotificationsWithUserResponse:^(BOOL accepted) { @@ -864,7 +799,7 @@ - (void)testPromptForPushNotificationsWithUserResponseOnIOS7 { - (void)testPromptedButNeveranswerNotificationPrompt { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [self initOneSignalAndThreadWait]; @@ -895,7 +830,7 @@ - (void)testNotificationTypesWhenAlreadyAcceptedWithAutoPromptOffOnFristStartPre - (void)testNeverPromptedStatus { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" @@ -912,10 +847,10 @@ - (void)testNeverPromptedStatus { } - (void)testNotAcceptingNotificationsWithoutBackgroundModes { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [self backgroundModesDisabledInXcode]; - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; // Don't make a network call right away. XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); @@ -930,7 +865,7 @@ - (void)testNotAcceptingNotificationsWithoutBackgroundModes { } - (void)testIdsAvailableNotAcceptingNotifications { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -1056,7 +991,7 @@ - (void)testFirebaseAnalyticsInfluenceNotificationOpen { // Trigger a new app session [self backgroundApp]; NSDateOverrider.timeOffset = 41; - [self resumeApp]; + [UnitTestCommonMethods resumeApp]; [UnitTestCommonMethods runBackgroundThreads]; // Since we opened the app under 2 mintues after receiving a notification @@ -1091,7 +1026,7 @@ - (void)testOSNotificationPayloadParsesTemplateFields { - (void)testNotificationOpenOn2ndColdStartWithoutAppId { [self initOneSignalAndThreadWait]; - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart]; __block BOOL openedWasFire = false; [OneSignal initWithLaunchOptions:nil appId:nil handleNotificationAction:^(OSNotificationOpenedResult *result) { @@ -1217,7 +1152,7 @@ - (void)notificationAlertButtonsDisplayWithFormat:(NSDictionary *)userInfo { [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:receiveBlock]; - [self resumeApp]; + [UnitTestCommonMethods resumeApp]; [UnitTestCommonMethods runBackgroundThreads]; id notifResponse = [self createBasiciOSNotificationResponseWithPayload:userInfo]; @@ -1532,7 +1467,7 @@ - (void)testGetTagsBeforePlayerId { } - (void)testGetTagsWithNestedDelete { - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; __block BOOL fireDeleteTags = false; @@ -1560,7 +1495,7 @@ - (void)testGetTagsWithNestedDelete { } - (void)testSendTagsBeforeRegisterComplete { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [self initOneSignalAndThreadWait]; @@ -1613,7 +1548,7 @@ - (void)testFirstInitWithNotificationsAlreadyDeclined { } - (void)testPermissionChangedInSettingsOutsideOfApp { - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart]; [self backgroundModesDisabledInXcode]; UNUserNotificationCenterOverrider.notifTypesOverride = 0; @@ -1630,7 +1565,7 @@ - (void)testPermissionChangedInSettingsOutsideOfApp { [self backgroundApp]; [self setCurrentNotificationPermission:true]; - [self resumeApp]; + [UnitTestCommonMethods resumeApp]; [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @15); @@ -1647,14 +1582,14 @@ - (void) testOnSessionWhenResuming { // Don't make an on_session call if only out of the app for 20 secounds [self backgroundApp]; NSDateOverrider.timeOffset = 10; - [self resumeApp]; + [UnitTestCommonMethods resumeApp]; [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); // Anything over 30 secounds should count as a session. [self backgroundApp]; NSDateOverrider.timeOffset = 41; - [self resumeApp]; + [UnitTestCommonMethods resumeApp]; [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/players/1234/on_session"); @@ -1854,7 +1789,7 @@ -(void)testInvalidJSONTags { */ -(void)testDelayedSubscriptionUpdate { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; From 8388a0409255b18b907161a04c545628e96e6fde Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Fri, 2 Feb 2018 18:38:54 -0800 Subject: [PATCH 11/36] OneSignal SDK + Email MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Added convenience methods to set/logout/etc. from email without completion blocks • Added another test to cover unauthenticated email • Fixed some errors with tests • Changed verbose logging with HTTP requests to log HTTP parameters as pretty-printed JSON for easier debugging with rest clients • Persists the email_auth_token and email address in NSUserDefaults for use to authenticate in future sessions • This commit now correctly sends in the email_auth_hash in the correct HTTP requests (send location, send tags, etc) • The iOS Params file is now downloaded at launch in all cases (since it now effects email) --- iOS_SDK/OneSignalSDK/Source/OSSubscription.h | 1 + iOS_SDK/OneSignalSDK/Source/OSSubscription.m | 4 + iOS_SDK/OneSignalSDK/Source/OneSignal.h | 5 + iOS_SDK/OneSignalSDK/Source/OneSignal.m | 118 ++++++++++++------ iOS_SDK/OneSignalSDK/Source/OneSignalClient.m | 13 +- .../Source/OneSignalCommonDefines.h | 6 +- .../OneSignalSDK/Source/OneSignalLocation.m | 7 +- .../OneSignalSDK/Source/OneSignalTracker.m | 13 +- iOS_SDK/OneSignalSDK/Source/Requests.h | 28 ++--- iOS_SDK/OneSignalSDK/Source/Requests.m | 84 +++++++++---- iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m | 69 +++++++++- .../UnitTests/UnitTestCommonMethods.h | 3 +- .../UnitTests/UnitTestCommonMethods.m | 7 +- iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m | 79 ++++++------ 14 files changed, 298 insertions(+), 139 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/Source/OSSubscription.h b/iOS_SDK/OneSignalSDK/Source/OSSubscription.h index 24f5a0c4c..a90d4f18d 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSSubscription.h +++ b/iOS_SDK/OneSignalSDK/Source/OSSubscription.h @@ -64,6 +64,7 @@ typedef OSObservable*, OSSubscriptionState // Redefine OSSubscriptionState @interface OSSubscriptionState () +@property (strong, nonatomic) NSString *emailAddress; @property (strong, nonatomic) NSString *emailAuthCode; @property (nonatomic) BOOL requiresEmailAuth; @property (nonatomic) BOOL accpeted; diff --git a/iOS_SDK/OneSignalSDK/Source/OSSubscription.m b/iOS_SDK/OneSignalSDK/Source/OSSubscription.m index ad2b3b061..f4bbb7abb 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSSubscription.m +++ b/iOS_SDK/OneSignalSDK/Source/OSSubscription.m @@ -44,6 +44,7 @@ - (instancetype)initAsToWithPermision:(BOOL)permission { _accpeted = permission; NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; + _emailAddress = [userDefaults objectForKey:EMAIL_ADDRESS]; _requiresEmailAuth = [[userDefaults objectForKey:REQUIRE_EMAIL_AUTH] boolValue]; _emailAuthCode = [userDefaults stringForKey:EMAIL_AUTH_CODE]; _emailUserId = [userDefaults stringForKey:EMAIL_USERID]; @@ -56,6 +57,7 @@ - (instancetype)initAsToWithPermision:(BOOL)permission { - (BOOL)compare:(OSSubscriptionState*)from { return ![self.userId ?: @"" isEqualToString:from.userId ?: @""] || + ![self.emailAddress ?: @"" isEqualToString:from.emailAddress ?: @""] || ![self.emailUserId ?: @"" isEqualToString:from.emailUserId ?: @""] || ![self.pushToken ?: @"" isEqualToString:from.pushToken ?: @""] || self.userSubscriptionSetting != from.userSubscriptionSetting || @@ -65,6 +67,7 @@ - (BOOL)compare:(OSSubscriptionState*)from { - (instancetype)initAsFrom { NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; + _emailAddress = [userDefaults objectForKey:EMAIL_ADDRESS]; _requiresEmailAuth = [[userDefaults objectForKey:REQUIRE_EMAIL_AUTH] boolValue]; _emailAuthCode = [userDefaults stringForKey:EMAIL_AUTH_CODE]; _emailUserId = [userDefaults stringForKey:EMAIL_USERID]; @@ -83,6 +86,7 @@ - (void)persistAsFrom { if (!_userSubscriptionSetting) strUserSubscriptionSetting = @"no"; + [userDefaults setObject:_emailAddress forKey:EMAIL_ADDRESS]; [userDefaults setObject:[NSNumber numberWithBool:_requiresEmailAuth] forKey:REQUIRE_EMAIL_AUTH]; [userDefaults setObject:_emailAuthCode forKey:EMAIL_AUTH_CODE]; [userDefaults setObject:strUserSubscriptionSetting forKey:SUBSCRIPTION_SETTING]; diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.h b/iOS_SDK/OneSignalSDK/Source/OneSignal.h index 35f78de82..67215cbe1 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.h +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.h @@ -391,4 +391,9 @@ typedef void (^OSMultipleSuccessBlock)(NSDictionary + (void)setUnauthenticatedEmail:(NSString * _Nonnull)email withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock; + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock; +//convenience - no completion blocks ++ (void)logoutEmail; ++ (void)setUnauthenticatedEmail:(NSString * _Nonnull)email; ++ (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _Nullable)hashToken; + @end diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index b3962f9fa..b028f82a8 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -131,8 +131,6 @@ @implementation OneSignal static BOOL shouldDelaySubscriptionUpdate = false; -static NSString *currentUserEmail; -static NSString *authHashToken; /* if setEmail: was called before the device was registered (push playerID = nil), @@ -292,6 +290,10 @@ + (NSString*)mUserId { return self.currentSubscriptionState.userId; } ++ (NSString *)mEmailAuthToken { + return self.currentSubscriptionState.emailAuthCode; +} + + (NSString *)mEmailUserId { return self.currentSubscriptionState.emailUserId; } @@ -433,9 +435,16 @@ + (id)initWithLaunchOptions:(NSDictionary*)launchOptions appId:(NSString*)appId if (NSClassFromString(@"UNUserNotificationCenter")) [OneSignalHelper clearCachedMedia]; + /* + downloads params file to see: + (A) if firebase analytics should be tracked + (B) if this app requires email authentication + */ + + [self downloadIOSParams]; + if ([OneSignalTrackFirebaseAnalytics needsRemoteParams]) { [OneSignalTrackFirebaseAnalytics init]; - [self downloadIOSParams]; } return self; @@ -714,10 +723,10 @@ + (void) sendTagsToServer { NSMutableDictionary *requests = [NSMutableDictionary new]; - requests[@"push"] = [OSRequestSendTagsToServer withUserId:self.currentSubscriptionState.userId appId:self.app_id tags:nowSendingTags networkType:[OneSignalHelper getNetType]]; + requests[@"push"] = [OSRequestSendTagsToServer withUserId:self.currentSubscriptionState.userId appId:self.app_id tags:nowSendingTags networkType:[OneSignalHelper getNetType] withEmailAuthHashToken:nil]; - if (self.currentSubscriptionState.emailUserId) - requests[@"email"] = [OSRequestSendTagsToServer withUserId:self.currentSubscriptionState.emailUserId appId:self.app_id tags:nowSendingTags networkType:[OneSignalHelper getNetType]]; + if (self.currentSubscriptionState.emailUserId && self.currentSubscriptionState.emailAuthCode) + requests[@"email"] = [OSRequestSendTagsToServer withUserId:self.currentSubscriptionState.emailUserId appId:self.app_id tags:nowSendingTags networkType:[OneSignalHelper getNetType] withEmailAuthHashToken:self.currentSubscriptionState.emailAuthCode]; [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:^(NSDictionary *results) { //the tags for email & push are identical so it doesn't matter what we return in the success block @@ -956,7 +965,7 @@ + (void)updateDeviceToken:(NSString*)deviceToken onSuccess:(OSResultSuccessBlock [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Calling OneSignal PUT updated pushToken!"]; - [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId appId:self.app_id deviceToken:deviceToken notificationTypes:@([self getNotificationTypes]) withParentId:nil] onSuccess:successBlock onFailure:failureBlock]; + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId appId:self.app_id deviceToken:deviceToken notificationTypes:@([self getNotificationTypes]) withParentId:nil emailAuthToken:nil] onSuccess:successBlock onFailure:failureBlock]; [self fireIdsAvailableCallback]; } @@ -1050,7 +1059,6 @@ + (void)registerUserInternal { [NSNumber numberWithInt:0], @"device_type", [[[UIDevice currentDevice] identifierForVendor] UUIDString], @"ad_id", ONESIGNAL_VERSION, @"sdk", - self.currentSubscriptionState.pushToken, @"identifier", // identifier MUST be at the end as it could be nil. nil]; if (deviceModel) @@ -1107,8 +1115,12 @@ + (void)registerUserInternal { [OneSignalLocation clearLastLocation]; } + + let pushDataDic = (NSMutableDictionary *)[dataDic mutableCopy]; + pushDataDic[@"identifier"] = self.currentSubscriptionState.pushToken; + let requests = [NSMutableDictionary new]; - requests[@"push"] = [OSRequestRegisterUser withData:dataDic userId:self.currentSubscriptionState.userId]; + requests[@"push"] = [OSRequestRegisterUser withData:pushDataDic userId:self.currentSubscriptionState.userId]; if (self.currentSubscriptionState.emailUserId && self.currentSubscriptionState.emailAuthCode) { let emailDataDic = (NSMutableDictionary *)[dataDic mutableCopy]; @@ -1128,6 +1140,14 @@ + (void)registerUserInternal { //update email player ID if (results[@"email"] && results[@"email"][@"id"]) { + + // check to see if the email player_id or email_auth_token are different from what were previously saved + // if so, we should update the server with this change + + if (self.currentSubscriptionState.emailUserId && ![self.currentSubscriptionState.emailUserId isEqualToString:results[@"email"][@"id"]] && self.currentSubscriptionState.emailAuthCode) { + [self emailChangedWithNewEmailPlayerId:results[@"email"][@"id"]]; + } + self.currentSubscriptionState.emailUserId = results[@"email"][@"id"]; [[NSUserDefaults standardUserDefaults] setObject:self.currentSubscriptionState.emailUserId forKey:EMAIL_USERID]; //NSUserDefaults Synchronize: called after the next if-statement @@ -1577,32 +1597,28 @@ + (UNMutableNotificationContent*)serviceExtensionTimeWillExpireRequest:(UNNotifi #pragma mark Email -+ (void)updateEmailPlayerId:(NSString *)emailPlayerId { - if (!emailPlayerId) - return; - - [[NSUserDefaults standardUserDefaults] setObject:emailPlayerId forKey:EMAIL_USERID]; - [[NSUserDefaults standardUserDefaults] synchronize]; - - if (![emailPlayerId isEqualToString:_currentSubscriptionState.emailUserId]) { - //if the ID has changed, call Edit Edvice - - [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailPlayerId appId:self.app_id deviceToken:currentUserEmail notificationTypes:@([self getNotificationTypes]) withParentId:self.currentSubscriptionState.emailUserId] onSuccess:nil onFailure:nil]; - } -} - + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _Nullable)hashToken withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { + + //checks to ensure it is a valid email if (![OneSignalHelper isValidEmail:email]) { if (failureBlock) failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"error" : @"Email is invalid"}]); return; } + //checks to make sure that if email_auth is required, the user has passed in a hash token if (self.currentSubscriptionState.requiresEmailAuth && (!hashToken || hashToken.length == 0)) { failureBlock([NSError errorWithDomain:@"com.onesignal.email" code:0 userInfo:@{@"error" : @"Email authentication (auth token) is set to REQUIRED for this application. Please provide an auth token from your backend server or change the setting in the OneSignal dashboard."}]); return; } + // if both the email address & hash token are the same, there's no need to make a network call here. + if ([self.currentSubscriptionState.emailAddress isEqualToString:email] && [self.currentSubscriptionState.emailAuthCode isEqualToString:hashToken]) { + if (successBlock) + successBlock(); + return; + } + /* if the iOS params (with the require_email_auth setting) has not been downloaded yet, we should delay the request however, if this method was called with an email auth code passed in, then there is no need to check this setting @@ -1614,13 +1630,13 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ return; } - currentUserEmail = email; - authHashToken = hashToken; - let emailId = (NSString *)[[NSUserDefaults standardUserDefaults] objectForKey:EMAIL_USERID]; + // if the user already has a onesignal email player_id, then we should call update the device token + // otherwise we should call Create Device + if (emailId) { - [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:email notificationTypes:@([self getNotificationTypes]) withParentId:nil] onSuccess:^(NSDictionary *result) { + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:email notificationTypes:@([self getNotificationTypes]) withParentId:nil emailAuthToken:hashToken] onSuccess:^(NSDictionary *result) { if (successBlock) successBlock(); } onFailure:^(NSError *error) { @@ -1628,17 +1644,17 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ failureBlock(error); }]; } else { - [OneSignalClient.sharedClient executeRequest:[OSRequestCreateDevice withAppId:self.app_id withDeviceType:@11 withEmail:currentUserEmail withPlayerId:self.currentSubscriptionState.userId withEmailAuthHash:authHashToken] onSuccess:^(NSDictionary *result) { + [OneSignalClient.sharedClient executeRequest:[OSRequestCreateDevice withAppId:self.app_id withDeviceType:@11 withEmail:email withPlayerId:self.currentSubscriptionState.userId withEmailAuthHash:hashToken] onSuccess:^(NSDictionary *result) { let emailPlayerId = (NSString *)result[@"id"]; if (emailPlayerId) { - [[NSUserDefaults standardUserDefaults] setObject:hashToken forKey:EMAIL_AUTH_CODE]; - [[NSUserDefaults standardUserDefaults] setObject:emailPlayerId forKey:EMAIL_USERID]; - [[NSUserDefaults standardUserDefaults] synchronize]; - + self.currentSubscriptionState.emailAddress = email; self.currentSubscriptionState.emailAuthCode = hashToken; self.currentSubscriptionState.emailUserId = emailPlayerId; + //call persistAsFrom in order to save the hashToken & playerId to NSUserDefaults + [self.currentSubscriptionState persistAsFrom]; + if (successBlock) successBlock(); } else { @@ -1652,7 +1668,7 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ } + (void)setUnauthenticatedEmail:(NSString * _Nonnull)email withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { - + [self setEmail:email withEmailAuthHashToken:nil withSuccess:successBlock withFailure:failureBlock]; } + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { @@ -1662,13 +1678,12 @@ + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withF return; } - [OneSignalClient.sharedClient executeRequest:[OSRequestLogoutEmail withAppId: self.app_id emailPlayerId:self.currentSubscriptionState.emailUserId devicePlayerId:self.currentSubscriptionState.userId emailAuthHash:authHashToken] onSuccess:^(NSDictionary *result) { + [OneSignalClient.sharedClient executeRequest:[OSRequestLogoutEmail withAppId: self.app_id emailPlayerId:self.currentSubscriptionState.emailUserId devicePlayerId:self.currentSubscriptionState.userId emailAuthHash:self.currentSubscriptionState.emailAuthCode] onSuccess:^(NSDictionary *result) { [[NSUserDefaults standardUserDefaults] removeObjectForKey:EMAIL_USERID]; [[NSUserDefaults standardUserDefaults] synchronize]; - [self.currentSubscriptionState setEmailUserId:nil]; - currentUserEmail = nil; - authHashToken = nil; + self.currentSubscriptionState.emailAuthCode = nil; + self.currentSubscriptionState.emailUserId = nil; if (successBlock) successBlock(); @@ -1677,6 +1692,35 @@ + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withF failureBlock(error); }]; } + ++ (void)setUnauthenticatedEmail:(NSString * _Nonnull)email { + [self setUnauthenticatedEmail:email withSuccess:nil withFailure:nil]; +} + ++ (void)logoutEmail { + [self logoutEmailWithSuccess:nil withFailure:nil]; +} + ++ (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _Nullable)hashToken { + [self setEmail:email withEmailAuthHashToken:hashToken withSuccess:nil withFailure:nil]; +} + ++ (void)emailChangedWithNewEmailPlayerId:(NSString * _Nullable)emailPlayerId { + //make sure that the email player ID has changed otherwise there's no point in this request + if ([self.currentSubscriptionState.emailUserId isEqualToString:emailPlayerId]) + return; + + let request = [OSRequestUpdateDeviceToken withUserId:emailPlayerId == nil ? self.currentSubscriptionState.emailUserId : emailPlayerId + appId:self.app_id deviceToken:self.currentSubscriptionState.emailAddress + notificationTypes:@([self getNotificationTypes]) + withParentId:self.currentSubscriptionState.emailUserId + emailAuthToken:self.currentSubscriptionState.emailAuthCode]; + + [OneSignalClient.sharedClient executeRequest:request onSuccess:nil onFailure:^(NSError *error) { + [self onesignal_Log:ONE_S_LL_ERROR message:[NSString stringWithFormat:@"Encountered an error updating this user's email player record: %@", error.description]]; + }]; +} + @end // Swizzles UIApplication class to swizzling the following: diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m index f1fb21aa7..7d00f1d61 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m @@ -152,7 +152,7 @@ - (BOOL)validRequest:(OneSignalRequest *)request { return false; } - [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"HTTP Request (%@) with URL: %@, with parameters: %@", NSStringFromClass([request class]), request.request.URL.absoluteString, request.parameters]]; + [self prettyPrintDebugStatementWithRequest:request]; return true; } @@ -194,6 +194,17 @@ - (BOOL)willReattemptRequest:(int)statusCode withRequest:(OneSignalRequest *)req return false; } +- (void)prettyPrintDebugStatementWithRequest:(OneSignalRequest *)request { + if (![NSJSONSerialization isValidJSONObject:request.parameters]) + return; + + NSError *error; + let data = [NSJSONSerialization dataWithJSONObject:request.parameters options:NSJSONWritingPrettyPrinted error:&error]; + let jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"HTTP Request (%@) with URL: %@, with parameters: %@", NSStringFromClass([request class]), request.request.URL.absoluteString, jsonString]]; +} + - (void)handleJSONNSURLResponse:(NSURLResponse*)response data:(NSData*)data error:(NSError*)error isAsync:(BOOL)async withRequest:(OneSignalRequest *)request onSuccess:(OSResultSuccessBlock)successBlock onFailure:(OSFailureBlock)failureBlock { NSHTTPURLResponse* HTTPResponse = (NSHTTPURLResponse*)response; diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h b/iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h index 9ec383249..0fef44d9e 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h @@ -19,10 +19,6 @@ #define PUSH_TOKEN @"GT_DEVICE_TOKEN_LAST" #define ACCEPTED_PERMISSION @"ONESIGNAL_PERMISSION_ACCEPTED_LAST" #define REQUIRE_EMAIL_AUTH @"GT_REQUIRE_EMAIL_AUTH" - - - - - +#define EMAIL_ADDRESS @"EMAIL_ADDRESS" #endif /* OneSignalCommonDefines_h */ diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m b/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m index b80e6db7f..d1bdddb05 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m @@ -37,6 +37,7 @@ @interface OneSignal () void onesignal_Log(ONE_S_LOG_LEVEL logLevel, NSString* message); + (NSString *)mEmailUserId; + (NSString*)mUserId; ++ (NSString *)mEmailAuthToken; @end @implementation OneSignalLocation @@ -257,10 +258,10 @@ + (void)sendLocation { NSMutableDictionary *requests = [NSMutableDictionary new]; - if ([OneSignal mEmailUserId]) - requests[@"email"] = [OSRequestSendLocation withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] location:lastLocation networkType:[OneSignalHelper getNetType] backgroundState:([UIApplication sharedApplication].applicationState != UIApplicationStateActive)]; + if ([OneSignal mEmailUserId] && [OneSignal mEmailAuthToken]) + requests[@"email"] = [OSRequestSendLocation withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] location:lastLocation networkType:[OneSignalHelper getNetType] backgroundState:([UIApplication sharedApplication].applicationState != UIApplicationStateActive) emailAuthHashToken:[OneSignal mEmailAuthToken]]; - requests[@"push"] = [OSRequestSendLocation withUserId:[OneSignal mUserId] appId:[OneSignal app_id] location:lastLocation networkType:[OneSignalHelper getNetType] backgroundState:([UIApplication sharedApplication].applicationState != UIApplicationStateActive)]; + requests[@"push"] = [OSRequestSendLocation withUserId:[OneSignal mUserId] appId:[OneSignal app_id] location:lastLocation networkType:[OneSignalHelper getNetType] backgroundState:([UIApplication sharedApplication].applicationState != UIApplicationStateActive) emailAuthHashToken:nil]; [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:nil onFailure:nil]; } diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m b/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m index 68a454606..4382b93d2 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m @@ -40,6 +40,7 @@ + (BOOL)sendNotificationTypesUpdate; + (BOOL)clearBadgeCount:(BOOL)fromNotifOpened; + (NSString*)mUserId; + (NSString *)mEmailUserId; ++ (NSString *)mEmailAuthToken; @end @@ -122,10 +123,10 @@ + (void)onFocus:(BOOL)toBackground { if (wasBadgeSet && !toBackground) { NSMutableDictionary *requests = [NSMutableDictionary new]; - requests[@"push"] = [OSRequestOnFocus withUserId:[OneSignal mUserId] appId:[OneSignal app_id] badgeCount:@0]; + requests[@"push"] = [OSRequestOnFocus withUserId:[OneSignal mUserId] appId:[OneSignal app_id] badgeCount:@0 emailAuthToken:nil]; - if ([OneSignal mEmailUserId]) - requests[@"email"] = [OSRequestOnFocus withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] badgeCount:@0]; + if ([OneSignal mEmailUserId] && [OneSignal mEmailAuthToken]) + requests[@"email"] = [OSRequestOnFocus withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] badgeCount:@0 emailAuthToken:[OneSignal mEmailAuthToken]]; [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:nil onFailure:nil]; @@ -140,10 +141,10 @@ + (void)onFocus:(BOOL)toBackground { NSMutableDictionary *requests = [NSMutableDictionary new]; - requests[@"push"] = [OSRequestOnFocus withUserId:[OneSignal mUserId] appId:[OneSignal app_id] state:@"ping" type:@1 activeTime:@(timeToPingWith) netType:[OneSignalHelper getNetType]]; + requests[@"push"] = [OSRequestOnFocus withUserId:[OneSignal mUserId] appId:[OneSignal app_id] state:@"ping" type:@1 activeTime:@(timeToPingWith) netType:[OneSignalHelper getNetType] emailAuthToken:nil]; - if ([OneSignal mEmailUserId]) - requests[@"email"] = [OSRequestOnFocus withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] state:@"ping" type:@1 activeTime:@(timeToPingWith) netType:[OneSignalHelper getNetType]]; + if ([OneSignal mEmailUserId] && [OneSignal mEmailAuthToken]) + requests[@"email"] = [OSRequestOnFocus withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] state:@"ping" type:@1 activeTime:@(timeToPingWith) netType:[OneSignalHelper getNetType] emailAuthToken:[OneSignal mEmailAuthToken]]; [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:nil onFailure:nil]; diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.h b/iOS_SDK/OneSignalSDK/Source/Requests.h index d052c9673..812a6df19 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.h +++ b/iOS_SDK/OneSignalSDK/Source/Requests.h @@ -42,10 +42,6 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId; @end -@interface OSRequestSendTagsToServer : OneSignalRequest -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId tags:(NSDictionary *)tags networkType:(NSNumber *)netType; -@end - @interface OSRequestPostNotification : OneSignalRequest + (instancetype)withAppId:(NSString *)appId withJson:(NSMutableDictionary *)json; @end @@ -66,19 +62,10 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId email:(NSString *)email networkType:(NSNumber *)netType; @end -@interface OSRequestSendLocation : OneSignalRequest -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId location:(os_last_location *)coordinate networkType:(NSNumber *)netType backgroundState:(BOOL)backgroundState; -@end - -@interface OSRequestOnFocus : OneSignalRequest -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId badgeCount:(NSNumber *)badgeCount; -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId state:(NSString *)state type:(NSNumber *)type activeTime:(NSNumber *)activeTime netType:(NSNumber *)netType; -@end - NS_ASSUME_NONNULL_END @interface OSRequestUpdateDeviceToken : OneSignalRequest -+ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nonnull)identifier notificationTypes:(NSNumber * _Nonnull)notificationTypes withParentId:(NSString * _Nullable)parentId; ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nonnull)identifier notificationTypes:(NSNumber * _Nonnull)notificationTypes withParentId:(NSString * _Nullable)parentId emailAuthToken:(NSString * _Nullable)emailAuthHash; @end @interface OSRequestRegisterUser : OneSignalRequest @@ -93,5 +80,18 @@ NS_ASSUME_NONNULL_END + (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId emailPlayerId:(NSString * _Nonnull)emailPlayerId devicePlayerId:(NSString * _Nonnull)devicePlayerId emailAuthHash:(NSString * _Nullable)emailAuthHash; @end +@interface OSRequestSendTagsToServer : OneSignalRequest ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId tags:(NSDictionary * _Nonnull)tags networkType:(NSNumber * _Nonnull)netType withEmailAuthHashToken:(NSString * _Nullable)emailAuthToken; +@end + +@interface OSRequestSendLocation : OneSignalRequest ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId location:(os_last_location * _Nonnull)coordinate networkType:(NSNumber * _Nonnull)netType backgroundState:(BOOL)backgroundState emailAuthHashToken:(NSString * _Nullable)emailAuthHash; +@end + +@interface OSRequestOnFocus : OneSignalRequest ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId badgeCount:(NSNumber * _Nonnull)badgeCount emailAuthToken:(NSString * _Nullable)emailAuthHash; ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId state:(NSString * _Nonnull)state type:(NSNumber * _Nonnull)type activeTime:(NSNumber * _Nonnull)activeTime netType:(NSNumber * _Nonnull)netType emailAuthToken:(NSString * _Nullable)emailAuthHash; +@end + #endif /* Requests_h */ diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.m b/iOS_SDK/OneSignalSDK/Source/Requests.m index a8d0236e8..a209429d8 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.m +++ b/iOS_SDK/OneSignalSDK/Source/Requests.m @@ -69,10 +69,18 @@ -(BOOL)missingAppId { @end @implementation OSRequestSendTagsToServer -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId tags:(NSDictionary *)tags networkType:(NSNumber *)netType { ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId tags:(NSDictionary * _Nonnull)tags networkType:(NSNumber * _Nonnull)netType withEmailAuthHashToken:(NSString * _Nullable)emailAuthToken { let request = [OSRequestSendTagsToServer new]; - request.parameters = @{@"app_id" : appId, @"tags" : tags, @"net_type" : netType}; + let params = [NSMutableDictionary new]; + params[@"app_id"] = appId; + params[@"tags"] = tags; + params[@"net_type"] = netType; + + if (emailAuthToken && emailAuthToken.length > 0) + params[@"email_auth_hash"] = emailAuthToken; + + request.parameters = params; request.method = PUT; request.path = [NSString stringWithFormat:@"players/%@", userId]; @@ -96,17 +104,19 @@ + (instancetype)withAppId:(NSString *)appId withJson:(NSMutableDictionary *)json @end @implementation OSRequestUpdateDeviceToken -+ (instancetype)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nonnull)identifier notificationTypes:(NSNumber * _Nonnull)notificationTypes withParentId:(NSString * _Nullable)parentId { ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nonnull)identifier notificationTypes:(NSNumber * _Nonnull)notificationTypes withParentId:(NSString * _Nullable)parentId emailAuthToken:(NSString * _Nullable)emailAuthHash { let request = [OSRequestUpdateDeviceToken new]; - request.parameters = @{ - @"app_id" : appId, - @"identifier" : identifier, - @"notification_types" : notificationTypes, - @"parent_player_id" : [NSNull nullIfObjectIsNil:parentId] - }; + let params = [NSMutableDictionary new]; + params[@"app_id"] = appId; + params[@"identifier"] = identifier; + params[@"notification_types"] = notificationTypes; + if (emailAuthHash && emailAuthHash.length > 0) + params[@"email_auth_hash"] = emailAuthHash; + + request.parameters = params; request.method = PUT; request.path = [NSString stringWithFormat:@"players/%@", userId]; @@ -119,12 +129,12 @@ + (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId withDeviceType:(NS let request = [OSRequestCreateDevice new]; request.parameters = @{ - @"app_id" : appId, - @"device_type" : deviceType, - @"identifier" : [NSNull nullIfObjectIsNil:email], - @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthHash], - @"device_player_id" : [NSNull nullIfObjectIsNil:playerId] - }; + @"app_id" : appId, + @"device_type" : deviceType, + @"identifier" : [NSNull nullIfObjectIsNil:email], + @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthHash], + @"device_player_id" : [NSNull nullIfObjectIsNil:playerId] + }; request.method = POST; request.path = @"players"; @@ -136,8 +146,6 @@ + (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId withDeviceType:(NS @implementation OSRequestLogoutEmail + (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId emailPlayerId:(NSString * _Nonnull)emailPlayerId devicePlayerId:(NSString * _Nonnull)devicePlayerId emailAuthHash:(NSString * _Nullable)emailAuthHash { - - let request = [OSRequestLogoutEmail new]; request.parameters = @{ @@ -222,10 +230,22 @@ + (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId email:(NSS @end @implementation OSRequestSendLocation -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId location:(os_last_location *)coordinate networkType:(NSNumber *)netType backgroundState:(BOOL)backgroundState { ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId location:(os_last_location * _Nonnull)coordinate networkType:(NSNumber * _Nonnull)netType backgroundState:(BOOL)backgroundState emailAuthHashToken:(NSString * _Nullable)emailAuthHash { let request = [OSRequestSendLocation new]; - request.parameters = @{@"app_id" : appId, @"lat" : @(coordinate->cords.latitude), @"long" : @(coordinate->cords.longitude), @"loc_acc_vert" : @(coordinate->verticalAccuracy), @"loc_acc" : @(coordinate->horizontalAccuracy), @"net_type" : netType, @"loc_bg" : @(backgroundState)}; + let params = [NSMutableDictionary new]; + params[@"app_id"] = appId; + params[@"lat"] = @(coordinate->cords.latitude); + params[@"long"] = @(coordinate->cords.longitude); + params[@"loc_acc_vert"] = @(coordinate->verticalAccuracy); + params[@"loc_acc"] = @(coordinate->horizontalAccuracy); + params[@"net_type"] = netType; + params[@"loc_bg"] = @(backgroundState); + + if (emailAuthHash && emailAuthHash.length > 0) + params[@"email_auth_hash"] = emailAuthHash; + + request.parameters = params; request.method = PUT; request.path = [NSString stringWithFormat:@"players/%@", userId]; @@ -234,20 +254,38 @@ + (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId location:( @end @implementation OSRequestOnFocus -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId badgeCount:(NSNumber *)badgeCount { ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId badgeCount:(NSNumber * _Nonnull)badgeCount emailAuthToken:(NSString * _Nullable)emailAuthHash { let request = [OSRequestOnFocus new]; - request.parameters = @{@"app_id" : appId, @"badge_count" : badgeCount}; + let params = [NSMutableDictionary new]; + params[@"app_id"] = appId; + params[@"badgeCount"] = badgeCount; + + if (emailAuthHash && emailAuthHash.length > 0) + params[@"email_auth_hash"] = emailAuthHash; + + request.parameters = params; request.method = PUT; request.path = [NSString stringWithFormat:@"players/%@", userId]; return request; } -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId state:(NSString *)state type:(NSNumber *)type activeTime:(NSNumber *)activeTime netType:(NSNumber *)netType { ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId state:(NSString * _Nonnull)state type:(NSNumber * _Nonnull)type activeTime:(NSNumber * _Nonnull)activeTime netType:(NSNumber * _Nonnull)netType emailAuthToken:(NSString * _Nullable)emailAuthHash { let request = [OSRequestOnFocus new]; - request.parameters = @{@"app_id" : appId, @"state" : state, @"type" : type, @"active_time" : activeTime, @"net_type" : netType}; + + let params = [NSMutableDictionary new]; + params[@"app_id"] = appId; + params[@"state"] = state; + params[@"type"] = type; + params[@"active_time"] = activeTime; + params[@"net_type"] = netType; + + if (emailAuthHash && emailAuthHash.length > 0) + params[@"email_auth_hash"] = emailAuthHash; + + request.parameters = params; request.method = POST; request.path = [NSString stringWithFormat:@"players/%@/on_focus", userId]; diff --git a/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m index 1b36177cb..8f40f5117 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m @@ -21,6 +21,7 @@ #import "UNUserNotificationCenter+OneSignal.h" #import "NSBundleOverrider.h" #import "NSUserDefaultsOverrider.h" +#import "OneSignalCommonDefines.h" @interface EmailTests : XCTestCase @@ -43,7 +44,7 @@ - (void)setUp { [NSUserDefaultsOverrider clearInternalDictionary]; - [UnitTestCommonMethods clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [UnitTestCommonMethods beforeAllTest]; } @@ -61,7 +62,7 @@ - (void)testEmailValidation { XCTAssertFalse([OneSignalHelper isValidEmail:@"testing123@22."]); } -- (void)testSetEmail { +- (void)testSetAuthenticatedEmail { [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" @@ -76,25 +77,81 @@ - (void)testSetEmail { [NSObjectOverrider runPendingSelectors]; [UnitTestCommonMethods runBackgroundThreads]; + //the userId should already be set at this point, check to make sure. XCTAssertEqualObjects(observer->last.to.userId, @"1234"); [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; [UnitTestCommonMethods runBackgroundThreads]; - NSLog(@"EMAIL AFTER IS: %@", OneSignalClientOverrider.lastHTTPRequestType); - XCTAssertTrue([@"OSRequestCreateDevice" isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + + //check to make sure that the push token & auth were saved to NSUserDefaults + XCTAssertNotNil([[NSUserDefaults standardUserDefaults] objectForKey:EMAIL_USERID]); + XCTAssertNotNil([[NSUserDefaults standardUserDefaults] objectForKey:EMAIL_AUTH_CODE]); + + //check to make sure the OSRequestCreateDevice HTTP call was made, and was formatted correctly + XCTAssertTrue([NSStringFromClass([OSRequestCreateDevice class]) isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"device_type"], @11); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"test@test.com"); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"], @"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c"); - [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; + //we will change the email and make sure the HTTP call to update the device token is made + [OneSignal setEmail:@"test2@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; [UnitTestCommonMethods runBackgroundThreads]; - XCTAssertTrue([@"OSRequestUpdateDeviceToken" isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + //check to make sure the server gets updated with the new email + XCTAssertTrue([NSStringFromClass([OSRequestUpdateDeviceToken class]) isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"test2@test.com"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"], @"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c"); + + [self logoutEmail]; +} + +- (void)testUnauthenticatedEmail { + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; + [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" + handleNotificationAction:nil + settings:@{kOSSettingsKeyAutoPrompt: @false}]; + + OSSubscriptionStateTestObserver* observer = [OSSubscriptionStateTestObserver new]; + [OneSignal addSubscriptionObserver:observer]; + // Triggers the 30 fallback to register device right away. + [UnitTestCommonMethods runBackgroundThreads]; + [NSObjectOverrider runPendingSelectors]; + [UnitTestCommonMethods runBackgroundThreads]; + + //the userId should already be set at this point, check to make sure. + XCTAssertEqualObjects(observer->last.to.userId, @"1234"); + + [OneSignal setUnauthenticatedEmail:@"test@test.com" withSuccess:nil withFailure:nil]; + + [UnitTestCommonMethods runBackgroundThreads]; + + //check to make sure the OSRequestCreateDevice HTTP call was made, and was formatted correctly + XCTAssertTrue([NSStringFromClass([OSRequestCreateDevice class]) isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"device_type"], @11); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"test@test.com"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"], [NSNull null]); + + //now we will change the unauthenticated email to something else + [OneSignal setEmail:@"test2@test.com" withEmailAuthHashToken:nil withSuccess:nil withFailure:nil]; + + [UnitTestCommonMethods runBackgroundThreads]; + + //check to make sure the server gets updated with the new email + XCTAssertTrue([NSStringFromClass([OSRequestUpdateDeviceToken class]) isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"test2@test.com"); + XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"]); + + [self logoutEmail]; +} + +- (void)logoutEmail { //test email logout let expectation = [self expectationWithDescription:@"email_logout"]; expectation.expectedFulfillmentCount = 1; diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h index 3112b9453..770b74d5f 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h @@ -7,6 +7,7 @@ // #import +#import #import "OneSignal.h" @interface UnitTestCommonMethods : NSObject @@ -16,7 +17,7 @@ + (void)initOneSignal; + (void)runBackgroundThreads; + (void)beforeAllTest; -+ (void)clearStateForAppRestart; ++ (void)clearStateForAppRestart:(XCTestCase *)testCase; @end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m index 85524b3f1..06c72ff2b 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m @@ -61,12 +61,13 @@ + (void)runBackgroundThreads { NSLog(@"END runBackgroundThreads"); } -+ (void)clearStateForAppRestart { ++ (void)clearStateForAppRestart:(XCTestCase *)testCase { NSLog(@"======= APP RESTART ======\n\n"); NSDateOverrider.timeOffset = 0; - [OneSignalClientOverrider reset:self]; - [UNUserNotificationCenterOverrider reset:self]; + + [OneSignalClientOverrider reset:testCase]; + [UNUserNotificationCenterOverrider reset:testCase]; [UIApplicationOverrider reset]; [OneSignalTrackFirebaseAnalyticsOverrider reset]; diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m index 61d13a31c..de191fdbb 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m @@ -100,7 +100,7 @@ - (void)setUp { [NSUserDefaultsOverrider clearInternalDictionary]; - [UnitTestCommonMethods clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [UnitTestCommonMethods beforeAllTest]; @@ -240,9 +240,8 @@ - (void)testBasicInitTest { // 2nd init call should not fire another on_session call. OneSignalClientOverrider.lastHTTPRequest = nil; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]; - XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); } @@ -291,9 +290,8 @@ - (void)testRegisterationOniOS7 { // 2nd init call should not fire another on_session call. OneSignalClientOverrider.lastHTTPRequest = nil; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]; - XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); // Make the following methods were not called as they are not available on iOS 7 XCTAssertFalse(UIApplicationOverrider.calledRegisterForRemoteNotifications); @@ -327,9 +325,8 @@ - (void)testInitOnSimulator { // 2nd init call should not fire another on_session call. OneSignalClientOverrider.lastHTTPRequest = nil; [self initOneSignalAndThreadWait]; - XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); } @@ -341,7 +338,7 @@ - (void)testFocusSettingsOnInit { XCTAssertEqual(OneSignal.inFocusDisplayType, OSNotificationDisplayTypeNone); - [UnitTestCommonMethods clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; // Test old very old kOSSettingsKeyInAppAlerts [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" @@ -363,9 +360,9 @@ - (void)testCallingMethodsBeforeInit { XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key"], @"value"); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); - [UnitTestCommonMethods clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [OneSignal sendTag:@"key" value:@"value"]; [OneSignal setSubscription:true]; @@ -374,7 +371,7 @@ - (void)testCallingMethodsBeforeInit { [UnitTestCommonMethods runBackgroundThreads]; [self initOneSignalAndThreadWait]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 0); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); } - (void)testPermissionChangeObserverIOS10 { @@ -385,6 +382,7 @@ - (void)testPermissionChangeObserverIOS8 { OneSignalHelperOverrider.mockIOSVersion = 8; [self sharedTestPermissionChangeObserver]; } + - (void)testPermissionChangeObserverIOS7 { OneSignalHelperOverrider.mockIOSVersion = 7; [self sharedTestPermissionChangeObserver]; @@ -443,7 +441,7 @@ - (void)testPermissionChangeObserverFireAfterAppRestart { [OneSignal addPermissionObserver:observer]; // User kills app, turns off notifications, then opnes it agian. - [UnitTestCommonMethods clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [self setCurrentNotificationPermission:false]; [self initOneSignalAndThreadWait]; @@ -485,7 +483,7 @@ - (void)sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart { [UnitTestCommonMethods runBackgroundThreads]; // Restart App - [UnitTestCommonMethods clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil @@ -543,7 +541,7 @@ - (void)testSubscriptionChangeObserverFireAfterAppRestart { // User kills app, turns off notifications, then opnes it agian. - [UnitTestCommonMethods clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [self setCurrentNotificationPermission:false]; [self initOneSignalAndThreadWait]; @@ -742,11 +740,12 @@ - (void)testInitAcceptingNotificationsWithoutCapabilitesSet { [UnitTestCommonMethods initOneSignal]; XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); + [UnitTestCommonMethods runBackgroundThreads]; [self answerNotifiationPrompt:true]; [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @-13); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); } @@ -929,7 +928,7 @@ - (void)testNotificationOpen { XCTAssertNil(OneSignalClientOverrider.lastUrl); XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); } @@ -1026,7 +1025,7 @@ - (void)testOSNotificationPayloadParsesTemplateFields { - (void)testNotificationOpenOn2ndColdStartWithoutAppId { [self initOneSignalAndThreadWait]; - [UnitTestCommonMethods clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; __block BOOL openedWasFire = false; [OneSignal initWithLaunchOptions:nil appId:nil handleNotificationAction:^(OSNotificationOpenedResult *result) { @@ -1086,7 +1085,7 @@ - (void)testNotificationOpenFromButtonPress { XCTAssertNil(OneSignalClientOverrider.lastUrl); XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); } @@ -1134,7 +1133,7 @@ - (void)testNotificationOpenFromButtonPressWithNewformat { XCTAssertNil(OneSignalClientOverrider.lastUrl); XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); } // Testing iOS 10 - 2.4.0+ button fromat - with os_data aps payload format @@ -1360,7 +1359,7 @@ - (void)testGeneratingLocalNotificationWithButtonsiOS8 { - (void)testSendTags { [self initOneSignalAndThreadWait]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); // Simple test with a sendTag and sendTags call. [OneSignal sendTag:@"key" value:@"value"]; @@ -1374,7 +1373,7 @@ - (void)testSendTags { XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key"], @"value"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key1"], @"value1"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key2"], @"value2"); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); let expectation = [self expectationWithDescription:@"wait_tags"]; expectation.expectedFulfillmentCount = 3; @@ -1400,13 +1399,13 @@ - (void)testSendTags { XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key11"], @"value11"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key12"], @"value12"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key13"], @"value13"); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 4); } - (void)testDeleteTags { [self initOneSignalAndThreadWait]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); NSLog(@"Calling sendTag and deleteTag"); // send 2 tags and delete 1 before they get sent off. @@ -1422,7 +1421,7 @@ - (void)testDeleteTags { XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key"]); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key2"], @"value2"); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); [OneSignal sendTags:@{@"someKey": @NO}]; [OneSignal deleteTag:@"someKey"]; @@ -1430,7 +1429,7 @@ - (void)testDeleteTags { - (void)testGetTags { [self initOneSignalAndThreadWait]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); __block BOOL fireGetTags = false; @@ -1449,7 +1448,7 @@ - (void)testGetTags { - (void)testGetTagsBeforePlayerId { [self initOneSignalAndThreadWait]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); __block BOOL fireGetTags = false; @@ -1490,7 +1489,7 @@ - (void)testGetTagsWithNestedDelete { [NSObjectOverrider runPendingSelectors]; // create, ge tags, then sendTags call. - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 4); XCTAssertTrue(fireDeleteTags); } @@ -1505,13 +1504,13 @@ - (void)testSendTagsBeforeRegisterComplete { [UnitTestCommonMethods runBackgroundThreads]; // Do not try to send tag update yet as there isn't a player_id yet. - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 0); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); [self answerNotifiationPrompt:false]; [UnitTestCommonMethods runBackgroundThreads]; // A single POST player create call should be made with tags included. - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key"], @"value"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @0); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"0000000000000000000000000000000000000000000000000000000000000000"); @@ -1519,18 +1518,18 @@ - (void)testSendTagsBeforeRegisterComplete { - (void)testPostNotification { [self initOneSignalAndThreadWait]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); // Normal post should auto add add_id. [OneSignal postNotification:@{@"contents": @{@"en": @"message body"}}]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"contents"][@"en"], @"message body"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); // Should allow overriding the app_id [OneSignal postNotification:@{@"contents": @{@"en": @"message body"}, @"app_id": @"override_app_UUID"}]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 4); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"contents"][@"en"], @"message body"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"override_app_UUID"); } @@ -1544,11 +1543,11 @@ - (void)testFirstInitWithNotificationsAlreadyDeclined { [self initOneSignalAndThreadWait]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @0); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); } - (void)testPermissionChangedInSettingsOutsideOfApp { - [UnitTestCommonMethods clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [self backgroundModesDisabledInXcode]; UNUserNotificationCenterOverrider.notifTypesOverride = 0; @@ -1570,7 +1569,7 @@ - (void)testPermissionChangedInSettingsOutsideOfApp { XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @15); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"0000000000000000000000000000000000000000000000000000000000000000"); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); XCTAssertEqual(observer->last.from.accepted, false); XCTAssertEqual(observer->last.to.accepted, true); @@ -1584,7 +1583,7 @@ - (void) testOnSessionWhenResuming { NSDateOverrider.timeOffset = 10; [UnitTestCommonMethods resumeApp]; [UnitTestCommonMethods runBackgroundThreads]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); // Anything over 30 secounds should count as a session. [self backgroundApp]; @@ -1593,7 +1592,7 @@ - (void) testOnSessionWhenResuming { [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/players/1234/on_session"); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); } // Tests that a slient content-available 1 notification doesn't trigger an on_session or count it has opened. @@ -1618,7 +1617,7 @@ - (void)testContentAvailableDoesNotTriggerOpen { [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(receivedWasFire, true); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 0); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); } -(UNNotificationCategory*)unNotificagionCategoryWithId:(NSString*)identifier { @@ -1744,7 +1743,7 @@ - (void) testServiceExtensionTimeWillExpireRequest { } -(void)testBuildOSRequest { - let request = [OSRequestSendTagsToServer withUserId:@"12345" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" tags:@{@"tag1" : @"test1", @"tag2" : @"test2"} networkType:[OneSignalHelper getNetType]]; + let request = [OSRequestSendTagsToServer withUserId:@"12345" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" tags:@{@"tag1" : @"test1", @"tag2" : @"test2"} networkType:[OneSignalHelper getNetType] withEmailAuthHashToken:nil]; XCTAssert([request.parameters[@"app_id"] isEqualToString:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"]); XCTAssert([request.parameters[@"tags"][@"tag1"] isEqualToString:@"test1"]); @@ -1764,7 +1763,7 @@ -(void)testInvalidJSONTags { let invalidJson = @{@{@"invalid1" : @"invalid2"} : @"test"}; //Keys are required to be strings, this would crash the app if not handled appropriately - let request = [OSRequestSendTagsToServer withUserId:@"12345" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" tags:invalidJson networkType:[OneSignalHelper getNetType]]; + let request = [OSRequestSendTagsToServer withUserId:@"12345" appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" tags:invalidJson networkType:[OneSignalHelper getNetType] withEmailAuthHashToken:nil]; let urlRequest = request.request; From 9b472089da56b7d58cc7e800902ec2b524fb53c7 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Mon, 5 Feb 2018 13:58:54 -0800 Subject: [PATCH 12/36] Email Fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Made some changes to parameter names for a few endpoints to match what the backend requires • The backend needs the client to call UpdateDeviceToken even when the user just receives a new email player_id. Added this HTTP request in the setEmail: method. • Adjusted unit tests to account for the fact that all successful calls to setEmail: will end with a call to UpdateDeviceToken in one way or another • Added some more checks to the unit tests for Email to ensure it is correctly setting the email and also logs out correctly • Adjusted the dev app to include an email text field and Set Email + Logout Email buttons to make testing easier. --- .../Base.lproj/Main.storyboard | 23 ++++++++++++ .../OneSignalDevApp/Info.plist | 26 +++++++------- .../OneSignalDevApp/ViewController.m | 23 ++++++++++++ iOS_SDK/OneSignalSDK/Source/OneSignal.m | 30 +++++++++++----- iOS_SDK/OneSignalSDK/Source/Requests.h | 2 +- iOS_SDK/OneSignalSDK/Source/Requests.m | 18 +++++++--- iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m | 35 ++++++++++++++----- .../UnitTests/TestHelperFunctions.m | 2 ++ 8 files changed, 123 insertions(+), 36 deletions(-) diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard index 8588bd71f..a7c9f0421 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard @@ -53,6 +53,24 @@ + + @@ -60,12 +78,17 @@ + + + + + diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Info.plist b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Info.plist index 9981fc0f4..7e2d08241 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Info.plist +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Info.plist @@ -2,14 +2,6 @@ - OneSignal_app_groups_key - group.com.onesignal.example.testgroup - OneSignal_disable_badge_clearing - - NSLocationWhenInUseUsageDescription - Test Location2 - NSLocationUsageDescription - Test Location CFBundleDevelopmentRegion en CFBundleExecutable @@ -28,6 +20,19 @@ 1 LSRequiresIPhoneOS + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSLocationUsageDescription + Test Location + NSLocationWhenInUseUsageDescription + Test Location2 + OneSignal_app_groups_key + group.com.onesignal.example.testgroup + OneSignal_disable_badge_clearing + UIBackgroundModes remote-notification @@ -53,10 +58,5 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m index 0fbc5abc0..684ebd334 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m @@ -34,6 +34,7 @@ @interface ViewController () @property (weak, nonatomic) IBOutlet UITextField *textField; +@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicatorView; @end @@ -42,6 +43,13 @@ @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. + + self.activityIndicatorView.hidden = true; +} + +- (void)changeAnimationState:(BOOL)animating { + animating ? [self.activityIndicatorView startAnimating] : [self.activityIndicatorView stopAnimating]; + self.activityIndicatorView.hidden = !animating; } - (IBAction)sendTagButton:(id)sender { @@ -74,13 +82,28 @@ - (IBAction)sendTagButton:(id)sender { - (IBAction)setEmailButtonPressed:(UIButton *)sender { + [self changeAnimationState:true]; [OneSignal setEmail:self.textField.text withEmailAuthHashToken:@"aa3e3201f8f8bfd2fcbe8a899c161b7acb5a86545196c5465bef47fd757ca356" withSuccess:^{ NSLog(@"Successfully sent email"); + dispatch_async(dispatch_get_main_queue(), ^{ + [self changeAnimationState:false]; + }); } withFailure:^(NSError *error) { NSLog(@"Encountered error: %@", error); + dispatch_async(dispatch_get_main_queue(), ^{ + [self changeAnimationState:false]; + }); }]; } +- (IBAction)logoutButtonPressed:(UIButton *)sender +{ + [OneSignal logoutEmailWithSuccess:^{ + NSLog(@"Successfully logged out of email"); + } withFailure:^(NSError *error) { + NSLog(@"Encountered error while logging out of email: %@", error); + }]; +} - (void)promptForNotificationsWithNativeiOS10Code { diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index b028f82a8..e68c52b31 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -965,7 +965,7 @@ + (void)updateDeviceToken:(NSString*)deviceToken onSuccess:(OSResultSuccessBlock [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Calling OneSignal PUT updated pushToken!"]; - [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId appId:self.app_id deviceToken:deviceToken notificationTypes:@([self getNotificationTypes]) withParentId:nil emailAuthToken:nil] onSuccess:successBlock onFailure:failureBlock]; + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId appId:self.app_id deviceToken:deviceToken notificationTypes:@([self getNotificationTypes]) withParentId:nil emailAuthToken:nil email: nil] onSuccess:successBlock onFailure:failureBlock]; [self fireIdsAvailableCallback]; } @@ -1636,7 +1636,7 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ // otherwise we should call Create Device if (emailId) { - [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:email notificationTypes:@([self getNotificationTypes]) withParentId:nil emailAuthToken:hashToken] onSuccess:^(NSDictionary *result) { + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:email notificationTypes:nil withParentId:nil emailAuthToken:hashToken email:nil] onSuccess:^(NSDictionary *result) { if (successBlock) successBlock(); } onFailure:^(NSError *error) { @@ -1655,8 +1655,13 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ //call persistAsFrom in order to save the hashToken & playerId to NSUserDefaults [self.currentSubscriptionState persistAsFrom]; - if (successBlock) - successBlock(); + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId appId:self.app_id deviceToken:nil notificationTypes:@([self getNotificationTypes]) withParentId:self.currentSubscriptionState.emailUserId emailAuthToken:hashToken email:email] onSuccess:^(NSDictionary *result) { + if (successBlock) + successBlock(); + } onFailure:^(NSError *error) { + if (failureBlock) + failureBlock(error); + }]; } else { [self onesignal_Log:ONE_S_LL_ERROR message:@"Missing OneSignal Email Player ID"]; } @@ -1678,13 +1683,18 @@ + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withF return; } + [OneSignalClient.sharedClient executeRequest:[OSRequestLogoutEmail withAppId: self.app_id emailPlayerId:self.currentSubscriptionState.emailUserId devicePlayerId:self.currentSubscriptionState.userId emailAuthHash:self.currentSubscriptionState.emailAuthCode] onSuccess:^(NSDictionary *result) { [[NSUserDefaults standardUserDefaults] removeObjectForKey:EMAIL_USERID]; [[NSUserDefaults standardUserDefaults] synchronize]; + self.currentSubscriptionState.emailAddress = nil; self.currentSubscriptionState.emailAuthCode = nil; self.currentSubscriptionState.emailUserId = nil; + //call persistAsFrom in order to save the hashToken & playerId to NSUserDefaults + [self.currentSubscriptionState persistAsFrom]; + if (successBlock) successBlock(); } onFailure:^(NSError *error) { @@ -1710,11 +1720,13 @@ + (void)emailChangedWithNewEmailPlayerId:(NSString * _Nullable)emailPlayerId { if ([self.currentSubscriptionState.emailUserId isEqualToString:emailPlayerId]) return; - let request = [OSRequestUpdateDeviceToken withUserId:emailPlayerId == nil ? self.currentSubscriptionState.emailUserId : emailPlayerId - appId:self.app_id deviceToken:self.currentSubscriptionState.emailAddress - notificationTypes:@([self getNotificationTypes]) - withParentId:self.currentSubscriptionState.emailUserId - emailAuthToken:self.currentSubscriptionState.emailAuthCode]; + let request = [OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId + appId:self.app_id + deviceToken:nil + notificationTypes: @([self getNotificationTypes]) + withParentId:emailPlayerId + emailAuthToken:self.currentSubscriptionState.emailAuthCode + email:self.currentSubscriptionState.emailAddress]; [OneSignalClient.sharedClient executeRequest:request onSuccess:nil onFailure:^(NSError *error) { [self onesignal_Log:ONE_S_LL_ERROR message:[NSString stringWithFormat:@"Encountered an error updating this user's email player record: %@", error.description]]; diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.h b/iOS_SDK/OneSignalSDK/Source/Requests.h index 812a6df19..674e7543f 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.h +++ b/iOS_SDK/OneSignalSDK/Source/Requests.h @@ -65,7 +65,7 @@ NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END @interface OSRequestUpdateDeviceToken : OneSignalRequest -+ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nonnull)identifier notificationTypes:(NSNumber * _Nonnull)notificationTypes withParentId:(NSString * _Nullable)parentId emailAuthToken:(NSString * _Nullable)emailAuthHash; ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nullable)identifier notificationTypes:(NSNumber * _Nullable)notificationTypes withParentId:(NSString * _Nullable)parentId emailAuthToken:(NSString * _Nullable)emailAuthHash email:(NSString * _Nullable)email; @end @interface OSRequestRegisterUser : OneSignalRequest diff --git a/iOS_SDK/OneSignalSDK/Source/Requests.m b/iOS_SDK/OneSignalSDK/Source/Requests.m index a209429d8..616dcc9e5 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.m +++ b/iOS_SDK/OneSignalSDK/Source/Requests.m @@ -104,14 +104,22 @@ + (instancetype)withAppId:(NSString *)appId withJson:(NSMutableDictionary *)json @end @implementation OSRequestUpdateDeviceToken -+ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nonnull)identifier notificationTypes:(NSNumber * _Nonnull)notificationTypes withParentId:(NSString * _Nullable)parentId emailAuthToken:(NSString * _Nullable)emailAuthHash { ++ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId appId:(NSString * _Nonnull)appId deviceToken:(NSString * _Nullable)identifier notificationTypes:(NSNumber * _Nullable)notificationTypes withParentId:(NSString * _Nullable)parentId emailAuthToken:(NSString * _Nullable)emailAuthHash email:(NSString * _Nullable)email { let request = [OSRequestUpdateDeviceToken new]; let params = [NSMutableDictionary new]; params[@"app_id"] = appId; - params[@"identifier"] = identifier; - params[@"notification_types"] = notificationTypes; + params[@"email"] = email; + + if (notificationTypes) + params[@"notification_types"] = notificationTypes; + + if (identifier) + params[@"identifier"] = identifier; + + if (parentId) + params[@"parent_player_id"] = parentId; if (emailAuthHash && emailAuthHash.length > 0) params[@"email_auth_hash"] = emailAuthHash; @@ -149,13 +157,13 @@ + (instancetype _Nonnull)withAppId:(NSString * _Nonnull)appId emailPlayerId:(NSS let request = [OSRequestLogoutEmail new]; request.parameters = @{ - @"device_player_id" : [NSNull nullIfObjectIsNil:devicePlayerId], + @"parent_player_id" : [NSNull nullIfObjectIsNil:emailPlayerId], @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthHash], @"app_id" : appId }; request.method = POST; - request.path = [NSString stringWithFormat:@"%@/email_logout", emailPlayerId]; + request.path = [NSString stringWithFormat:@"players/%@/email_logout", devicePlayerId]; return request; } diff --git a/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m index 8f40f5117..d6e133aaf 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m @@ -23,6 +23,13 @@ #import "NSUserDefaultsOverrider.h" #import "OneSignalCommonDefines.h" + +@interface OneSignal () +void onesignal_Log(ONE_S_LOG_LEVEL logLevel, NSString* message); ++ (NSString *)mEmailUserId; ++ (NSString *)mEmailAuthToken; +@end + @interface EmailTests : XCTestCase @end @@ -89,10 +96,10 @@ - (void)testSetAuthenticatedEmail { XCTAssertNotNil([[NSUserDefaults standardUserDefaults] objectForKey:EMAIL_AUTH_CODE]); //check to make sure the OSRequestCreateDevice HTTP call was made, and was formatted correctly - XCTAssertTrue([NSStringFromClass([OSRequestCreateDevice class]) isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + XCTAssertTrue([NSStringFromClass([OSRequestUpdateDeviceToken class]) isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); - XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"device_type"], @11); - XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"test@test.com"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"parent_player_id"], @"1234"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email"], @"test@test.com"); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"], @"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c"); //we will change the email and make sure the HTTP call to update the device token is made @@ -106,7 +113,13 @@ - (void)testSetAuthenticatedEmail { XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"test2@test.com"); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"], @"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c"); + XCTAssertEqual([OneSignal mEmailUserId], @"1234"); + XCTAssertEqual([OneSignal mEmailAuthToken], @"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c"); + [self logoutEmail]; + + XCTAssertNil([OneSignal mEmailUserId]); + XCTAssertNil([OneSignal mEmailAuthToken]); } - (void)testUnauthenticatedEmail { @@ -129,13 +142,13 @@ - (void)testUnauthenticatedEmail { [OneSignal setUnauthenticatedEmail:@"test@test.com" withSuccess:nil withFailure:nil]; [UnitTestCommonMethods runBackgroundThreads]; - + NSLog(@"LAST REQ: %@", OneSignalClientOverrider.lastHTTPRequest); //check to make sure the OSRequestCreateDevice HTTP call was made, and was formatted correctly - XCTAssertTrue([NSStringFromClass([OSRequestCreateDevice class]) isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + XCTAssertTrue([NSStringFromClass([OSRequestUpdateDeviceToken class]) isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); - XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"device_type"], @11); - XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"test@test.com"); - XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"], [NSNull null]); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"parent_player_id"], @"1234"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email"], @"test@test.com"); + XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"]); //now we will change the unauthenticated email to something else [OneSignal setEmail:@"test2@test.com" withEmailAuthHashToken:nil withSuccess:nil withFailure:nil]; @@ -148,7 +161,13 @@ - (void)testUnauthenticatedEmail { XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"identifier"], @"test2@test.com"); XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"]); + XCTAssertEqual([OneSignal mEmailUserId], @"1234"); + XCTAssertNil([OneSignal mEmailAuthToken]); + [self logoutEmail]; + + XCTAssertNil([OneSignal mEmailUserId]); + XCTAssertNil([OneSignal mEmailAuthToken]); } - (void)logoutEmail { diff --git a/iOS_SDK/OneSignalSDK/UnitTests/TestHelperFunctions.m b/iOS_SDK/OneSignalSDK/UnitTests/TestHelperFunctions.m index 4824c42c6..ad3097ba1 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/TestHelperFunctions.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/TestHelperFunctions.m @@ -74,3 +74,5 @@ BOOL injectStaticSelector(Class newClass, SEL newSel, Class addToClass, SEL make return existing; } + + From 5964fec3160b678ff3b4f9cd0fd6bd753f4b4966 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Mon, 5 Feb 2018 16:38:15 -0800 Subject: [PATCH 13/36] Add Carrier Name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Added "carrier" as a parameter on registration. Uses reflection to access the Core Telephony framework (if available) and uses it to attach the carrier name to the body of the request. • Fixes an issue with unauthenticated email users where some requests (on_session, update location, etc) weren't being called for Email • Added emailUserId and emailAddress as parameters in the dictionary & string representations of OSSubscriptionState --- iOS_SDK/OneSignalSDK/Source/OSSubscription.m | 16 ++++++++++------ iOS_SDK/OneSignalSDK/Source/OneSignal.m | 14 ++++++++++++-- iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m | 2 +- iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m | 4 ++-- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/Source/OSSubscription.m b/iOS_SDK/OneSignalSDK/Source/OSSubscription.m index f4bbb7abb..09b250c51 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSSubscription.m +++ b/iOS_SDK/OneSignalSDK/Source/OSSubscription.m @@ -172,15 +172,19 @@ - (BOOL)subscribed { } - (NSString*)description { - static NSString* format = @""; - return [NSString stringWithFormat:format, self.userId, self.pushToken, self.userSubscriptionSetting, self.subscribed]; + static NSString* format = @""; + return [NSString stringWithFormat:format, self.userId, self.pushToken, self.userSubscriptionSetting, self.subscribed, self.emailUserId]; } - (NSDictionary*)toDictionary { - return @{@"userId": _userId ?: [NSNull null], - @"pushToken": _pushToken ?: [NSNull null], - @"userSubscriptionSetting": @(_userSubscriptionSetting), - @"subscribed": @(self.subscribed)}; + return @{ + @"userId": _userId ?: [NSNull null], + @"pushToken": _pushToken ?: [NSNull null], + @"userSubscriptionSetting": @(_userSubscriptionSetting), + @"subscribed": @(self.subscribed), + @"emailUserId": _emailUserId ?: [NSNull null], + @"emailAddress": _emailAddress ?: [NSNull null] + }; } @end diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index e68c52b31..01879246e 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -725,7 +725,7 @@ + (void) sendTagsToServer { requests[@"push"] = [OSRequestSendTagsToServer withUserId:self.currentSubscriptionState.userId appId:self.app_id tags:nowSendingTags networkType:[OneSignalHelper getNetType] withEmailAuthHashToken:nil]; - if (self.currentSubscriptionState.emailUserId && self.currentSubscriptionState.emailAuthCode) + if (self.currentSubscriptionState.emailUserId && (self.currentSubscriptionState.requiresEmailAuth == false || self.currentSubscriptionState.emailAuthCode)) requests[@"email"] = [OSRequestSendTagsToServer withUserId:self.currentSubscriptionState.emailUserId appId:self.app_id tags:nowSendingTags networkType:[OneSignalHelper getNetType] withEmailAuthHashToken:self.currentSubscriptionState.emailAuthCode]; [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:^(NSDictionary *results) { @@ -1095,6 +1095,16 @@ + (void)registerUserInternal { dataDic[@"as_id"] = @"OptedOut"; } + let CTTelephonyNetworkInfoClass = NSClassFromString(@"CTTelephonyNetworkInfo"); + if (CTTelephonyNetworkInfoClass) { + id instance = [[CTTelephonyNetworkInfoClass alloc] init]; + let carrierName = (NSString *)[[instance valueForKey:@"subscriberCellularProvider"] valueForKey:@"carrierName"]; + + if (carrierName) { + dataDic[@"carrier"] = carrierName; + } + } + let releaseMode = [OneSignalMobileProvision releaseMode]; if (releaseMode == UIApplicationReleaseDev || releaseMode == UIApplicationReleaseAdHoc || releaseMode == UIApplicationReleaseWildcard) dataDic[@"test_type"] = [NSNumber numberWithInt:releaseMode]; @@ -1122,7 +1132,7 @@ + (void)registerUserInternal { let requests = [NSMutableDictionary new]; requests[@"push"] = [OSRequestRegisterUser withData:pushDataDic userId:self.currentSubscriptionState.userId]; - if (self.currentSubscriptionState.emailUserId && self.currentSubscriptionState.emailAuthCode) { + if (self.currentSubscriptionState.emailUserId && (!self.currentSubscriptionState.requiresEmailAuth || self.currentSubscriptionState.emailAuthCode)) { let emailDataDic = (NSMutableDictionary *)[dataDic mutableCopy]; emailDataDic[@"device_type"] = @11; emailDataDic[@"email_auth_hash"] = self.currentSubscriptionState.emailAuthCode; diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m b/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m index d1bdddb05..59e4f533e 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m @@ -258,7 +258,7 @@ + (void)sendLocation { NSMutableDictionary *requests = [NSMutableDictionary new]; - if ([OneSignal mEmailUserId] && [OneSignal mEmailAuthToken]) + if ([OneSignal mEmailUserId]) requests[@"email"] = [OSRequestSendLocation withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] location:lastLocation networkType:[OneSignalHelper getNetType] backgroundState:([UIApplication sharedApplication].applicationState != UIApplicationStateActive) emailAuthHashToken:[OneSignal mEmailAuthToken]]; requests[@"push"] = [OSRequestSendLocation withUserId:[OneSignal mUserId] appId:[OneSignal app_id] location:lastLocation networkType:[OneSignalHelper getNetType] backgroundState:([UIApplication sharedApplication].applicationState != UIApplicationStateActive) emailAuthHashToken:nil]; diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m b/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m index 4382b93d2..dbaf775d1 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m @@ -125,7 +125,7 @@ + (void)onFocus:(BOOL)toBackground { requests[@"push"] = [OSRequestOnFocus withUserId:[OneSignal mUserId] appId:[OneSignal app_id] badgeCount:@0 emailAuthToken:nil]; - if ([OneSignal mEmailUserId] && [OneSignal mEmailAuthToken]) + if ([OneSignal mEmailUserId]) requests[@"email"] = [OSRequestOnFocus withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] badgeCount:@0 emailAuthToken:[OneSignal mEmailAuthToken]]; [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:nil onFailure:nil]; @@ -143,7 +143,7 @@ + (void)onFocus:(BOOL)toBackground { requests[@"push"] = [OSRequestOnFocus withUserId:[OneSignal mUserId] appId:[OneSignal app_id] state:@"ping" type:@1 activeTime:@(timeToPingWith) netType:[OneSignalHelper getNetType] emailAuthToken:nil]; - if ([OneSignal mEmailUserId] && [OneSignal mEmailAuthToken]) + if ([OneSignal mEmailUserId]) requests[@"email"] = [OSRequestOnFocus withUserId:[OneSignal mEmailUserId] appId:[OneSignal app_id] state:@"ping" type:@1 activeTime:@(timeToPingWith) netType:[OneSignalHelper getNetType] emailAuthToken:[OneSignal mEmailAuthToken]]; [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:nil onFailure:nil]; From 04084c6b79da02102553c9970d5a915d9ac5aa25 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Mon, 5 Feb 2018 17:20:00 -0800 Subject: [PATCH 14/36] Fixed Tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Changed the OSSubscriptionState `description` property to include Email & Email player id in the previous commit. • This broke a test checking the `description` property. • Removed this description value as it seems somewhat superfluous to test. --- iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m index de191fdbb..b77ca12e7 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m @@ -497,9 +497,6 @@ - (void)sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart { XCTAssertNil(observer->last); } - - - - (void)testPermissionChangeObserverDontLoseFromChanges { [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" @@ -682,9 +679,6 @@ - (void)testSubscriptionChangeObserverBasic { XCTAssertEqual(observer->last.from.subscribed, true); XCTAssertEqual(observer->last.to.subscribed, false); - - XCTAssertEqualObjects([observer->last description], @",\nto: \n>"); - NSLog(@"Test description: %@", observer->last); } - (void)testSubscriptionChangeObserverWhenPromptNotShown { @@ -743,6 +737,7 @@ - (void)testInitAcceptingNotificationsWithoutCapabilitesSet { [UnitTestCommonMethods runBackgroundThreads]; [self answerNotifiationPrompt:true]; [UnitTestCommonMethods runBackgroundThreads]; + XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @-13); XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); From e8ca60e2733aa8259b51d52f2a0c52ec5fc4b068 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Tue, 6 Feb 2018 14:01:52 -0800 Subject: [PATCH 15/36] Email Callbacks on Main Thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Because some developers will probably make UI adjustments when the completion block for setEmail() is called, I have changed the SDK to always call the success/fail completion blocks on the main thread. --- iOS_SDK/OneSignalSDK/Source/OneSignal.m | 43 ++++++++++++++++--------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index 01879246e..ca2c107c4 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -1607,6 +1607,22 @@ + (UNMutableNotificationContent*)serviceExtensionTimeWillExpireRequest:(UNNotifi #pragma mark Email ++ (void)callFailureBlockOnMainThread:(OSFailureBlock)failureBlock withError:(NSError *)error { + if (failureBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + failureBlock(error); + }); + } +} + ++ (void)callSuccessBlockOnMainThread:(OSEmailSuccessBlock)successBlock { + if (successBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + successBlock(); + }); + } +} + + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _Nullable)hashToken withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { //checks to ensure it is a valid email @@ -1645,13 +1661,13 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ // if the user already has a onesignal email player_id, then we should call update the device token // otherwise we should call Create Device + // since developers may be making UI changes when this call finishes, we will call callbacks on the main thread. + if (emailId) { [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:emailId appId:self.app_id deviceToken:email notificationTypes:nil withParentId:nil emailAuthToken:hashToken email:nil] onSuccess:^(NSDictionary *result) { - if (successBlock) - successBlock(); + [self callSuccessBlockOnMainThread:successBlock]; } onFailure:^(NSError *error) { - if (failureBlock) - failureBlock(error); + [self callFailureBlockOnMainThread:failureBlock withError:error]; }]; } else { [OneSignalClient.sharedClient executeRequest:[OSRequestCreateDevice withAppId:self.app_id withDeviceType:@11 withEmail:email withPlayerId:self.currentSubscriptionState.userId withEmailAuthHash:hashToken] onSuccess:^(NSDictionary *result) { @@ -1666,18 +1682,15 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _ [self.currentSubscriptionState persistAsFrom]; [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId appId:self.app_id deviceToken:nil notificationTypes:@([self getNotificationTypes]) withParentId:self.currentSubscriptionState.emailUserId emailAuthToken:hashToken email:email] onSuccess:^(NSDictionary *result) { - if (successBlock) - successBlock(); + [self callSuccessBlockOnMainThread:successBlock]; } onFailure:^(NSError *error) { - if (failureBlock) - failureBlock(error); + [self callFailureBlockOnMainThread:failureBlock withError:error]; }]; } else { [self onesignal_Log:ONE_S_LL_ERROR message:@"Missing OneSignal Email Player ID"]; } } onFailure:^(NSError *error) { - if (failureBlock) - failureBlock(error); + [self callFailureBlockOnMainThread:failureBlock withError:error]; }]; } } @@ -1689,7 +1702,9 @@ + (void)setUnauthenticatedEmail:(NSString * _Nonnull)email withSuccess:(OSEmailS + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock { if (!self.currentSubscriptionState.emailUserId) { [OneSignal onesignal_Log:ONE_S_LL_ERROR message:@"Email Player ID does not exist, cannot logout"]; - failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"error" : @"Attempted to log out of the user's email with OneSignal. The user does not currently have an email player ID and is not logged in, so it is not possible to log out of the email for this device"}]); + + if (failureBlock) + failureBlock([NSError errorWithDomain:@"com.onesignal" code:0 userInfo:@{@"error" : @"Attempted to log out of the user's email with OneSignal. The user does not currently have an email player ID and is not logged in, so it is not possible to log out of the email for this device"}]); return; } @@ -1705,11 +1720,9 @@ + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withF //call persistAsFrom in order to save the hashToken & playerId to NSUserDefaults [self.currentSubscriptionState persistAsFrom]; - if (successBlock) - successBlock(); + [self callSuccessBlockOnMainThread:successBlock]; } onFailure:^(NSError *error) { - if (failureBlock) - failureBlock(error); + [self callFailureBlockOnMainThread:failureBlock withError:error]; }]; } From 5661dc4009cda85f2377426bed82705f6b110ac5 Mon Sep 17 00:00:00 2001 From: Brad Hesse Date: Tue, 6 Feb 2018 14:50:48 -0800 Subject: [PATCH 16/36] Add Email to Swift Demo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Adds Email functionality to the Swift demo project, both the ability to setUnauthenticatedEmail() and logoutEmail() • Fixes some minor compiler warnings due to optional string variables • Adds warning to the syncEmail() method in the SDK so that it will print a warning if the email is invalid or nil. Otherwise developers may not notice that the email isn't actually getting synced since no error is returned. --- .../OneSignalDemo/AppDelegate.swift | 10 +- .../OneSignalDemo/Base.lproj/Main.storyboard | 81 +++++++++--- .../OneSignalDemo/ViewController.swift | 122 +++++++++++++++--- iOS_SDK/OneSignalSDK/Source/OneSignal.m | 8 +- 4 files changed, 180 insertions(+), 41 deletions(-) diff --git a/Examples/SwiftExample/OneSignalDemo/AppDelegate.swift b/Examples/SwiftExample/OneSignalDemo/AppDelegate.swift index da509a6e6..e53ca7c14 100644 --- a/Examples/SwiftExample/OneSignalDemo/AppDelegate.swift +++ b/Examples/SwiftExample/OneSignalDemo/AppDelegate.swift @@ -46,8 +46,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, OSPermissionObserver, OSS let notificationReceivedBlock: OSHandleNotificationReceivedBlock = { notification in print("Received Notification: \(notification!.payload.notificationID)") - print("launchURL = \(notification?.payload.launchURL)") - print("content_available = \(notification?.payload.contentAvailable)") + print("launchURL = \(notification?.payload.launchURL ?? "None")") + print("content_available = \(notification?.payload.contentAvailable ?? false)") } let notificationOpenedBlock: OSHandleNotificationActionBlock = { result in @@ -55,8 +55,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, OSPermissionObserver, OSS let payload: OSNotificationPayload? = result?.notification.payload print("Message = \(payload!.body)") - print("badge number = \(payload?.badge)") - print("notification sound = \(payload?.sound)") + print("badge number = \(payload?.badge ?? 0)") + print("notification sound = \(payload?.sound ?? "None")") if let additionalData = result!.notification.payload!.additionalData { print("additionalData = \(additionalData)") @@ -104,7 +104,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, OSPermissionObserver, OSS let onesignalInitSettings = [kOSSettingsKeyAutoPrompt: false, kOSSettingsKeyInAppLaunchURL: true, ] - OneSignal.initWithLaunchOptions(launchOptions, appId: " - + + - + @@ -23,7 +23,7 @@ - + - + + + + + + + + + + + + + + +