From 32e4ace647ccf01554cb40f75d0f4a5ad747e714 Mon Sep 17 00:00:00 2001 From: emawby Date: Wed, 3 Mar 2021 14:05:59 -0800 Subject: [PATCH 1/4] saving OS_IAM_REDISPLAY_DICTIONARY as a codeable OS_IAM_REDISPLAY_DICTIONARY contains OSInAppMessage objects so we can't save it as a dictionary to userdefaults or we will crash. --- iOS_SDK/OneSignalSDK/Source/OSMessagingController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m b/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m index 6efbeda4e..30464bc30 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m +++ b/iOS_SDK/OneSignalSDK/Source/OSMessagingController.m @@ -204,7 +204,7 @@ - (void)deleteOldRedisplayedInAppMessages { [newRedisplayDictionary removeObjectForKey:messageId]; } - [OneSignalUserDefaults.initStandard saveDictionaryForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:newRedisplayDictionary]; + [OneSignalUserDefaults.initStandard saveCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:newRedisplayDictionary]; } } From 285115f7094deeb18c2b87c7232a4078305f17a7 Mon Sep 17 00:00:00 2001 From: emawby Date: Wed, 3 Mar 2021 15:30:44 -0800 Subject: [PATCH 2/4] Removing NSUserDefaultsOverrider Now that our unit tests are run inside a test app we no longer need to mock NSUserDefaults. This gets us detection of bad storage like trying to store a custom object in a dictionary instead of as a codeable. This commit also fixes the crash in InAppMessagingIntegrationTests by changing save/getDictionary to save/getCodeableData --- .../OneSignal.xcodeproj/project.pbxproj | 6 - iOS_SDK/OneSignalSDK/UnitTests/BadgeTests.m | 3 +- iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m | 1 - .../InAppMessagingIntegrationTests.m | 7 +- .../UnitTests/OutcomeIntegrationTests.m | 1 - .../UnitTests/OutcomeIntegrationV2Tests.m | 1 - .../UnitTests/ProvisionalAuthorizationTests.m | 1 - .../Shadows/NSUserDefaultsOverrider.h | 32 ---- .../Shadows/NSUserDefaultsOverrider.m | 142 ------------------ .../UnitTests/UnitTestCommonMethods.h | 1 + .../UnitTests/UnitTestCommonMethods.m | 20 ++- iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m | 1 - 12 files changed, 22 insertions(+), 194 deletions(-) delete mode 100644 iOS_SDK/OneSignalSDK/UnitTests/Shadows/NSUserDefaultsOverrider.h delete mode 100644 iOS_SDK/OneSignalSDK/UnitTests/Shadows/NSUserDefaultsOverrider.m diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index 4cdfd370f..e6fe1df23 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -61,7 +61,6 @@ 3E66F5821D90A2C600E45A01 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E08E2701D49A5C8002176DE /* SystemConfiguration.framework */; }; 4529DED21FA81EA800CEAB1D /* NSObjectOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = 4529DED11FA81EA800CEAB1D /* NSObjectOverrider.m */; }; 4529DED51FA823B900CEAB1D /* TestHelperFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4529DED41FA823B900CEAB1D /* TestHelperFunctions.m */; }; - 4529DED81FA8253D00CEAB1D /* NSUserDefaultsOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = 4529DED71FA8253D00CEAB1D /* NSUserDefaultsOverrider.m */; }; 4529DEDB1FA8284E00CEAB1D /* NSDataOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = 4529DEDA1FA8284E00CEAB1D /* NSDataOverrider.m */; }; 4529DEDE1FA828E500CEAB1D /* NSDateOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = 4529DEDD1FA828E500CEAB1D /* NSDateOverrider.m */; }; 4529DEE11FA82AB300CEAB1D /* NSBundleOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = 4529DEE01FA82AB300CEAB1D /* NSBundleOverrider.m */; }; @@ -521,8 +520,6 @@ 4529DED11FA81EA800CEAB1D /* NSObjectOverrider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSObjectOverrider.m; sourceTree = ""; }; 4529DED31FA823B900CEAB1D /* TestHelperFunctions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestHelperFunctions.h; sourceTree = ""; }; 4529DED41FA823B900CEAB1D /* TestHelperFunctions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestHelperFunctions.m; sourceTree = ""; }; - 4529DED61FA8253D00CEAB1D /* NSUserDefaultsOverrider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSUserDefaultsOverrider.h; sourceTree = ""; }; - 4529DED71FA8253D00CEAB1D /* NSUserDefaultsOverrider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSUserDefaultsOverrider.m; sourceTree = ""; }; 4529DED91FA8284E00CEAB1D /* NSDataOverrider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSDataOverrider.h; sourceTree = ""; }; 4529DEDA1FA8284E00CEAB1D /* NSDataOverrider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSDataOverrider.m; sourceTree = ""; }; 4529DEDC1FA828E500CEAB1D /* NSDateOverrider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSDateOverrider.h; sourceTree = ""; }; @@ -923,8 +920,6 @@ 4529DED11FA81EA800CEAB1D /* NSObjectOverrider.m */, 4529DEE21FA82C6200CEAB1D /* NSURLSessionOverrider.h */, 4529DEE31FA82C6200CEAB1D /* NSURLSessionOverrider.m */, - 4529DED61FA8253D00CEAB1D /* NSUserDefaultsOverrider.h */, - 4529DED71FA8253D00CEAB1D /* NSUserDefaultsOverrider.m */, 4529DEEB1FA83C5D00CEAB1D /* OneSignalHelperOverrider.h */, 4529DEEC1FA83C5D00CEAB1D /* OneSignalHelperOverrider.m */, CA08FC821FE99BB4004C445F /* OneSignalClientOverrider.h */, @@ -1948,7 +1943,6 @@ CA63AF8720211FF800E340FB /* UnitTestCommonMethods.m in Sources */, CA70E3372023D51300019273 /* OneSignalSetEmailParameters.m in Sources */, 7A5A8185248990CD002E07C8 /* OSIndirectNotification.m in Sources */, - 4529DED81FA8253D00CEAB1D /* NSUserDefaultsOverrider.m in Sources */, 4529DEED1FA83C5D00CEAB1D /* OneSignalHelperOverrider.m in Sources */, 7AF98666244975AD00C36EAE /* OSOutcomeSourceBody.m in Sources */, CA1A6E6C20DC2E31001C41B9 /* OneSignalDialogController.m in Sources */, diff --git a/iOS_SDK/OneSignalSDK/UnitTests/BadgeTests.m b/iOS_SDK/OneSignalSDK/UnitTests/BadgeTests.m index bf5775bf3..75ea7c3ce 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/BadgeTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/BadgeTests.m @@ -28,7 +28,6 @@ of this software and associated documentation files (the "Software"), to deal #import #import "UnitTestCommonMethods.h" #import "OneSignalExtensionBadgeHandler.h" -#import "NSUserDefaultsOverrider.h" #import "UNUserNotificationCenterOverrider.h" #import "UNUserNotificationCenter+OneSignal.h" #import "OneSignalHelperOverrider.h" @@ -52,7 +51,7 @@ - (void)setUp { UNUserNotificationCenterOverrider.authorizationStatus = [NSNumber numberWithInteger:UNAuthorizationStatusAuthorized]; - [NSUserDefaultsOverrider clearInternalDictionary]; + [UnitTestCommonMethods clearUserDefaults]; } /* diff --git a/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m index 758d9328b..43a8f41b1 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/EmailTests.m @@ -41,7 +41,6 @@ of this software and associated documentation files (the "Software"), to deal #import "UNUserNotificationCenterOverrider.h" #import "UNUserNotificationCenter+OneSignal.h" #import "NSBundleOverrider.h" -#import "NSUserDefaultsOverrider.h" #import "OneSignalCommonDefines.h" #import "OneSignalTracker.h" #import "OneSignalInternal.h" diff --git a/iOS_SDK/OneSignalSDK/UnitTests/InAppMessagingIntegrationTests.m b/iOS_SDK/OneSignalSDK/UnitTests/InAppMessagingIntegrationTests.m index d8101e617..4da14b507 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/InAppMessagingIntegrationTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/InAppMessagingIntegrationTests.m @@ -44,7 +44,6 @@ of this software and associated documentation files (the "Software"), to deal #import "UIApplicationOverrider.h" #import "OneSignalHelperOverrider.h" #import "UNUserNotificationCenterOverrider.h" -#import "NSUserDefaultsOverrider.h" #import "NSBundleOverrider.h" #import "UNUserNotificationCenter+OneSignal.h" #import "Requests.h" @@ -544,7 +543,7 @@ - (void)testIAMWithNoTriggersDisplayOnePerSession_Redisplay { message.displayStats.lastDisplayTime = firstInterval - delay; // Save IAM for redisplay - [OneSignalUserDefaults.initStandard saveDictionaryForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:redisplayedInAppMessages]; + [OneSignalUserDefaults.initStandard saveCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:redisplayedInAppMessages]; // Set data for redisplay [OSMessagingControllerOverrider setMessagesForRedisplay:redisplayedInAppMessages]; // Save IAM for dismiss @@ -680,11 +679,11 @@ - (void)testIAMRemoveFromCache_Redisplay { [redisplayedInAppMessages setObject:message2 forKey:message2.messageId]; [OSMessagingControllerOverrider setMessagesForRedisplay:redisplayedInAppMessages]; - [standardUserDefaults saveDictionaryForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:redisplayedInAppMessages]; + [standardUserDefaults saveCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:redisplayedInAppMessages]; [self initOneSignalWithInAppMessage:message]; - let redisplayMessagesCache = [standardUserDefaults getSavedDictionaryForKey:OS_IAM_REDISPLAY_DICTIONARY defaultValue:nil]; + NSMutableDictionary *redisplayMessagesCache = [standardUserDefaults getSavedCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY defaultValue:nil]; XCTAssertTrue([redisplayMessagesCache objectForKey:message1.messageId]); XCTAssertFalse([redisplayMessagesCache objectForKey:message2.messageId]); } diff --git a/iOS_SDK/OneSignalSDK/UnitTests/OutcomeIntegrationTests.m b/iOS_SDK/OneSignalSDK/UnitTests/OutcomeIntegrationTests.m index e840eb2cb..d5ab77259 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/OutcomeIntegrationTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/OutcomeIntegrationTests.m @@ -40,7 +40,6 @@ of this software and associated documentation files (the "Software"), to deal #import "UNUserNotificationCenterOverrider.h" #import "RestClientAsserts.h" #import "CommonAsserts.h" -#import "NSUserDefaultsOverrider.h" #import "OneSignalClientOverrider.h" #import "UIApplicationOverrider.h" #import "OneSignalNotificationServiceExtensionHandler.h" diff --git a/iOS_SDK/OneSignalSDK/UnitTests/OutcomeIntegrationV2Tests.m b/iOS_SDK/OneSignalSDK/UnitTests/OutcomeIntegrationV2Tests.m index 8d5aa567e..0bd154f13 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/OutcomeIntegrationV2Tests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/OutcomeIntegrationV2Tests.m @@ -41,7 +41,6 @@ #import "UNUserNotificationCenterOverrider.h" #import "RestClientAsserts.h" #import "CommonAsserts.h" -#import "NSUserDefaultsOverrider.h" #import "OneSignalClientOverrider.h" #import "UIApplicationOverrider.h" #import "OneSignalNotificationServiceExtensionHandler.h" diff --git a/iOS_SDK/OneSignalSDK/UnitTests/ProvisionalAuthorizationTests.m b/iOS_SDK/OneSignalSDK/UnitTests/ProvisionalAuthorizationTests.m index 09f666349..56f170fc4 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/ProvisionalAuthorizationTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/ProvisionalAuthorizationTests.m @@ -28,7 +28,6 @@ of this software and associated documentation files (the "Software"), to deal #import #import "UnitTestCommonMethods.h" #import "OneSignalExtensionBadgeHandler.h" -#import "NSUserDefaultsOverrider.h" #import "UNUserNotificationCenterOverrider.h" #import "UNUserNotificationCenter+OneSignal.h" #import "OneSignalHelperOverrider.h" diff --git a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/NSUserDefaultsOverrider.h b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/NSUserDefaultsOverrider.h deleted file mode 100644 index 6df6160fb..000000000 --- a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/NSUserDefaultsOverrider.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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 NSUserDefaultsOverrider : NSObject -+ (void)clearInternalDictionary; -@end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/NSUserDefaultsOverrider.m b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/NSUserDefaultsOverrider.m deleted file mode 100644 index b16c611d7..000000000 --- a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/NSUserDefaultsOverrider.m +++ /dev/null @@ -1,142 +0,0 @@ -/** - * 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 "NSUserDefaultsOverrider.h" -#import "OneSignalSelectorHelpers.h" - -static NSMutableDictionary* defaultsDictionary; - -@implementation NSUserDefaultsOverrider { - NSString *suiteName; -} - -+ (void)load { - defaultsDictionary = [NSMutableDictionary new]; - - // Adding helpers to NSUserDefaults to be used internally - injectToProperClass(@selector(getObjectForKey:), @selector(getObjectForKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - injectToProperClass(@selector(setObjectForKey:withValue:), @selector(setObjectForKey:withValue:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - - // initWithSuiteName overrider - injectToProperClass(@selector(overrideInitWithSuiteName:), @selector(initWithSuiteName:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - - // Sets - injectToProperClass(@selector(overrideSetObject:forKey:), @selector(setObject:forKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - injectToProperClass(@selector(overrideSetString:forKey:), @selector(setString:forKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - injectToProperClass(@selector(overrideSetInteger:forKey:), @selector(setInteger:forKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - injectToProperClass(@selector(overrideSetDouble:forKey:), @selector(setDouble:forKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - injectToProperClass(@selector(overrideSetBool:forKey:), @selector(setBool:forKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - - // Gets - injectToProperClass(@selector(overrideObjectForKey:), @selector(objectForKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - injectToProperClass(@selector(overrideStringForKey:), @selector(stringForKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - injectToProperClass(@selector(overrideIntegerForKey:), @selector(integerForKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - injectToProperClass(@selector(overrideDoubleForKey:), @selector(doubleForKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); - injectToProperClass(@selector(overrideBoolForKey:), @selector(boolForKey:), @[], [NSUserDefaultsOverrider class], [NSUserDefaults class]); -} - -+ (void)clearInternalDictionary { - defaultsDictionary = [NSMutableDictionary new]; -} - -- (nullable instancetype)overrideInitWithSuiteName:(nullable NSString *)suitename { - suiteName = suitename; - return [self overrideInitWithSuiteName:suitename]; -} - -- (void)setObjectForKey:(NSString*)key withValue:(id)value { - if (!suiteName) - suiteName = @"default"; - - if (!defaultsDictionary[suiteName]) - defaultsDictionary[suiteName] = [NSMutableDictionary new]; - - defaultsDictionary[suiteName][key] = value; -} - -// Sets --(void)overrideSetObject:(id)value forKey:(NSString*)key { - [self setObjectForKey:key withValue:value]; -} - --(void)overrideSetString:(NSString*)value forKey:(NSString*)key { - [self setObjectForKey:key withValue:value]; -} - -- (void)overrideSetDouble:(double)value forKey:(NSString*)key { - [self setObjectForKey:key withValue:[NSNumber numberWithDouble:value]]; -} - -- (void)overrideSetBool:(BOOL)value forKey:(NSString*)key { - [self setObjectForKey:key withValue:[NSNumber numberWithBool:value]]; -} - -- (void)overrideSetInteger:(NSInteger)value forKey:(NSString*)key { - [self setObjectForKey:key withValue:[NSNumber numberWithInteger:value]]; -} - -- (id)getObjectForKey:(NSString*)key { - if (!suiteName) - suiteName = @"default"; - - if (!defaultsDictionary[suiteName]) - defaultsDictionary[suiteName] = [NSMutableDictionary new]; - - return defaultsDictionary[suiteName][key]; -} - -// Gets -- (nullable id)overrideObjectForKey:(NSString*)key { - if ([key isEqualToString:@"XCTIDEConnectionTimeout"]) - return [NSNumber numberWithInt:60]; - - return [self getObjectForKey:key]; -} - -- (NSString*)overrideStringForKey:(NSString*)key { - return [self getObjectForKey:key]; -} - -- (NSInteger)overrideIntegerForKey:(NSString*)key { - if ([key isEqualToString:@"XCTIDEConnectionTimeout"]) - return 60.0; - - return [[self getObjectForKey:key] integerValue]; -} - -- (double)overrideDoubleForKey:(NSString*)key { - if ([key isEqualToString:@"XCTIDEConnectionTimeout"]) - return 60.0; - - return [[self getObjectForKey:key] doubleValue]; -} - -- (BOOL)overrideBoolForKey:(NSString*)key { - return [[self getObjectForKey:key] boolValue]; -} - -@end diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h index 14a94487d..ed7b226f8 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h @@ -69,6 +69,7 @@ withNotificationWillShowInForegroundHandler:(OSNotificationWillShowInForegroundB + (void)pullDownNotificationCenter; + (void)useSceneLifecycle:(BOOL)useSceneLifecycle; + (void)setCurrentNotificationPermissionAsUnanswered; ++ (void)clearUserDefaults; + (UNNotificationResponse*)createBasiciOSNotificationResponseWithPayload:(NSDictionary*)userInfo; + (UNNotification *)createBasiciOSNotificationWithPayload:(NSDictionary *)userInfo; diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m index 04187e12e..5fb5dcd50 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m @@ -52,13 +52,13 @@ #import "OSOutcomeEventsCache.h" #import "OSInfluenceDataRepository.h" #import "OneSignalLocation.h" -#import "NSUserDefaultsOverrider.h" #import "OneSignalNotificationServiceExtensionHandler.h" #import "OneSignalTrackFirebaseAnalytics.h" #import "OSMessagingControllerOverrider.h" #import "OneSignalLifecycleObserver.h" #import "OneSignalLocationOverrider.h" #import "OneSignalOverrider.h" +#import "OneSignalUserDefaults.h" NSString * serverUrlWithPath(NSString *path) { return [OS_API_SERVER_URL stringByAppendingString:path]; @@ -245,15 +245,29 @@ + (void) beforeEachTest:(XCTestCase *)testCase { _currentXCTestCase = testCase; [self beforeAllTest]; [self clearStateForAppRestart:testCase]; - + [self clearUserDefaults]; [NSDateOverrider reset]; [OneSignalOverrider reset]; [OneSignalClientOverrider reset:testCase]; - [NSUserDefaultsOverrider clearInternalDictionary]; UNUserNotificationCenterOverrider.notifTypesOverride = 7; UNUserNotificationCenterOverrider.authorizationStatus = [NSNumber numberWithInteger:UNAuthorizationStatusAuthorized]; } ++ (void)clearUserDefaults { + let userDefaults = OneSignalUserDefaults.initStandard.userDefaults; + let dictionary = [userDefaults dictionaryRepresentation]; + for (NSString *key in dictionary.allKeys) { + [userDefaults removeObjectForKey:key]; + } + + let sharedUserDefaults = OneSignalUserDefaults.initShared.userDefaults; + let sharedDictionary = [sharedUserDefaults dictionaryRepresentation]; + for (NSString *key in sharedDictionary.allKeys) { + [sharedUserDefaults removeObjectForKey:key]; + } + +} + + (void)foregroundApp { UIApplicationOverrider.currentUIApplicationState = UIApplicationStateActive; diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m index 8a3bcc0e8..76ace28e6 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m @@ -58,7 +58,6 @@ // Shadows #import "NSObjectOverrider.h" -#import "NSUserDefaultsOverrider.h" #import "NSDateOverrider.h" #import "NSBundleOverrider.h" #import "UNUserNotificationCenterOverrider.h" From 2fc4120608b7ab2c04f80d08c41b7d0434849934 Mon Sep 17 00:00:00 2001 From: emawby Date: Thu, 4 Mar 2021 11:32:08 -0800 Subject: [PATCH 3/4] Creating migration for Dictionary -> CodeableData for the redisplay dict If the OS_IAM_REDISPLAY_CACHE is successfully read as Codeable data we don't need to migrate. If that fails try to read it as a dictionary and then save it as Codeable data. If that also fails then clear the cache because we cannot determine what data type was stored for the key. --- .../Source/OSMigrationController.m | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/iOS_SDK/OneSignalSDK/Source/OSMigrationController.m b/iOS_SDK/OneSignalSDK/Source/OSMigrationController.m index d25773f2b..7f07ce241 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSMigrationController.m +++ b/iOS_SDK/OneSignalSDK/Source/OSMigrationController.m @@ -34,6 +34,7 @@ of this software and associated documentation files (the "Software"), to deal #import "OneSignal.h" #import "OneSignalUserDefaults.h" #import "OneSignalCommonDefines.h" +#import "OSInAppMessagingDefines.h" #import "OneSignalHelper.h" @interface OneSignal () @@ -45,6 +46,7 @@ @implementation OSMigrationController - (void)migrate { [self migrateToVersion_02_14_00_AndGreater]; + [self migrateIAMRedisplayCache]; [self saveCurrentSDKVersion]; } @@ -78,6 +80,36 @@ - (void)migrateToVersion_02_14_00_AndGreater { } } +// Devices could potentially have bad data in the OS_IAM_REDISPLAY_DICTIONARY +// that was saved as a dictionary and not CodeableData. Try to detect if that is the case +// and save it is as CodeableData instead. +- (void)migrateIAMRedisplayCache { + let iamRedisplayCacheFixVersion = 30203; + long sdkVersion = [OneSignalUserDefaults.initShared getSavedIntegerForKey:OSUD_CACHED_SDK_VERSION defaultValue:0]; + if (sdkVersion >= iamRedisplayCacheFixVersion) + return; + + @try { + __unused NSMutableDictionary *redisplayDict =[[NSMutableDictionary alloc] initWithDictionary:[OneSignalUserDefaults.initStandard + getSavedCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY + defaultValue:[NSMutableDictionary new]]]; + } @catch (NSException *exception) { + @try { + // The redisplay IAMs might have been saved as a dictionary. + // Try to read them as a dictionary and then save them as a codeable. + NSMutableDictionary *redisplayDict = [[NSMutableDictionary alloc] initWithDictionary:[OneSignalUserDefaults.initStandard + getSavedDictionaryForKey:OS_IAM_REDISPLAY_DICTIONARY + defaultValue:[NSMutableDictionary new]]]; + [OneSignalUserDefaults.initStandard saveCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY + withValue:redisplayDict]; + } @catch (NSException *exception) { + //Clear the cached redisplay dictionary of bad data + [OneSignalUserDefaults.initStandard saveCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY + withValue:nil]; + } + } +} + - (void)saveCurrentSDKVersion { let currentVersion = [[OneSignal sdkVersionRaw] intValue]; [OneSignalUserDefaults.initShared saveIntegerForKey:OSUD_CACHED_SDK_VERSION withValue:currentVersion]; From a51401dda3058b4fa52c17460e3ee9284d482d42 Mon Sep 17 00:00:00 2001 From: emawby Date: Thu, 4 Mar 2021 11:53:30 -0800 Subject: [PATCH 4/4] unit tests for dictionary to codeabledata IAM redisplay migration testing empty dictionary to codeable data testing codeabledata unchanged testing nil dictionary to nil --- .../OneSignalSDK/UnitTests/MigrationTests.m | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/iOS_SDK/OneSignalSDK/UnitTests/MigrationTests.m b/iOS_SDK/OneSignalSDK/UnitTests/MigrationTests.m index e7bf23669..b79c11e84 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/MigrationTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/MigrationTests.m @@ -37,6 +37,10 @@ #import "UnitTestCommonMethods.h" #import "OneSignalUserDefaults.h" #import "OneSignalCommonDefines.h" +#import "OSInAppMessagingDefines.h" +#import "OneSignalUserDefaults.h" +#import "OSInAppMessage.h" +#import "OSInAppMessagingHelpers.h" #import "CommonAsserts.h" @interface MigrationTests : XCTestCase @@ -237,4 +241,55 @@ - (void)testCachedUniqueOutcomeToCachedUniqueOutcomeMigration { XCTAssertEqual([[OneSignal sdkVersionRaw] intValue], sdkVersionAfterMigration); } +- (void)testIAMCachedEmptyDictionaryToCachedCodeableMigration { + NSDictionary*emptyDict = [NSMutableDictionary new]; + [OneSignalUserDefaults.initStandard saveDictionaryForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:emptyDict]; + + [migrationController migrate]; +} + +- (void)testIAMCachedDictionaryToCachedCodeableMigration { + NSMutableDictionary *emptyDict = [NSMutableDictionary new]; + + [OneSignalUserDefaults.initStandard saveDictionaryForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:emptyDict]; + + [migrationController migrate]; + + NSDictionary*retrievedDict = [OneSignalUserDefaults.initStandard + getSavedCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY defaultValue:nil]; + XCTAssertEqualObjects(emptyDict, retrievedDict); +} + +- (void)testIAMCachedCodeableMigration { + let limit = 5; + let delay = 60; + let message = [OSInAppMessageTestHelper testMessageWithRedisplayLimit:limit delay:@(delay)]; + message.isDisplayedInSession = true; + NSMutableDictionary *redisplayedInAppMessages = [NSMutableDictionary new]; + [redisplayedInAppMessages setObject:message forKey:message.messageId]; + + [OneSignalUserDefaults.initStandard saveCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:redisplayedInAppMessages]; + + [migrationController migrate]; + + NSDictionary*retrievedDict = [OneSignalUserDefaults.initStandard + getSavedCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY defaultValue:nil]; + XCTAssertEqualObjects(redisplayedInAppMessages, retrievedDict); +} + +- (void)testIAMNilCacheToNilMigration { + + [OneSignalUserDefaults.initStandard saveDictionaryForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:nil]; + + [migrationController migrate]; + + NSDictionary*retrievedDict = [OneSignalUserDefaults.initStandard + getSavedCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY defaultValue:nil]; + XCTAssertNil(retrievedDict); +} + + + + + @end