diff --git a/.travis.yml b/.travis.yml index a025f07df..ff2a93520 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,4 @@ before_install: script: - xcodebuild -list - xcodebuild build -scheme OneSignal-Dynamic - - xcodebuild -scheme UnitTests -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPad Air,OS=11.0' test \ No newline at end of file + - xcodebuild -scheme UnitTests -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 8 Plus,OS=11.0' test 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 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 1cf994674..684ebd334 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m @@ -33,6 +33,8 @@ #import @interface ViewController () +@property (weak, nonatomic) IBOutlet UITextField *textField; +@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicatorView; @end @@ -41,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 { @@ -71,6 +80,32 @@ - (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 { 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 d9848ce8a..a21712b05 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -132,6 +132,23 @@ 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 */; }; + 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 */; }; + 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 */; }; + CA810FD1202BA97300A60FED /* OSEmailSubscription.m in Sources */ = {isa = PBXBuildFile; fileRef = CA810FD0202BA97300A60FED /* OSEmailSubscription.m */; }; + CA810FD2202BA97600A60FED /* OSEmailSubscription.m in Sources */ = {isa = PBXBuildFile; fileRef = CA810FD0202BA97300A60FED /* OSEmailSubscription.m */; }; + CA810FD3202BA97600A60FED /* OSEmailSubscription.m in Sources */ = {isa = PBXBuildFile; fileRef = CA810FD0202BA97300A60FED /* OSEmailSubscription.m */; }; + CAEA1C66202BB3C600FBFE9E /* OSEmailSubscription.h in Headers */ = {isa = PBXBuildFile; fileRef = CA810FCF202BA97300A60FED /* OSEmailSubscription.h */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -254,6 +271,18 @@ 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + CA810FCF202BA97300A60FED /* OSEmailSubscription.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSEmailSubscription.h; sourceTree = ""; }; + CA810FD0202BA97300A60FED /* OSEmailSubscription.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OSEmailSubscription.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -377,6 +406,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 */, @@ -396,9 +428,12 @@ 91F58D7B1E7C7EE30017D24D /* NotificationSettings */, 912412461E73349500E41FD7 /* Categories */, 912412451E73346700E41FD7 /* UI */, - 912411F01E73342200E41FD7 /* OneSignal.h */, + 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 */, @@ -443,6 +478,8 @@ 912412091E73342200E41FD7 /* UIApplicationDelegate+OneSignal.m */, 9124120A1E73342200E41FD7 /* UNUserNotificationCenter+OneSignal.h */, 9124120B1E73342200E41FD7 /* UNUserNotificationCenter+OneSignal.m */, + CA63AF6E201FF83500E340FB /* NSNull+OneSignal.h */, + CA63AF6F201FF83500E340FB /* NSNull+OneSignal.m */, ); name = Categories; sourceTree = ""; @@ -456,6 +493,8 @@ 9129C6B61E89E59B009CB6A0 /* OSPermission.m */, 9129C6BB1E89E7AB009CB6A0 /* OSSubscription.h */, 9129C6BC1E89E7AB009CB6A0 /* OSSubscription.m */, + CA810FCF202BA97300A60FED /* OSEmailSubscription.h */, + CA810FD0202BA97300A60FED /* OSEmailSubscription.m */, ); name = State; sourceTree = ""; @@ -483,6 +522,8 @@ CA08FC771FE99B13004C445F /* OneSignalRequest.m */, CA08FC7C1FE99B25004C445F /* Requests.h */, CA08FC7D1FE99B25004C445F /* Requests.m */, + CA63AFC02022670A00E340FB /* ReattemptRequest.h */, + CA63AFC12022670A00E340FB /* ReattemptRequest.m */, ); name = API; path = Source; @@ -501,6 +542,7 @@ 912412291E73342200E41FD7 /* OneSignalReachability.h in Headers */, 912412251E73342200E41FD7 /* OneSignalMobileProvision.h in Headers */, 91F58D7A1E7C7D3F0017D24D /* OneSignalNotificationSettings.h in Headers */, + CAEA1C66202BB3C600FBFE9E /* OSEmailSubscription.h in Headers */, 91F58D811E7C80C30017D24D /* OneSignalNotificationSettingsIOS8.h in Headers */, CA08FC7E1FE99B25004C445F /* Requests.h in Headers */, 912412411E73342200E41FD7 /* UNUserNotificationCenter+OneSignal.h in Headers */, @@ -512,6 +554,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 */, @@ -519,6 +562,7 @@ 912412391E73342200E41FD7 /* OneSignalWebView.h in Headers */, 91C7725E1E7CCE1000D612D0 /* OneSignalInternal.h in Headers */, 9129C6BD1E89E7AB009CB6A0 /* OSSubscription.h in Headers */, + CA63AF70201FF83500E340FB /* NSNull+OneSignal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -675,11 +719,15 @@ 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 */, + CA810FD1202BA97300A60FED /* OSEmailSubscription.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 */, 454F94F51FAD2E5A00D74CCF /* OSNotificationPayload.m in Sources */, 9129C6BE1E89E7AB009CB6A0 /* OSSubscription.m in Sources */, 912412361E73342200E41FD7 /* OneSignalTrackIAP.m in Sources */, @@ -708,11 +756,15 @@ 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 */, + CA810FD2202BA97600A60FED /* OSEmailSubscription.m in Sources */, + CA63AFC42022670A00E340FB /* ReattemptRequest.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 */, @@ -729,12 +781,15 @@ 91F60F7D1E80E4E400706E60 /* UncaughtExceptionHandler.m in Sources */, 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 */, 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 */, @@ -742,9 +797,11 @@ 9124122C1E73342200E41FD7 /* OneSignalReachability.m in Sources */, 03389F691FB548A0006537F0 /* OneSignalTrackFirebaseAnalyticsOverrider.m in Sources */, CA08FC811FE99B25004C445F /* Requests.m in Sources */, + CA810FD3202BA97600A60FED /* OSEmailSubscription.m in Sources */, 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 */, @@ -757,6 +814,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/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..1cdcc12d3 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/NSNull+OneSignal.m @@ -0,0 +1,48 @@ +/** + * 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" + +// Helper method to make sure nil is never inserted into a dictionary when building HTTP requests + +@implementation NSNull (OneSignal) + ++ (id)nullIfObjectIsNil:(id)object { + return object != nil ? object : [NSNull null]; +} + +@end diff --git a/iOS_SDK/OneSignalSDK/Source/OSEmailSubscription.h b/iOS_SDK/OneSignalSDK/Source/OSEmailSubscription.h new file mode 100644 index 000000000..5559f78ad --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/OSEmailSubscription.h @@ -0,0 +1,73 @@ +/** + * 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 + +#import "OneSignal.h" + +#import "OSObservable.h" + +#import "OSPermission.h" + + + +@protocol OSEmailSubscriptionStateObserver +-(void)onChanged:(OSEmailSubscriptionState*)state; +@end + + +typedef OSObservable*, OSEmailSubscriptionState*> ObservableEmailSubscriptionStateType; +typedef OSObservable*, OSEmailSubscriptionStateChanges*> ObservableEmailSubscriptionStateChangesType; + + +@interface OSEmailSubscriptionState () +@property (nonatomic) ObservableEmailSubscriptionStateType *observable; +@property (strong, nonatomic) NSString *emailAuthCode; +@property (nonatomic) BOOL requiresEmailAuth; +- (void)persist; +- (void)setEmailUserId:(NSString *)emailUserId; +- (void)setEmailAddress:(NSString *)emailAddress; +- (BOOL)compare:(OSEmailSubscriptionState *)from; +@end + + +@interface OSEmailSubscriptionStateChanges () +@property (readwrite) OSEmailSubscriptionState* to; +@property (readwrite) OSEmailSubscriptionState* from; +@end + + +@interface OSEmailSubscriptionChangedInternalObserver : NSObject ++ (void)fireChangesObserver:(OSEmailSubscriptionState*)state; +@end + + +@interface OneSignal (EmailSubscriptionAdditions) +@property (class) OSEmailSubscriptionState *lastEmailSubscriptionState; +@property (class) OSEmailSubscriptionState *currentEmailSubscriptionState; +@property (class) ObservableEmailSubscriptionStateChangesType *emailSubscriptionStateChangesObserver; +@end diff --git a/iOS_SDK/OneSignalSDK/Source/OSEmailSubscription.m b/iOS_SDK/OneSignalSDK/Source/OSEmailSubscription.m new file mode 100644 index 000000000..2afed5f48 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/OSEmailSubscription.m @@ -0,0 +1,146 @@ +/** + * 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 "OSEmailSubscription.h" +#import "OneSignalHelper.h" +#import "OneSignalCommonDefines.h" + +@implementation OSEmailSubscriptionState + +- (ObservableEmailSubscriptionStateType *)observable { + if (!_observable) + _observable = [OSObservable new]; + return _observable; +} + +- (instancetype)init { + let 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]; + + return self; +} + +-(BOOL)subscribed { + return self.emailUserId != nil; +} + +- (void)persist { + let userDefaults = [NSUserDefaults standardUserDefaults]; + + [userDefaults setObject:_emailAddress forKey:EMAIL_ADDRESS]; + [userDefaults setObject:[NSNumber numberWithBool:_requiresEmailAuth] forKey:REQUIRE_EMAIL_AUTH]; + [userDefaults setObject:_emailAuthCode forKey:EMAIL_AUTH_CODE]; + [userDefaults setObject:_emailUserId forKey:EMAIL_USERID]; + + [userDefaults synchronize]; +} + +- (NSString *)description { + @synchronized (self) { + return [NSString stringWithFormat:@"", self.emailAddress, self.emailUserId]; + } +} + +- (instancetype)copyWithZone:(NSZone *)zone { + OSEmailSubscriptionState *copy = [OSEmailSubscriptionState new]; + + if (copy) { + copy->_requiresEmailAuth = _requiresEmailAuth; + copy->_emailAuthCode = [_emailAuthCode copy]; + copy->_emailUserId = [_emailUserId copy]; + copy->_emailAddress = [_emailAddress copy]; + } + + return copy; +} + + +- (void)setEmailUserId:(NSString *)emailUserId { + BOOL changed = emailUserId != _emailUserId; + _emailUserId = emailUserId; + + if (changed) + [self.observable notifyChange:self]; +} + +- (void)setEmailAddress:(NSString *)emailAddress { + _emailAddress = emailAddress; +} + +- (NSDictionary *)toDictionary { + return @{ + @"emailUserId": _emailUserId ?: [NSNull null], + @"emailAddress": _emailAddress ?: [NSNull null] + }; +} + +-(BOOL)compare:(OSEmailSubscriptionState *)from { + return ![self.emailAddress ?: @"" isEqualToString:from.emailAddress ?: @""] || ![self.emailUserId ?: @"" isEqualToString:from.emailUserId ?: @""]; +} + +@end + + +@implementation OSEmailSubscriptionChangedInternalObserver + +- (void)onChanged:(OSEmailSubscriptionState*)state { + [OSEmailSubscriptionChangedInternalObserver fireChangesObserver:state]; +} + ++ (void)fireChangesObserver:(OSEmailSubscriptionState*)state { + OSEmailSubscriptionStateChanges* stateChanges = [[OSEmailSubscriptionStateChanges alloc] init]; + stateChanges.from = OneSignal.lastEmailSubscriptionState; + stateChanges.to = [state copy]; + + // wants OSEmailSubscriptionState + + + BOOL hasReceiver = [OneSignal.emailSubscriptionStateChangesObserver notifyChange:stateChanges]; + if (hasReceiver) { + OneSignal.lastEmailSubscriptionState = [state copy]; + [OneSignal.lastEmailSubscriptionState persist]; + } +} + +@end + +@implementation OSEmailSubscriptionStateChanges +- (NSString*)description { + static NSString* format = @""; + return [NSString stringWithFormat:format, _from, _to]; +} + +- (NSDictionary*)toDictionary { + return @{@"from": [_from toDictionary], + @"to": [_to toDictionary]}; +} + +@end diff --git a/iOS_SDK/OneSignalSDK/Source/OSSubscription.h b/iOS_SDK/OneSignalSDK/Source/OSSubscription.h index 37116f7a1..156252cae 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSSubscription.h +++ b/iOS_SDK/OneSignalSDK/Source/OSSubscription.h @@ -40,7 +40,8 @@ -(void)onChanged:(OSSubscriptionState*)state; @end -typedef OSObservable*, OSSubscriptionState*> ObserableSubscriptionStateType; + +typedef OSObservable*, OSSubscriptionState*> ObservableSubscriptionStateType; // Redefine OSSubscriptionState @interface OSSubscriptionState () { @@ -53,7 +54,7 @@ typedef OSObservable*, OSSubscriptionState @property (readwrite, nonatomic) BOOL userSubscriptionSetting; // returns setSubscription state. @property (readwrite, nonatomic) NSString* userId; // AKA OneSignal PlayerId @property (readwrite, nonatomic) NSString* pushToken; // AKA Apple Device Token -@property (nonatomic) ObserableSubscriptionStateType* observable; +@property (nonatomic) ObservableSubscriptionStateType* observable; - (instancetype)initAsToWithPermision:(BOOL)permission; - (instancetype)initAsFrom; @@ -65,7 +66,6 @@ typedef OSObservable*, OSSubscriptionState @interface OSSubscriptionState () @property (nonatomic) BOOL accpeted; - - (void)setAccepted:(BOOL)inAccpeted; - (void)persistAsFrom; - (BOOL)compare:(OSSubscriptionState*)from; @@ -83,8 +83,7 @@ typedef OSObservable*, OSSubscriptionState @end -typedef OSObservable*, OSSubscriptionStateChanges*> ObserableSubscriptionStateChangesType; - +typedef OSObservable*, OSSubscriptionStateChanges*> ObservableSubscriptionStateChangesType; @interface OSSubscriptionChangedInternalObserver : NSObject + (void)fireChangesObserver:(OSSubscriptionState*)state; @@ -96,6 +95,6 @@ typedef OSObservable*, OSSubscriptionStateChang @property (class) OSSubscriptionState* currentSubscriptionState; // Used to manage observers added by the app developer. -@property (class) ObserableSubscriptionStateChangesType* subscriptionStateChangesObserver; +@property (class) ObservableSubscriptionStateChangesType* subscriptionStateChangesObserver; @end diff --git a/iOS_SDK/OneSignalSDK/Source/OSSubscription.m b/iOS_SDK/OneSignalSDK/Source/OSSubscription.m index b45d023bf..fd1a8afa0 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 @@ -33,7 +34,7 @@ @implementation OSSubscriptionState -- (ObserableSubscriptionStateType*)observable { +- (ObservableSubscriptionStateType*)observable { if (!_observable) _observable = [OSObservable new]; return _observable; @@ -43,9 +44,9 @@ - (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; + _userId = [userDefaults stringForKey:USERID]; + _pushToken = [userDefaults stringForKey:DEVICE_TOKEN]; + _userSubscriptionSetting = [userDefaults objectForKey:SUBSCRIPTION] == nil; return self; } @@ -60,10 +61,10 @@ - (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"]; + _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 +75,11 @@ - (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:strUserSubscriptionSetting forKey:SUBSCRIPTION_SETTING]; + [userDefaults setObject:_userId forKey:USERID_LAST]; + [userDefaults setObject:_pushToken forKey:PUSH_TOKEN]; + [userDefaults setBool:_accpeted forKey:ACCEPTED_PERMISSION]; [userDefaults synchronize]; } @@ -112,7 +113,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) @@ -127,6 +128,7 @@ - (void)setUserSubscriptionSetting:(BOOL)userSubscriptionSetting { [self.observable notifyChange:self]; } + - (void)setAccepted:(BOOL)inAccpeted { BOOL lastSubscribed = self.subscribed; @@ -154,10 +156,12 @@ - (NSString*)description { } - (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) + }; } @end diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.h b/iOS_SDK/OneSignalSDK/Source/OneSignal.h index 971a3e4c8..c1a192948 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.h +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.h @@ -236,19 +236,34 @@ typedef NS_ENUM(NSInteger, OSNotificationPermission) { @end -@interface OSSubscriptionStateChanges : NSObject +@interface OSEmailSubscriptionState : NSObject +@property (readonly, nonatomic) NSString* emailUserId; // The new Email user ID +@property (readonly, nonatomic) NSString *emailAddress; +@property (readonly, nonatomic) BOOL subscribed; +- (NSDictionary*)toDictionary; +@end + +@interface OSSubscriptionStateChanges : NSObject @property (readonly) OSSubscriptionState* to; @property (readonly) OSSubscriptionState* from; - - (NSDictionary*)toDictionary; +@end +@interface OSEmailSubscriptionStateChanges : NSObject +@property (readonly) OSEmailSubscriptionState* to; +@property (readonly) OSEmailSubscriptionState* from; +- (NSDictionary*)toDictionary; @end @protocol OSSubscriptionObserver - (void)onOSSubscriptionChanged:(OSSubscriptionStateChanges*)stateChanges; @end +@protocol OSEmailSubscriptionObserver +- (void)onOSEmailSubscriptionChanged:(OSEmailSubscriptionStateChanges*)stateChanges; +@end + // Permission+Subscription Classes @@ -256,6 +271,7 @@ typedef NS_ENUM(NSInteger, OSNotificationPermission) { @property (readonly) OSPermissionState* permissionStatus; @property (readonly) OSSubscriptionState* subscriptionStatus; +@property (readonly) OSEmailSubscriptionState *emailSubscriptionStatus; - (NSDictionary*)toDictionary; @end @@ -356,6 +372,9 @@ typedef NS_ENUM(NSUInteger, ONE_S_LOG_LEVEL) { + (void)addSubscriptionObserver:(NSObject*)observer; + (void)removeSubscriptionObserver:(NSObject*)observer; ++ (void)addEmailSubscriptionObserver:(NSObject*)observer; ++ (void)removeEmailSubscriptionObserver:(NSObject*)observer; + + (void)setSubscription:(BOOL)enable; // - Posting Notification @@ -379,4 +398,27 @@ typedef NS_ENUM(NSUInteger, ONE_S_LOG_LEVEL) { + (UNMutableNotificationContent*)didReceiveNotificationExtensionRequest:(UNNotificationRequest*)request withMutableNotificationContent:(UNMutableNotificationContent*)replacementContent; + (UNMutableNotificationContent*)serviceExtensionTimeWillExpireRequest:(UNNotificationRequest*)request withMutableNotificationContent:(UNMutableNotificationContent*)replacementContent; +// Email methods + +// Typedefs defining completion blocks for email & simultaneous HTTP requests +typedef void (^OSEmailFailureBlock)(NSError* error); +typedef void (^OSEmailSuccessBlock)(); + +// Allows you to set the email for this user. +// Email Auth Token is a (recommended) optional parameter that should *NOT* be generated on the client. +// For security purposes, the emailAuthToken should be generated by your backend server. +// If you do not have a backend server for your application, use the version of thge setEmail: method without an emailAuthToken parameter. ++ (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _Nullable)hashToken withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock; + +// Sets email without an authentication token ++ (void)setEmail:(NSString * _Nonnull)email withSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock; + +// Logs the device out of the current email. ++ (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withFailure:(OSEmailFailureBlock _Nullable)failureBlock; + +//convenience - no completion blocks ++ (void)logoutEmail; ++ (void)setEmail:(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 56d26b958..10d6f335f 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 @@ -128,6 +131,15 @@ @implementation OneSignal static BOOL shouldDelaySubscriptionUpdate = false; + +/* + 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; @@ -141,6 +153,10 @@ @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 BOOL didCallDownloadParameters = false; + static OneSignalTrackIAP* trackIAPPurchase; static NSString* app_id; NSString* emailToSet; @@ -205,6 +221,27 @@ + (void)setLastPermissionState:(OSPermissionState *)lastPermissionState { _lastPermissionState = lastPermissionState; } +static OSEmailSubscriptionState* _currentEmailSubscriptionState; ++ (OSEmailSubscriptionState *)currentEmailSubscriptionState { + if (!_currentEmailSubscriptionState) { + _currentEmailSubscriptionState = [[OSEmailSubscriptionState alloc] init]; + + [_currentEmailSubscriptionState.observable addObserver:[OSEmailSubscriptionChangedInternalObserver alloc]]; + } + return _currentEmailSubscriptionState; +} + +static OSEmailSubscriptionState *_lastEmailSubscriptionState; ++ (OSEmailSubscriptionState *)lastEmailSubscriptionState { + if (!_lastEmailSubscriptionState) { + _lastEmailSubscriptionState = [[OSEmailSubscriptionState alloc] init]; + } + return _lastEmailSubscriptionState; +} + ++ (void)setLastEmailSubscriptionState:(OSEmailSubscriptionState *)lastEmailSubscriptionState { + _lastEmailSubscriptionState = lastEmailSubscriptionState; +} // static property def for current OSSubscriptionState static OSSubscriptionState* _currentSubscriptionState; @@ -240,13 +277,20 @@ + (ObserablePermissionStateChangesType*)permissionStateChangesObserver { return _permissionStateChangesObserver; } -static ObserableSubscriptionStateChangesType* _subscriptionStateChangesObserver; -+ (ObserableSubscriptionStateChangesType*)subscriptionStateChangesObserver { +static ObservableSubscriptionStateChangesType* _subscriptionStateChangesObserver; ++ (ObservableSubscriptionStateChangesType*)subscriptionStateChangesObserver { if (!_subscriptionStateChangesObserver) _subscriptionStateChangesObserver = [[OSObservable alloc] initWithChangeSelector:@selector(onOSSubscriptionChanged:)]; return _subscriptionStateChangesObserver; } +static ObservableEmailSubscriptionStateChangesType* _emailSubscriptionStateChangesObserver; ++ (ObservableEmailSubscriptionStateChangesType *)emailSubscriptionStateChangesObserver { + if (!_emailSubscriptionStateChangesObserver) + _emailSubscriptionStateChangesObserver = [[OSObservable alloc] initWithChangeSelector:@selector(onOSEmailSubscriptionChanged:)]; + return _emailSubscriptionStateChangesObserver; +} + + (void)setMSubscriptionStatus:(NSNumber*)status { mSubscriptionStatus = [status intValue]; } @@ -260,7 +304,6 @@ + (NSString*)sdk_version_raw { } + (NSString*)sdk_semantic_version { - // examples: // ONESIGNAL_VERSION = @"020402" returns 2.4.2 // ONESIGNAL_VERSION = @"001000" returns 0.10.0 @@ -275,6 +318,14 @@ + (NSString*)mUserId { return self.currentSubscriptionState.userId; } ++ (NSString *)mEmailAuthToken { + return self.currentEmailSubscriptionState.emailAuthCode; +} + ++ (NSString *)mEmailUserId { + return self.currentEmailSubscriptionState.emailUserId; +} + + (void)setMSDKType:(NSString*)type { mSDKType = type; } @@ -292,10 +343,14 @@ + (void)clearStatics { _lastPermissionState = nil; _currentPermissionState = nil; + _currentEmailSubscriptionState = nil; + _lastEmailSubscriptionState = nil; _lastSubscriptionState = nil; _currentSubscriptionState = nil; _permissionStateChangesObserver = nil; + + didCallDownloadParameters = false; } // Set to false as soon as it's read. @@ -346,10 +401,12 @@ + (id)initWithLaunchOptions:(NSDictionary*)launchOptions appId:(NSString*)appId registeredWithApple = self.currentSubscriptionState.pushToken || [userDefaults boolForKey:@"GT_REGISTERED_WITH_APPLE"]; // Check if disabled in-app launch url if passed a NO - if (settings[kOSSettingsKeyInAppLaunchURL] && [settings[kOSSettingsKeyInAppLaunchURL] isKindOfClass:[NSNumber class]]) + if (settings[kOSSettingsKeyInAppLaunchURL] && [settings[kOSSettingsKeyInAppLaunchURL] isKindOfClass:[NSNumber class]]) { [self enableInAppLaunchURL:settings[kOSSettingsKeyInAppLaunchURL]]; - else + } else if (![[NSUserDefaults standardUserDefaults] objectForKey:@"ONESIGNAL_INAPP_LAUNCH_URL"]) { + //only need to default to @YES if the app doesn't already have this setting saved in NSUserDefaults [self enableInAppLaunchURL:@YES]; + } var autoPrompt = YES; @@ -412,9 +469,19 @@ + (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 + */ + + //Some wrapper SDK's call init multiple times and pass nil/NSNull as the appId on the first call + //the app ID is required to download parameters, so do not download params until the appID is provided + if (!didCallDownloadParameters && appId != nil && appId != (id)[NSNull null]) + [self downloadIOSParams]; + if ([OneSignalTrackFirebaseAnalytics needsRemoteParams]) { [OneSignalTrackFirebaseAnalytics init]; - [self downloadIOSParams]; } return self; @@ -441,7 +508,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]; } @@ -457,13 +524,27 @@ +(bool)initAppId:(NSString*)appId withUserDefaults:(NSUserDefaults*)userDefaults } +(void)downloadIOSParams { + [self onesignal_Log:ONE_S_LL_DEBUG message:@"Downloading iOS parameters for this application"]; + didCallDownloadParameters = true; + [OneSignalClient.sharedClient executeRequest:[OSRequestGetIosParams withUserId:self.currentSubscriptionState.userId appId:self.app_id] onSuccess:^(NSDictionary *result) { + if (result[@"require_email_auth"]) { + self.currentEmailSubscriptionState.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]; } + (void)setLogLevel:(ONE_S_LOG_LEVEL)nsLogLevel visualLevel:(ONE_S_LOG_LEVEL)visualLogLevel { - NSLog(@"ONESIGNAL - Setting log level: %d", (int)nsLogLevel); _nsLogLevel = nsLogLevel; _visualLogLevel = visualLogLevel; } @@ -562,6 +643,7 @@ + (OSPermissionSubscriptionState*)getPermissionSubscriptionState { status.subscriptionStatus = self.currentSubscriptionState; status.permissionStatus = self.currentPermissionState; + status.emailSubscriptionStatus = self.currentEmailSubscriptionState; return status; } @@ -586,11 +668,21 @@ + (void)addSubscriptionObserver:(NSObject*)observer { if ([self.currentSubscriptionState compare:self.lastSubscriptionState]) [OSSubscriptionChangedInternalObserver fireChangesObserver:self.currentSubscriptionState]; } + + (void)removeSubscriptionObserver:(NSObject*)observer { [self.subscriptionStateChangesObserver removeObserver:observer]; } ++ (void)addEmailSubscriptionObserver:(NSObject*)observer { + [self.emailSubscriptionStateChangesObserver addObserver:observer]; + + if ([self.currentEmailSubscriptionState compare:self.lastEmailSubscriptionState]) + [OSEmailSubscriptionChangedInternalObserver fireChangesObserver:self.currentEmailSubscriptionState]; +} ++ (void)removeEmailSubscriptionObserver:(NSObject*)observer { + [self.emailSubscriptionStateChangesObserver removeObserver:observer]; +} // Block not assigned if userID nil and there is a device token + (void)IdsAvailable:(OSIdsAvailableBlock)idsAvailableBlock { @@ -679,16 +771,34 @@ + (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] withEmailAuthHashToken:nil]; + + if (self.currentEmailSubscriptionState.emailUserId && (self.currentEmailSubscriptionState.requiresEmailAuth == false || self.currentEmailSubscriptionState.emailAuthCode)) + requests[@"email"] = [OSRequestSendTagsToServer withUserId:self.currentEmailSubscriptionState.emailUserId appId:self.app_id tags:nowSendingTags networkType:[OneSignalHelper getNetType] withEmailAuthHashToken:self.currentEmailSubscriptionState.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 + + 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) { - if (nowProcessingCallbacks) - for (OSPendingCallbacks *callbackSet in nowProcessingCallbacks) - if (callbackSet.failureBlock) - callbackSet.failureBlock(error); + callbackSet.successBlock(resultTags); + + } onFailure:^(NSDictionary *errors) { + if (nowProcessingCallbacks) { + for (OSPendingCallbacks *callbackSet in nowProcessingCallbacks) { + if (callbackSet.failureBlock) { + callbackSet.failureBlock((NSError *)(errors[@"push"] ?: errors[@"email"])); + } + } + } }]; } @@ -821,7 +931,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; @@ -904,7 +1014,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 emailAuthToken:nil email: nil] onSuccess:successBlock onFailure:failureBlock]; [self fireIdsAvailableCallback]; } @@ -918,6 +1028,11 @@ + (BOOL)isHighPriorityCall { static BOOL waitingForOneSReg = false; +//needed so that tests can make sure registerUserInternal executes ++ (void)setNextRegistrationHighPriority:(BOOL)highPriority { + nextRegistrationIsHighPriority = highPriority; +} + + (void)updateLastSessionDateTime { NSTimeInterval now = [[NSDate date] timeIntervalSince1970]; @@ -998,7 +1113,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) @@ -1035,6 +1149,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]; @@ -1055,7 +1179,22 @@ + (void)registerUserInternal { [OneSignalLocation clearLastLocation]; } - [OneSignalClient.sharedClient executeRequest:[OSRequestRegisterUser withData:dataDic userId:self.currentSubscriptionState.userId] onSuccess:^(NSDictionary *result) { + + let pushDataDic = (NSMutableDictionary *)[dataDic mutableCopy]; + pushDataDic[@"identifier"] = self.currentSubscriptionState.pushToken; + + let requests = [NSMutableDictionary new]; + requests[@"push"] = [OSRequestRegisterUser withData:pushDataDic userId:self.currentSubscriptionState.userId]; + + if (self.currentEmailSubscriptionState.emailUserId && (!self.currentEmailSubscriptionState.requiresEmailAuth || self.currentEmailSubscriptionState.emailAuthCode)) { + let emailDataDic = (NSMutableDictionary *)[dataDic mutableCopy]; + emailDataDic[@"device_type"] = @11; + emailDataDic[@"email_auth_hash"] = self.currentEmailSubscriptionState.emailAuthCode; + + requests[@"email"] = [OSRequestRegisterUser withData:emailDataDic userId:self.currentEmailSubscriptionState.emailUserId]; + } + + [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:^(NSDictionary *results) { waitingForOneSReg = false; // Success, no more high priority @@ -1063,10 +1202,33 @@ + (void)registerUserInternal { [self updateLastSessionDateTime]; - if (result[@"id"]) { - self.currentSubscriptionState.userId = result[@"id"]; - [[NSUserDefaults standardUserDefaults] setObject:self.currentSubscriptionState.userId forKey:@"GT_PLAYER_ID"]; - [[NSUserDefaults standardUserDefaults] synchronize]; + //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.currentEmailSubscriptionState.emailUserId && ![self.currentEmailSubscriptionState.emailUserId isEqualToString:results[@"email"][@"id"]] && self.currentEmailSubscriptionState.emailAuthCode) { + [self emailChangedWithNewEmailPlayerId:results[@"email"][@"id"]]; + } + + self.currentEmailSubscriptionState.emailUserId = results[@"email"][@"id"]; + [[NSUserDefaults standardUserDefaults] setObject:self.currentEmailSubscriptionState.emailUserId forKey:EMAIL_USERID]; + //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"]; + + 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) [self updateDeviceToken:self.currentSubscriptionState.pushToken @@ -1095,9 +1257,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; @@ -1147,7 +1314,14 @@ + (void)sendPurchases:(NSArray*)purchases { if (!self.currentSubscriptionState.userId) return; - [OneSignalClient.sharedClient executeRequest:[OSRequestSendPurchases withUserId:self.currentSubscriptionState.userId appId:self.app_id withPurchases:purchases] onSuccess:nil onFailure:nil]; + let requests = [NSMutableDictionary new]; + + requests[@"push"] = [OSRequestSendPurchases withUserId:self.currentSubscriptionState.userId appId:self.app_id withPurchases:purchases]; + + if (self.currentEmailSubscriptionState.emailUserId) + requests[@"email"] = [OSRequestSendPurchases withUserId:self.currentEmailSubscriptionState.emailUserId emailAuthToken:self.currentEmailSubscriptionState.emailAuthCode appId:self.app_id withPurchases:purchases]; + + [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:nil onFailure:nil]; } @@ -1460,13 +1634,17 @@ + (void)processLocalActionBasedNotification:(UILocalNotification*) notification } + (void)syncHashedEmail:(NSString *)email { - if (!email) + if (!email) { + [self onesignal_Log:ONE_S_LL_WARN message:@"OneSignal syncHashedEmail: The provided email is nil"]; return; + } let trimmedEmail = [email stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - if (![OneSignalHelper isValidEmail:trimmedEmail]) + if (![OneSignalHelper isValidEmail:trimmedEmail]) { + [self onesignal_Log:ONE_S_LL_WARN message:@"OneSignal syncHashedEmail: The provided email is invalid"]; return; + } if (!self.currentSubscriptionState.userId) { emailToSet = email; @@ -1491,6 +1669,168 @@ + (UNMutableNotificationContent*)serviceExtensionTimeWillExpireRequest:(UNNotifi serviceExtensionTimeWillExpireRequest:request withMutableNotificationContent:replacementContent]; } + +#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 { + + //some clients/wrappers may send NSNull instead of nil as the auth token + NSString *emailAuthToken = hashToken; + if (hashToken == (id)[NSNull null]) + emailAuthToken = nil; + + //checks to ensure it is a valid email + if (![OneSignalHelper isValidEmail:email]) { + [self onesignal_Log:ONE_S_LL_WARN message:[NSString stringWithFormat:@"Invalid email (%@) passed to setEmail", 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.currentEmailSubscriptionState.requiresEmailAuth && (!emailAuthToken || emailAuthToken.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.currentEmailSubscriptionState.emailAddress isEqualToString:email] && ([self.currentEmailSubscriptionState.emailAuthCode isEqualToString:emailAuthToken] || (self.currentEmailSubscriptionState.emailAuthCode == nil && emailAuthToken == nil))) { + [self onesignal_Log:ONE_S_LL_VERBOSE message:@"Email already exists, there is no need to call setEmail again"]; + 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 + and we do not need to delay the request + */ + + if (!self.currentSubscriptionState.userId || (downloadedParameters == false && emailAuthToken != nil)) { + [self onesignal_Log:ONE_S_LL_VERBOSE message:@"iOS Parameters for this application has not yet been downloaded. Delaying call to setEmail: until the parameters have been downloaded."]; + delayedParameters = [OneSignalSetEmailParameters withEmail:email withAuthToken:emailAuthToken withSuccess:successBlock withFailure:failureBlock]; + return; + } + + // 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 (self.currentEmailSubscriptionState.emailUserId) { + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentEmailSubscriptionState.emailUserId appId:self.app_id deviceToken:email notificationTypes:nil withParentId:nil emailAuthToken:emailAuthToken email:nil] onSuccess:^(NSDictionary *result) { + [self callSuccessBlockOnMainThread:successBlock]; + } onFailure:^(NSError *error) { + [self callFailureBlockOnMainThread:failureBlock withError:error]; + }]; + } else { + [OneSignalClient.sharedClient executeRequest:[OSRequestCreateDevice withAppId:self.app_id withDeviceType:@11 withEmail:email withPlayerId:self.currentSubscriptionState.userId withEmailAuthHash:emailAuthToken] onSuccess:^(NSDictionary *result) { + + let emailPlayerId = (NSString *)result[@"id"]; + + if (emailPlayerId) { + self.currentEmailSubscriptionState.emailAddress = email; + self.currentEmailSubscriptionState.emailAuthCode = emailAuthToken; + self.currentEmailSubscriptionState.emailUserId = emailPlayerId; + + //call persistAsFrom in order to save the emailAuthToken & playerId to NSUserDefaults + [self.currentEmailSubscriptionState persist]; + + [OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId appId:self.app_id deviceToken:nil notificationTypes:@([self getNotificationTypes]) withParentId:self.currentEmailSubscriptionState.emailUserId emailAuthToken:hashToken email:email] onSuccess:^(NSDictionary *result) { + [self callSuccessBlockOnMainThread:successBlock]; + } onFailure:^(NSError *error) { + [self callFailureBlockOnMainThread:failureBlock withError:error]; + }]; + } else { + [self onesignal_Log:ONE_S_LL_ERROR message:@"Missing OneSignal Email Player ID"]; + } + } onFailure:^(NSError *error) { + [self callFailureBlockOnMainThread:failureBlock withError:error]; + }]; + } +} + ++ (void)setEmail:(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 { + if (!self.currentEmailSubscriptionState.emailUserId) { + [OneSignal onesignal_Log:ONE_S_LL_ERROR message:@"Email Player ID does not exist, cannot logout"]; + + 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; + } + + + [OneSignalClient.sharedClient executeRequest:[OSRequestLogoutEmail withAppId: self.app_id emailPlayerId:self.currentEmailSubscriptionState.emailUserId devicePlayerId:self.currentSubscriptionState.userId emailAuthHash:self.currentEmailSubscriptionState.emailAuthCode] onSuccess:^(NSDictionary *result) { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:EMAIL_USERID]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + self.currentEmailSubscriptionState.emailAddress = nil; + self.currentEmailSubscriptionState.emailAuthCode = nil; + self.currentEmailSubscriptionState.emailUserId = nil; + + //call persistAsFrom in order to save the hashToken & playerId to NSUserDefaults + [self.currentEmailSubscriptionState persist]; + + [self callSuccessBlockOnMainThread:successBlock]; + } onFailure:^(NSError *error) { + [self callFailureBlockOnMainThread:failureBlock withError:error]; + }]; +} + ++ (void)setEmail:(NSString * _Nonnull)email { + [self setEmail: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.currentEmailSubscriptionState.emailUserId isEqualToString:emailPlayerId]) + return; + + self.currentEmailSubscriptionState.emailUserId = emailPlayerId; + + [self.currentEmailSubscriptionState persist]; + + let request = [OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId + appId:self.app_id + deviceToken:nil + notificationTypes: @([self getNotificationTypes]) + withParentId:emailPlayerId + emailAuthToken:self.currentEmailSubscriptionState.emailAuthCode + email:self.currentEmailSubscriptionState.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]]; + }]; +} + @end // Swizzles UIApplication class to swizzling the following: diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.h b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.h index 02bf97f7f..fc80d7769 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.h +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.h @@ -32,10 +32,16 @@ #ifndef OneSignalClient_h #define OneSignalClient_h +typedef void (^OSMultipleFailureBlock)(NSDictionary *errors); +typedef void (^OSMultipleSuccessBlock)(NSDictionary *results); + @interface OneSignalClient : NSObject + (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..7d00f1d61 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalClient.m @@ -27,6 +27,12 @@ #import "OneSignalClient.h" #import "UIApplicationDelegate+OneSignal.h" +#import "ReattemptRequest.h" + +#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; @@ -45,12 +51,54 @@ + (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; } +- (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_FOREVER); + } + + //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]; @@ -58,12 +106,14 @@ - (void)executeRequest:(OneSignalRequest *)request onSuccess:(OSResultSuccessBlo } let task = [self.sharedSession dataTaskWithRequest:request.request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - [OneSignalClient handleJSONNSURLResponse:response data:data error:error onSuccess:successBlock onFailure:failureBlock]; + [self handleJSONNSURLResponse:response data:data error:error isAsync:true withRequest:request onSuccess:successBlock onFailure:failureBlock]; }]; [task resume]; } +// while this method still uses completion blocks like the asynchronous method, +// it pauses execution of the thread until the request is finished - (void)executeSynchronousRequest:(OneSignalRequest *)request onSuccess:(OSResultSuccessBlock)successBlock onFailure:(OSFailureBlock)failureBlock { if (![self validRequest:request]) { [self handleMissingAppIdError:failureBlock withRequest:request]; @@ -84,9 +134,9 @@ - (void)executeSynchronousRequest:(OneSignalRequest *)request onSuccess:(OSResul [dataTask resume]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, REQUEST_TIMEOUT_RESOURCE * NSEC_PER_SEC)); - [OneSignalClient handleJSONNSURLResponse:httpResponse data:nil error:httpError onSuccess:successBlock onFailure:failureBlock]; + [self handleJSONNSURLResponse:httpResponse data:nil error:httpError isAsync:false withRequest:request onSuccess:successBlock onFailure:failureBlock]; } - (void)handleMissingAppIdError:(OSFailureBlock)failureBlock withRequest:(OneSignalRequest *)request { @@ -102,13 +152,60 @@ - (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; } +// reattempts a failed HTTP request +// only occurs if the request encountered a 500+ server error (or timeout) code. +// only asynchronous HTTP requests will get reattempted with a delay +// synchronous requests (ie. image downloads) will be reattempted immediately +- (void)reattemptRequest:(ReattemptRequest *)reattempt { + if (!reattempt) { + return; + } + + //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.reattemptCount++; + + [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)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 onSuccess:(OSResultSuccessBlock)successBlock onFailure:(OSFailureBlock)failureBlock { +- (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; NSInteger statusCode = [HTTPResponse statusCode]; @@ -117,7 +214,7 @@ + (void)handleJSONNSURLResponse:(NSURLResponse*) response data:(NSData*) data er 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}]); @@ -125,6 +222,9 @@ + (void)handleJSONNSURLResponse:(NSURLResponse*) response data:(NSData*) data er } } + if ([self willReattemptRequest:(int)statusCode withRequest:request success:successBlock failure:failureBlock asyncRequest:async]) + return; + if (error == nil && statusCode == 200) { if (successBlock != nil) { if (innerJson != nil) diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h b/iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h new file mode 100644 index 000000000..70b611fdb --- /dev/null +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h @@ -0,0 +1,29 @@ +// +// OneSignalCommonDefines.h +// OneSignal +// +// Created by Brad Hesse on 2/1/18. +// Copyright © 2018 Hiptic. All rights reserved. +// + +#ifndef OneSignalCommonDefines_h +#define OneSignalCommonDefines_h + +// networking +#define API_VERSION @"api/v1/" +#define SERVER_URL @"https://onesignal.com/" + +// NSUserDefaults parameter names +#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" +#define EMAIL_ADDRESS @"EMAIL_ADDRESS" + +#endif /* OneSignalCommonDefines_h */ diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalInternal.h b/iOS_SDK/OneSignalSDK/Source/OneSignalInternal.h index 6005cab90..3ae829798 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalInternal.h +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalInternal.h @@ -37,6 +37,7 @@ #import "OSPermission.h" #import "OSSubscription.h" +#import "OSEmailSubscription.h" // Permission + Subscription - Redefine OSPermissionSubscriptionState @@ -44,6 +45,7 @@ @property (readwrite) OSPermissionState* permissionStatus; @property (readwrite) OSSubscriptionState* subscriptionStatus; +@property (readwrite) OSEmailSubscriptionState *emailSubscriptionStatus; @end diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m b/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m index 433e195a5..59e4f533e 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m @@ -35,7 +35,9 @@ @interface OneSignal () void onesignal_Log(ONE_S_LOG_LEVEL logLevel, NSString* message); ++ (NSString *)mEmailUserId; + (NSString*)mUserId; ++ (NSString *)mEmailAuthToken; @end @implementation OneSignalLocation @@ -254,7 +256,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) 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]; + + [OneSignalClient.sharedClient executeSimultaneousRequests:requests withSuccess:nil onFailure:nil]; } } 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/OneSignalRequest.h b/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.h index fdc363383..1c7f79346 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) 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 16dd55f31..67fb5d11f 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalRequest.m @@ -29,13 +29,13 @@ #import "OneSignalHelper.h" #import "OneSignal.h" #import "Requests.h" - -#define API_VERSION @"api/v1/" -#define SERVER_URL @"https://onesignal.com/" +#import "OneSignalCommonDefines.h" @implementation OneSignalRequest - (id)init { - self = [super init]; + if (self = [super init]) { + self.reattemptCount = 0; + } return self; } 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 diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalTrackIAP.m b/iOS_SDK/OneSignalSDK/Source/OneSignalTrackIAP.m index 26fa9fcd9..cad408186 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalTrackIAP.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalTrackIAP.m @@ -47,7 +47,7 @@ @implementation OneSignalTrackIAP + (BOOL)canTrack { skPaymentQueue = NSClassFromString(@"SKPaymentQueue"); - return (skPaymentQueue != nil && [skPaymentQueue performSelector:@selector(canMakePayments)]); + return (skPaymentQueue != nil && [skPaymentQueue respondsToSelector:@selector(canMakePayments)] && [skPaymentQueue performSelector:@selector(canMakePayments)]); } - (id)init { diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m b/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m index 0df368cc6..bd9f64a37 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalTracker.m @@ -39,6 +39,8 @@ + (void)registerUser; + (BOOL)sendNotificationTypesUpdate; + (BOOL)clearBadgeCount:(BOOL)fromNotifOpened; + (NSString*)mUserId; ++ (NSString *)mEmailUserId; ++ (NSString *)mEmailAuthToken; @end @@ -57,6 +59,9 @@ + (void)resetLocals { lastOnFocusWasToBackground = YES; } ++ (void)setLastOpenedTime:(NSTimeInterval)lastOpened { + lastOpenedTime = lastOpened; +} + (void) beginBackgroundFocusTask { @@ -119,7 +124,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 emailAuthToken:nil]; + + 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]; return; } @@ -130,7 +142,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] emailAuthToken:nil]; + + 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]; [OneSignalTracker endBackgroundFocusTask]; }); 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.h b/iOS_SDK/OneSignalSDK/Source/Requests.h index 88714b4a6..486cb5462 100644 --- a/iOS_SDK/OneSignalSDK/Source/Requests.h +++ b/iOS_SDK/OneSignalSDK/Source/Requests.h @@ -42,24 +42,17 @@ 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 -@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 @interface OSRequestSendPurchases : OneSignalRequest + (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId withPurchases:(NSArray *)purchases; ++ (instancetype)withUserId:(NSString *)userId emailAuthToken:(NSString *)emailAuthToken appId:(NSString *)appId withPurchases:(NSArray *)purchases; @end @interface OSRequestSubmitNotificationOpened : OneSignalRequest @@ -70,20 +63,36 @@ 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 +NS_ASSUME_NONNULL_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; +@interface OSRequestUpdateDeviceToken : OneSignalRequest ++ (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 -NS_ASSUME_NONNULL_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)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 07d5e0a1d..6a8152a83 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 @@ -68,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]; @@ -95,15 +104,27 @@ + (instancetype)withAppId:(NSString *)appId withJson:(NSMutableDictionary *)json @end @implementation OSRequestUpdateDeviceToken -+ (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId deviceToken:(NSString *)identifier notificationTypes:(NSNumber *)notificationTypes { ++ (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]; - request.parameters = @{ - @"app_id" : appId, - @"identifier" : identifier, - @"notification_types" : notificationTypes - }; + let params = [NSMutableDictionary new]; + params[@"app_id"] = appId; + 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; + + request.parameters = params; request.method = PUT; request.path = [NSString stringWithFormat:@"players/%@", userId]; @@ -111,6 +132,43 @@ + (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, + @"identifier" : [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)withAppId:(NSString * _Nonnull)appId emailPlayerId:(NSString * _Nonnull)emailPlayerId devicePlayerId:(NSString * _Nonnull)devicePlayerId emailAuthHash:(NSString * _Nullable)emailAuthHash { + let request = [OSRequestLogoutEmail new]; + + request.parameters = @{ + @"parent_player_id" : [NSNull nullIfObjectIsNil:emailPlayerId], + @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthHash], + @"app_id" : appId + }; + + request.method = POST; + request.path = [NSString stringWithFormat:@"players/%@/email_logout", devicePlayerId]; + + return request; +} + +@end @implementation OSRequestUpdateNotificationTypes + (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId notificationTypes:(NSNumber *)notificationTypes { @@ -134,6 +192,16 @@ + (instancetype)withUserId:(NSString *)userId appId:(NSString *)appId withPurcha return request; } + ++ (instancetype)withUserId:(NSString *)userId emailAuthToken:(NSString *)emailAuthToken appId:(NSString *)appId withPurchases:(NSArray *)purchases { + let request = [OSRequestSendPurchases new]; + + request.parameters = @{@"app_id" : appId, @"purchases" : purchases, @"email_auth_hash" : [NSNull nullIfObjectIsNil:emailAuthToken]}; + request.method = POST; + request.path = [NSString stringWithFormat:@"players/%@/on_purchase", purchases]; + + return request; +} @end @implementation OSRequestSubmitNotificationOpened @@ -180,10 +248,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]; @@ -192,20 +272,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 new file mode 100644 index 000000000..2e1a62195 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m @@ -0,0 +1,496 @@ +// +// 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" +#import "OSEmailSubscription.h" +#import "UIApplicationOverrider.h" +#import "NSObjectOverrider.h" +#import "OneSignalHelperOverrider.h" +#import "UNUserNotificationCenterOverrider.h" +#import "UNUserNotificationCenter+OneSignal.h" +#import "NSBundleOverrider.h" +#import "NSUserDefaultsOverrider.h" +#import "OneSignalCommonDefines.h" +#import "OneSignalTracker.h" + +@interface OneSignalTracker () ++ (void)setLastOpenedTime:(NSTimeInterval)lastOpened; +@end + +@interface OneSignal () +void onesignal_Log(ONE_S_LOG_LEVEL logLevel, NSString* message); ++ (NSString *)mEmailUserId; ++ (NSString *)mEmailAuthToken; ++ (void)registerUserInternal; ++ (void)setNextRegistrationHighPriority:(BOOL)highPriority; +@end + +@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. + + 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:self]; + + [UnitTestCommonMethods beforeAllTest]; + + [OneSignalClientOverrider runBackgroundThreads]; +} + +- (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)testSetAuthenticatedEmail { + + [self setupEmailTest]; + + [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" withSuccess:nil withFailure:nil]; + + [UnitTestCommonMethods runBackgroundThreads]; + + //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([OSRequestUpdateDeviceToken class]) isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); + 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 + [OneSignal setEmail:@"test2@test.com" withEmailAuthHashToken:@"c7e76fb9579df964fa9dffd418619aa30767b864b1c025f5df22458cae65033c" 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"); + 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 { + + [self setupEmailTest]; + + [OneSignal setEmail:@"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([OSRequestUpdateDeviceToken class]) isEqualToString:OneSignalClientOverrider.lastHTTPRequestType]); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"parent_player_id"], @"1234"); + XCTAssertEqual(OneSignalClientOverrider.lastHTTPRequest[@"email"], @"test@test.com"); + XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"]); + + let expectation = [self expectationWithDescription:@"email"]; + expectation.expectedFulfillmentCount = 1; + + //now we will change the unauthenticated email to something else + [OneSignal setEmail:@"test2@test.com" withEmailAuthHashToken:nil withSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail(@"An error occurred: %@", error); + }]; + + [self waitForExpectations:@[expectation] timeout:0.1]; + + [UnitTestCommonMethods runBackgroundThreads]; + + NSLog(@"LAST HTTP TYPE: %@", OneSignalClientOverrider.lastHTTPRequestType); + NSLog(@"LAST HTTP REQ: %@", OneSignalClientOverrider.lastHTTPRequest); + //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"]); + + XCTAssertEqual([OneSignal mEmailUserId], @"1234"); + XCTAssertNil([OneSignal mEmailAuthToken]); + + [self logoutEmail]; + + XCTAssertNil([OneSignal mEmailUserId]); + XCTAssertNil([OneSignal mEmailAuthToken]); +} + +- (void)testInvalidEmail { + [self setupEmailTest]; + + let expectation = [self expectationWithDescription:@"email"]; + expectation.expectedFulfillmentCount = 1; + + [OneSignal setEmail:@"bad_email" withSuccess:^{ + XCTFail(@"setEmail: should reject invalid emails"); + + } withFailure:^(NSError *error) { + XCTAssertNotNil(error); + + [expectation fulfill]; + }]; + + [self waitForExpectations:@[expectation] timeout:0.1]; +} + +- (void)logoutEmail { + //test email logout + let expectation = [self expectationWithDescription:@"email_logout"]; + expectation.expectedFulfillmentCount = 1; + + [OneSignal logoutEmailWithSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail("Failed with error: %@", error); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[expectation] timeout:0.1]; +} + + +// tests to make sure the SDK correctly rejects setEmail when authToken == nil if +// the auth token is required (via iOS params file) for this application +- (void)testRequiresEmailAuth { + [OneSignalClientOverrider setRequiresEmailAuth:true]; + + [self setupEmailTest]; + + let expectation = [self expectationWithDescription:@"email"]; + expectation.expectedFulfillmentCount = 3; + + //this should work since we are providing a token + [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"test_hash_token" withSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail(@"Encountered error: %@", error); + }]; + + //logout to clear the email + [OneSignal logoutEmailWithSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail(@"Encountered error: %@", error); + }]; + + //this should fail since require_email_auth == true and we aren't providing an auth token + [OneSignal setEmail:@"test@test.com" withSuccess:^{ + XCTFail(@"Email authentication should be required."); + } withFailure:^(NSError *error) { + XCTAssertNotNil(error); + + [expectation fulfill]; + }]; + + [self waitForExpectations:@[expectation] timeout:0.1]; + + //reset so we don't interfere with other tests + [OneSignalClientOverrider setRequiresEmailAuth:false]; +} + +- (void)testDoesNotRequireEmailAuth { + + [UnitTestCommonMethods clearStateForAppRestart:self]; + + [OneSignalClientOverrider setRequiresEmailAuth:false]; + + [self setupEmailTest]; + + let expectation = [self expectationWithDescription:@"email"]; + expectation.expectedFulfillmentCount = 1; + + [OneSignal setEmail:@"testEmail@test.com" withSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail(@"Encountered error: %@", error); + }]; + + [UnitTestCommonMethods runBackgroundThreads]; + + [self waitForExpectations:@[expectation] timeout:0.1]; + + // Triggers the 30 fallback to register device right away. + [UnitTestCommonMethods runBackgroundThreads]; + [NSObjectOverrider runPendingSelectors]; + [UnitTestCommonMethods runBackgroundThreads]; + + + + [UnitTestCommonMethods clearStateForAppRestart:self]; +} + +- (void)setupEmailTest { + // Restart App + [UnitTestCommonMethods clearStateForAppRestart:self]; + + [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" + handleNotificationAction:nil + settings:@{kOSSettingsKeyAutoPrompt: @false}]; + + // Triggers the 30 fallback to register device right away. + [UnitTestCommonMethods runBackgroundThreads]; + [NSObjectOverrider runPendingSelectors]; + [UnitTestCommonMethods runBackgroundThreads]; +} + +- (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]; +} + +- (void)testSubscriptionState { + [OneSignalClientOverrider setRequiresEmailAuth:true]; + + [self setupEmailTest]; + + let unsubscribedSubscriptionStatus = [OneSignal getPermissionSubscriptionState].emailSubscriptionStatus; + + XCTAssertNil(unsubscribedSubscriptionStatus.emailAuthCode); + XCTAssertNil(unsubscribedSubscriptionStatus.emailAddress); + XCTAssertNil(unsubscribedSubscriptionStatus.emailUserId); + XCTAssertFalse(unsubscribedSubscriptionStatus.subscribed); + + + let expectation = [self expectationWithDescription:@"email"]; + expectation.expectedFulfillmentCount = 2; + + [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"test-hash-token" withSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail(@"Encountered an error: %@", error); + }]; + + let loggedInSubscriptionStatus = [OneSignal getPermissionSubscriptionState].emailSubscriptionStatus; + + XCTAssertEqual(loggedInSubscriptionStatus.emailUserId, @"1234"); + XCTAssertEqual(loggedInSubscriptionStatus.emailAddress, @"test@test.com"); + XCTAssertEqual(loggedInSubscriptionStatus.emailAuthCode, @"test-hash-token"); + XCTAssertEqual(loggedInSubscriptionStatus.subscribed, true); + + [OneSignal logoutEmailWithSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail(@"Encountered an error: %@", error); + }]; + + [self waitForExpectations:@[expectation] timeout:0.1]; + + let loggedOutSubscriptionStatus = [OneSignal getPermissionSubscriptionState].emailSubscriptionStatus; + + XCTAssertNil(loggedOutSubscriptionStatus.emailAuthCode); + XCTAssertNil(loggedOutSubscriptionStatus.emailAddress); + XCTAssertNil(loggedOutSubscriptionStatus.emailUserId); + XCTAssertFalse(loggedOutSubscriptionStatus.subscribed); + + //reset so we don't interfere with other tests + [OneSignalClientOverrider setRequiresEmailAuth:false]; +} + +- (void)testEmailSubscriptionObserver { + [UnitTestCommonMethods runBackgroundThreads]; + + let observer = [OSEmailSubscriptionStateTestObserver new]; + [OneSignal addEmailSubscriptionObserver:observer]; + + [OneSignalClientOverrider setRequiresEmailAuth:true]; + + [self setupEmailTest]; + + let expectation = [self expectationWithDescription:@"email"]; + expectation.expectedFulfillmentCount = 1; + + [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"test-hash-token" withSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail(@"Encountered error: %@", error); + }]; + + [self waitForExpectations:@[expectation] timeout:0.1]; + + NSLog(@"CHECKING REQUIRES EMAIL AUTH"); + XCTAssertEqual(observer->last.to.emailAddress, @"test@test.com"); + XCTAssertEqual(observer->last.to.emailUserId, @"1234"); + XCTAssertEqual(observer->last.to.emailAuthCode, @"test-hash-token"); + XCTAssertEqual(observer->last.to.requiresEmailAuth, true); + + let logoutExpectation = [self expectationWithDescription:@"logout-email"]; + logoutExpectation.expectedFulfillmentCount = 1; + + // test that logout clears the observer + [OneSignal logoutEmailWithSuccess:^{ + [logoutExpectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail(@"Encountered error: %@", error); + }]; + + [self waitForExpectations:@[logoutExpectation] timeout:0.1]; + + XCTAssertNil(observer->last.to.emailAddress); + XCTAssertNil(observer->last.to.emailAuthCode); + XCTAssertNil(observer->last.to.emailUserId); + + //reset so we don't interfere with other tests + [OneSignalClientOverrider setRequiresEmailAuth:false]; +} + +//when the user is logged in with email, on_focus requests should be duplicated for the email player id as well +- (void)testOnFocusEmailRequest { + + [UnitTestCommonMethods runBackgroundThreads]; + + [self setupEmailTest]; + + [OneSignalClientOverrider reset:self]; + + NSTimeInterval now = [[NSDate date] timeIntervalSince1970]; + + //if we don't artificially set lastOpenedTime back at least 30 seconds, the on_focus request will not execute + [OneSignalTracker setLastOpenedTime:now - 4000]; + + [OneSignalTracker onFocus:false]; + + [OneSignalTracker setLastOpenedTime:now - 4000]; + + [OneSignalTracker onFocus:true]; + + [UnitTestCommonMethods runBackgroundThreads]; + + XCTAssertTrue([OneSignalClientOverrider.lastHTTPRequestType isEqualToString:NSStringFromClass([OSRequestOnFocus class])]); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + + [OneSignalClientOverrider reset:self]; + + [OneSignalClientOverrider setRequiresEmailAuth:true]; + + let expectation = [self expectationWithDescription:@"email"]; + expectation.expectedFulfillmentCount = 1; + + [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"test-hash-token" withSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail(@"Encountered an error: %@", error); + }]; + + [self waitForExpectations:@[expectation] timeout:0.1]; + + [OneSignalClientOverrider reset:self]; + + //check to make sure request count gets reset to 0 + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 0); + + [OneSignalTracker setLastOpenedTime:now - 4000]; + + [OneSignalTracker onFocus:false]; + + [OneSignalTracker setLastOpenedTime:now - 4000]; + + [OneSignalTracker onFocus:true]; + + [UnitTestCommonMethods runBackgroundThreads]; + + // on_focus should fire off two requests, one for the email player ID and one for push player ID + XCTAssertTrue([OneSignalClientOverrider.lastHTTPRequestType isEqualToString:NSStringFromClass([OSRequestOnFocus class])]); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + + [OneSignalClientOverrider setRequiresEmailAuth:false]; + + [OneSignalClientOverrider reset:self]; +} + +- (void)testRegistration { + // Restart App + [self setupEmailTest]; + + [OneSignalClientOverrider setRequiresEmailAuth:true]; + + let expectation = [self expectationWithDescription:@"email"]; + expectation.expectedFulfillmentCount = 1; + + [OneSignal setEmail:@"test@test.com" withEmailAuthHashToken:@"test-hash-token" withSuccess:^{ + [expectation fulfill]; + } withFailure:^(NSError *error) { + XCTFail(@"Encountered an error: %@", error); + }]; + + [self waitForExpectations:@[expectation] timeout:0.1]; + + //reset network request count back to zero + [OneSignalClientOverrider reset:self]; + + //set this flag to true so that registerUserInternal() actually executes + [OneSignal setNextRegistrationHighPriority:true]; + + [OneSignal registerUserInternal]; + [UnitTestCommonMethods runBackgroundThreads]; + + //should make two requests (one for email player ID, one for push) + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); + XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"email_auth_hash"], @"test-hash-token"); + + [OneSignalClientOverrider setRequiresEmailAuth:false]; + + [OneSignalClientOverrider reset:self]; +} + +@end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.h b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.h index 7aa98d223..01f8b511f 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.h +++ b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.h @@ -19,5 +19,8 @@ +(NSString*)lastUrl; +(void)setShouldExecuteInstantaneously:(BOOL)instant; + (dispatch_queue_t)getHTTPQueue; ++(void)runBackgroundThreads; ++(NSString *)lastHTTPRequestType; ++(void)setRequiresEmailAuth:(BOOL)required; @end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m index 092e5664e..b60731a17 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalClientOverrider.m @@ -22,12 +22,12 @@ #import "OneSignalClient.h" #import "OneSignalRequest.h" #import "OneSignalSelectorHelpers.h" +#import "Requests.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" @implementation OneSignalClientOverrider - static dispatch_queue_t serialMockMainLooper; static NSString* lastUrl; static int networkRequestCount; @@ -35,6 +35,8 @@ @implementation OneSignalClientOverrider static XCTestCase* currentTestInstance; static BOOL executeInstantaneously = true; static dispatch_queue_t executionQueue; +static NSString *lastHTTPRequestType; +static BOOL requiresEmailAuth = false; + (void)load { serialMockMainLooper = dispatch_queue_create("com.onesignal.unittest", DISPATCH_QUEUE_SERIAL); @@ -42,12 +44,41 @@ + (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 +89,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 isKindOfClass:[OSRequestGetIosParams class]]) + successBlock(@{@"fba": @true, @"require_email_auth" : @(requiresEmailAuth)}); + else + successBlock(@{@"id": @"1234"}); + } } } @@ -86,6 +120,10 @@ +(dispatch_queue_t)getHTTPQueue { return executionQueue; } ++ (NSString *)lastHTTPRequestType { + return lastHTTPRequestType; +} + +(void)setShouldExecuteInstantaneously:(BOOL)instant { executeInstantaneously = instant; } @@ -117,5 +155,13 @@ +(NSString*)lastUrl { return lastUrl; } ++(void)runBackgroundThreads { + dispatch_sync(executionQueue, ^{}); +} + ++(void)setRequiresEmailAuth:(BOOL)required { + requiresEmailAuth = required; +} + @end 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; } + + diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h new file mode 100644 index 000000000..318357452 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h @@ -0,0 +1,47 @@ +// +// UnitTestCommonMethods.h +// UnitTests +// +// Created by Brad Hesse on 1/30/18. +// Copyright © 2018 Hiptic. All rights reserved. +// + +#import +#import +#import "OneSignal.h" + +@interface UnitTestCommonMethods : NSObject + ++ (void)setCurrentNotificationPermissionAsUnanswered; ++ (void)resumeApp; ++ (void)initOneSignal; ++ (void)runBackgroundThreads; ++ (void)beforeAllTest; ++ (void)clearStateForAppRestart:(XCTestCase *)testCase; + +@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 + +@interface OSEmailSubscriptionStateTestObserver : NSObject { + @package OSEmailSubscriptionStateChanges *last; + @package int fireCount; +} +- (void)onOSEmailSubscriptionChanged:(OSEmailSubscriptionStateChanges *)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..37f15e6cb --- /dev/null +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m @@ -0,0 +1,160 @@ +// +// 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" +#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; +@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"); +} + ++ (void)clearStateForAppRestart:(XCTestCase *)testCase { + NSLog(@"======= APP RESTART ======\n\n"); + + NSDateOverrider.timeOffset = 0; + + [OneSignalClientOverrider reset:testCase]; + [UNUserNotificationCenterOverrider reset:testCase]; + [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 + + +@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 + +@implementation OSEmailSubscriptionStateTestObserver +- (void)onOSEmailSubscriptionChanged:(OSEmailSubscriptionStateChanges *)stateChanges { + NSLog(@"UnitTest:onOSEmailSubscriptionChanged: \n%@", stateChanges); + last = stateChanges; + fireCount++; +} +@end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m index d14837191..bc6e97544 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 @@ -73,45 +75,12 @@ #import "OneSignalClient.h" #import "Requests.h" #import "OneSignalClientOverrider.h" +#import "OneSignalCommonDefines.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++; +NSString * serverUrlWithPath(NSString *path) { + return [NSString stringWithFormat:@"%@%@%@", SERVER_URL, API_VERSION, path]; } -@end - -// END - Observers - @interface UnitTests : XCTestCase @@ -119,53 +88,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]; @@ -181,9 +103,9 @@ - (void)setUp { [NSUserDefaultsOverrider clearInternalDictionary]; - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; - [self beforeAllTest]; + [UnitTestCommonMethods beforeAllTest]; // Uncomment to simulate slow travis-CI runs. /*float minRange = 0, maxRange = 15; @@ -195,18 +117,13 @@ - (void)setUp { // Called after each test. - (void)tearDown { [super tearDown]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; } - (void)backgroundModesDisabledInXcode { NSBundleOverrider.nsbundleDictionary = @{}; } -- (void)setCurrentNotificationPermissionAsUnanswered { - UNUserNotificationCenterOverrider.notifTypesOverride = 0; - UNUserNotificationCenterOverrider.authorizationStatus = [NSNumber numberWithInteger:UNAuthorizationStatusNotDetermined]; -} - - (void)setCurrentNotificationPermission:(BOOL)accepted { if (accepted) { UNUserNotificationCenterOverrider.notifTypesOverride = 7; @@ -236,7 +153,7 @@ - (void)answerNotifiationPrompt:(BOOL)accept { if (triggerDidRegisterForRemoteNotfications) [self setCurrentNotificationPermission:false]; - [self resumeApp]; + [UnitTestCommonMethods resumeApp]; [self setCurrentNotificationPermission:accept]; if (triggerDidRegisterForRemoteNotfications && NSBundleOverrider.nsbundleDictionary[@"UIBackgroundModes"]) @@ -259,46 +176,6 @@ - (void)backgroundApp { [sharedApp.delegate applicationWillResignActive:sharedApp]; } -- (void)resumeApp { - UIApplicationOverrider.currentUIApplicationState = UIApplicationStateActive; - UIApplication *sharedApp = [UIApplication sharedApplication]; - [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, httpQueue; - 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, ^{}); - - httpQueue = [OneSignalClientOverrider getHTTPQueue]; - if (httpQueue) - dispatch_sync(httpQueue, ^{}); - - [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 @@ -323,34 +200,32 @@ - (UNNotificationResponse*)createBasiciOSNotificationResponseWithPayload:(NSDict - (UNNotificationResponse*)createBasiciOSNotificationResponse { id userInfo = @{@"custom": - @{@"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb"} + @{@"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"} }; 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]; - [self runBackgroundThreads]; + [UnitTestCommonMethods initOneSignal]; + [UnitTestCommonMethods runBackgroundThreads]; } - (void)testBasicInitTest { + [UnitTestCommonMethods clearStateForAppRestart:self]; + NSLog(@"iOS VERSION: %@", [[UIDevice currentDevice] systemVersion]); - [self initOneSignal]; - [self runBackgroundThreads]; + [UnitTestCommonMethods initOneSignal]; + [UnitTestCommonMethods 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,17 +235,25 @@ - (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"); XCTAssertEqualObjects(status.subscriptionStatus.pushToken, @"0000000000000000000000000000000000000000000000000000000000000000"); + //email has not been set so the email properties should be nil + XCTAssertFalse(status.emailSubscriptionStatus.subscribed); + XCTAssertNil(status.emailSubscriptionStatus.emailUserId); + XCTAssertNil(status.emailSubscriptionStatus.emailAddress); + // 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, 2); + + } - (void)testVersionStringLength { @@ -403,6 +286,8 @@ - (void)testSymanticVersioning { } - (void)testRegisterationOniOS7 { + [UnitTestCommonMethods clearStateForAppRestart:self]; + OneSignalHelperOverrider.mockIOSVersion = 7; [self initOneSignalAndThreadWait]; @@ -417,9 +302,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, 2); // Make the following methods were not called as they are not available on iOS 7 XCTAssertFalse(UIApplicationOverrider.calledRegisterForRemoteNotifications); @@ -434,14 +318,16 @@ - (void)testInitWithEmptyPreferredLanguages { } - (void)testInitOnSimulator { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods clearStateForAppRestart:self]; + + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [self backgroundModesDisabledInXcode]; UIApplicationOverrider.didFailRegistarationErrorCode = 3010; [self initOneSignalAndThreadWait]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest[@"identifier"]); @@ -453,9 +339,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, 2); } @@ -467,7 +352,7 @@ - (void)testFocusSettingsOnInit { XCTAssertEqual(OneSignal.inFocusDisplayType, OSNotificationDisplayTypeNone); - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; // Test old very old kOSSettingsKeyInAppAlerts [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" @@ -483,24 +368,24 @@ - (void)testCallingMethodsBeforeInit { [OneSignal setSubscription:true]; [OneSignal promptLocation]; [OneSignal promptForPushNotificationsWithUserResponse:nil]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [self initOneSignalAndThreadWait]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key"], @"value"); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [OneSignal sendTag:@"key" value:@"value"]; [OneSignal setSubscription:true]; [OneSignal promptLocation]; [OneSignal promptForPushNotificationsWithUserResponse:nil]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [self initOneSignalAndThreadWait]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 0); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); } - (void)testPermissionChangeObserverIOS10 { @@ -511,13 +396,14 @@ - (void)testPermissionChangeObserverIOS8 { OneSignalHelperOverrider.mockIOSVersion = 8; [self sharedTestPermissionChangeObserver]; } + - (void)testPermissionChangeObserverIOS7 { OneSignalHelperOverrider.mockIOSVersion = 7; [self sharedTestPermissionChangeObserver]; } - (void)sharedTestPermissionChangeObserver { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -526,7 +412,7 @@ - (void)sharedTestPermissionChangeObserver { [OneSignal addPermissionObserver:observer]; [self registerForPushNotifications]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.hasPrompted, false); XCTAssertEqual(observer->last.from.answeredPrompt, false); @@ -535,7 +421,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); @@ -553,7 +439,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); @@ -569,14 +455,14 @@ - (void)testPermissionChangeObserverFireAfterAppRestart { [OneSignal addPermissionObserver:observer]; // User kills app, turns off notifications, then opnes it agian. - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [self setCurrentNotificationPermission:false]; [self initOneSignalAndThreadWait]; // 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); @@ -596,7 +482,7 @@ - (void)testPermissionObserverDontFireIfNothingChangedAfterAppRestartiOS7 { [self sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart]; } - (void)sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; OSPermissionStateTestObserver* observer = [OSPermissionStateTestObserver new]; [OneSignal addPermissionObserver:observer]; @@ -604,14 +490,14 @@ - (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]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil @@ -620,28 +506,25 @@ - (void)sharedPermissionObserverDontFireIfNothingChangedAfterAppRestart { observer = [OSPermissionStateTestObserver new]; [OneSignal addPermissionObserver:observer]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertNil(observer->last); } - - - - (void)testPermissionChangeObserverDontLoseFromChanges { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [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); @@ -654,7 +537,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); @@ -669,14 +552,14 @@ - (void)testSubscriptionChangeObserverFireAfterAppRestart { // User kills app, turns off notifications, then opnes it agian. - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [self setCurrentNotificationPermission:false]; [self initOneSignalAndThreadWait]; // 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); @@ -684,7 +567,7 @@ - (void)testSubscriptionChangeObserverFireAfterAppRestart { - (void)testPermissionChangeObserverWithNativeiOS10PromptCall { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -696,14 +579,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); @@ -716,7 +599,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}]; @@ -730,7 +613,7 @@ - (void)testTestPermissionChangeObserverWithNativeiOS10PromptCall { [self backgroundApp]; // Full bug details explained in answerNotifiationPrompt [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->fireCount, 3); @@ -739,7 +622,7 @@ - (void)testTestPermissionChangeObserverWithNativeiOS10PromptCall { } - (void)testPermissionChangeObserverWithDecline { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -748,7 +631,7 @@ - (void)testPermissionChangeObserverWithDecline { [OneSignal addPermissionObserver:observer]; [self registerForPushNotifications]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(observer->last.from.hasPrompted, false); XCTAssertEqual(observer->last.from.answeredPrompt, false); @@ -757,7 +640,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); @@ -767,7 +650,7 @@ - (void)testPermissionChangeObserverWithDecline { - (void)testPermissionAndSubscriptionChangeObserverRemove { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [self backgroundModesDisabledInXcode]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil @@ -783,14 +666,14 @@ - (void)testPermissionAndSubscriptionChangeObserverRemove { [self registerForPushNotifications]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertNil(permissionObserver->last); XCTAssertNil(subscriptionObserver->last); } - (void)testSubscriptionChangeObserverBasic { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -800,23 +683,20 @@ - (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); - - XCTAssertEqualObjects([observer->last description], @",\nto: \n>"); - NSLog(@"Test description: %@", observer->last); } - (void)testSubscriptionChangeObserverWhenPromptNotShown { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -825,16 +705,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); @@ -847,7 +727,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); @@ -855,7 +735,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); } @@ -863,23 +743,25 @@ - (void)testSubscriptionChangeObserverWhenPromptNotShown { - (void)testInitAcceptingNotificationsWithoutCapabilitesSet { [self backgroundModesDisabledInXcode]; UIApplicationOverrider.didFailRegistarationErrorCode = 3000; - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); + [UnitTestCommonMethods runBackgroundThreads]; [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); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); } - (void)testPromptForPushNotificationsWithUserResponse { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; __block BOOL didAccept; [OneSignal promptForPushNotificationsWithUserResponse:^(BOOL accepted) { @@ -887,15 +769,15 @@ - (void)testPromptForPushNotificationsWithUserResponse { }]; [self backgroundApp]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(didAccept); } - (void)testPromptForPushNotificationsWithUserResponseOnIOS8 { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; OneSignalHelperOverrider.mockIOSVersion = 8; - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; __block BOOL didAccept; [OneSignal promptForPushNotificationsWithUserResponse:^(BOOL accepted) { @@ -903,15 +785,15 @@ - (void)testPromptForPushNotificationsWithUserResponseOnIOS8 { }]; [self backgroundApp]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(didAccept); } - (void)testPromptForPushNotificationsWithUserResponseOnIOS7 { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; OneSignalHelperOverrider.mockIOSVersion = 7; - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; __block BOOL didAccept; [OneSignal promptForPushNotificationsWithUserResponse:^(BOOL accepted) { @@ -919,13 +801,13 @@ - (void)testPromptForPushNotificationsWithUserResponseOnIOS7 { }]; [self backgroundApp]; [self answerNotifiationPrompt:true]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(didAccept); } - (void)testPromptedButNeveranswerNotificationPrompt { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [self initOneSignalAndThreadWait]; @@ -934,7 +816,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); @@ -949,49 +831,49 @@ - (void)testNotificationTypesWhenAlreadyAcceptedWithAutoPromptOffOnFristStartPre handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @7); } - (void)testNeverPromptedStatus { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" 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); } - (void)testNotAcceptingNotificationsWithoutBackgroundModes { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [self backgroundModesDisabledInXcode]; - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; // Don't make a network call right away. XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); [self answerNotifiationPrompt:false]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; - XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/players"); + XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, serverUrlWithPath(@"players")); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest[@"identifier"]); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @0); } - (void)testIdsAvailableNotAcceptingNotifications { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; @@ -1001,13 +883,13 @@ - (void)testIdsAvailableNotAcceptingNotifications { idsAvailable1Called = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [self registerForPushNotifications]; [self answerNotifiationPrompt:false]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(idsAvailable1Called); @@ -1020,7 +902,7 @@ - (void)testIdsAvailableNotAcceptingNotifications { idsAvailable2Called = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(idsAvailable2Called); } @@ -1034,7 +916,7 @@ - (void)testNotificationOpen { XCTAssertNil(result.action.actionID); openedWasFire = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; id notifResponse = [self createBasiciOSNotificationResponse]; UNUserNotificationCenter *notifCenter = [UNUserNotificationCenter currentNotificationCenter]; @@ -1044,7 +926,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, serverUrlWithPath(@"notifications/b2f7f966-d8cc-11e4-bed1-df8f05be55ba")); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"opened"], @1); @@ -1055,13 +937,13 @@ - (void)testNotificationOpen { XCTAssertNil(OneSignalClientOverrider.lastUrl); XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); } - (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 +967,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 +991,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); @@ -1117,8 +999,8 @@ - (void)testFirebaseAnalyticsInfluenceNotificationOpen { // Trigger a new app session [self backgroundApp]; NSDateOverrider.timeOffset = 41; - [self resumeApp]; - [self runBackgroundThreads]; + [UnitTestCommonMethods resumeApp]; + [UnitTestCommonMethods runBackgroundThreads]; // Since we opened the app under 2 mintues after receiving a notification // an influence_open should be sent to firebase. @@ -1127,7 +1009,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); @@ -1152,13 +1034,13 @@ - (void)testOSNotificationPayloadParsesTemplateFields { - (void)testNotificationOpenOn2ndColdStartWithoutAppId { [self initOneSignalAndThreadWait]; - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; __block BOOL openedWasFire = false; [OneSignal initWithLaunchOptions:nil appId:nil handleNotificationAction:^(OSNotificationOpenedResult *result) { openedWasFire = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; id notifResponse = [self createBasiciOSNotificationResponse]; UNUserNotificationCenter *notifCenter = [UNUserNotificationCenter currentNotificationCenter]; @@ -1179,14 +1061,14 @@ - (void)testNotificationOpenFromButtonPress { XCTAssertEqualObjects(result.action.actionID, @"id1"); openedWasFire = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; UIApplicationOverrider.currentUIApplicationState = UIApplicationStateInactive; id userInfo = @{@"aps": @{@"content_available": @1}, @"m": @"alert body only", @"o": @[@{@"i": @"id1", @"n": @"text1"}], @"custom": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb" + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" } }; @@ -1201,7 +1083,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, serverUrlWithPath(@"notifications/b2f7f966-d8cc-11e4-bed1-df8f05be55ba")); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"opened"], @1); @@ -1212,7 +1094,7 @@ - (void)testNotificationOpenFromButtonPress { XCTAssertNil(OneSignalClientOverrider.lastUrl); XCTAssertNil(OneSignalClientOverrider.lastHTTPRequest); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); } @@ -1226,7 +1108,7 @@ - (void)testNotificationOpenFromButtonPressWithNewformat { XCTAssertEqualObjects(result.action.actionID, @"id1"); openedWasFire = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; UIApplicationOverrider.currentUIApplicationState = UIApplicationStateInactive; id userInfo = @{@"aps": @{ @@ -1234,7 +1116,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 +1131,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, serverUrlWithPath(@"notifications/b2f7f966-d8cc-11e4-bed1-df8f05be55ba")); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"app_id"], @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"opened"], @1); @@ -1260,7 +1142,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 @@ -1278,8 +1160,8 @@ - (void)notificationAlertButtonsDisplayWithFormat:(NSDictionary *)userInfo { [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:receiveBlock]; - [self resumeApp]; - [self runBackgroundThreads]; + [UnitTestCommonMethods resumeApp]; + [UnitTestCommonMethods runBackgroundThreads]; id notifResponse = [self createBasiciOSNotificationResponseWithPayload:userInfo]; [notifResponse setValue:@"id1" forKeyPath:@"actionIdentifier"]; @@ -1335,10 +1217,10 @@ - (void)testOpeningWithAdditionalData { openedWasFire = true; }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; id userInfo = @{@"custom": @{ - @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55bb", + @"i": @"b2f7f966-d8cc-11e4-bed1-df8f05be55ba", @"a": @{ @"foo": @"bar" } }}; @@ -1380,7 +1262,7 @@ - (void)receivedCallbackWithButtonsWithUserInfo:(NSDictionary *)userInfo { } handleNotificationAction:nil settings:nil]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; let notifResponse = [self createBasiciOSNotificationResponseWithPayload:userInfo]; UNUserNotificationCenter *notifCenter = [UNUserNotificationCenter currentNotificationCenter]; @@ -1397,7 +1279,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 +1341,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 +1357,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" } }; @@ -1486,7 +1368,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"]; @@ -1494,46 +1376,45 @@ - (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"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key1"], @"value1"); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key2"], @"value2"); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); - // 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]; + [UnitTestCommonMethods runBackgroundThreads]; [NSObjectOverrider runPendingSelectors]; - [self runBackgroundThreads]; + [UnitTestCommonMethods 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(OneSignalClientOverrider.networkRequestCount, 4); - XCTAssertEqual(didRunSuccess1, true); - XCTAssertEqual(didRunSuccess2, true); - XCTAssertEqual(didRunSuccess3, true); } - (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. @@ -1544,12 +1425,12 @@ - (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"]); XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"tags"][@"key2"], @"value2"); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); [OneSignal sendTags:@{@"someKey": @NO}]; [OneSignal deleteTag:@"someKey"]; @@ -1557,7 +1438,7 @@ - (void)testDeleteTags { - (void)testGetTags { [self initOneSignalAndThreadWait]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); __block BOOL fireGetTags = false; @@ -1568,7 +1449,7 @@ - (void)testGetTags { NSLog(@"getTags onFailure HERE"); }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(fireGetTags); } @@ -1576,7 +1457,7 @@ - (void)testGetTags { - (void)testGetTagsBeforePlayerId { [self initOneSignalAndThreadWait]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); __block BOOL fireGetTags = false; @@ -1587,14 +1468,14 @@ - (void)testGetTagsBeforePlayerId { NSLog(@"getTags onFailure HERE"); }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(fireGetTags); } - (void)testGetTagsWithNestedDelete { - [self initOneSignal]; + [UnitTestCommonMethods initOneSignal]; __block BOOL fireDeleteTags = false; @@ -1611,34 +1492,34 @@ - (void)testGetTagsWithNestedDelete { }]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; [NSObjectOverrider runPendingSelectors]; // create, ge tags, then sendTags call. - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 4); XCTAssertTrue(fireDeleteTags); } - (void)testSendTagsBeforeRegisterComplete { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [self initOneSignalAndThreadWait]; 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); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); [self answerNotifiationPrompt:false]; - [self runBackgroundThreads]; + [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"); @@ -1646,18 +1527,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"); } @@ -1671,11 +1552,11 @@ - (void)testFirstInitWithNotificationsAlreadyDeclined { [self initOneSignalAndThreadWait]; XCTAssertEqualObjects(OneSignalClientOverrider.lastHTTPRequest[@"notification_types"], @0); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); } - (void)testPermissionChangedInSettingsOutsideOfApp { - [self clearStateForAppRestart]; + [UnitTestCommonMethods clearStateForAppRestart:self]; [self backgroundModesDisabledInXcode]; UNUserNotificationCenterOverrider.notifTypesOverride = 0; @@ -1692,12 +1573,12 @@ - (void)testPermissionChangedInSettingsOutsideOfApp { [self backgroundApp]; [self setCurrentNotificationPermission:true]; - [self resumeApp]; - [self runBackgroundThreads]; + [UnitTestCommonMethods resumeApp]; + [UnitTestCommonMethods runBackgroundThreads]; 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); @@ -1709,18 +1590,18 @@ - (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]; - [self runBackgroundThreads]; - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); + [UnitTestCommonMethods resumeApp]; + [UnitTestCommonMethods runBackgroundThreads]; + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); // Anything over 30 secounds should count as a session. [self backgroundApp]; NSDateOverrider.timeOffset = 41; - [self resumeApp]; - [self runBackgroundThreads]; + [UnitTestCommonMethods resumeApp]; + [UnitTestCommonMethods runBackgroundThreads]; - XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, @"https://onesignal.com/api/v1/players/1234/on_session"); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 2); + XCTAssertEqualObjects(OneSignalClientOverrider.lastUrl, serverUrlWithPath(@"players/1234/on_session")); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 3); } // Tests that a slient content-available 1 notification doesn't trigger an on_session or count it has opened. @@ -1733,7 +1614,7 @@ - (void)testContentAvailableDoesNotTriggerOpen { } handleNotificationAction:nil settings:nil]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; id userInfo = @{@"aps": @{@"content_available": @1}, @"custom": @{ @@ -1742,10 +1623,10 @@ - (void)testContentAvailableDoesNotTriggerOpen { }; [self fireDidReceiveRemoteNotification:userInfo]; - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertEqual(receivedWasFire, true); - XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 0); + XCTAssertEqual(OneSignalClientOverrider.networkRequestCount, 1); } -(UNNotificationCategory*)unNotificagionCategoryWithId:(NSString*)identifier { @@ -1778,7 +1659,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 +1690,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 +1715,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 +1735,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,15 +1752,15 @@ - (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] withEmailAuthHashToken:nil]; - 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"]); let urlRequest = request.request; - XCTAssert([urlRequest.URL.absoluteString isEqualToString:@"https://onesignal.com/api/v1/players/12345"]); + XCTAssert([urlRequest.URL.absoluteString isEqualToString:serverUrlWithPath(@"players/12345")]); XCTAssert([urlRequest.HTTPMethod isEqualToString:@"PUT"]); XCTAssert([urlRequest.allHTTPHeaderFields[@"Content-Type"] isEqualToString:@"application/json"]); } @@ -1891,7 +1772,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] withEmailAuthHashToken:nil]; let urlRequest = request.request; @@ -1901,7 +1782,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 @@ -1916,27 +1797,27 @@ -(void)testInvalidJSONTags { */ -(void)testDelayedSubscriptionUpdate { - [self setCurrentNotificationPermissionAsUnanswered]; + [UnitTestCommonMethods setCurrentNotificationPermissionAsUnanswered]; [OneSignal initWithLaunchOptions:nil appId:@"b2f7f966-d8cc-11e4-bed1-df8f05be55ba" handleNotificationAction:nil settings:@{kOSSettingsKeyAutoPrompt: @false}]; 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); @@ -1949,11 +1830,9 @@ -(void)testDelayedSubscriptionUpdate { [OneSignalClientOverrider setShouldExecuteInstantaneously:true]; XCTAssertFalse(observer->last.to.subscribed); - [self runBackgroundThreads]; + [UnitTestCommonMethods runBackgroundThreads]; XCTAssertTrue(observer->last.to.subscribed); - - } @end