From 3ff2259e5ab6fafbe674b389b7da59a5f67752b2 Mon Sep 17 00:00:00 2001 From: Nan Date: Tue, 28 May 2024 12:47:26 -0700 Subject: [PATCH 01/13] [wip] dump method --- .../OneSignalDevApp/SwiftTest.swift | 1 + .../OneSignalDevApp/ViewController.m | 1 + .../OneSignal.xcodeproj/project.pbxproj | 4 ++ .../OneSignalCore/Source/OneSignalLog.h | 3 +- .../OneSignalCore/Source/OneSignalLog.m | 19 ++++++-- .../OneSignalOSCore/Source/OSLoggable.swift | 30 ++++++++++++ .../Source/OSOperationExecutor.swift | 2 +- .../Source/OSOperationRepo.swift | 17 ++++++- .../OSIdentityOperationExecutor.swift | 22 +++++++++ .../OSPropertyOperationExecutor.swift | 16 +++++++ .../OSSubscriptionOperationExecutor.swift | 26 ++++++++++ .../Source/Executors/OSUserExecutor.swift | 11 +++++ .../Source/OneSignalUserManagerImpl.swift | 47 +++++++++++++++++++ iOS_SDK/OneSignalSDK/Source/OneSignal.m | 11 +++++ 14 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSLoggable.swift diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift index eae4a119f..0ba2773de 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift @@ -32,5 +32,6 @@ class SwiftTest: NSObject { func testSwiftUserModel() { let token1 = OneSignal.User.pushSubscription.token let token = OneSignal.User.pushSubscription.token + OneSignal.Debug.dump() } } diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m index 9bcc2d37f..0d10fba2e 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m @@ -133,6 +133,7 @@ - (IBAction)sendTagButton:(id)sender { - (IBAction)getTagsButton:(id)sender { NSDictionary *tags = [OneSignal.User getTags]; NSLog(@"Tags: %@", tags); + [OneSignal.Debug dump]; } - (IBAction)sendTagsButton:(id)sender { diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index ac5dacd67..4966694fd 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -56,6 +56,7 @@ 3C0151922C2E298F0079E076 /* OneSignalInAppMessages.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEBAAE282A4211D900BF2C1C /* OneSignalInAppMessages.framework */; }; 3C01519C2C2E29F90079E076 /* IAMRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C01519B2C2E29F90079E076 /* IAMRequestTests.m */; }; 3C0EF49E28A1DBCB00E5434B /* OSUserInternalImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C0EF49D28A1DBCB00E5434B /* OSUserInternalImpl.swift */; }; + 3C10E0632BF5651500A7B37F /* OSLoggable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C10E0622BF5651500A7B37F /* OSLoggable.swift */; }; 3C115165289A259500565C41 /* OneSignalOSCore.docc in Sources */ = {isa = PBXBuildFile; fileRef = 3C115164289A259500565C41 /* OneSignalOSCore.docc */; }; 3C115171289A259500565C41 /* OneSignalOSCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C115163289A259500565C41 /* OneSignalOSCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3C115185289ADE4F00565C41 /* OSModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C115184289ADE4F00565C41 /* OSModel.swift */; }; @@ -1208,6 +1209,7 @@ 3C01518E2C2E298E0079E076 /* OneSignalInAppMessagesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OneSignalInAppMessagesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3C01519B2C2E29F90079E076 /* IAMRequestTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IAMRequestTests.m; sourceTree = ""; }; 3C0EF49D28A1DBCB00E5434B /* OSUserInternalImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OSUserInternalImpl.swift; sourceTree = ""; }; + 3C10E0622BF5651500A7B37F /* OSLoggable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSLoggable.swift; sourceTree = ""; }; 3C115161289A259500565C41 /* OneSignalOSCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OneSignalOSCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3C115163289A259500565C41 /* OneSignalOSCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalOSCore.h; sourceTree = ""; }; 3C115164289A259500565C41 /* OneSignalOSCore.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = OneSignalOSCore.docc; sourceTree = ""; }; @@ -2061,6 +2063,7 @@ 4710EA522B8FCFB200435356 /* OSDispatchQueue.swift */, DEFB3E642BB7346D00E65DAD /* OSLiveActivities.swift */, DEFB3E662BB735B500E65DAD /* OSStubLiveActivities.swift */, + 3C10E0622BF5651500A7B37F /* OSLoggable.swift */, ); path = Source; sourceTree = ""; @@ -4035,6 +4038,7 @@ DEFB3E652BB7346D00E65DAD /* OSLiveActivities.swift in Sources */, 3C4F9E4428A4466C009F453A /* OSOperationRepo.swift in Sources */, 3C11518B289ADEEB00565C41 /* OSEventProducer.swift in Sources */, + 3C10E0632BF5651500A7B37F /* OSLoggable.swift in Sources */, 3C115165289A259500565C41 /* OneSignalOSCore.docc in Sources */, 3C115189289ADEA300565C41 /* OSModelStore.swift in Sources */, 3C115185289ADE4F00565C41 /* OSModel.swift in Sources */, diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h index d5e72bcbe..2e986229b 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h @@ -39,9 +39,10 @@ typedef NS_ENUM(NSUInteger, ONE_S_LOG_LEVEL) { @protocol OSDebug + (void)setLogLevel:(ONE_S_LOG_LEVEL)logLevel; + (void)setAlertLevel:(ONE_S_LOG_LEVEL)logLevel NS_REFINED_FOR_SWIFT; ++ (void)dump; @end -@interface OneSignalLog : NSObject +@interface OneSignalLog : NSObject + (Class)Debug; + (void)onesignalLog:(ONE_S_LOG_LEVEL)logLevel message:(NSString* _Nonnull)message; + (ONE_S_LOG_LEVEL)getLogLevel; diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m index 0a840128b..ca47f8a8d 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m @@ -29,6 +29,10 @@ #import "OneSignalLog.h" #import "OSDialogInstanceManager.h" +/** + Implements the setter methods of protocol `OSDebug`. + The `log` method will be implemented in OneSignal module. + */ @implementation OneSignalLog static ONE_S_LOG_LEVEL _nsLogLevel = ONE_S_LL_WARN; @@ -46,6 +50,9 @@ + (void)setAlertLevel:(ONE_S_LOG_LEVEL)logLevel { _alertLogLevel = logLevel; } ++ (void)dump {} + + + (void)onesignalLog:(ONE_S_LOG_LEVEL)logLevel message:(NSString* _Nonnull)message { onesignal_Log(logLevel, message); } @@ -79,9 +86,15 @@ void onesignal_Log(ONE_S_LOG_LEVEL logLevel, NSString* message) { default: break; } - - if (logLevel <= _nsLogLevel) - NSLog(@"%@", [levelString stringByAppendingString:message]); + + if (logLevel <= _nsLogLevel) { + NSString *thread = @""; + if ([NSThread.currentThread isMainThread]) { + thread = @"[main]: "; + } + // NSLog(@"%@", [levelString stringByAppendingString:message]); + NSLog(@"%@", [NSString stringWithFormat:@"%@%@%@", thread, levelString, message]); + } if (logLevel <= _alertLogLevel) { [[OSDialogInstanceManager sharedInstance] presentDialogWithTitle:levelString withMessage:message withActions:nil cancelTitle:NSLocalizedString(@"Close", @"Close button") withActionCompletion:nil]; diff --git a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSLoggable.swift b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSLoggable.swift new file mode 100644 index 000000000..a82b34927 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSLoggable.swift @@ -0,0 +1,30 @@ +/* + 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. + */ + +public protocol OSLoggable { + func logSelf() +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationExecutor.swift index 63c5e7a5d..6b1800075 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationExecutor.swift @@ -30,7 +30,7 @@ import OneSignalCore /** Concrete executors drop OSDeltas and Requests when initializing from the cache, when they cannot be connected to their respective models anymore. These cannot be sent, so they are dropped.. */ -public protocol OSOperationExecutor { +public protocol OSOperationExecutor: OSLoggable { var supportedDeltas: [String] { get } var deltaQueue: [OSDelta] { get } diff --git a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift index 338fadd50..b148688d5 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift @@ -33,7 +33,7 @@ import OneSignalCore OSDeltas are enqueued when model store observers observe changes to their models, and sorted to their appropriate executors. */ public class OSOperationRepo: NSObject { - public static let sharedInstance = OSOperationRepo() + @objc public static let sharedInstance = OSOperationRepo() private var hasCalledStart = false // The Operation Repo dispatch queue, serial. This synchronizes access to `deltaQueue` and flushing behavior. @@ -173,3 +173,18 @@ public class OSOperationRepo: NSObject { } } } + +extension OSOperationRepo: OSLoggable { + @objc + public func logSelf() { + print("💛 Operation Repo: executors") + for executor in self.executors { + executor.logSelf() + } + + print("💛 Operation Repo: deltaQueue: ") + for delta in self.deltaQueue { + print(delta) + } + } +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift index 0a428abaa..b6aab45e0 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift @@ -291,3 +291,25 @@ class OSIdentityOperationExecutor: OSOperationExecutor { } } } + +extension OSIdentityOperationExecutor: OSLoggable { + func logSelf() { + print("💛 OSIdentityOperationExecutor has the following queues: ") + print( + """ + 💛 addRequestQueue: \(self.addRequestQueue) + """ + ) + print( + """ + 💛 removeRequestQueue: \(self.removeRequestQueue) + """ + ) + print( + """ + 💛 deltaQueue: \(self.deltaQueue) + """ + ) + + } +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift index 5a0625f67..053cc3693 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift @@ -286,3 +286,19 @@ class OSPropertyOperationExecutor: OSOperationExecutor { } } } + +extension OSPropertyOperationExecutor: OSLoggable { + func logSelf() { + print("💛 OSPropertyOperationExecutor has the following queues: ") + print( + """ + 💛 updateRequestQueue: \(self.updateRequestQueue) + """ + ) + print( + """ + 💛 deltaQueue: \(self.deltaQueue) + """ + ) + } +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift index 837a49eb1..5ed6e749f 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift @@ -419,3 +419,29 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { } } } + +extension OSSubscriptionOperationExecutor: OSLoggable { + func logSelf() { + print("💛 OSSubscriptionOperationExecutor has the following queues: ") + print( + """ + 💛 addRequestQueue: \(self.addRequestQueue) + """ + ) + print( + """ + 💛 removeRequestQueue: \(self.removeRequestQueue) + """ + ) + print( + """ + 💛 updateRequestQueue: \(self.updateRequestQueue) + """ + ) + print( + """ + 💛 deltaQueue: \(self.deltaQueue) + """ + ) + } +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift index 52e5d0534..04af7d459 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift @@ -558,3 +558,14 @@ extension OSUserExecutor { return response?["identity"] as? [String: String] } } + +extension OSUserExecutor: OSLoggable { + func logSelf() { + print("💛 OSUserExecutor has the following queues: ") + print( + """ + 💛 userRequestQueue: \(self.userRequestQueue) + """ + ) + } +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index 9a2973d74..32d9a4b02 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -871,3 +871,50 @@ extension OneSignalUserManagerImpl: OneSignalNotificationsDelegate { user.pushSubscriptionModel.address = pushToken } } + +extension OneSignalUserManagerImpl: OSLoggable { + @objc + public func logSelf() { + print("💛 _user: \(String(describing: _user))") + print( + """ + 💛 identityModel: + aliases: \(String(describing: _user?.identityModel.aliases)) | + modelId: \(String(describing: _user?.identityModel.modelId)) | + """ + ) + print( + """ + 💛 propertiesModel: + tags: \(String(describing: _user?.propertiesModel.tags)) | + language: \(String(describing: _user?.propertiesModel.language)) | + modelId: \(String(describing: _user?.propertiesModel.modelId)) | + """ + ) + let subscriptionModels = subscriptionModelStore.getModels().values + for sub in subscriptionModels { + print( + """ + 💛 subscription model from store | + addess: \(String(describing: sub.address)) | + subscriptionId: \(String(describing: sub.subscriptionId)) | + enabled: \(sub.enabled) | + modelId: \(sub.modelId) + """ + ) + } + let pushSubModel = pushSubscriptionModelStore.getModel(key: OS_PUSH_SUBSCRIPTION_MODEL_KEY) + print( + """ + 💛 push sub model from store | + token: \(String(describing: pushSubModel?.address)) | + subscriptionId: \(String(describing: pushSubModel?.subscriptionId)) | + enabled: \(String(describing: pushSubModel?.enabled)) | + notification_types: \(String(describing: pushSubModel?.notificationTypes)) | + optedIn: \(String(describing: pushSubModel?.optedIn)) | + modelId: \(String(describing: pushSubModel?.modelId)) | + """ + ) + print("") + } +} diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index c3937cdbd..d9476ec5a 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -77,6 +77,17 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" +/** + Implements the `log` method of protocol `OSDebug`. + */ +@implementation OneSignalLog (OSLoggable) ++ (void)dump { + NSLog(@"💛 calling LOG!"); + [OneSignalUserManagerImpl.sharedInstance logSelf]; + [OSOperationRepo.sharedInstance logSelf]; +} +@end + @interface OneSignal (SessionStatusDelegate) @end From 615dd8e036af4ebc53cdb7f0f764bbf4b05652b0 Mon Sep 17 00:00:00 2001 From: Nan Date: Sat, 3 Aug 2024 19:42:18 -0700 Subject: [PATCH 02/13] [dev app] for testing, revert later * Add text fields and buttons for testing * remove app clips --- .../Base.lproj/Main.storyboard | 51 +++- .../OneSignalDevApp/SwiftTest.swift | 1 + .../OneSignalDevApp/ViewController.h | 2 + .../OneSignalDevApp/ViewController.m | 12 +- .../OneSignalDevAppClip/ViewController.h | 1 + .../project.pbxproj | 227 +----------------- 6 files changed, 59 insertions(+), 235 deletions(-) diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard index 9b87b6079..88c710cc0 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -257,17 +257,25 @@ - - + + + + + + + + + @@ -528,13 +536,24 @@ + - @@ -566,22 +585,25 @@ + - + + + @@ -598,9 +620,11 @@ + + @@ -616,6 +640,7 @@ + @@ -630,6 +655,7 @@ + @@ -645,10 +671,12 @@ + + @@ -658,7 +686,6 @@ - @@ -667,7 +694,6 @@ - @@ -675,7 +701,6 @@ - @@ -747,7 +772,9 @@ + + @@ -760,7 +787,7 @@ - + diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift index 0ba2773de..aa779908e 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift @@ -33,5 +33,6 @@ class SwiftTest: NSObject { let token1 = OneSignal.User.pushSubscription.token let token = OneSignal.User.pushSubscription.token OneSignal.Debug.dump() + OneSignal.updateUserJwt("externalId", withToken: "jwtToken") } } diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.h b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.h index 8e4b15988..6e778926d 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.h +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.h @@ -52,7 +52,9 @@ @property (weak, nonatomic) IBOutlet UIButton *removeSmsButton; @property (weak, nonatomic) IBOutlet UITextField *externalUserIdTextField; +@property (weak, nonatomic) IBOutlet UITextField *tokenTextField; @property (weak, nonatomic) IBOutlet UIButton *loginExternalUserIdButton; +@property (weak, nonatomic) IBOutlet UIButton *updateJwtButton; @property (weak, nonatomic) IBOutlet UIButton *logoutButton; @property (weak, nonatomic) IBOutlet UITextField *addAliasLabelTextField; diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m index 0d10fba2e..d9b07fdda 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/ViewController.m @@ -196,8 +196,16 @@ - (IBAction)inAppMessagingSegmentedControlValueChanged:(UISegmentedControl *)sen - (IBAction)loginExternalUserId:(UIButton *)sender { NSString* externalUserId = self.externalUserIdTextField.text; - NSLog(@"Dev App: Logging in to external user ID %@", externalUserId); - [OneSignal login:externalUserId]; + NSString* token = self.tokenTextField.text; + NSLog(@"❌ Dev App: Logging in to external user ID %@ and token %@", externalUserId, token); + [OneSignal login:externalUserId withToken:token]; +} + +- (IBAction)updateJwt:(id)sender { + NSString* externalUserId = self.externalUserIdTextField.text; + NSString* token = self.tokenTextField.text; + NSLog(@"❌ Dev App: updating JWT for external user ID %@ and token %@", externalUserId, token); + [OneSignal updateUserJwt:externalUserId withToken:token]; } - (IBAction)logout:(UIButton *)sender { diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.h b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.h index b11c2c05b..f1a45b976 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.h +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevAppClip/ViewController.h @@ -54,6 +54,7 @@ @property (weak, nonatomic) IBOutlet UITextField *addTriggerValue; @property (weak, nonatomic) IBOutlet UIButton *addTriggerButton; @property (weak, nonatomic) IBOutlet UITextField *removeTriggerKey; +- (IBAction)updateJwt:(id)sender; @property (weak, nonatomic) IBOutlet UITextField *getTriggerKey; @property (weak, nonatomic) IBOutlet UILabel *infoLabel; @property (weak, nonatomic) IBOutlet UITextField *outcomeName; diff --git a/iOS_SDK/OneSignalDevApp/OneSignalExample.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalDevApp/OneSignalExample.xcodeproj/project.pbxproj index 556c0c10e..52c8cceae 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalExample.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalDevApp/OneSignalExample.xcodeproj/project.pbxproj @@ -47,32 +47,6 @@ DE12F3F8289B2B7F002F63AA /* OneSignalOSCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE12F3F6289B2B7F002F63AA /* OneSignalOSCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DE61E483294810B900CD12F1 /* OneSignalFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E482294810B900CD12F1 /* OneSignalFramework.framework */; }; DE61E484294810B900CD12F1 /* OneSignalFramework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E482294810B900CD12F1 /* OneSignalFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - DE61E4852948117000CD12F1 /* OneSignalExampleClip.app in Embed App Clips */ = {isa = PBXBuildFile; fileRef = DE68DA5724C7695900FC95A8 /* OneSignalExampleClip.app */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - DE61E48A2948117A00CD12F1 /* OneSignalCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E4892948117A00CD12F1 /* OneSignalCore.framework */; }; - DE61E48B2948117A00CD12F1 /* OneSignalCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E4892948117A00CD12F1 /* OneSignalCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - DE61E48E2948117D00CD12F1 /* OneSignalExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E48D2948117D00CD12F1 /* OneSignalExtension.framework */; }; - DE61E48F2948117D00CD12F1 /* OneSignalExtension.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E48D2948117D00CD12F1 /* OneSignalExtension.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - DE61E4912948118200CD12F1 /* OneSignalFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E4902948118200CD12F1 /* OneSignalFramework.framework */; }; - DE61E4922948118200CD12F1 /* OneSignalFramework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E4902948118200CD12F1 /* OneSignalFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - DE61E4942948118500CD12F1 /* OneSignalNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E4932948118500CD12F1 /* OneSignalNotifications.framework */; }; - DE61E4952948118500CD12F1 /* OneSignalNotifications.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E4932948118500CD12F1 /* OneSignalNotifications.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - DE61E4972948118900CD12F1 /* OneSignalOSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E4962948118900CD12F1 /* OneSignalOSCore.framework */; }; - DE61E4982948118900CD12F1 /* OneSignalOSCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E4962948118900CD12F1 /* OneSignalOSCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - DE61E49A2948118C00CD12F1 /* OneSignalOutcomes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E4992948118C00CD12F1 /* OneSignalOutcomes.framework */; }; - DE61E49B2948118D00CD12F1 /* OneSignalOutcomes.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E4992948118C00CD12F1 /* OneSignalOutcomes.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - DE61E49D2948119100CD12F1 /* OneSignalUser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E49C2948119100CD12F1 /* OneSignalUser.framework */; }; - DE61E49E2948119100CD12F1 /* OneSignalUser.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE61E49C2948119100CD12F1 /* OneSignalUser.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - DE68DA5B24C7695900FC95A8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DE68DA5A24C7695900FC95A8 /* AppDelegate.m */; }; - DE68DA5E24C7695900FC95A8 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DE68DA5D24C7695900FC95A8 /* SceneDelegate.m */; }; - DE68DA6124C7695900FC95A8 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DE68DA6024C7695900FC95A8 /* ViewController.m */; }; - DE68DA6424C7695900FC95A8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DE68DA6224C7695900FC95A8 /* Main.storyboard */; }; - DE68DA6624C7695A00FC95A8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DE68DA6524C7695A00FC95A8 /* Assets.xcassets */; }; - DE68DA6924C7695A00FC95A8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DE68DA6724C7695A00FC95A8 /* LaunchScreen.storyboard */; }; - DE68DA6C24C7695A00FC95A8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DE68DA6B24C7695A00FC95A8 /* main.m */; }; - DE68DA7724C769F200FC95A8 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03432CDB1EBD426A0071FC48 /* CoreLocation.framework */; }; - DE68DA7824C769F900FC95A8 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9112E8A61E724EE00022A1CB /* SystemConfiguration.framework */; }; - DE68DA7924C76A0300FC95A8 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91B6EA051E83215000B5CF01 /* UserNotifications.framework */; }; - DE68DA7A24C76A1800FC95A8 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CACBAAB5218A7136000ACAA5 /* WebKit.framework */; }; DE7D180727026BB5002D3A5D /* OneSignalCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE7D180627026BB5002D3A5D /* OneSignalCore.framework */; }; DE7D180827026BB5002D3A5D /* OneSignalCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE7D180627026BB5002D3A5D /* OneSignalCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DE7D180927026BC5002D3A5D /* OneSignalCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE7D180627026BB5002D3A5D /* OneSignalCore.framework */; }; @@ -107,13 +81,6 @@ remoteGlobalIDString = 945C59DB296CF2A00097041D; remoteInfo = OneSignalWidgetExtensionExtension; }; - DE61E4862948117000CD12F1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 9112E87A1E724C320022A1CB /* Project object */; - proxyType = 1; - remoteGlobalIDString = DE68DA5624C7695900FC95A8; - remoteInfo = OneSignalExampleClip; - }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -135,28 +102,10 @@ dstPath = "$(CONTENTS_FOLDER_PATH)/AppClips"; dstSubfolderSpec = 16; files = ( - DE61E4852948117000CD12F1 /* OneSignalExampleClip.app in Embed App Clips */, ); name = "Embed App Clips"; runOnlyForDeploymentPostprocessing = 0; }; - DE61E48C2948117A00CD12F1 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - DE61E4952948118500CD12F1 /* OneSignalNotifications.framework in Embed Frameworks */, - DE61E49E2948119100CD12F1 /* OneSignalUser.framework in Embed Frameworks */, - DE61E4922948118200CD12F1 /* OneSignalFramework.framework in Embed Frameworks */, - DE61E48B2948117A00CD12F1 /* OneSignalCore.framework in Embed Frameworks */, - DE61E48F2948117D00CD12F1 /* OneSignalExtension.framework in Embed Frameworks */, - DE61E49B2948118D00CD12F1 /* OneSignalOutcomes.framework in Embed Frameworks */, - DE61E4982948118900CD12F1 /* OneSignalOSCore.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; DEA226FA261F74060092FF58 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -232,7 +181,6 @@ DE61E4962948118900CD12F1 /* OneSignalOSCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = OneSignalOSCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DE61E4992948118C00CD12F1 /* OneSignalOutcomes.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = OneSignalOutcomes.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DE61E49C2948119100CD12F1 /* OneSignalUser.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = OneSignalUser.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - DE68DA5724C7695900FC95A8 /* OneSignalExampleClip.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OneSignalExampleClip.app; sourceTree = BUILT_PRODUCTS_DIR; }; DE68DA5924C7695900FC95A8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; DE68DA5A24C7695900FC95A8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; DE68DA5C24C7695900FC95A8 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; @@ -309,24 +257,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - DE68DA5424C7695900FC95A8 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - DE61E48A2948117A00CD12F1 /* OneSignalCore.framework in Frameworks */, - DE61E4912948118200CD12F1 /* OneSignalFramework.framework in Frameworks */, - DE61E4972948118900CD12F1 /* OneSignalOSCore.framework in Frameworks */, - DE61E48E2948117D00CD12F1 /* OneSignalExtension.framework in Frameworks */, - DE61E49A2948118C00CD12F1 /* OneSignalOutcomes.framework in Frameworks */, - DE61E49D2948119100CD12F1 /* OneSignalUser.framework in Frameworks */, - DE68DA7A24C76A1800FC95A8 /* WebKit.framework in Frameworks */, - DE61E4942948118500CD12F1 /* OneSignalNotifications.framework in Frameworks */, - DE68DA7924C76A0300FC95A8 /* UserNotifications.framework in Frameworks */, - DE68DA7724C769F200FC95A8 /* CoreLocation.framework in Frameworks */, - DE68DA7824C769F900FC95A8 /* SystemConfiguration.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -349,7 +279,6 @@ children = ( 9112E8821E724C320022A1CB /* OneSignalExample.app */, 9150E7721E73BEDC00C5D46A /* OneSignalNotificationServiceExtension.appex */, - DE68DA5724C7695900FC95A8 /* OneSignalExampleClip.app */, 945C59DC296CF2A00097041D /* OneSignalWidgetExtensionExtension.appex */, ); name = Products; @@ -491,7 +420,6 @@ ); dependencies = ( 9150E7791E73BEDD00C5D46A /* PBXTargetDependency */, - DE61E4872948117000CD12F1 /* PBXTargetDependency */, 945C59EF296CF2A10097041D /* PBXTargetDependency */, ); name = OneSignalExample; @@ -537,26 +465,6 @@ productReference = 945C59DC296CF2A00097041D /* OneSignalWidgetExtensionExtension.appex */; productType = "com.apple.product-type.app-extension"; }; - DE68DA5624C7695900FC95A8 /* OneSignalExampleClip */ = { - isa = PBXNativeTarget; - buildConfigurationList = DE68DA7424C7695A00FC95A8 /* Build configuration list for PBXNativeTarget "OneSignalExampleClip" */; - buildPhases = ( - DE68DA5324C7695900FC95A8 /* Sources */, - DE68DA5424C7695900FC95A8 /* Frameworks */, - DE68DA5524C7695900FC95A8 /* Resources */, - DE61E48C2948117A00CD12F1 /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = OneSignalExampleClip; - packageProductDependencies = ( - ); - productName = OneSignalDevAppClip; - productReference = DE68DA5724C7695900FC95A8 /* OneSignalExampleClip.app */; - productType = "com.apple.product-type.application.on-demand-install-capable"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -569,7 +477,6 @@ TargetAttributes = { 9112E8811E724C320022A1CB = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 99SW8E36CT; LastSwiftMigration = 1320; ProvisioningStyle = Automatic; SystemCapabilities = { @@ -597,9 +504,6 @@ 945C59DB296CF2A00097041D = { CreatedOnToolsVersion = 14.1; }; - DE68DA5624C7695900FC95A8 = { - CreatedOnToolsVersion = 12.0; - }; }; }; buildConfigurationList = 9112E87D1E724C320022A1CB /* Build configuration list for PBXProject "OneSignalExample" */; @@ -620,7 +524,6 @@ targets = ( 9112E8811E724C320022A1CB /* OneSignalExample */, 9150E7711E73BEDC00C5D46A /* OneSignalNotificationServiceExtension */, - DE68DA5624C7695900FC95A8 /* OneSignalExampleClip */, 945C59DB296CF2A00097041D /* OneSignalWidgetExtensionExtension */, ); }; @@ -656,16 +559,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - DE68DA5524C7695900FC95A8 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - DE68DA6924C7695A00FC95A8 /* LaunchScreen.storyboard in Resources */, - DE68DA6624C7695A00FC95A8 /* Assets.xcassets in Resources */, - DE68DA6424C7695900FC95A8 /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -703,17 +596,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - DE68DA5324C7695900FC95A8 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - DE68DA6124C7695900FC95A8 /* ViewController.m in Sources */, - DE68DA5B24C7695900FC95A8 /* AppDelegate.m in Sources */, - DE68DA6C24C7695A00FC95A8 /* main.m in Sources */, - DE68DA5E24C7695900FC95A8 /* SceneDelegate.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -728,12 +610,6 @@ target = 945C59DB296CF2A00097041D /* OneSignalWidgetExtensionExtension */; targetProxy = 945C59EE296CF2A10097041D /* PBXContainerItemProxy */; }; - DE61E4872948117000CD12F1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - platformFilter = ios; - target = DE68DA5624C7695900FC95A8 /* OneSignalExampleClip */; - targetProxy = DE61E4862948117000CD12F1 /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -887,7 +763,7 @@ ); MARKETING_VERSION = 1.4.4; OTHER_LDFLAGS = "-ObjC"; - PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example; + PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.staging; PRODUCT_NAME = OneSignalExample; SUPPORTS_MACCATALYST = YES; SWIFT_OBJC_BRIDGING_HEADER = "OneSignalDevApp/OneSignalExample-Bridging-Header.h"; @@ -919,7 +795,7 @@ ); MARKETING_VERSION = 1.4.4; OTHER_LDFLAGS = "-ObjC"; - PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example; + PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.staging; PRODUCT_NAME = OneSignalExample; SUPPORTS_MACCATALYST = YES; SWIFT_OBJC_BRIDGING_HEADER = "OneSignalDevApp/OneSignalExample-Bridging-Header.h"; @@ -947,7 +823,7 @@ ); MARKETING_VERSION = 1.4.4; OTHER_LDFLAGS = "-ObjC"; - PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.OneSignalNotificationServiceExtensionA; + PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.staging.OneSignalNotificationServiceExtensionA; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = YES; @@ -975,7 +851,7 @@ ); MARKETING_VERSION = 1.4.4; OTHER_LDFLAGS = "-ObjC"; - PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.OneSignalNotificationServiceExtensionA; + PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.staging.OneSignalNotificationServiceExtensionA; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = YES; @@ -1018,7 +894,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.OneSignalWidgetExtension; + PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.staging.OneSignalWidgetExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = YES; @@ -1065,7 +941,7 @@ ); MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.OneSignalWidgetExtension; + PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.staging.OneSignalWidgetExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = YES; @@ -1077,88 +953,6 @@ }; name = Release; }; - DE68DA7224C7695A00FC95A8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_ENTITLEMENTS = OneSignalDevAppClip/OneSignalDevAppClip.entitlements; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1.4.4; - DEVELOPMENT_TEAM = 99SW8E36CT; - "DYLIB_INSTALL_NAME_BASE[arch=*]" = "@rpath"; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - OS_APP_CLIP, - ); - INFOPLIST_FILE = OneSignalDevAppClip/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.4.4; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.Clip; - PRODUCT_NAME = OneSignalExampleClip; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - DE68DA7324C7695A00FC95A8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_ENTITLEMENTS = OneSignalDevAppClip/OneSignalDevAppClip.entitlements; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1.4.4; - DEVELOPMENT_TEAM = 99SW8E36CT; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_PREPROCESSOR_DEFINITIONS = OS_APP_CLIP; - INFOPLIST_FILE = OneSignalDevAppClip/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.4.4; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.onesignal.example.Clip; - PRODUCT_NAME = OneSignalExampleClip; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1198,15 +992,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - DE68DA7424C7695A00FC95A8 /* Build configuration list for PBXNativeTarget "OneSignalExampleClip" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - DE68DA7224C7695A00FC95A8 /* Debug */, - DE68DA7324C7695A00FC95A8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = 9112E87A1E724C320022A1CB /* Project object */; From 366da5d2ace1362f7ada1ba9bbfafb8acb16f004 Mon Sep 17 00:00:00 2001 From: Nan Date: Sun, 4 Aug 2024 10:04:31 -0700 Subject: [PATCH 03/13] [wip] Add internal JWT Config management and listener * Make OSUserJwtConfig object to handle auth status etc * User manager owns an instance of the JWT Config --- .../OneSignal.xcodeproj/project.pbxproj | 12 ++ .../Source/Jwt/OSUserJwtConfig.swift | 114 ++++++++++++++++++ .../Source/OneSignalUserManagerImpl.swift | 2 +- 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/Jwt/OSUserJwtConfig.swift diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index 4966694fd..1bbd1b755 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -74,6 +74,7 @@ 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 */; }; + 3C2FF9D02C5FCD760081293B /* OSUserJwtConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C2FF9CF2C5FCD760081293B /* OSUserJwtConfig.swift */; }; 3C44673E296D099D0039A49E /* OneSignalMobileProvision.m in Sources */ = {isa = PBXBuildFile; fileRef = 912411FD1E73342200E41FD7 /* OneSignalMobileProvision.m */; }; 3C44673F296D09CC0039A49E /* OneSignalMobileProvision.h in Headers */ = {isa = PBXBuildFile; fileRef = 912411FC1E73342200E41FD7 /* OneSignalMobileProvision.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3C448B9D2936ADFD002F96BC /* OSBackgroundTaskHandlerImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C448B9B2936ADFD002F96BC /* OSBackgroundTaskHandlerImpl.h */; }; @@ -1226,6 +1227,7 @@ 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 = ""; }; + 3C2FF9CF2C5FCD760081293B /* OSUserJwtConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSUserJwtConfig.swift; sourceTree = ""; }; 3C448B9B2936ADFD002F96BC /* OSBackgroundTaskHandlerImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSBackgroundTaskHandlerImpl.h; sourceTree = ""; }; 3C448B9C2936ADFD002F96BC /* OSBackgroundTaskHandlerImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OSBackgroundTaskHandlerImpl.m; sourceTree = ""; }; 3C448BA12936B474002F96BC /* OSBackgroundTaskManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSBackgroundTaskManager.swift; sourceTree = ""; }; @@ -2050,6 +2052,7 @@ isa = PBXGroup; children = ( 3C115163289A259500565C41 /* OneSignalOSCore.h */, + 3C2FF9CE2C5FCD590081293B /* Jwt */, 3C115188289ADEA300565C41 /* OSModelStore.swift */, 3C115186289ADE7700565C41 /* OSModelStoreListener.swift */, 3C115184289ADE4F00565C41 /* OSModel.swift */, @@ -2068,6 +2071,14 @@ path = Source; sourceTree = ""; }; + 3C2FF9CE2C5FCD590081293B /* Jwt */ = { + isa = PBXGroup; + children = ( + 3C2FF9CF2C5FCD760081293B /* OSUserJwtConfig.swift */, + ); + path = Jwt; + sourceTree = ""; + }; 3C8544B72C5AEFF700F542A9 /* OneSignalOSCoreMocks */ = { isa = PBXGroup; children = ( @@ -4047,6 +4058,7 @@ 3CE5F9E3289D88DC004A156E /* OSModelStoreChangedHandler.swift in Sources */, 3C2D8A5928B4C4E300BE41F6 /* OSDelta.swift in Sources */, 4710EA532B8FCFB200435356 /* OSDispatchQueue.swift in Sources */, + 3C2FF9D02C5FCD760081293B /* OSUserJwtConfig.swift in Sources */, DEFB3E672BB735B500E65DAD /* OSStubLiveActivities.swift in Sources */, 3C11518D289AF5E800565C41 /* OSModelChangedHandler.swift in Sources */, 3C8E6DF928A6D89E0031E48A /* OSOperationExecutor.swift in Sources */, diff --git a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/Jwt/OSUserJwtConfig.swift b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/Jwt/OSUserJwtConfig.swift new file mode 100644 index 000000000..19ce2891f --- /dev/null +++ b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/Jwt/OSUserJwtConfig.swift @@ -0,0 +1,114 @@ +/* + 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 +import OneSignalCore + +/** + Use an enum to avoid working with optional Bool, which is unsightly to cache and uncache. + */ +enum OSRequiresUserAuth: String { + case on + case off + case unknown + // TODO: JWT 🔐 consider additional reasons such as detecting this by dev calling loginWithJWT / onViaRemoteParams + + func isRequired() -> Bool? { + return switch self { + case .on: + true + case .off: + false + default: + nil + } + } +} + +/** + Internal listener. + */ +public protocol OSUserJwtConfigListener { + // TODO: JWT 🔐 exact callbacks TBD + func onRequiresUserAuthChanged(from: Bool?, to: Bool?) + func onJwtInvalidated(externalId: String, error: String?) + func onJwtUpdated(externalId: String, jwtToken: String) + func onJwtTokenChanged(externalId: String, from: String?, to: String?) +} + +public class OSUserJwtConfig { + public let changeNotifier = OSEventProducer() + + private var requiresUserAuth: OSRequiresUserAuth { + didSet { + guard oldValue != requiresUserAuth else { + return + } + + print("💛 OSUserJwtConfig.requiresUserAuth: changing from \(oldValue) to \(requiresUserAuth), firing \(changeNotifier)") + + // Persist new value + OneSignalUserDefaults.initShared().saveString(forKey: OSUD_USE_IDENTITY_VERIFICATION, withValue: requiresUserAuth.rawValue) + + self.changeNotifier.fire { listener in + listener.onRequiresUserAuthChanged(from: oldValue.isRequired(), to: requiresUserAuth.isRequired()) + } + } + } + + public var isRequired: Bool? { + get { + return requiresUserAuth.isRequired() + } + set { + requiresUserAuth = switch newValue { + case true: + OSRequiresUserAuth.on + case false: + OSRequiresUserAuth.off + default: + OSRequiresUserAuth.unknown + } + } + } + + public init() { + let rawValue = OneSignalUserDefaults.initShared().getSavedString(forKey: OSUD_USE_IDENTITY_VERIFICATION, defaultValue: OSRequiresUserAuth.unknown.rawValue) + + print("❌ OSUserJwtConfig init rawValue: \(String(describing: rawValue)), OSRequiresUserAuth: \(String(describing: OSRequiresUserAuth(rawValue: rawValue!)))") + + requiresUserAuth = OSRequiresUserAuth(rawValue: rawValue ?? OSRequiresUserAuth.unknown.rawValue) ?? OSRequiresUserAuth.unknown + } + + public func onJwtTokenChanged(externalId: String, from: String?, to: String?) { + if to == "invalid" { + changeNotifier.fire { listener in + listener.onJwtInvalidated(externalId: externalId, error: nil) + } + } + } +} diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index 32d9a4b02..354fecebb 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -148,7 +148,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { propertiesModel: OSPropertiesModel(changeNotifier: OSEventProducer()), pushSubscriptionModel: OSSubscriptionModel(type: .push, address: nil, subscriptionId: nil, reachable: false, isDisabled: true, changeNotifier: OSEventProducer())) - @objc public var requiresUserAuth = false + var jwtConfig = OSUserJwtConfig() // User State Observer private var _userStateChangesObserver: OSObservable? From 768b52bfba6a951ff4d7f0bd629adc3eddb56028 Mon Sep 17 00:00:00 2001 From: Nan Date: Sun, 4 Aug 2024 10:04:54 -0700 Subject: [PATCH 04/13] Add OSUD_USE_IDENTITY_VERIFICATION flag to OneSignalCommonDefines --- .../OneSignalCore/Source/OneSignalCommonDefines.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h index 2c9fc0a8d..e0b347703 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalCommonDefines.h @@ -77,6 +77,8 @@ // Remote Params #define OSUD_LOCATION_ENABLED @"OSUD_LOCATION_ENABLED" #define OSUD_REQUIRES_USER_PRIVACY_CONSENT @"OSUD_REQUIRES_USER_PRIVACY_CONSENT" +// TODO: JWT 🔐 Figure out the key below and may need to relate to existing key IOS_REQUIRES_USER_ID_AUTHENTICATION +#define OSUD_USE_IDENTITY_VERIFICATION @"OSUD_USE_IDENTITY_VERIFICATION" // Remote Params - Receive Receipts #define OSUD_RECEIVE_RECEIPTS_ENABLED @"OS_ENABLE_RECEIVE_RECEIPTS" // * OSUD_RECEIVE_RECEIPTS_ENABLED // Outcomes @@ -128,7 +130,7 @@ #define IOS_USES_PROVISIONAL_AUTHORIZATION @"uses_provisional_auth" #define IOS_REQUIRES_EMAIL_AUTHENTICATION @"require_email_auth" #define IOS_REQUIRES_SMS_AUTHENTICATION @"require_sms_auth" -#define IOS_REQUIRES_USER_ID_AUTHENTICATION @"require_user_id_auth" +#define IOS_REQUIRES_USER_ID_AUTHENTICATION @"require_user_id_auth" // TODO: JWT 🔐 Figure out the key, also think about needing to migrate this value #define IOS_RECEIVE_RECEIPTS_ENABLE @"receive_receipts_enable" #define IOS_OUTCOMES_V2_SERVICE_ENABLE @"v2_enabled" #define IOS_LOCATION_SHARED @"location_shared" From c6a2bbc2193fe5337f2dca5656bd02c0be9e736c Mon Sep 17 00:00:00 2001 From: Nan Date: Sun, 4 Aug 2024 10:35:10 -0700 Subject: [PATCH 05/13] public updateUserJwt method --- .../Source/OneSignalUserManagerImpl.swift | 17 ++++++++++++++++- iOS_SDK/OneSignalSDK/Source/OneSignal.m | 7 +++++++ .../OneSignalSDK/Source/OneSignalFramework.h | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index 354fecebb..4939af43a 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -30,13 +30,14 @@ import OneSignalOSCore import OneSignalNotifications /** - Public-facing API to access the User Manager. + Internal API to access the User Manager. */ @objc protocol OneSignalUserManager { // swiftlint:disable identifier_name var User: OSUser { get } func login(externalId: String, token: String?) func logout() + func updateUserJwt(externalId: String, token: String) // Location func setLocation(latitude: Float, longitude: Float) // Purchase Tracking @@ -523,6 +524,20 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { updatePropertiesDeltas(property: .purchases, value: purchases) } + @objc + public func updateUserJwt(externalId: String, token: String) { + guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: "updateUserJwt") else { + return + } + guard let identityModel = getIdentityModel(externalId: externalId) else { + OneSignalLog.onesignalLog(ONE_S_LOG_LEVEL.LL_ERROR, message: "Update User JWT called with external ID \(externalId) that does not exist") + return + } + OneSignalLog.onesignalLog(ONE_S_LOG_LEVEL.LL_VERBOSE, message: "Update User JWT called with externalId: \(externalId) and token: \(token)") + + identityModel.jwtToken = token + } + private func fireJwtExpired() { guard let externalId = user.identityModel.externalId, let jwtExpiredHandler = self.jwtExpiredHandler else { return diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index d9476ec5a..13d772577 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -208,6 +208,13 @@ + (void)logout { [OneSignalUserManagerImpl.sharedInstance logout]; } ++ (void)updateUserJwt:(NSString * _Nonnull)externalId withToken:(NSString * _Nonnull)token { + if ([OneSignalConfigManager shouldAwaitAppIdAndLogMissingPrivacyConsentForMethod:@"updateUserJwt"]) { + return; + } + [OneSignalUserManagerImpl.sharedInstance updateUserJwtWithExternalId:externalId token:token]; +} + #pragma mark: Namespaces + (Class)Notifications { diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalFramework.h b/iOS_SDK/OneSignalSDK/Source/OneSignalFramework.h index a1a2d557b..4c1af7399 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalFramework.h +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalFramework.h @@ -70,6 +70,7 @@ typedef void (^OSFailureBlock)(NSError* error); + (void)login:(NSString * _Nonnull)externalId; + (void)login:(NSString * _Nonnull)externalId withToken:(NSString * _Nullable)token NS_SWIFT_NAME(login(externalId:token:)); ++ (void)updateUserJwt:(NSString * _Nonnull)externalId withToken:(NSString * _Nonnull)token; + (void)logout; #pragma mark Notifications From 16fc8859f2ad63702adbf84d079e82efdab004ad Mon Sep 17 00:00:00 2001 From: Nan Date: Sun, 4 Aug 2024 10:51:56 -0700 Subject: [PATCH 06/13] mock remote params returning use JWT = true --- iOS_SDK/OneSignalSDK/Source/OneSignal.m | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignal.m b/iOS_SDK/OneSignalSDK/Source/OneSignal.m index 13d772577..62ace4be7 100755 --- a/iOS_SDK/OneSignalSDK/Source/OneSignal.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignal.m @@ -636,10 +636,12 @@ + (void)downloadIOSParamsWithAppId:(NSString *)appId { NSString *userId = nil; [OneSignalCoreImpl.sharedClient executeRequest:[OSRequestGetIosParams withUserId:userId appId:appId] onSuccess:^(NSDictionary *result) { - - if (result[IOS_REQUIRES_USER_ID_AUTHENTICATION]) { - OneSignalUserManagerImpl.sharedInstance.requiresUserAuth = [result[IOS_REQUIRES_USER_ID_AUTHENTICATION] boolValue]; - } + + // TODO: JWT 🔐 Mock it for now to always be true + OneSignalUserManagerImpl.sharedInstance.requiresUserAuth = true; +// if (result[IOS_REQUIRES_USER_ID_AUTHENTICATION]) { +// OneSignalUserManagerImpl.sharedInstance.requiresUserAuth = [result[IOS_REQUIRES_USER_ID_AUTHENTICATION] boolValue]; +// } if (result[IOS_USES_PROVISIONAL_AUTHORIZATION] != (id)[NSNull null]) { [OneSignalUserDefaults.initStandard saveBoolForKey:OSUD_USES_PROVISIONAL_PUSH_AUTHORIZATION withValue:[result[IOS_USES_PROVISIONAL_AUTHORIZATION] boolValue]]; From 68de895f9cad01ac7654deaff2ec7dfddaf17af5 Mon Sep 17 00:00:00 2001 From: Nan Date: Sun, 4 Aug 2024 13:01:23 -0700 Subject: [PATCH 07/13] [wip] Add internal JWT Config management and listener --- .../OneSignalUser/Source/OneSignalUserManagerImpl.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index 4939af43a..de1dc2042 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -151,6 +151,11 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { var jwtConfig = OSUserJwtConfig() + @objc + public func setRequiresUserAuth(_ required: Bool) { + jwtConfig.isRequired = required + } + // User State Observer private var _userStateChangesObserver: OSObservable? var userStateChangesObserver: OSObservable { From ef8f8d4621c7424283ddbdd1f7996cbbd7a29faf Mon Sep 17 00:00:00 2001 From: Nan Date: Mon, 5 Aug 2024 10:25:27 -0700 Subject: [PATCH 08/13] Totally WIP: user manager --- .../Source/OneSignalUserManagerImpl.swift | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift index de1dc2042..170e662c6 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OneSignalUserManagerImpl.swift @@ -149,6 +149,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { propertiesModel: OSPropertiesModel(changeNotifier: OSEventProducer()), pushSubscriptionModel: OSSubscriptionModel(type: .push, address: nil, subscriptionId: nil, reachable: false, isDisabled: true, changeNotifier: OSEventProducer())) + // TODO: JWT 🔐 make private var jwtConfig = OSUserJwtConfig() @objc @@ -228,13 +229,14 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { // Setup the executors // The OSUserExecutor has to run first, before other executors - self.userExecutor = OSUserExecutor() - OSOperationRepo.sharedInstance.start() + self.userExecutor = OSUserExecutor(requiresAuth: jwtConfig.isRequired) + OSOperationRepo.sharedInstance.start(requiresAuth: jwtConfig.isRequired) + jwtConfig.changeNotifier.subscribe(OSOperationRepo.sharedInstance, key: "OSOperationRepo") // TODO: JWT 🔐 not great this way // Cannot initialize these executors in `init` as they reference the sharedInstance - let propertyExecutor = OSPropertyOperationExecutor() - let identityExecutor = OSIdentityOperationExecutor() - let subscriptionExecutor = OSSubscriptionOperationExecutor() + let propertyExecutor = OSPropertyOperationExecutor(requiresAuth: jwtConfig.isRequired) + let identityExecutor = OSIdentityOperationExecutor(requiresAuth: jwtConfig.isRequired) + let subscriptionExecutor = OSSubscriptionOperationExecutor(requiresAuth: jwtConfig.isRequired) self.propertyExecutor = propertyExecutor self.identityExecutor = identityExecutor self.subscriptionExecutor = subscriptionExecutor @@ -249,8 +251,14 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { OneSignalUserDefaults.initShared().saveString(forKey: OSUD_PUSH_SUBSCRIPTION_ID, withValue: legacyPlayerId) OneSignalUserDefaults.initStandard().removeValue(forKey: OSUD_LEGACY_PLAYER_ID) OneSignalUserDefaults.initShared().removeValue(forKey: OSUD_LEGACY_PLAYER_ID) + } + + // Path 3. There is no user because JWT is on + // Is this actually true? + if jwtConfig.isRequired == true { + // Do nothing } else { - // Path 3. Creates an anonymous user if there isn't one in the cache nor a legacy player + // Path 4. Creates an anonymous user if there isn't one in the cache nor a legacy player, and JWT is unknown or off createUserIfNil() } @@ -272,6 +280,11 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { return identityModelRepo.get(modelId: modelId) } + func getIdentityModel(externalId: String) -> OSIdentityModel? { +// guard let externalId = externalId else { return nil } + return identityModelRepo.get(externalId: externalId) + } + @objc public func login(externalId: String, token: String?) { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else { @@ -322,7 +335,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { let newUser = setNewInternalUser(externalId: externalId, pushSubscriptionModel: pushSubscriptionModel) newUser.identityModel.jwtBearerToken = token - userExecutor!.createUser(newUser) + userExecutor!.createUser(newUser) // TODO: JWT 🔐 Not call this before remote params return self.user } @@ -387,17 +400,20 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { } OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OneSignalUserManager internal _login called with externalId: \(externalId ?? "nil")") + userExecutor!.logSelf() // If have token, validate token. Account for this being a requirement. // Logging into an identified user from an anonymous user if let externalId = externalId, let user = _user, - user.isAnonymous { + user.isAnonymous, + jwtConfig.isRequired != true { user.identityModel.jwtBearerToken = token identifyUser(externalId: externalId, currentUser: user) return self.user } - // Logging into anon -> anon, identified -> anon, identified -> identified, or nil -> any user + // JWT Off: Logging into anon -> anon, identified -> anon, identified -> identified, or nil -> any user + // JWT On: Logging into anon -> identified ,identified -> identified, or nil -> any user return createNewUser(externalId: externalId, token: token) } @@ -540,7 +556,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager { } OneSignalLog.onesignalLog(ONE_S_LOG_LEVEL.LL_VERBOSE, message: "Update User JWT called with externalId: \(externalId) and token: \(token)") - identityModel.jwtToken = token + identityModel.jwtBearerToken = token } private func fireJwtExpired() { From cc757c6c673141f8c2345e6768b8b0012fa0f166 Mon Sep 17 00:00:00 2001 From: Nan Date: Mon, 5 Aug 2024 10:25:47 -0700 Subject: [PATCH 09/13] Totally WIP: executors --- .../OSIdentityOperationExecutor.swift | 162 +++++++++++++++--- .../OSPropertyOperationExecutor.swift | 114 ++++++++++-- .../OSSubscriptionOperationExecutor.swift | 19 +- .../Source/Executors/OSUserExecutor.swift | 129 +++++++++++++- 4 files changed, 370 insertions(+), 54 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift index b6aab45e0..12f3830af 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSIdentityOperationExecutor.swift @@ -34,28 +34,43 @@ class OSIdentityOperationExecutor: OSOperationExecutor { // To simplify uncaching, we maintain separate request queues for each type var addRequestQueue: [OSRequestAddAliases] = [] var removeRequestQueue: [OSRequestRemoveAlias] = [] + var requiresAuth: Bool? // The Identity executor dispatch queue, serial. This synchronizes access to the delta and request queues. private let dispatchQueue = DispatchQueue(label: "OneSignal.OSIdentityOperationExecutor", target: .global()) - init() { + init(requiresAuth: Bool?) { + self.requiresAuth = requiresAuth // Read unfinished deltas and requests from cache, if any... uncacheDeltas() uncacheAddAliasRequests() uncacheRemoveAliasRequests() + OneSignalUserManagerImpl.sharedInstance.jwtConfig.changeNotifier.subscribe(self, key: "OSIdentityOperationExecutor") } private func uncacheDeltas() { 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.getIdentityModel(delta.model.modelId) { - // The model exists in the repo, set it to be the Delta's model - delta.model = modelInStore + // TODO: JWT 🔐 change this logic along with Deltas in other executors + if requiresAuth == true { + guard let externalId = (delta.model as? OSIdentityModel)?.externalId else { + deltaQueue.remove(at: index) + continue + } + if let modelInStore = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(externalId: externalId) { + // 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) + 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) + } } } self.deltaQueue = deltaQueue @@ -69,16 +84,31 @@ 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.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 request.prepareForExecution() { - // 2. The request can be sent, add the model to the repo - OneSignalUserManagerImpl.sharedInstance.addIdentityModelToRepo(request.identityModel) + if requiresAuth == true { + // Requires external ID + guard request.identityModel.externalId != nil else { + addRequestQueue.remove(at: index) + continue + } + if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(externalId: request.identityModel.externalId) { + // 1. The identity model exist in the repo, set it to be the Request's model + request.identityModel = identityModel + } else { + // 2. 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) + 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 request.prepareForExecution(requiresJwt: requiresAuth) { + // 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)") + addRequestQueue.remove(at: index) + } } } self.addRequestQueue = addRequestQueue @@ -92,16 +122,31 @@ 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.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 request.prepareForExecution() { - // 2. The request can be sent, add the model to the repo - OneSignalUserManagerImpl.sharedInstance.addIdentityModelToRepo(request.identityModel) + if requiresAuth == true { + // Requires external ID + guard request.identityModel.externalId != nil else { + addRequestQueue.remove(at: index) + continue + } + if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(externalId: request.identityModel.externalId) { + // 1. The identity model exist in the repo, set it to be the Request's model + request.identityModel = identityModel + } else { + // 2. 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) + if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(modelId: request.identityModel.modelId) { + // 1. The model exists in the repo, so set it to be the Request's model + request.identityModel = identityModel + } else if request.prepareForExecution(requiresJwt: requiresAuth) { + // 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) + } } } self.removeRequestQueue = removeRequestQueue @@ -191,7 +236,7 @@ class OSIdentityOperationExecutor: OSOperationExecutor { guard !request.sentToClient else { return } - guard request.prepareForExecution() else { + guard request.prepareForExecution(requiresJwt: requiresAuth) else { return } request.sentToClient = true @@ -250,7 +295,7 @@ class OSIdentityOperationExecutor: OSOperationExecutor { guard !request.sentToClient else { return } - guard request.prepareForExecution() else { + guard request.prepareForExecution(requiresJwt: requiresAuth) else { return } request.sentToClient = true @@ -292,6 +337,69 @@ class OSIdentityOperationExecutor: OSOperationExecutor { } } +extension OSIdentityOperationExecutor: OSUserJwtConfigListener { + func removeInvalidDeltasAndRequests() { + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSIdentityOperationExecutor.removeInvalidDeltasAndRequests") + + var persistDeltas = false + for (index, delta) in self.deltaQueue.enumerated().reversed() { + guard let externalId = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(externalId: (delta.model as? OSIdentityModel)?.externalId) + else { + persistDeltas = true + self.deltaQueue.remove(at: index) + continue + } + } + if persistDeltas { + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_DELTA_QUEUE_KEY, withValue: self.deltaQueue) + } + + var persistRequests = false + for (index, request) in self.addRequestQueue.enumerated().reversed() { + if request.identityModel.externalId == nil { + persistRequests = true + self.addRequestQueue.remove(at: index) + } + } + if persistRequests { + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_ADD_REQUEST_QUEUE_KEY, withValue: self.addRequestQueue) + } + + persistRequests = false + for (index, request) in self.removeRequestQueue.enumerated().reversed() { + if request.identityModel.externalId == nil { + persistRequests = true + self.removeRequestQueue.remove(at: index) + } + } + if persistRequests { + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_IDENTITY_EXECUTOR_REMOVE_REQUEST_QUEUE_KEY, withValue: self.removeRequestQueue) + } + } + + func onUserAuthChanged(from: Bool?, to: Bool?) { + print("❌ OSIdentityOperationExecutor onUserAuthChanged from \(from) to \(to)") + + // If auth changed from false or unknown to true, process requests + if to == true { + removeInvalidDeltasAndRequests() + } + } + + func onJwtInvalidated(externalId: String, error: String?) { + // + } + + func onJwtUpdated(externalId: String, jwtToken: String) { + // + } + + func onJwtTokenChanged(externalId: String, from: String?, to: String?) { + // + } + +} + extension OSIdentityOperationExecutor: OSLoggable { func logSelf() { print("💛 OSIdentityOperationExecutor has the following queues: ") diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift index 053cc3693..12a68d375 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSPropertyOperationExecutor.swift @@ -64,24 +64,37 @@ class OSPropertyOperationExecutor: OSOperationExecutor { var supportedDeltas: [String] = [OS_UPDATE_PROPERTIES_DELTA] var deltaQueue: [OSDelta] = [] var updateRequestQueue: [OSRequestUpdateProperties] = [] + var requiresAuth: Bool? // The property executor dispatch queue, serial. This synchronizes access to `deltaQueue` and `updateRequestQueue`. private let dispatchQueue = DispatchQueue(label: "OneSignal.OSPropertyOperationExecutor", target: .global()) - init() { + init(requiresAuth: Bool?) { + print("❌ OSPropertyOperationExecutor start(\(requiresAuth))") + + self.requiresAuth = requiresAuth // Read unfinished deltas and requests from cache, if any... // Note that we should only have deltas for the current user as old ones are flushed.. uncacheDeltas() uncacheUpdateRequests() + OneSignalUserManagerImpl.sharedInstance.jwtConfig.changeNotifier.subscribe(self, key: "OSPropertyOperationExecutor") } private func uncacheDeltas() { + print("❌ OSPropertyOperationExecutor uncacheDeltas") + if var deltaQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_PROPERTIES_EXECUTOR_DELTA_QUEUE_KEY, defaultValue: []) as? [OSDelta] { for (index, delta) in deltaQueue.enumerated().reversed() { - if OneSignalUserManagerImpl.sharedInstance.getIdentityModel(delta.identityModelId) == nil { + guard let model = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(delta.identityModelId) else { // The identity model does not exist, drop this Delta OneSignalLog.onesignalLog(.LL_WARN, message: "OSPropertyOperationExecutor.init dropped: \(delta)") deltaQueue.remove(at: index) + continue + } + + // If the external ID does not exist, drop this Delta + if requiresAuth == true, model.externalId != nil { + deltaQueue.remove(at: index) } } self.deltaQueue = deltaQueue @@ -95,18 +108,34 @@ 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() { - 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 request.prepareForExecution() { - // 2. The request can be sent, add the model to the repo - OneSignalUserManagerImpl.sharedInstance.addIdentityModelToRepo(request.identityModel) + if requiresAuth == true { + // Requires external ID + guard let euid = request.identityModel.externalId else { + updateRequestQueue.remove(at: index) + continue + } + if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(externalId: euid) { + // 1. The identity model exist in the repo, set it to be the Request's model + request.identityModel = identityModel + } else { + // 2. 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) + 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 request.prepareForExecution(requiresJwt: requiresAuth) { + // 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) + } } } + self.updateRequestQueue = updateRequestQueue OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self.updateRequestQueue) } else { @@ -127,9 +156,13 @@ class OSPropertyOperationExecutor: OSOperationExecutor { } } - /// The `deltaQueue` should only contain updates for one user. + /// The `deltaQueue` should typically only contain updates for one user /// Even when login -> addTag -> login -> addTag are called in immediate succession. + /// However, when Identity Verification requirements are unknown, we keep deltas in the queue even when users switch. func processDeltaQueue(inBackground: Bool) { + guard requiresAuth != nil else { + return + } self.dispatchQueue.async { if self.deltaQueue.isEmpty { // Delta queue is empty but there may be pending requests @@ -233,7 +266,7 @@ class OSPropertyOperationExecutor: OSOperationExecutor { guard !request.sentToClient else { return } - guard request.prepareForExecution() else { + guard request.prepareForExecution(requiresJwt: requiresAuth) else { return } request.sentToClient = true @@ -287,6 +320,61 @@ class OSPropertyOperationExecutor: OSOperationExecutor { } } +extension OSPropertyOperationExecutor: OSUserJwtConfigListener { + func removeInvalidDeltasAndRequests() { + self.dispatchQueue.async { + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSPropertyOperationExecutor.removeInvalidDeltasAndRequests") + + var persistDeltas = false + for (index, delta) in self.deltaQueue.enumerated().reversed() { + guard let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(delta.identityModelId), + let externalId = identityModel.externalId + else { + persistDeltas = true + self.deltaQueue.remove(at: index) + continue + } + } + if persistDeltas { + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_PROPERTIES_EXECUTOR_DELTA_QUEUE_KEY, withValue: self.deltaQueue) + } + + var persistRequests = false + for (index, request) in self.updateRequestQueue.enumerated().reversed() { + if request.identityModel.externalId == nil { + persistRequests = true + self.updateRequestQueue.remove(at: index) + } + } + if persistRequests { + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_PROPERTIES_EXECUTOR_UPDATE_REQUEST_QUEUE_KEY, withValue: self.updateRequestQueue) + } + } + } + + func onRequiresUserAuthChanged(from: Bool?, to: Bool?) { + print("❌ OSPropertyOperationExecutor onUserAuthChanged from \(from) to \(to)") + + // If auth changed from false or unknown to true, process requests + if to == true { + removeInvalidDeltasAndRequests() + } + } + + func onJwtInvalidated(externalId: String, error: String?) { + // + } + + func onJwtUpdated(externalId: String, jwtToken: String) { + // + } + + func onJwtTokenChanged(externalId: String, from: String?, to: String?) { + // + } + +} + extension OSPropertyOperationExecutor: OSLoggable { func logSelf() { print("💛 OSPropertyOperationExecutor has the following queues: ") diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift index 5ed6e749f..77168d581 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSSubscriptionOperationExecutor.swift @@ -36,11 +36,14 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { var removeRequestQueue: [OSRequestDeleteSubscription] = [] var updateRequestQueue: [OSRequestUpdateSubscription] = [] var subscriptionModels: [String: OSSubscriptionModel] = [:] + var requiresAuth: Bool? // The Subscription executor dispatch queue, serial. This synchronizes access to the delta and request queues. private let dispatchQueue = DispatchQueue(label: "OneSignal.OSSubscriptionOperationExecutor", target: .global()) - init() { + // TODO: JWT 🔐 Subscription details are TBD + init(requiresAuth: Bool?) { + self.requiresAuth = requiresAuth // Read unfinished deltas and requests from cache, if any... uncacheDeltas() uncacheCreateSubscriptionRequests() @@ -86,10 +89,10 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { subscriptionModels[request.subscriptionModel.modelId] = request.subscriptionModel } // 2. Hook up the identity model - if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(request.identityModel.modelId) { + if let identityModel = OneSignalUserManagerImpl.sharedInstance.getIdentityModel(modelId: request.identityModel.modelId) { // a. The model exist in the repo request.identityModel = identityModel - } else if request.prepareForExecution() { + } else if request.prepareForExecution(requiresJwt: nil) { // b. The request can be sent, add the model to the repo OneSignalUserManagerImpl.sharedInstance.addIdentityModelToRepo(request.identityModel) } else { @@ -116,7 +119,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { } else if let subscriptionModel = subscriptionModels[request.subscriptionModel.modelId] { // 2. The model exists in the dict of seen subscription models request.subscriptionModel = subscriptionModel - } else if !request.prepareForExecution() { + } else if !request.prepareForExecution(requiresJwt: nil) { // 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) @@ -139,7 +142,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { } else if let subscriptionModel = subscriptionModels[request.subscriptionModel.modelId] { // 2. The model exists in the dict of seen subscription models request.subscriptionModel = subscriptionModel - } else if !request.prepareForExecution() { + } else if !request.prepareForExecution(requiresJwt: nil) { // 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) @@ -271,7 +274,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { guard !request.sentToClient else { return } - guard request.prepareForExecution() else { + guard request.prepareForExecution(requiresJwt: nil) else { return } request.sentToClient = true @@ -336,7 +339,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { guard !request.sentToClient else { return } - guard request.prepareForExecution() else { + guard request.prepareForExecution(requiresJwt: nil) else { return } request.sentToClient = true @@ -381,7 +384,7 @@ class OSSubscriptionOperationExecutor: OSOperationExecutor { guard !request.sentToClient else { return } - guard request.prepareForExecution() else { + guard request.prepareForExecution(requiresJwt: nil) else { return } request.sentToClient = true diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift index 04af7d459..64695a82f 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Executors/OSUserExecutor.swift @@ -35,25 +35,34 @@ import OneSignalOSCore */ class OSUserExecutor { var userRequestQueue: [OSUserRequest] = [] + var requiresAuth: Bool? // The User executor dispatch queue, serial. This synchronizes access to the request queues. private let dispatchQueue = DispatchQueue(label: "OneSignal.OSUserExecutor", target: .global()) - init() { + init(requiresAuth: Bool?) { + print("❌ OSUserExecutor start(\(String(describing: requiresAuth))") + self.requiresAuth = requiresAuth uncacheUserRequests() migrateTransferSubscriptionRequests() + OneSignalUserManagerImpl.sharedInstance.jwtConfig.changeNotifier.subscribe(self, key: "OSUserExecutor") // TODO: JWT 🔐 executePendingRequests() } // Read in requests from the cache, do not read in FetchUser requests as this is not needed. private func uncacheUserRequests() { var userRequestQueue: [OSUserRequest] = [] - + print("❌ OSUserExecutor uncacheUserRequests") // Read unfinished Create User + Identify User + Get Identity By Subscription requests from cache, if any... if let cachedRequestQueue = OneSignalUserDefaults.initShared().getSavedCodeableData(forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, defaultValue: []) as? [OSUserRequest] { // Hook each uncached Request to the right model reference for request in cachedRequestQueue { + if request.isKind(of: OSRequestFetchIdentityBySubscription.self), let req = request as? OSRequestFetchIdentityBySubscription { + // Remove this request if JWT is enabled + guard requiresAuth != true else { + continue + } 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 @@ -65,6 +74,13 @@ class OSUserExecutor { userRequestQueue.append(req) } else if request.isKind(of: OSRequestCreateUser.self), let req = request as? OSRequestCreateUser { + + if requiresAuth == true, + req.identityModel.externalId == nil { + // Remove this request if there is no EUID + continue + } + 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 @@ -72,10 +88,17 @@ class OSUserExecutor { // 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 JWT is enabled, we migrate this request into a Create User request + if requiresAuth == true { + createUser(aliasLabel: req.aliasLabel, aliasId: req.aliasId, identityModel: req.identityModelToUpdate) + continue + } + 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 @@ -90,7 +113,7 @@ class OSUserExecutor { // 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 { + guard request.prepareForExecution(requiresJwt: requiresAuth) else { OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor.start() dropped: \(request)") continue } @@ -99,6 +122,7 @@ class OSUserExecutor { } userRequestQueue.append(req) } + } } self.userRequestQueue = userRequestQueue @@ -143,9 +167,39 @@ class OSUserExecutor { } } + private func executePendingRequestsWithAuth() { + self.dispatchQueue.async { + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSUserExecutor.executePendingRequestsWithAuth called with queue \(self.userRequestQueue)") + + for request in self.userRequestQueue { + if request.isKind(of: OSRequestCreateUser.self), let createUserRequest = request as? OSRequestCreateUser { + self.executeCreateUserRequest(createUserRequest) + } else if request.isKind(of: OSRequestFetchUser.self), let fetchUserRequest = request as? OSRequestFetchUser { + self.executeFetchUserRequest(fetchUserRequest) + } else { + // Log Error + OneSignalLog.onesignalLog(.LL_ERROR, message: "OSUserExecutor met incompatible Request type that cannot be executed.") + } + } + } + } + func executePendingRequests() { + guard let requiresAuth = requiresAuth else { + OneSignalLog.onesignalLog(.LL_DEBUG, message: "OSUserExecutor.executePendingRequests returning early due to unknown Identity Verification status.") + return + } + + if requiresAuth { + executePendingRequestsWithAuth() + } else { + executePendingRequestsWithoutAuth() + } + } + + func executePendingRequestsWithoutAuth() { self.dispatchQueue.async { - OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSUserExecutor.executePendingRequests called with queue \(self.userRequestQueue)") + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSUserExecutor.executePendingRequestsWithoutAuth called with queue \(self.userRequestQueue)") if self.userRequestQueue.isEmpty { return @@ -153,7 +207,7 @@ class OSUserExecutor { for request in self.userRequestQueue { // Return as soon as we reach an un-executable request - if !request.prepareForExecution() { + if !request.prepareForExecution(requiresJwt: self.requiresAuth) { OneSignalLog.onesignalLog(.LL_WARN, message: "OSUserExecutor.executePendingRequests() is blocked by unexecutable request \(request)") return } @@ -205,7 +259,6 @@ extension OSUserExecutor { return } guard request.prepareForExecution() else { - // Currently there are no requirements needed before sending this request, so this will set the path return } request.sentToClient = true @@ -411,6 +464,7 @@ extension OSUserExecutor { if let response = response { // Clear local data in preparation for hydration + // TODO: JWT 🔐 um the following line feels wrong... maybe the user's changed OneSignalUserManagerImpl.sharedInstance.clearUserData() self.parseFetchUserResponse(response: response, identityModel: request.identityModel, originalPushToken: OneSignalUserManagerImpl.sharedInstance.pushSubscriptionImpl.token) @@ -559,6 +613,69 @@ extension OSUserExecutor { } } +extension OSUserExecutor: OSUserJwtConfigListener { + + /// Returns if the Request is invalid when user auth is required + func isRequestInvalidWithAuth(_ request: OSUserRequest) -> Bool { + if request.isKind(of: OSRequestFetchIdentityBySubscription.self) { + return true + } + if request.isKind(of: OSRequestCreateUser.self), + let createUserRequest = request as? OSRequestCreateUser, + createUserRequest.identityModel.externalId == nil { + return true + } + if request.isKind(of: OSRequestFetchUser.self), + let fetchUserRequest = request as? OSRequestFetchUser, + fetchUserRequest.identityModel.externalId == nil { + return true + } + return false + } + + func removeInvalidRequests() { + self.dispatchQueue.async { + OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSUserExecutor.removeInvalidRequests") + + if self.userRequestQueue.isEmpty { + return + } + + for request in self.userRequestQueue { + if self.isRequestInvalidWithAuth(request) { + self.userRequestQueue.removeAll(where: { $0 == request}) + } else if request.isKind(of: OSRequestIdentifyUser.self), let req = request as? OSRequestIdentifyUser { + self.createUser(aliasLabel: req.aliasLabel, aliasId: req.aliasId, identityModel: req.identityModelToUpdate) + } + } + OneSignalUserDefaults.initShared().saveCodeableData(forKey: OS_USER_EXECUTOR_USER_REQUEST_QUEUE_KEY, withValue: self.userRequestQueue) + } + } + + func onRequiresUserAuthChanged(from: Bool?, to: Bool?) { + print("❌ OSUserExecutor onUserAuthChanged from \(String(describing: from)) to \(String(describing: to))") + + // If auth changed from false or unknown to true, process requests + if to == true { + removeInvalidRequests() + } + self.requiresAuth = to + self.executePendingRequests() + } + + func onJwtInvalidated(externalId: String, error: String?) { + // + } + + func onJwtUpdated(externalId: String, jwtToken: String) { + // + } + + func onJwtTokenChanged(externalId: String, from: String?, to: String?) { + // + } +} + extension OSUserExecutor: OSLoggable { func logSelf() { print("💛 OSUserExecutor has the following queues: ") From 726ce0cfe6f44cb2284fe9d47c0985e647709fb3 Mon Sep 17 00:00:00 2001 From: Nan Date: Mon, 5 Aug 2024 10:26:04 -0700 Subject: [PATCH 10/13] Totally WIP: Operation Repo --- .../Source/OSOperationRepo.swift | 58 +++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift index b148688d5..77ea2de1f 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift @@ -48,12 +48,21 @@ public class OSOperationRepo: NSObject { var pollIntervalMilliseconds = Int(POLL_INTERVAL_MS) public var paused = false + var requiresAuth: Bool? + /** Initilize this Operation Repo. Read from the cache. Executors may not be available by this time. If everything starts up on initialize(), order can matter, ideally not but it can. Likely call init on this from oneSignal but exeuctors can come from diff modules. */ - public func start() { + public func start(requiresAuth: Bool?) { + print("❌ OSOperationRepo start(\(requiresAuth))") + + if self.requiresAuth == nil { + self.requiresAuth = requiresAuth + } + // TODO: JWT OneSignalUserManagerImpl.sharedInstance.jwtConfig.changeNotifier.subscribe(self) + guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else { return } @@ -93,7 +102,13 @@ public class OSOperationRepo: NSObject { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else { return } - start() + + // TODO: JWT 🔐 +// start() + if !hasCalledStart { + fatalError("❌❌❌❌ Operation Repo has not called start in addExecutor()") + } + executors.append(executor) for delta in executor.supportedDeltas { deltasToExecutorMap[delta] = executor @@ -111,7 +126,11 @@ public class OSOperationRepo: NSObject { guard !OneSignalConfigManager.shouldAwaitAppIdAndLogMissingPrivacyConsent(forMethod: nil) else { return } - start() + // TODO: JWT 🔐 +// start() + if !hasCalledStart { + fatalError("❌❌❌❌ Operation Repo has not called start in enqueueDelta()") + } self.dispatchQueue.async { OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo enqueueDelta: \(delta)") self.deltaQueue.append(delta) @@ -140,7 +159,11 @@ public class OSOperationRepo: NSObject { OSBackgroundTaskManager.beginBackgroundTask(OPERATION_REPO_BACKGROUND_TASK) } - self.start() + // TODO: JWT 🔐 +// self.start() + if !hasCalledStart { + fatalError("❌❌❌❌ Operation Repo has not called start in flushDeltaQueue()") + } if !self.deltaQueue.isEmpty { OneSignalLog.onesignalLog(.LL_VERBOSE, message: "OSOperationRepo flushDeltaQueue in background: \(inBackground) with queue: \(self.deltaQueue)") @@ -174,6 +197,33 @@ public class OSOperationRepo: NSObject { } } +extension OSOperationRepo: OSUserJwtConfigListener { + func removeInvalidDeltas() { + print("❌ OSOperationRepo removeInvalidDeltas") + } + public func onRequiresUserAuthChanged(from: Bool?, to: Bool?) { + print("❌ OSOperationRepo onUserAuthChanged from \(from) to \(to)") + + // If auth changed from false or unknown to true, process requests + if to == true { + removeInvalidDeltas() + } + pollFlushQueue() + } + + public func onJwtInvalidated(externalId: String, error: String?) { + // + } + + public func onJwtUpdated(externalId: String, jwtToken: String) { + // + } + + public func onJwtTokenChanged(externalId: String, from: String?, to: String?) { + // + } +} + extension OSOperationRepo: OSLoggable { @objc public func logSelf() { From 65354a4c70c32a775dde3829e2632226f2a6181d Mon Sep 17 00:00:00 2001 From: Nan Date: Mon, 5 Aug 2024 10:28:09 -0700 Subject: [PATCH 11/13] WIP: additional user request checks to consider JWT * User request instances already have a `prepareForExecution()` method that indicates if this request can be sent yet. For example, does the onesignal ID exist to make this request. * Add additional helpers to work with auth status / JWT --- .../Source/Requests/OSUserRequest.swift | 55 ++++++++++++++++--- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSUserRequest.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSUserRequest.swift index f6020c140..33caa7add 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSUserRequest.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSUserRequest.swift @@ -29,21 +29,49 @@ import OneSignalCore protocol OSUserRequest: OneSignalRequest, NSCoding { var sentToClient: Bool { get set } - func prepareForExecution() -> Bool + func prepareForExecution(requiresJwt: Bool?) -> Bool } internal extension OneSignalRequest { - func addJWTHeader(identityModel: OSIdentityModel) { -// guard let token = identityModel.jwtBearerToken else { -// return -// } -// var additionalHeaders = self.additionalHeaders ?? [String:String]() -// additionalHeaders["Authorization"] = "Bearer \(token)" -// self.additionalHeaders = additionalHeaders + + /** + Returns the alias pair to use to send this request for. Defaults to Onesignal Id, unless Identity Verification is on. + */ + func getAlias(authRequired: Bool?, identityModel: OSIdentityModel) -> (label: String, id: String?) { + var label = OS_ONESIGNAL_ID + var id = identityModel.onesignalId + if authRequired == true { + label = OS_EXTERNAL_ID + id = identityModel.externalId + } + return (label, id) + } + + /** + Adds JWT token to header if valid, regardless of requirement. + Returns false if JWT requirement is unknown, or turned on but the token is missing or invalid. + + | | unknown | on | off | + | --------------- | -------------- | ------- | ------- | + | hasToken | | ✔️ | ✔️ | + | noToken | | | ✔️ | + | --------------- | -------------- | ------- | ------- | + */ + func addJWTHeaderIsValid(required: Bool?, identityModel: OSIdentityModel) -> Bool { + let tokenIsValid = identityModel.isJwtValid() + let canBeSent = required == false || (required == true && tokenIsValid) + if canBeSent, tokenIsValid { + // Add the JWT token if it is valid, regardless of requirements + var additionalHeaders = self.additionalHeaders ?? [String: String]() + additionalHeaders["Authorization"] = "Bearer \(identityModel.jwtBearerToken ?? "")" + self.additionalHeaders = additionalHeaders + } + return canBeSent } /** Returns if the `OneSignal-Subscription-Id` header was added successfully. */ func addPushSubscriptionIdToAdditionalHeaders() -> Bool { + addPushToken() if let pushSubscriptionId = OneSignalUserManagerImpl.sharedInstance.pushSubscriptionId { var additionalHeaders = self.additionalHeaders ?? [String: String]() additionalHeaders["OneSignal-Subscription-Id"] = pushSubscriptionId @@ -53,4 +81,15 @@ internal extension OneSignalRequest { return false } } + + func addPushToken() -> Bool { + if let token = OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModel?.address { + var additionalHeaders = self.additionalHeaders ?? [String: String]() + additionalHeaders["Device-Auth-Push-Token"] = "Basic \(token)" + return true + } else { + return false + } + + } } From 4ca5862b914fbbd6e15d5303bc01e2773af96650 Mon Sep 17 00:00:00 2001 From: Nan Date: Mon, 5 Aug 2024 10:30:26 -0700 Subject: [PATCH 12/13] WIP: Identity model stuff Definitely WIP everything is WIp --- .../Source/OSIdentityModel.swift | 19 +++++++++++++++++-- .../Source/OSIdentityModelRepo.swift | 11 +++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift index 6e70b5057..8eb2a80e6 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModel.swift @@ -42,8 +42,20 @@ class OSIdentityModel: OSModel { var aliases: [String: String] = [:] private let aliasesLock = NSRecursiveLock() - // TODO: We need to make this token secure - public var jwtBearerToken: String? + public var jwtBearerToken: String? { + didSet { + guard jwtBearerToken != oldValue else { + return + } + self.set(property: "jwtBearerToken", newValue: jwtBearerToken, hydrating: true) + guard let euid = externalId else { return } + OneSignalUserManagerImpl.sharedInstance.jwtConfig.onJwtTokenChanged(externalId: euid, from: oldValue, to: jwtBearerToken) + } + } + + func isJwtValid() -> Bool { + return jwtBearerToken != nil && jwtBearerToken != "" && jwtBearerToken != "invalid" + } // MARK: - Initialization @@ -57,6 +69,7 @@ class OSIdentityModel: OSModel { aliasesLock.withLock { super.encode(with: coder) coder.encode(aliases, forKey: "aliases") + coder.encode(jwtBearerToken, forKey: "jwtBearerToken") } } @@ -66,6 +79,7 @@ class OSIdentityModel: OSModel { // log error return nil } + self.jwtBearerToken = coder.decodeObject(forKey: "jwtBearerToken") as? String self.aliases = aliases } @@ -85,6 +99,7 @@ class OSIdentityModel: OSModel { } } self.set(property: "aliases", newValue: aliases) + } /** diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelRepo.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelRepo.swift index 781fe4e8f..06412881d 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelRepo.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/OSIdentityModelRepo.swift @@ -52,4 +52,15 @@ class OSIdentityModelRepo { return models[modelId] } } + + func get(externalId: String) -> OSIdentityModel? { + lock.withLock { + for model in models.values { + if model.externalId == externalId { + return model + } + } + return nil + } + } } From 3722a314b07339b5e21883f7da89f842cebe5f99 Mon Sep 17 00:00:00 2001 From: Nan Date: Mon, 5 Aug 2024 09:45:17 -0700 Subject: [PATCH 13/13] Update request objects to consider JWT * User request instances already have a `prepareForExecution()` method that indicates if this request can be sent yet. For example, does the onesignal ID exist to make this request. * Use this existing method to check for auth and block sending this request if auth is required but invalid, or auth is unknown, etc. * Remove any call to `prepareForExecution()` within `init` in order to set the path, this is unnecessary * OSRequestCreateUser * OSRequestAddAliases * OSRequestFetchUser * OSRequestFetchIdentityBySubscription * OSRequestCreateSubscription * OSRequestDeleteSubscription --- .../Source/Requests/OSRequestAddAliases.swift | 14 +++++++------- .../Requests/OSRequestCreateSubscription.swift | 14 +++++++------- .../Source/Requests/OSRequestCreateUser.swift | 14 +++++++++----- .../Requests/OSRequestDeleteSubscription.swift | 5 ++--- .../OSRequestFetchIdentityBySubscription.swift | 9 +++++---- .../Source/Requests/OSRequestFetchUser.swift | 10 ++++------ 6 files changed, 34 insertions(+), 32 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestAddAliases.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestAddAliases.swift index 52d4d0cfa..2df26efa7 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestAddAliases.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestAddAliases.swift @@ -37,11 +37,13 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { var identityModel: OSIdentityModel let aliases: [String: String] - // requires a `onesignal_id` to send this request - func prepareForExecution() -> Bool { - if let onesignalId = identityModel.onesignalId, let appId = OneSignalConfigManager.getAppId() { - self.addJWTHeader(identityModel: identityModel) - self.path = "apps/\(appId)/users/by/\(OS_ONESIGNAL_ID)/\(onesignalId)/identity" + /// Needs the `onesignal_id` without JWT on or `external_id` with valid JWT to send this request + func prepareForExecution(requiresJwt: Bool?) -> Bool { + let alias = getAlias(authRequired: requiresJwt, identityModel: identityModel) + if let id = alias.id, + let appId = OneSignalConfigManager.getAppId(), + addJWTHeaderIsValid(required: requiresJwt, identityModel: identityModel) { + self.path = "apps/\(appId)/users/by/\(alias.label)/\(id)/identity" return true } else { // self.path is non-nil, so set to empty string @@ -57,7 +59,6 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { super.init() self.parameters = ["identity": aliases] self.method = PATCH - _ = prepareForExecution() // sets the path property } func encode(with coder: NSCoder) { @@ -86,6 +87,5 @@ class OSRequestAddAliases: OneSignalRequest, OSUserRequest { self.parameters = parameters self.method = HTTPMethod(rawValue: rawMethod) self.timestamp = timestamp - _ = prepareForExecution() } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateSubscription.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateSubscription.swift index e46870b2c..ed7d7a424 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateSubscription.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateSubscription.swift @@ -42,11 +42,13 @@ class OSRequestCreateSubscription: OneSignalRequest, OSUserRequest { var subscriptionModel: OSSubscriptionModel var identityModel: OSIdentityModel - // Need the onesignal_id of the user - func prepareForExecution() -> Bool { - if let onesignalId = identityModel.onesignalId, let appId = OneSignalConfigManager.getAppId() { - self.addJWTHeader(identityModel: identityModel) - self.path = "apps/\(appId)/users/by/\(OS_ONESIGNAL_ID)/\(onesignalId)/subscriptions" + /// Needs the `onesignal_id` without JWT on or `external_id` with valid JWT to send this request + func prepareForExecution(requiresJwt: Bool?) -> Bool { + let alias = getAlias(authRequired: requiresJwt, identityModel: identityModel) + if let id = alias.id, + let appId = OneSignalConfigManager.getAppId(), + addJWTHeaderIsValid(required: requiresJwt, identityModel: identityModel) { + self.path = "apps/\(appId)/users/by/\(alias.label)/\(id)/subscriptions" return true } else { self.path = "" // self.path is non-nil, so set to empty string @@ -61,7 +63,6 @@ class OSRequestCreateSubscription: OneSignalRequest, OSUserRequest { super.init() self.parameters = ["subscription": subscriptionModel.jsonRepresentation()] self.method = POST - _ = prepareForExecution() // sets the path property } func encode(with coder: NSCoder) { @@ -90,6 +91,5 @@ class OSRequestCreateSubscription: OneSignalRequest, OSUserRequest { self.parameters = parameters self.method = HTTPMethod(rawValue: rawMethod) self.timestamp = timestamp - _ = prepareForExecution() } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift index d88bde925..d9b434a15 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestCreateUser.swift @@ -44,13 +44,17 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { var pushSubscriptionModel: OSSubscriptionModel? var originalPushToken: String? - func prepareForExecution() -> Bool { - guard let appId = OneSignalConfigManager.getAppId() else { - OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the create user request due to null app ID.") + /** + When Identity Verification is disabled, there are no requirements before sending this request. + */ + func prepareForExecution(requiresJwt: Bool?) -> Bool { + guard let appId = OneSignalConfigManager.getAppId(), + addJWTHeaderIsValid(required: requiresJwt, identityModel: identityModel) + else { + OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the create user request due to null app ID or unknown/invalid JWT.") return false } _ = self.addPushSubscriptionIdToAdditionalHeaders() - self.addJWTHeader(identityModel: identityModel) self.path = "apps/\(appId)/users" // The pushSub doesn't need to have a token. return true @@ -99,7 +103,7 @@ class OSRequestCreateUser: OneSignalRequest, OSUserRequest { super.init() self.parameters = [ "identity": [aliasLabel: aliasId], - "refresh_device_metadata": true, + "refresh_device_metadata": true ] self.method = POST } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestDeleteSubscription.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestDeleteSubscription.swift index 83c7275b1..35630a78b 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestDeleteSubscription.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestDeleteSubscription.swift @@ -42,7 +42,8 @@ class OSRequestDeleteSubscription: OneSignalRequest, OSUserRequest { var subscriptionModel: OSSubscriptionModel // Need the subscription_id - func prepareForExecution() -> Bool { + // TODO: JWT 🔐 handle needing token on subscription ID? + func prepareForExecution(requiresJwt: Bool?) -> Bool { if let subscriptionId = subscriptionModel.subscriptionId, let appId = OneSignalConfigManager.getAppId() { self.path = "apps/\(appId)/subscriptions/\(subscriptionId)" return true @@ -58,7 +59,6 @@ class OSRequestDeleteSubscription: OneSignalRequest, OSUserRequest { self.stringDescription = "" super.init() self.method = DELETE - _ = prepareForExecution() // sets the path property } func encode(with coder: NSCoder) { @@ -81,6 +81,5 @@ class OSRequestDeleteSubscription: OneSignalRequest, OSUserRequest { super.init() self.method = HTTPMethod(rawValue: rawMethod) self.timestamp = timestamp - _ = prepareForExecution() } } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchIdentityBySubscription.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchIdentityBySubscription.swift index 25dd80be0..33874e9ae 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchIdentityBySubscription.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchIdentityBySubscription.swift @@ -38,18 +38,19 @@ class OSRequestFetchIdentityBySubscription: OneSignalRequest, OSUserRequest { var identityModel: OSIdentityModel var pushSubscriptionModel: OSSubscriptionModel - func prepareForExecution() -> Bool { + /// Only send this request if Identity Verification is off. + func prepareForExecution(requiresJwt: Bool?) -> Bool { guard let appId = OneSignalConfigManager.getAppId() else { OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the FetchIdentityBySubscription request due to null app ID.") return false } - if let subscriptionId = pushSubscriptionModel.subscriptionId { + if let subscriptionId = pushSubscriptionModel.subscriptionId, + requiresJwt == false { self.path = "apps/\(appId)/subscriptions/\(subscriptionId)/user/identity" return true } else { - // This is an error, and should never happen - OneSignalLog.onesignalLog(.LL_ERROR, message: "Cannot generate the FetchIdentityBySubscription request due to null subscriptionId.") + OneSignalLog.onesignalLog(.LL_ERROR, message: "Cannot generate the FetchIdentityBySubscription request due to null subscriptionId or auth status: \(String(describing: requiresJwt)).") self.path = "" return false } diff --git a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift index cd12416b3..af13b4171 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift +++ b/iOS_SDK/OneSignalSDK/OneSignalUser/Source/Requests/OSRequestFetchUser.swift @@ -43,12 +43,12 @@ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { let aliasId: String let onNewSession: Bool - func prepareForExecution() -> Bool { - guard let appId = OneSignalConfigManager.getAppId() else { - OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the fetch user request due to null app ID.") + func prepareForExecution(requiresJwt: Bool?) -> Bool { + guard let appId = OneSignalConfigManager.getAppId(), + addJWTHeaderIsValid(required: requiresJwt, identityModel: identityModel) else { + OneSignalLog.onesignalLog(.LL_DEBUG, message: "Cannot generate the fetch user request due to null app ID or auth status: \(String(describing: requiresJwt)).") return false } - self.addJWTHeader(identityModel: identityModel) self.path = "apps/\(appId)/users/by/\(aliasLabel)/\(aliasId)" return true } @@ -61,7 +61,6 @@ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { self.stringDescription = "" super.init() self.method = GET - _ = prepareForExecution() // sets the path property } func encode(with coder: NSCoder) { @@ -92,6 +91,5 @@ class OSRequestFetchUser: OneSignalRequest, OSUserRequest { super.init() self.method = HTTPMethod(rawValue: rawMethod) self.timestamp = timestamp - _ = prepareForExecution() } }