diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index 60cd1a807..dc27505fb 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -68,6 +68,7 @@ 3C14E3A12AFAE461006ED053 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3C14E3A02AFAE461006ED053 /* PrivacyInfo.xcprivacy */; }; 3C14E3A42AFAE54C006ED053 /* OneSignalSwiftInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC08AFF2947D4E900C81DA3 /* OneSignalSwiftInterface.swift */; }; 3C24B0EC2BD09D7A0052E771 /* OneSignalCoreObjCTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C24B0EB2BD09D7A0052E771 /* OneSignalCoreObjCTests.m */; }; + 3C277D7E2BD76E0000857606 /* OSIdentityModelRepo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C277D7D2BD76E0000857606 /* OSIdentityModelRepo.swift */; }; 3C2C7DC8288F3C020020F9AE /* OSSubscriptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C2C7DC7288F3C020020F9AE /* OSSubscriptionModel.swift */; }; 3C2D8A5928B4C4E300BE41F6 /* OSDelta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C2D8A5828B4C4E300BE41F6 /* OSDelta.swift */; }; 3C44673E296D099D0039A49E /* OneSignalMobileProvision.m in Sources */ = {isa = PBXBuildFile; fileRef = 912411FD1E73342200E41FD7 /* OneSignalMobileProvision.m */; }; @@ -86,6 +87,11 @@ 3C7A39C12B7BED900082665E /* OneSignalCoreMocks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CC0639A2B6D7A8C002BB07F /* OneSignalCoreMocks.framework */; }; 3C7A39C22B7BED900082665E /* OneSignalCoreMocks.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3CC0639A2B6D7A8C002BB07F /* OneSignalCoreMocks.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3C7A39DC2B7C1C580082665E /* UNUserNotificationCenterOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = 4529DEE61FA82CDC00CEAB1D /* UNUserNotificationCenterOverrider.m */; }; + 3C87066D2BDE05B8000D8CD2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C7A39D42B7C18EE0082665E /* XCTest.framework */; platformFilter = ios; }; + 3C87066E2BDE05B8000D8CD2 /* XCTest.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3C7A39D42B7C18EE0082665E /* XCTest.framework */; platformFilter = ios; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3C8706702BDE0957000D8CD2 /* MockUserRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C87066F2BDE0957000D8CD2 /* MockUserRequests.swift */; }; + 3C8706722BDEE076000D8CD2 /* MockUserDefines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C8706712BDEE076000D8CD2 /* MockUserDefines.swift */; }; + 3C8706762BDEED75000D8CD2 /* NSDictionary+UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C8706752BDEED75000D8CD2 /* NSDictionary+UnitTests.swift */; }; 3C8E6DF928A6D89E0031E48A /* OSOperationExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C8E6DF828A6D89E0031E48A /* OSOperationExecutor.swift */; }; 3C8E6DFF28AB09AE0031E48A /* OSPropertyOperationExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C8E6DFE28AB09AE0031E48A /* OSPropertyOperationExecutor.swift */; }; 3C8E6E0128AC0BA10031E48A /* OSIdentityOperationExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C8E6E0028AC0BA10031E48A /* OSIdentityOperationExecutor.swift */; }; @@ -899,6 +905,7 @@ dstSubfolderSpec = 10; files = ( 3CEE93582B7C78FE008440BD /* OneSignalCore.framework in Embed Frameworks */, + 3C87066E2BDE05B8000D8CD2 /* XCTest.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -957,6 +964,7 @@ 3C14E3A02AFAE461006ED053 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 3C24B0EA2BD09D790052E771 /* OneSignalCoreTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OneSignalCoreTests-Bridging-Header.h"; sourceTree = ""; }; 3C24B0EB2BD09D7A0052E771 /* OneSignalCoreObjCTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalCoreObjCTests.m; sourceTree = ""; }; + 3C277D7D2BD76E0000857606 /* OSIdentityModelRepo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSIdentityModelRepo.swift; sourceTree = ""; }; 3C2C7DC2288E007E0020F9AE /* UnitTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UnitTests-Bridging-Header.h"; sourceTree = ""; }; 3C2C7DC7288F3C020020F9AE /* OSSubscriptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSSubscriptionModel.swift; sourceTree = ""; }; 3C2D8A5828B4C4E300BE41F6 /* OSDelta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSDelta.swift; sourceTree = ""; }; @@ -968,6 +976,9 @@ 3C4F9E4328A4466C009F453A /* OSOperationRepo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSOperationRepo.swift; sourceTree = ""; }; 3C5117162B15C31E00563465 /* OSUserState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSUserState.swift; sourceTree = ""; }; 3C7A39D42B7C18EE0082665E /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + 3C87066F2BDE0957000D8CD2 /* MockUserRequests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUserRequests.swift; sourceTree = ""; }; + 3C8706712BDEE076000D8CD2 /* MockUserDefines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUserDefines.swift; sourceTree = ""; }; + 3C8706752BDEED75000D8CD2 /* NSDictionary+UnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDictionary+UnitTests.swift"; sourceTree = ""; }; 3C8E6DF828A6D89E0031E48A /* OSOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSOperationExecutor.swift; sourceTree = ""; }; 3C8E6DFE28AB09AE0031E48A /* OSPropertyOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSPropertyOperationExecutor.swift; sourceTree = ""; }; 3C8E6E0028AC0BA10031E48A /* OSIdentityOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSIdentityOperationExecutor.swift; sourceTree = ""; }; @@ -1371,6 +1382,7 @@ buildActionMask = 2147483647; files = ( 3CEE93572B7C78FD008440BD /* OneSignalCore.framework in Frameworks */, + 3C87066D2BDE05B8000D8CD2 /* XCTest.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1668,6 +1680,14 @@ path = Source; sourceTree = ""; }; + 3C8706742BDEED53000D8CD2 /* Extensions */ = { + isa = PBXGroup; + children = ( + 3C8706752BDEED75000D8CD2 /* NSDictionary+UnitTests.swift */, + ); + path = Extensions; + sourceTree = ""; + }; 3C9AD6BA2B2284AB00BC1540 /* Executors */ = { isa = PBXGroup; children = ( @@ -1701,6 +1721,7 @@ 3CC0639B2B6D7A8D002BB07F /* OneSignalCoreMocks */ = { isa = PBXGroup; children = ( + 3C8706742BDEED53000D8CD2 /* Extensions */, 3CC0639C2B6D7A8D002BB07F /* OneSignalCoreMocks.h */, 3CC063B32B6D7BA2002BB07F /* OneSignalCoreMocks.swift */, 3CC063B12B6D7AD8002BB07F /* MockOneSignalClient.swift */, @@ -1722,6 +1743,8 @@ isa = PBXGroup; children = ( 3CC063DF2B6D7F2A002BB07F /* OneSignalUserMocks.h */, + 3C87066F2BDE0957000D8CD2 /* MockUserRequests.swift */, + 3C8706712BDEE076000D8CD2 /* MockUserDefines.swift */, 3CC063E52B6D7F96002BB07F /* OneSignalUserMocks.swift */, ); path = OneSignalUserMocks; @@ -1961,6 +1984,7 @@ DE69E1A9282ED8790090BB3D /* UnitTestApp-Bridging-Header.h */, 3C0EF49D28A1DBCB00E5434B /* OSUserInternalImpl.swift */, DE69E1AA282ED8790090BB3D /* OneSignalUserManagerImpl.swift */, + 3C277D7D2BD76E0000857606 /* OSIdentityModelRepo.swift */, 3C2C7DC7288F3C020020F9AE /* OSSubscriptionModel.swift */, 3CE92279289FA88B001B1062 /* OSIdentityModelStoreListener.swift */, 3CF8629D28A183F900776CA4 /* OSIdentityModel.swift */, @@ -3343,6 +3367,7 @@ buildActionMask = 2147483647; files = ( 3CC063B22B6D7AD8002BB07F /* MockOneSignalClient.swift in Sources */, + 3C8706762BDEED75000D8CD2 /* NSDictionary+UnitTests.swift in Sources */, 3CC063B42B6D7BA2002BB07F /* OneSignalCoreMocks.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3360,6 +3385,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3C8706702BDE0957000D8CD2 /* MockUserRequests.swift in Sources */, + 3C8706722BDEE076000D8CD2 /* MockUserDefines.swift in Sources */, 3CC063E62B6D7F96002BB07F /* OneSignalUserMocks.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3495,6 +3522,7 @@ 3CF862A028A1964F00776CA4 /* OSPropertiesModel.swift in Sources */, 3C8E6E0128AC0BA10031E48A /* OSIdentityOperationExecutor.swift in Sources */, 3CF862A228A197D200776CA4 /* OSPropertiesModelStoreListener.swift in Sources */, + 3C277D7E2BD76E0000857606 /* OSIdentityModelRepo.swift in Sources */, 3C9AD6C12B22886600BC1540 /* OSRequestUpdateSubscription.swift in Sources */, 3C0EF49E28A1DBCB00E5434B /* OSUserInternalImpl.swift in Sources */, 3C8E6DFF28AB09AE0031E48A /* OSPropertyOperationExecutor.swift in Sources */, @@ -3939,7 +3967,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -3947,8 +3979,9 @@ PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.OneSignalOSCore; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; @@ -4000,7 +4033,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -4057,7 +4094,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; @@ -4120,7 +4161,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; @@ -4179,7 +4224,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; @@ -4390,7 +4439,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; @@ -4453,7 +4506,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; @@ -4512,7 +4569,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; @@ -4739,7 +4800,11 @@ "$(PROJECT_DIR)", ); IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); OTHER_CFLAGS = "-fembed-bitcode"; OTHER_LDFLAGS = ""; PRODUCT_NAME = OneSignal; @@ -4771,7 +4836,11 @@ INFOPLIST_FILE = OneSignalFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MACH_O_TYPE = mh_dylib; MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = ""; @@ -4808,7 +4877,11 @@ HEADER_SEARCH_PATHS = $CONFIGURATION_TEMP_DIR/UnitTests.build/DerivedSources; INFOPLIST_FILE = UnitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = "-fembed-bitcode"; @@ -4907,7 +4980,11 @@ "$(PROJECT_DIR)", ); IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); OTHER_CFLAGS = "-fembed-bitcode"; OTHER_LDFLAGS = ""; PRODUCT_NAME = OneSignal; @@ -4939,7 +5016,11 @@ INFOPLIST_FILE = OneSignalFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MACH_O_TYPE = mh_dylib; MTL_ENABLE_DEBUG_INFO = NO; ONLY_ACTIVE_ARCH = NO; @@ -4977,7 +5058,11 @@ HEADER_SEARCH_PATHS = $CONFIGURATION_TEMP_DIR/UnitTests.build/DerivedSources; INFOPLIST_FILE = UnitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = "-fembed-bitcode"; @@ -5076,7 +5161,11 @@ "$(PROJECT_DIR)", ); IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); OTHER_CFLAGS = "-fembed-bitcode"; OTHER_LDFLAGS = ""; PRODUCT_NAME = OneSignal; @@ -5108,7 +5197,11 @@ INFOPLIST_FILE = OneSignalFramework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MACH_O_TYPE = mh_dylib; MTL_ENABLE_DEBUG_INFO = NO; ONLY_ACTIVE_ARCH = NO; @@ -5146,7 +5239,11 @@ HEADER_SEARCH_PATHS = $CONFIGURATION_TEMP_DIR/UnitTests.build/DerivedSources; INFOPLIST_FILE = UnitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = "-fembed-bitcode"; @@ -5234,7 +5331,11 @@ INFOPLIST_FILE = UnitTestApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = NO; @@ -5292,7 +5393,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -5351,7 +5456,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -5411,7 +5520,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -5468,7 +5581,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -5525,7 +5642,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -5533,8 +5654,9 @@ PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.OneSignalUser; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; @@ -5585,7 +5707,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -5645,7 +5771,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -5709,7 +5839,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -5765,7 +5899,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -5827,7 +5965,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -5883,7 +6025,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -5944,7 +6090,11 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2021 Hiptic. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -5999,7 +6149,11 @@ INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; @@ -6061,7 +6215,11 @@ INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; @@ -6120,7 +6278,11 @@ INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; @@ -6176,7 +6338,11 @@ INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; @@ -6238,7 +6404,11 @@ INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; @@ -6297,7 +6467,11 @@ INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; @@ -6354,10 +6528,15 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_TESTABILITY = YES; + GCC_PREPROCESSOR_DEFINITIONS = OS_TEST; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = OneSignalOSCoreFramework/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); OTHER_CFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.OneSignalOSCore; PRODUCT_NAME = OneSignalOSCore; @@ -6402,7 +6581,11 @@ INFOPLIST_FILE = UnitTestApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_CFLAGS = "-fembed-bitcode"; @@ -6451,7 +6634,11 @@ INFOPLIST_FILE = UnitTestApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = NO; @@ -6504,7 +6691,11 @@ INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -6565,7 +6756,11 @@ INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -6622,7 +6817,11 @@ INFOPLIST_KEY_NSPrincipalClass = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/xcshareddata/xcschemes/OneSignalUserTests.xcscheme b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/xcshareddata/xcschemes/OneSignalUserTests.xcscheme index a953a4573..bd94aea75 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/xcshareddata/xcschemes/OneSignalUserTests.xcscheme +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/xcshareddata/xcschemes/OneSignalUserTests.xcscheme @@ -7,7 +7,7 @@ buildImplicitDependencies = "YES"> Bool { + guard let dictVal = self[key] else { + return false + } + + return equals(dictVal, value) + } + + func contains(_ dict: [String: Any]) -> Bool { + for (key, value) in dict { + if !contains(key: key, value: value) { + return false + } + } + return true + } + + private func equals(_ x: Any, _ y: Any) -> Bool { + guard x is AnyHashable else { return false } + guard y is AnyHashable else { return false } + return (x as! AnyHashable) == (y as! AnyHashable) + } +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalCoreMocks/MockOneSignalClient.swift b/iOS_SDK/OneSignalSDK/OneSignalCoreMocks/MockOneSignalClient.swift index 163bf465a..6096ebed4 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCoreMocks/MockOneSignalClient.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalCoreMocks/MockOneSignalClient.swift @@ -40,6 +40,8 @@ public class MockOneSignalClient: NSObject, IOneSignalClient { var shouldUseProvisionalAuthorization = false // new in iOS 12 (aka Direct to History) var remoteParamsOutcomes: [String: Any] = [:] + public var allRequestsHandled = true + /** May add to or change this default remote params response*/ public func getRemoteParamsResponse() -> [String: Any] { return remoteParamsResponse ?? [ @@ -70,7 +72,7 @@ public class MockOneSignalClient: NSObject, IOneSignalClient { // Temp. method to log info while building unit tests @objc public func logSelfInfo() { - print("🧪 MockOneSignalClient with executionQueue \(executionQueue)") + print("🧪 MockOneSignalClient with executedRequests \(executedRequests)") } public func reset() { @@ -115,6 +117,7 @@ public class MockOneSignalClient: NSObject, IOneSignalClient { if (mockResponses[String(describing: request)]) != nil { successBlock(mockResponses[String(describing: request)]) } else { + allRequestsHandled = false print("🧪 cannot find a mock response for request: \(request)") } } @@ -137,3 +140,34 @@ public class MockOneSignalClient: NSObject, IOneSignalClient { mockResponses[request] = response } } + +// MARK: - Asserts + +extension MockOneSignalClient { + /** + Checks if there is only one executed request that contains the payload provided, and the url matches the path provided. + */ + public func onlyOneRequest(contains path: String, contains payload: [String: Any]) -> Bool { + var found = false + + for request in executedRequests { + guard let params = request.parameters as? NSDictionary else { + continue + } + + if params.contains(payload) { + if request.path == path { + guard !found else { + // False if more than 1 request satisfies both requirements + return false + } + found = true + } else { + return false + } + } + } + + return found + } +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalCoreMocks/OneSignalCoreMocks.swift b/iOS_SDK/OneSignalSDK/OneSignalCoreMocks/OneSignalCoreMocks.swift index 587eb102b..176ca3a94 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCoreMocks/OneSignalCoreMocks.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalCoreMocks/OneSignalCoreMocks.swift @@ -22,6 +22,7 @@ import Foundation import OneSignalCore +import XCTest @objc public class OneSignalCoreMocks: NSObject { @@ -43,4 +44,10 @@ public class OneSignalCoreMocks: NSObject { sharedUserDefaults.removeObject(forKey: key) } } + + /** Wait specified number of seconds for any async methods to run */ + public static func waitForBackgroundThreads(seconds: Double) { + let expectation = XCTestExpectation(description: "Wait for \(seconds) seconds") + _ = XCTWaiter.wait(for: [expectation], timeout: seconds) + } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSDelta.swift b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSDelta.swift index 39220214d..9cbba04e3 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSDelta.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSDelta.swift @@ -26,6 +26,7 @@ */ import Foundation +import OneSignalCore // TODO: Known Issue: Since these don't carry the app_id, it may have changed by the time Deltas become Requests, if app_id changes. // All requests requiring unique ID's will effectively be dropped. @@ -34,6 +35,7 @@ open class OSDelta: NSObject, NSCoding { public let name: String public let deltaId: String public let timestamp: Date + public let identityModelId: String public var model: OSModel public let property: String public let value: Any @@ -42,10 +44,11 @@ open class OSDelta: NSObject, NSCoding { return "" } - public init(name: String, model: OSModel, property: String, value: Any) { + public init(name: String, identityModelId: String, model: OSModel, property: String, value: Any) { self.name = name self.deltaId = UUID().uuidString self.timestamp = Date() + self.identityModelId = identityModelId self.model = model self.property = property self.value = value @@ -55,6 +58,7 @@ open class OSDelta: NSObject, NSCoding { coder.encode(name, forKey: "name") coder.encode(deltaId, forKey: "deltaId") coder.encode(timestamp, forKey: "timestamp") + coder.encode(identityModelId, forKey: "identityModelId") coder.encode(model, forKey: "model") coder.encode(property, forKey: "property") coder.encode(value, forKey: "value") @@ -64,17 +68,19 @@ open class OSDelta: NSObject, NSCoding { guard let name = coder.decodeObject(forKey: "name") as? String, let deltaId = coder.decodeObject(forKey: "deltaId") as? String, let timestamp = coder.decodeObject(forKey: "timestamp") as? Date, + let identityModelId = coder.decodeObject(forKey: "identityModelId") as? String, let model = coder.decodeObject(forKey: "model") as? OSModel, let property = coder.decodeObject(forKey: "property") as? String, let value = coder.decodeObject(forKey: "value") else { - // Log error + OneSignalLog.onesignalLog(.LL_ERROR, message: "Unable to init OSDelta from cache") return nil } self.name = name self.deltaId = deltaId self.timestamp = timestamp + self.identityModelId = identityModelId self.model = model self.property = property self.value = value diff --git a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStoreListener.swift b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStoreListener.swift index d6297fbd3..540dcca40 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStoreListener.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSModelStoreListener.swift @@ -47,7 +47,7 @@ extension OSModelStoreListener { store.changeSubscription.subscribe(self) } - func close() { + public func close() { store.changeSubscription.unsubscribe(self) } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift index fd4674e9f..3811c0077 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift @@ -40,11 +40,12 @@ class OSIdentityOperationExecutor: OSOperationExecutor { if var deltaQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_IDENTITY_EXECUTOR_DELTA_QUEUE_KEY, defaultValue: []) as? [OSDelta] { // Hook each uncached Delta to the model in the store for (index, delta) in deltaQueue.enumerated().reversed() { - if let modelInStore = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: delta.model.modelId) { - // The model exists in the store, set it to be the Delta's model + if let modelInStore = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(delta.model.modelId) { + // The model exists in the repo, set it to be the Delta's model delta.model = modelInStore } else { // The model does not exist, drop this Delta + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSIdentityOperationExecutor.init dropped \(delta)") deltaQueue.remove(at: index) } } @@ -59,14 +60,15 @@ class OSIdentityOperationExecutor: OSOperationExecutor { if var addRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestAddAliases] { // Hook each uncached Request to the model in the store for (index, request) in addRequestQueue.enumerated().reversed() { - if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) { - // 1. The model exists in the store, so set it to be the Request's models + if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(request.identityModel.modelId) { + // 1. The model exists in the repo, so set it to be the Request's models request.identityModel = identityModel - } else if let identityModel = OSUserExecutor.identityModels[request.identityModel.modelId] { - // 2. The model exists in the user executor - request.identityModel = identityModel - } else if !request.prepareForExecution() { - // 3. The models do not exist AND this request cannot be sent, drop this Request + } else if request.prepareForExecution() { + // 2. The request can be sent, add the model to the repo + OneSignalUserManagerImpl.sharedInstance.addIdentityModelToRepo(request.identityModel) + } else { + // 3. The model do not exist AND this request cannot be sent, drop this Request + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSIdentityOperationExecutor.init dropped \(request)") addRequestQueue.remove(at: index) } } @@ -79,14 +81,15 @@ class OSIdentityOperationExecutor: OSOperationExecutor { if var removeRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestRemoveAlias] { // Hook each uncached Request to the model in the store for (index, request) in removeRequestQueue.enumerated().reversed() { - if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) { - // 1. The model exists in the store, so set it to be the Request's model + if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(request.identityModel.modelId) { + // 1. The model exists in the repo, so set it to be the Request's model request.identityModel = identityModel - } else if let identityModel = OSUserExecutor.identityModels[request.identityModel.modelId] { - // 2. The model exists in the user executor - request.identityModel = identityModel - } else if !request.prepareForExecution() { + } else if request.prepareForExecution() { + // 2. The request can be sent, add the model to the repo + OneSignalUserManagerImpl.sharedInstance.addIdentityModelToRepo(request.identityModel) + } else { // 3. The model does not exist AND this request cannot be sent, drop this Request + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSIdentityOperationExecutor.init dropped \(request)") removeRequestQueue.remove(at: index) } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift index eea593d9e..7de21b0df 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift @@ -40,13 +40,9 @@ class OSPropertyOperationExecutor: OSOperationExecutor { // Read unfinished deltas from cache, if any... // Note that we should only have deltas for the current user as old ones are flushed.. if var deltaQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_PROPERTIES_EXECUTOR_DELTA_QUEUE_KEY, defaultValue: []) as? [OSDelta] { - // Hook each uncached Delta to the model in the store for (index, delta) in deltaQueue.enumerated().reversed() { - if let modelInStore = OneSignalUserManagerImpl.sharedInstance.propertiesModelStore.getModel(modelId: delta.model.modelId) { - // 1. The model exists in the properties model store, set it to be the Delta's model - delta.model = modelInStore - } else { - // 2. The model does not exist, drop this Delta + if OneSignalUserManagerImpl.sharedInstance.getIdentityModel(delta.identityModelId) == nil { + // The identity model does not exist, drop this Delta OneSignalLog.onesignalLog(.LL_WARN, message: "OSPropertyOperationExecutor.init dropped: \(delta)") deltaQueue.remove(at: index) } @@ -61,17 +57,13 @@ class OSPropertyOperationExecutor: OSOperationExecutor { if var updateRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSRequestUpdateProperties] { // Hook each uncached Request to the model in the store for (index, request) in updateRequestQueue.enumerated().reversed() { - // 0. Hook up the properties model if its the current user's so it can hydrate - if let propertiesModel = OneSignalUserManagerImpl.sharedInstance.propertiesModelStore.getModel(modelId: request.modelToUpdate.modelId) { - request.modelToUpdate = propertiesModel - } - if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) { - // 1. The identity model exist in the store, set it to be the Request's models + if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(request.identityModel.modelId) { + // 1. The identity model exist in the repo, set it to be the Request's model request.identityModel = identityModel - } else if let identityModel = OSUserExecutor.identityModels[request.identityModel.modelId] { - // 2. The model exists in the user executor - request.identityModel = identityModel - } else if !request.prepareForExecution() { + } else if request.prepareForExecution() { + // 2. The request can be sent, add the model to the repo + OneSignalUserManagerImpl.sharedInstance.addIdentityModelToRepo(request.identityModel) + } else { // 3. The identitymodel do not exist AND this request cannot be sent, drop this Request OneSignalLog.onesignalLog(.LL_WARN, message: "OSPropertyOperationExecutor.init dropped: \(request)") updateRequestQueue.remove(at: index) @@ -103,8 +95,10 @@ class OSPropertyOperationExecutor: OSOperationExecutor { OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSPropertyOperationExecutor processDeltaQueue with queue: \(self.deltaQueue)") } for delta in self.deltaQueue { - guard let model = delta.model as? OSPropertiesModel else { - // Log error + guard let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(delta.identityModelId) + else { + // drop this delta + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSPropertyOperationExecutor.processDeltaQueue dropped: \(delta)") continue } @@ -112,8 +106,7 @@ class OSPropertyOperationExecutor: OSOperationExecutor { properties: [delta.property: delta.value], deltas: nil, refreshDeviceMetadata: false, // Sort this out. - modelToUpdate: model, - identityModel: OneSignalUserManagerImpl.sharedInstance.user.identityModel // TODO: Make sure this is ok + identityModel: identityModel ) self.updateRequestQueue.append(request) } @@ -204,7 +197,6 @@ extension OSPropertyOperationExecutor { properties: [:], deltas: propertiesDeltas.jsonRepresentation(), refreshDeviceMetadata: refreshDeviceMetadata, - modelToUpdate: propertiesModel, identityModel: identityModel) if sendImmediately { diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift index 499705bad..4718de69f 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift @@ -47,6 +47,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { delta.model = modelInStore } else { // The model does not exist, drop this Delta + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSSubscriptionOperationExecutor.init dropped \(delta)") deltaQueue.remove(at: index) } } @@ -75,14 +76,15 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { subscriptionModels[request.subscriptionModel.modelId] = request.subscriptionModel } // 2. Hook up the identity model - if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: request.identityModel.modelId) { - // a. The model exist in the store + if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(request.identityModel.modelId) { + // a. The model exist in the repo request.identityModel = identityModel - } else if let identityModel = OSUserExecutor.identityModels[request.identityModel.modelId] { - // b. The model exist in the user executor - request.identityModel = identityModel - } else if !request.prepareForExecution() { - // The model do not exist AND this request cannot be sent, drop this Request + } else if request.prepareForExecution() { + // b. The request can be sent, add the model to the repo + OneSignalUserManagerImpl.sharedInstance.addIdentityModelToRepo(request.identityModel) + } else { + // c. The model do not exist AND this request cannot be sent, drop this Request + OneSignalLog.onesignalLog(.LL_WARN, message: "OSSubscriptionOperationExecutor.init dropped: \(request)") continue } requestQueue.append(request) @@ -104,6 +106,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { request.subscriptionModel = subscriptionModel } else if !request.prepareForExecution() { // 3. The model does not exist AND this request cannot be sent, drop this Request + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSSubscriptionOperationExecutor.init dropped \(request)") removeRequestQueue.remove(at: index) } } @@ -124,6 +127,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { request.subscriptionModel = subscriptionModel } else if !request.prepareForExecution() { // 3. The models do not exist AND this request cannot be sent, drop this Request + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSSubscriptionOperationExecutor.init dropped \(request)") updateRequestQueue.remove(at: index) } } @@ -161,29 +165,34 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSSubscriptionOperationExecutor processDeltaQueue with queue: \(deltaQueue)") } for delta in deltaQueue { - guard let model = delta.model as? OSSubscriptionModel else { - // Log error + guard let subModel = delta.model as? OSSubscriptionModel + else { + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSSubscriptionOperationExecutor.processDeltaQueue dropped \(delta)") continue } switch delta.name { case OS_ADD_SUBSCRIPTION_DELTA: - let request = OSRequestCreateSubscription( - subscriptionModel: model, - identityModel: OneSignalUserManagerImpl.sharedInstance.user.identityModel // TODO: Make sure this is ok - ) - addRequestQueue.append(request) - + // Only create the request if the identity model exists + if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(delta.identityModelId) { + let request = OSRequestCreateSubscription( + subscriptionModel: subModel, + identityModel: identityModel + ) + addRequestQueue.append(request) + } else { + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSSubscriptionOperationExecutor.processDeltaQueue dropped \(delta)") + } case OS_REMOVE_SUBSCRIPTION_DELTA: let request = OSRequestDeleteSubscription( - subscriptionModel: model + subscriptionModel: subModel ) removeRequestQueue.append(request) case OS_UPDATE_SUBSCRIPTION_DELTA: let request = OSRequestUpdateSubscription( subscriptionObject: [delta.property: delta.value], - subscriptionModel: model + subscriptionModel: subModel ) updateRequestQueue.append(request) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift index 0bb0ee47f..3d69e2f02 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift @@ -36,7 +36,6 @@ import OneSignalOSCore class OSUserExecutor { static var userRequestQueue: [OSUserRequest] = [] static var transferSubscriptionRequestQueue: [OSRequestTransferSubscription] = [] - static var identityModels: [String: OSIdentityModel] = [:] // Read in requests from the cache, do not read in FetchUser requests as this is not needed. static func start() { @@ -47,58 +46,48 @@ class OSUserExecutor { // Hook each uncached Request to the right model reference for request in cachedRequestQueue { if request.isKind(of: OSRequestFetchIdentityBySubscription.self), let req = request as? OSRequestFetchIdentityBySubscription { - if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: req.identityModel.modelId) { - // 1. The model exist in the store, set it to be the Request's model - req.identityModel = identityModel - } else if let identityModel = identityModels[req.identityModel.modelId] { - // 2. The model exists in the dict of identityModels already processed to use + if let identityModel = getIdentityModel(req.identityModel.modelId) { + // 1. The model exist in the repo, set it to be the Request's model + // It is the current user or the model has already been processed req.identityModel = identityModel } else { - // 3. The models do not exist, use the model on the request, and add to dict. - identityModels[req.identityModel.modelId] = req.identityModel + // 2. The model do not exist, use the model on the request, and add to repo. + addIdentityModel(req.identityModel) } userRequestQueue.append(req) } else if request.isKind(of: OSRequestCreateUser.self), let req = request as? OSRequestCreateUser { - if let identityModel = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: req.identityModel.modelId) { - // 1. The model exist in the store, set it to be the Request's model - req.identityModel = identityModel - } else if let identityModel = identityModels[req.identityModel.modelId] { - // 2. The model exists in the dict of identityModels already processed to use + if let identityModel = getIdentityModel(req.identityModel.modelId) { + // 1. The model exist in the repo, set it to be the Request's model req.identityModel = identityModel } else { - // 3. The models do not exist, use the model on the request, and add to dict. - identityModels[req.identityModel.modelId] = req.identityModel + // 2. The models do not exist, use the model on the request, and add to repo. + addIdentityModel(req.identityModel) } userRequestQueue.append(req) } else if request.isKind(of: OSRequestIdentifyUser.self), let req = request as? OSRequestIdentifyUser { - if let identityModelToIdentify = identityModels[req.identityModelToIdentify.modelId], - let identityModelToUpdate = OneSignalUserManagerImpl.sharedInstance.identityModelStore.getModel(modelId: req.identityModelToUpdate.modelId) { - // 1. A model exist in the dict and a model exist in the store, set it to be the Request's models - req.identityModelToIdentify = identityModelToIdentify - req.identityModelToUpdate = identityModelToUpdate - } else if let identityModelToIdentify = identityModels[req.identityModelToIdentify.modelId], - let identityModelToUpdate = identityModels[req.identityModelToUpdate.modelId] { - // 2. The two models exist in the dict, set it to be the Request's models + if let identityModelToIdentify = getIdentityModel(req.identityModelToIdentify.modelId), + let identityModelToUpdate = getIdentityModel(req.identityModelToUpdate.modelId) { + // 1. Both models exist in the repo, set it to be the Request's models req.identityModelToIdentify = identityModelToIdentify req.identityModelToUpdate = identityModelToUpdate - } else if let identityModelToIdentify = identityModels[req.identityModelToIdentify.modelId], - identityModels[req.identityModelToUpdate.modelId] == nil { - // 3. A model is in the dict, the other model does not exist + } else if let identityModelToIdentify = getIdentityModel(req.identityModelToIdentify.modelId), + getIdentityModel(req.identityModelToUpdate.modelId) == nil { + // 2. A model is in the repo, the other model does not exist req.identityModelToIdentify = identityModelToIdentify - identityModels[req.identityModelToUpdate.modelId] = req.identityModelToUpdate + addIdentityModel(req.identityModelToUpdate) } else { - // 4. Both models don't exist yet + // 3. Both models don't exist yet // Drop the request if the identityModelToIdentify does not already exist AND the request is missing OSID // Otherwise, this request will forever fail `prepareForExecution` and block pending requests such as recovery calls to `logout` or `login` guard request.prepareForExecution() else { OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor.start() dropped: \(request)") continue } - identityModels[req.identityModelToIdentify.modelId] = req.identityModelToIdentify - identityModels[req.identityModelToUpdate.modelId] = req.identityModelToUpdate + addIdentityModel(req.identityModelToIdentify) + addIdentityModel(req.identityModelToUpdate) } userRequestQueue.append(req) } @@ -128,6 +117,14 @@ class OSUserExecutor { executePendingRequests() } + static private func getIdentityModel(_ modelId: String) -> OSIdentityModel? { + return OneSignalUserManagerImpl.sharedInstance.getIdentityModel(modelId) + } + + static private func addIdentityModel(_ model: OSIdentityModel) { + OneSignalUserManagerImpl.sharedInstance.addIdentityModelToRepo(model) + } + static func appendToQueue(_ request: OSUserRequest) { if request.isKind(of: OSRequestTransferSubscription.self), let req = request as? OSRequestTransferSubscription { self.transferSubscriptionRequestQueue.append(req) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift index 194e6f16f..6e70b5057 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift @@ -86,7 +86,7 @@ class OSIdentityModel: OSModel { } self.set(property: "aliases", newValue: aliases) } - + /** Called to clear the model's data in preparation for hydration via a fetch user call. */ diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelRepo.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelRepo.swift new file mode 100644 index 000000000..781fe4e8f --- /dev/null +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelRepo.swift @@ -0,0 +1,55 @@ +/* + Modified MIT License + + Copyright 2024 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 Foundation + +/** + This class stores all Identity Models that are being used during an app session. + Its purpose is to manage the instances for all referencing objects. + The models are built up on each new cold start, so no caching occurs. + + When are Identity Models added to this repo? + 1. When the User Manager starts, and the Identity Model is loaded from cache. + 2. When users switch and new Identity Models are created. + 3. Identity Models are added when requests are uncached. + */ +class OSIdentityModelRepo { + let lock = NSLock() + var models: [String: OSIdentityModel] = [:] + + func add(model: OSIdentityModel) { + lock.withLock { + models[model.modelId] = model + } + } + + func get(modelId: String) -> OSIdentityModel? { + lock.withLock { + return models[modelId] + } + } +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelStoreListener.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelStoreListener.swift index 359050d20..4edabb321 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelStoreListener.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelStoreListener.swift @@ -60,6 +60,7 @@ class OSIdentityModelStoreListener: OSModelStoreListener { return OSDelta( name: name, + identityModelId: args.model.modelId, model: args.model, property: args.property, value: args.newValue diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModelStoreListener.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModelStoreListener.swift index 8d4ef768b..7db8b6d39 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModelStoreListener.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSPropertiesModelStoreListener.swift @@ -47,6 +47,7 @@ class OSPropertiesModelStoreListener: OSModelStoreListener { func getUpdateModelDelta(_ args: OSModelChangedArgs) -> OSDelta? { return OSDelta( name: OS_UPDATE_PROPERTIES_DELTA, + identityModelId: OneSignalUserManagerImpl.sharedInstance.user.identityModel.modelId, model: args.model, property: args.property, value: args.newValue diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModelStoreListener.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModelStoreListener.swift index 79183a893..6ffe8c9ac 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModelStoreListener.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSSubscriptionModelStoreListener.swift @@ -39,6 +39,7 @@ class OSSubscriptionModelStoreListener: OSModelStoreListener { func getAddModelDelta(_ model: OSSubscriptionModel) -> OSDelta? { return OSDelta( name: OS_ADD_SUBSCRIPTION_DELTA, + identityModelId: OneSignalUserManagerImpl.sharedInstance.user.identityModel.modelId, model: model, property: model.type.rawValue, // push, email, sms value: model.address ?? "" @@ -51,6 +52,7 @@ class OSSubscriptionModelStoreListener: OSModelStoreListener { func getRemoveModelDelta(_ model: OSSubscriptionModel) -> OSDelta? { return OSDelta( name: OS_REMOVE_SUBSCRIPTION_DELTA, + identityModelId: OneSignalUserManagerImpl.sharedInstance.user.identityModel.modelId, model: model, property: model.type.rawValue, // push, email, sms value: model.address ?? "" @@ -60,6 +62,7 @@ class OSSubscriptionModelStoreListener: OSModelStoreListener { func getUpdateModelDelta(_ args: OSModelChangedArgs) -> OSDelta? { return OSDelta( name: OS_UPDATE_SUBSCRIPTION_DELTA, + identityModelId: OneSignalUserManagerImpl.sharedInstance.user.identityModel.modelId, model: args.model, property: args.property, value: args.newValue diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index 92d161abd..f2d971be4 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -118,7 +118,9 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { @objc public let pushSubscriptionImpl: OSPushSubscriptionImpl - private var hasCalledStart = false + var identityModelRepo = OSIdentityModelRepo() + + var hasCalledStart = false private var jwtExpiredHandler: OSJwtExpiredHandler? @@ -137,7 +139,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { return user } - private var _user: OSUserInternal? + var _user: OSUserInternal? // This is a user instance to operate on when there is no app_id and/or privacy consent yet, effectively no-op. // The models are not added to any model stores. @@ -211,12 +213,14 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { let pushSubscription = pushSubscriptionModelStore.getModels()[OS_PUSH_SUBSCRIPTION_MODEL_KEY] { hasCachedUser = true _user = OSUserInternalImpl(identityModel: identityModel, propertiesModel: propertiesModel, pushSubscriptionModel: pushSubscription) + addIdentityModelToRepo(identityModel) OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OneSignalUserManager.start called, loaded the user from cache.") } // TODO: Update the push sub model with any new state from NotificationsManager // Setup the executors + // The OSUserExecutor has to run first, before other executors OSUserExecutor.start() OSOperationRepo.sharedInstance.start() @@ -253,6 +257,14 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { } } + func addIdentityModelToRepo(_ model: OSIdentityModel) { + self.identityModelRepo.add(model: model) + } + + func getIdentityModel(_ modelId: String) -> OSIdentityModel? { + return identityModelRepo.get(modelId: modelId) + } + @objc public func login(externalId: String, token: String?) { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else { @@ -429,6 +441,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { let identityModel = OSIdentityModel(aliases: aliases, changeNotifier: OSEventProducer()) self.identityModelStore.add(id: OS_IDENTITY_MODEL_KEY, model: identityModel, hydrating: false) + self.addIdentityModelToRepo(identityModel) let propertiesModel = OSPropertiesModel(changeNotifier: OSEventProducer()) self.propertiesModelStore.add(id: OS_PROPERTIES_MODEL_KEY, model: propertiesModel, hydrating: false) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift index b356bcd49..dbf8ed914 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift @@ -48,7 +48,7 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the create user request due to null app ID.") return false } - let _ = self.addPushSubscriptionIdToAdditionalHeaders() + _ = self.addPushSubscriptionIdToAdditionalHeaders() self.addJWTHeader(identityModel: identityModel) self.path = "apps/\(appId)/users" // The pushSub doesn't need to have a token. diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift index 92c7ddd0b..f9f1fe210 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift @@ -58,7 +58,7 @@ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { self.aliasLabel = aliasLabel self.aliasId = aliasId self.onNewSession = onNewSession - self.stringDescription = "OSRequestFetchUser with aliasLabel: \(aliasLabel) aliasId: \(aliasId)" + self.stringDescription = "" super.init() self.method = GET _ = prepareForExecution() // sets the path property @@ -88,7 +88,7 @@ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { self.aliasLabel = aliasLabel self.aliasId = aliasId self.onNewSession = coder.decodeBool(forKey: "onNewSession") - self.stringDescription = "OSRequestFetchUser with aliasLabel: \(aliasLabel) aliasId: \(aliasId)" + self.stringDescription = "" super.init() self.method = HTTPMethod(rawValue: rawMethod) self.timestamp = timestamp diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestUpdateProperties.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestUpdateProperties.swift index 1ad4b1de1..91290cead 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestUpdateProperties.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestUpdateProperties.swift @@ -34,9 +34,6 @@ class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { return stringDescription } - // TODO: does updating properties even have a response in which we need to hydrate from? Then we can get rid of modelToUpdate - // Yes we may, if we cleared local state - var modelToUpdate: OSPropertiesModel var identityModel: OSIdentityModel // TODO: Decide if addPushSubscriptionIdToAdditionalHeadersIfNeeded should block. @@ -44,7 +41,7 @@ class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { func prepareForExecution() -> Bool { if let onesignalId = identityModel.onesignalId, let appId = OneSignalConfigManager.getAppId() { - let _ = self.addPushSubscriptionIdToAdditionalHeaders() + _ = self.addPushSubscriptionIdToAdditionalHeaders() self.addJWTHeader(identityModel: identityModel) self.path = "apps/\(appId)/users/by/\(OS_ONESIGNAL_ID)/\(onesignalId)" return true @@ -55,10 +52,9 @@ class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { } } - init(properties: [String: Any], deltas: [String: Any]?, refreshDeviceMetadata: Bool?, modelToUpdate: OSPropertiesModel, identityModel: OSIdentityModel) { - self.modelToUpdate = modelToUpdate + init(properties: [String: Any], deltas: [String: Any]?, refreshDeviceMetadata: Bool?, identityModel: OSIdentityModel) { self.identityModel = identityModel - self.stringDescription = "" + self.stringDescription = "" super.init() var propertiesObject = properties @@ -79,7 +75,6 @@ class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { } func encode(with coder: NSCoder) { - coder.encode(modelToUpdate, forKey: "modelToUpdate") coder.encode(identityModel, forKey: "identityModel") coder.encode(parameters, forKey: "parameters") coder.encode(method.rawValue, forKey: "method") // Encodes as String @@ -88,7 +83,6 @@ class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { required init?(coder: NSCoder) { guard - let modelToUpdate = coder.decodeObject(forKey: "modelToUpdate") as? OSPropertiesModel, let identityModel = coder.decodeObject(forKey: "identityModel") as? OSIdentityModel, let rawMethod = coder.decodeObject(forKey: "method") as? UInt32, let parameters = coder.decodeObject(forKey: "parameters") as? [String: Any], @@ -97,7 +91,6 @@ class OSRequestUpdateProperties: OneSignalRequest, OSUserRequest { // Log error return nil } - self.modelToUpdate = modelToUpdate self.identityModel = identityModel self.stringDescription = "" super.init() diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSUserRequest.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSUserRequest.swift index 43057203e..f6020c140 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSUserRequest.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSUserRequest.swift @@ -41,7 +41,7 @@ internal extension OneSignalRequest { // additionalHeaders["Authorization"] = "Bearer \(token)" // self.additionalHeaders = additionalHeaders } - + /** Returns if the `OneSignal-Subscription-Id` header was added successfully. */ func addPushSubscriptionIdToAdditionalHeaders() -> Bool { if let pushSubscriptionId = OneSignalUserManagerImpl.sharedInstance.pushSubscriptionId { diff --git a/iOS_SDK/OneSignalSDK/OneSignalUserMocks/.swiftlint.yml b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/.swiftlint.yml new file mode 100644 index 000000000..9cbe9dd8f --- /dev/null +++ b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/.swiftlint.yml @@ -0,0 +1,2 @@ +disabled_rules: + - identifier_name diff --git a/iOS_SDK/OneSignalSDK/OneSignalUserMocks/MockUserDefines.swift b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/MockUserDefines.swift new file mode 100644 index 000000000..452c4c192 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/MockUserDefines.swift @@ -0,0 +1,5 @@ +public let anonUserOSID = "test_anon_user_onesignal_id" +public let userA_OSID = "test_user_a_onesignal_id" +public let userA_EUID = "test_user_a_external_id" +public let userB_OSID = "test_user_b_onesignal_id" +public let userB_EUID = "test_user_b_external_id" diff --git a/iOS_SDK/OneSignalSDK/OneSignalUserMocks/MockUserRequests.swift b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/MockUserRequests.swift new file mode 100644 index 000000000..fbc57ee59 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/MockUserRequests.swift @@ -0,0 +1,18 @@ +import OneSignalCore + +public class MockUserRequests { + + public static func testIdentityPayload(onesignalId: String, externalId: String?) -> [String: [String: String]] { + var aliases = [OS_ONESIGNAL_ID: onesignalId] + aliases[OS_EXTERNAL_ID] = externalId // only add if non-nil + return [ + "identity": aliases + ] + } + + public static func testPropertiesPayload(properties: [String: Any]) -> [String: Any] { + return [ + "properties": properties + ] + } +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalUserMocks/OneSignalUserMocks.swift b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/OneSignalUserMocks.swift index 229a6e27d..cedfb6868 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUserMocks/OneSignalUserMocks.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUserMocks/OneSignalUserMocks.swift @@ -36,39 +36,47 @@ public class OneSignalUserMocks: NSObject { public static func reset() { resetStaticUserExecutor() - resetUserManager() + // TODO: Reset Operation Repo first + // OSCoreMocks.resetOperationRepo() + OneSignalUserManagerImpl.sharedInstance.reset() } public static func resetStaticUserExecutor() { OSUserExecutor.userRequestQueue.removeAll() OSUserExecutor.transferSubscriptionRequestQueue.removeAll() - OSUserExecutor.identityModels.removeAll() } +} + +extension OSIdentityModelRepo { + func reset() { + self.models = [:] + } +} - /** +extension OneSignalUserManagerImpl { + /** User Manager needs to reset between tests until we dependency inject the User Manager. For example, executors it owns may have cached requests or deltas that would have carried over. This is adapting as more data needs to be considered and reset... */ - public static func resetUserManager() { - OneSignalUserManagerImpl.sharedInstance.identityModelStore.clearModelsFromStore() - OneSignalUserManagerImpl.sharedInstance.propertiesModelStore.clearModelsFromStore() - OneSignalUserManagerImpl.sharedInstance.subscriptionModelStore.clearModelsFromStore() - OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.clearModelsFromStore() + func reset() { + identityModelRepo.reset() - let propertyExecutor = OSPropertyOperationExecutor() - let identityExecutor = OSIdentityOperationExecutor() - let subscriptionExecutor = OSSubscriptionOperationExecutor() + // Model store listeners unsubscribe to their models + // User Manager start() will subscribe them + identityModelStoreListener.close() + propertiesModelStoreListener.close() + subscriptionModelStoreListener.close() + pushSubscriptionModelStoreListener.close() - OneSignalUserManagerImpl.sharedInstance.propertyExecutor = propertyExecutor - OneSignalUserManagerImpl.sharedInstance.identityExecutor = identityExecutor - OneSignalUserManagerImpl.sharedInstance.subscriptionExecutor = subscriptionExecutor + // Executor instances do no need to be reset, they are initailized in start() - // TODO: Reset Operation Repo first - // OSCoreMocks.resetOperationRepo() + identityModelStore.clearModelsFromStore() + propertiesModelStore.clearModelsFromStore() + subscriptionModelStore.clearModelsFromStore() + pushSubscriptionModelStore.clearModelsFromStore() - OSOperationRepo.sharedInstance.addExecutor(identityExecutor) - OSOperationRepo.sharedInstance.addExecutor(propertyExecutor) - OSOperationRepo.sharedInstance.addExecutor(subscriptionExecutor) + _user = nil + hasCalledStart = false } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserTests.swift b/iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserTests.swift index d8111396c..c3b5d3ce5 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserTests.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserTests.swift @@ -43,7 +43,7 @@ final class OneSignalUserTests: XCTestCase { } override func tearDownWithError() throws { - // TODO: Need to clear all data between tests for user manager, models, etc. + // TODO: Need to clear all data between tests for client, user manager, models, etc. OneSignalCoreMocks.clearUserDefaults() OneSignalUserMocks.reset() } @@ -137,4 +137,83 @@ final class OneSignalUserTests: XCTestCase { identityModel.clearData() } } + + func testSwitchUser_sendsCorrectTags() throws { + /* Setup */ + + let client = MockOneSignalClient() + + // 1. Set up mock responses for the anonymous user + let anonCreateResponse = MockUserRequests.testIdentityPayload(onesignalId: anonUserOSID, externalId: nil) + + client.setMockResponseForRequest( + request: "", + response: anonCreateResponse) + + // 2. Set up mock responses for User A + let tagsUserA = ["tag_a": "value_a"] + let createUserA = MockUserRequests.testIdentityPayload(onesignalId: userA_OSID, externalId: userA_EUID) + let tagsResponseUserA = MockUserRequests.testPropertiesPayload(properties: ["tags": tagsUserA]) + + client.setMockResponseForRequest( + request: "", + response: createUserA + ) + client.setMockResponseForRequest( + request: "", + response: createUserA + ) + client.setMockResponseForRequest( + request: "", + response: tagsResponseUserA + ) + + // 3. Set up mock responses for User B + let tagsUserB = ["tag_b": "value_b"] + let createUserB = MockUserRequests.testIdentityPayload(onesignalId: userB_OSID, externalId: userB_EUID) + let tagsResponseUserB = MockUserRequests.testPropertiesPayload(properties: ["tags": tagsUserB]) + + client.setMockResponseForRequest( + request: "", + response: createUserB + ) + client.setMockResponseForRequest( + request: "", + response: createUserB + ) + client.setMockResponseForRequest( + request: "", + response: tagsResponseUserB) + + OneSignalCoreImpl.setSharedClient(client) + + /* When */ + + // 1. Login to user A and add tag + OneSignalUserManagerImpl.sharedInstance.login(externalId: userA_EUID, token: nil) + OneSignalUserManagerImpl.sharedInstance.addTag(key: "tag_a", value: "value_a") + + // 2. Login to user B and add tag + OneSignalUserManagerImpl.sharedInstance.login(externalId: userB_EUID, token: nil) + OneSignalUserManagerImpl.sharedInstance.addTag(key: "tag_b", value: "value_b") + + // 3. Run background threads + OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.5) + + /* Then */ + + // Assert that every request SDK makes has a response set, and is handled + XCTAssertTrue(client.allRequestsHandled) + + // Assert there is only one request containing these tags and they are sent to userA + XCTAssertTrue(client.onlyOneRequest( + contains: "apps/test-app-id/users/by/onesignal_id/\(userA_OSID)", + contains: ["properties": ["tags": tagsUserA]]) + ) + // Assert there is only one request containing these tags and they are sent to userB + XCTAssertTrue(client.onlyOneRequest( + contains: "apps/test-app-id/users/by/onesignal_id/\(userB_OSID)", + contains: ["properties": ["tags": tagsUserB]]) + ) + } }