Skip to content

Commit

Permalink
Merge pull request #1152 from OneSignal/live-activity-support
Browse files Browse the repository at this point in the history
Live Activity Support
  • Loading branch information
fhboswell authored Nov 16, 2022
2 parents cd07b44 + 720d1f4 commit 32de71b
Show file tree
Hide file tree
Showing 6 changed files with 356 additions and 7 deletions.
14 changes: 13 additions & 1 deletion iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OSRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,22 @@ NS_ASSUME_NONNULL_END
+ (instancetype _Nonnull)withUserId:(NSString * _Nullable)externalId withUserIdHashToken:(NSString * _Nullable)hashToken withOneSignalUserId:(NSString * _Nonnull)userId withSMSHashToken:(NSString * _Nullable)smsHashToken appId:(NSString * _Nonnull)appId;
@end


@interface OSRequestTrackV1 : OneSignalRequest
+ (instancetype _Nonnull)trackUsageData:(NSString * _Nonnull)osUsageData
appId:(NSString * _Nonnull)appId;
@end

@interface OSRequestLiveActivityEnter: OneSignalRequest
+ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId
appId:(NSString * _Nonnull)appId
activityId:(NSString * _Nonnull)activityId
token:(NSString * _Nonnull)token;
@end

@interface OSRequestLiveActivityExit: OneSignalRequest
+ (instancetype _Nonnull)withUserId:(NSString * _Nonnull)userId
appId:(NSString * _Nonnull)appId
activityId:(NSString * _Nonnull)activityId;
@end
#endif /* Requests_h */

27 changes: 27 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignalCore/Source/API/OSRequests.m
Original file line number Diff line number Diff line change
Expand Up @@ -492,3 +492,30 @@ + (instancetype)trackUsageData:(NSString *)osUsageData appId:(NSString *)appId {
return request;
}
@end

@implementation OSRequestLiveActivityEnter
+ (instancetype)withUserId:(NSString * _Nonnull)userId
appId:(NSString * _Nonnull)appId
activityId:(NSString * _Nonnull)activityId
token:(NSString * _Nonnull)token {
let request = [OSRequestLiveActivityEnter new];
let params = [NSMutableDictionary new];
params[@"push_token"] = token;
params[@"subscription_id"] = userId; // pre-5.X.X subscription_id = player_id = userId
request.parameters = params;
request.method = POST;
request.path = [NSString stringWithFormat:@"apps/%@/live_activities/%@/token", appId, activityId];
return request;
}
@end

@implementation OSRequestLiveActivityExit
+ (instancetype)withUserId:(NSString * _Nonnull)userId
appId:(NSString * _Nonnull)appId
activityId:(NSString * _Nonnull)activityId {
let request = [OSRequestLiveActivityExit new];
request.method = DELETE;
request.path = [NSString stringWithFormat:@"apps/%@/live_activities/%@/token/%@", appId, activityId, userId];
return request;
}
@end
8 changes: 8 additions & 0 deletions iOS_SDK/OneSignalSDK/Source/OneSignal.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,14 @@ typedef void (^OSFailureBlock)(NSError* error);
+ (void)setLaunchURLsInApp:(BOOL)launchInApp;
+ (void)setProvidesNotificationSettingsView:(BOOL)providesView;


#pragma mark Live Activity
+ (void)enterLiveActivity:(NSString * _Nonnull)activityId withToken:(NSString * _Nonnull)token;
+ (void)enterLiveActivity:(NSString * _Nonnull)activityId withToken:(NSString * _Nonnull)token withSuccess:(OSResultSuccessBlock _Nullable)successBlock withFailure:(OSFailureBlock _Nullable)failureBlock;

+ (void)exitLiveActivity:(NSString * _Nonnull)activityId;
+ (void)exitLiveActivity:(NSString * _Nonnull)activityId withSuccess:(OSResultSuccessBlock _Nullable)successBlock withFailure:(OSFailureBlock _Nullable)failureBlock;

#pragma mark Logging
+ (void)setLogLevel:(ONE_S_LOG_LEVEL)logLevel visualLevel:(ONE_S_LOG_LEVEL)visualLogLevel;
+ (void)onesignalLog:(ONE_S_LOG_LEVEL)logLevel message:(NSString* _Nonnull)message;
Expand Down
154 changes: 148 additions & 6 deletions iOS_SDK/OneSignalSDK/Source/OneSignal.m
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,34 @@ - (NSDictionary*)toDictionary {
}
@end

@interface OSPendingLiveActivityUpdate: NSObject
@property NSString* token;
@property NSString* activityId;
@property BOOL isEnter;
@property OSResultSuccessBlock successBlock;
@property OSFailureBlock failureBlock;
- (id)initWith:(NSString * _Nonnull)activityId
withToken:(NSString * _Nonnull)token
isEnter:(BOOL)isEnter
withSuccess:(OSResultSuccessBlock _Nullable)successBlock
withFailure:(OSFailureBlock _Nullable)failureBlock;
@end
@implementation OSPendingLiveActivityUpdate

- (id)initWith:(NSString *)activityId
withToken:(NSString *)token
isEnter:(BOOL)isEnter
withSuccess:(OSResultSuccessBlock)successBlock
withFailure:(OSFailureBlock)failureBlock {
self.token = token;
self.activityId = activityId;
self.isEnter = isEnter;
self.successBlock = successBlock;
self.failureBlock = failureBlock;
return self;
};
@end

@interface OneSignal (SessionStatusDelegate)
@end

Expand Down Expand Up @@ -138,6 +166,8 @@ + (OneSignalSetSMSParameters *)delayedSMSParameters {
static OSResultSuccessBlock pendingGetTagsSuccessBlock;
static OSFailureBlock pendingGetTagsFailureBlock;

static NSMutableArray* pendingLiveActivityUpdates;

// Has attempted to register for push notifications with Apple since app was installed.
static BOOL registeredWithApple = NO;

Expand Down Expand Up @@ -664,6 +694,110 @@ + (void)setProvidesNotificationSettingsView:(BOOL)providesView {
appSettings = newSettings;
}

+ (void)enterLiveActivity:(NSString * _Nonnull)activityId withToken:(NSString * _Nonnull)token {

if ([OSPrivacyConsentController shouldLogMissingPrivacyConsentErrorWithMethodName:@"enterLiveActivity:"])
return;

[self enterLiveActivity:activityId withToken:token withSuccess:nil withFailure:nil];
}

#pragma mark: LIVE ACTIVITIES


+ (void)addPendingLiveActivityUpdate:(NSString * _Nonnull)activityId
withToken:(NSString * _Nullable)token
isEnter:(BOOL)isEnter
withSuccess:(OSResultSuccessBlock _Nullable)successBlock
withFailure:(OSFailureBlock _Nullable)failureBlock {
OSPendingLiveActivityUpdate *pendingLiveActivityUpdate = [[OSPendingLiveActivityUpdate alloc] initWith:activityId withToken:token isEnter:isEnter withSuccess:successBlock withFailure:failureBlock];

if (!pendingLiveActivityUpdates) {
pendingLiveActivityUpdates = [NSMutableArray new];
}
[pendingLiveActivityUpdates addObject:pendingLiveActivityUpdate];
}

+ (void)executePendingLiveActivityUpdates {
if(pendingLiveActivityUpdates.count <= 0) {
return;
}

OSPendingLiveActivityUpdate * updateToProcess = [pendingLiveActivityUpdates objectAtIndex:0];
[pendingLiveActivityUpdates removeObjectAtIndex: 0];
if (updateToProcess.isEnter) {
[OneSignalClient.sharedClient executeRequest:[OSRequestLiveActivityEnter withUserId:self.currentSubscriptionState.userId appId:appId activityId:updateToProcess.activityId token:updateToProcess.token]
onSuccess:^(NSDictionary *result) {
[self callSuccessBlockOnMainThread:updateToProcess.successBlock withResult:result];
[self executePendingLiveActivityUpdates];
} onFailure:^(NSError *error) {
[self callFailureBlockOnMainThread:updateToProcess.failureBlock withError:error];
[self executePendingLiveActivityUpdates];
}];
} else {
[OneSignalClient.sharedClient executeRequest:[OSRequestLiveActivityExit withUserId:self.currentSubscriptionState.userId appId:appId activityId:updateToProcess.activityId]
onSuccess:^(NSDictionary *result) {
[self callSuccessBlockOnMainThread:updateToProcess.successBlock withResult:result];
[self executePendingLiveActivityUpdates];
} onFailure:^(NSError *error) {
[self callFailureBlockOnMainThread:updateToProcess.failureBlock withError:error];
[self executePendingLiveActivityUpdates];
}];
}
}

+ (void)enterLiveActivity:(NSString * _Nonnull)activityId withToken:(NSString * _Nonnull)token withSuccess:(OSResultSuccessBlock _Nullable)successBlock withFailure:(OSFailureBlock _Nullable)failureBlock{

if ([OSPrivacyConsentController shouldLogMissingPrivacyConsentErrorWithMethodName:@"enterLiveActivity:onSuccess:onFailure:"]) {
if (failureBlock) {
NSError *error = [NSError errorWithDomain:@"com.onesignal.tags" code:0 userInfo:@{@"error" : @"Your application has called enterLiveActivity:onSuccess:onFailure: before the user granted privacy permission. Please call `consentGranted(bool)` in order to provide user privacy consent"}];
failureBlock(error);
}
return;
}


if(self.currentSubscriptionState.userId) {
[OneSignalClient.sharedClient executeRequest:[OSRequestLiveActivityEnter withUserId:self.currentSubscriptionState.userId appId:appId activityId:activityId token:token]
onSuccess:^(NSDictionary *result) {
[self callSuccessBlockOnMainThread:successBlock withResult:result];
} onFailure:^(NSError *error) {
[self callFailureBlockOnMainThread:failureBlock withError:error];
}];
} else {
[self addPendingLiveActivityUpdate:activityId withToken:token isEnter:true withSuccess:successBlock withFailure:failureBlock];
}
}

+ (void)exitLiveActivity:(NSString * _Nonnull)activityId{

if ([OSPrivacyConsentController shouldLogMissingPrivacyConsentErrorWithMethodName:@"enterLiveActivity:"])
return;

[self exitLiveActivity:activityId withSuccess:nil withFailure:nil];
}

+ (void)exitLiveActivity:(NSString * _Nonnull)activityId withSuccess:(OSResultSuccessBlock _Nullable)successBlock withFailure:(OSFailureBlock _Nullable)failureBlock{

if ([OSPrivacyConsentController shouldLogMissingPrivacyConsentErrorWithMethodName:@"exitLiveActivity:onSuccess:onFailure:"]) {
if (failureBlock) {
NSError *error = [NSError errorWithDomain:@"com.onesignal.tags" code:0 userInfo:@{@"error" : @"Your application has called exitLiveActivity:onSuccess:onFailure: before the user granted privacy permission. Please call `consentGranted(bool)` in order to provide user privacy consent"}];
failureBlock(error);
}
return;
}
if(self.currentSubscriptionState.userId) {
[OneSignalClient.sharedClient executeRequest:[OSRequestLiveActivityExit withUserId:self.currentSubscriptionState.userId appId:appId activityId:activityId]
onSuccess:^(NSDictionary *result) {
[self callSuccessBlockOnMainThread:successBlock withResult:result];
} onFailure:^(NSError *error) {
[self callFailureBlockOnMainThread:failureBlock withError:error];
}];
} else {
[self addPendingLiveActivityUpdate:activityId withToken:nil isEnter:false withSuccess:successBlock withFailure:failureBlock];
}
}

+ (void)setNotificationWillShowInForegroundHandler:(OSNotificationWillShowInForegroundBlock)block {
[OneSignal onesignalLog:ONE_S_LL_VERBOSE message:@"Notification will show in foreground handler set successfully"];
[OneSignalHelper setNotificationWillShowInForegroundBlock:block];
Expand Down Expand Up @@ -1209,7 +1343,7 @@ + (void)sendTags:(NSDictionary*)keyValuePair onSuccess:(OSResultSuccessBlock)suc

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(sendTagsToServer) object:nil];

// Can't send tags yet as their isn't a player_id.
// Can't send tags yet as there isn't a player_id.
// tagsToSend will be sent with the POST create player call later in this case.
if (self.currentSubscriptionState.userId)
[OneSignalHelper performSelector:@selector(sendTagsToServer) onMainThreadOnObject:self withObject:nil afterDelay:5];
Expand Down Expand Up @@ -1840,7 +1974,7 @@ + (void)registerUserInternal {
pendingGetTagsSuccessBlock = nil;
pendingGetTagsFailureBlock = nil;
}

[self executePendingLiveActivityUpdates];
}

if (results[@"push"][@"in_app_messages"]) {
Expand Down Expand Up @@ -2303,7 +2437,15 @@ + (void)callFailureBlockOnMainThread:(OSFailureBlock)failureBlock withError:(NSE
}
}

+ (void)callSuccessBlockOnMainThread:(OSEmailSuccessBlock)successBlock {
+ (void)callSuccessBlockOnMainThread:(OSResultSuccessBlock)successBlock withResult:(NSDictionary *)result{
if (successBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
successBlock(result);
});
}
}

+ (void)callEmailSuccessBlockOnMainThread:(OSEmailSuccessBlock)successBlock {
if (successBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
successBlock();
Expand Down Expand Up @@ -2386,7 +2528,7 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _
// Since developers may be making UI changes when this call finishes, we will call callbacks on the main thread.
if (self.currentEmailSubscriptionState.emailUserId) {
[OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentEmailSubscriptionState.emailUserId appId:self.appId deviceToken:email withParentId:nil emailAuthToken:emailAuthToken email:nil externalIdAuthToken:[self mExternalIdAuthToken]] onSuccess:^(NSDictionary *result) {
[self callSuccessBlockOnMainThread:successBlock];
[self callEmailSuccessBlockOnMainThread:successBlock];
} onFailure:^(NSError *error) {
[self callFailureBlockOnMainThread:failureBlock withError:error];
}];
Expand All @@ -2398,7 +2540,7 @@ + (void)setEmail:(NSString * _Nonnull)email withEmailAuthHashToken:(NSString * _
if (emailPlayerId) {
[OneSignal saveEmailAddress:email withAuthToken:emailAuthToken userId:emailPlayerId];
[OneSignalClient.sharedClient executeRequest:[OSRequestUpdateDeviceToken withUserId:self.currentSubscriptionState.userId appId:self.appId deviceToken:nil withParentId:self.currentEmailSubscriptionState.emailUserId emailAuthToken:hashToken email:email externalIdAuthToken:[self mExternalIdAuthToken] ] onSuccess:^(NSDictionary *result) {
[self callSuccessBlockOnMainThread:successBlock];
[self callEmailSuccessBlockOnMainThread:successBlock];
} onFailure:^(NSError *error) {
[self callFailureBlockOnMainThread:failureBlock withError:error];
}];
Expand Down Expand Up @@ -2439,7 +2581,7 @@ + (void)logoutEmailWithSuccess:(OSEmailSuccessBlock _Nullable)successBlock withF
[OneSignalUserDefaults.initStandard removeValueForKey:OSUD_EMAIL_PLAYER_ID];
[OneSignal saveEmailAddress:nil withAuthToken:nil userId:nil];

[self callSuccessBlockOnMainThread:successBlock];
[self callEmailSuccessBlockOnMainThread:successBlock];
} onFailure:^(NSError *error) {
[self callFailureBlockOnMainThread:failureBlock withError:error];
}];
Expand Down
37 changes: 37 additions & 0 deletions iOS_SDK/OneSignalSDK/UnitTests/RequestTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ @implementation RequestTests {
NSString *testInAppMessageAppId;
NSString *testInAppMessageVariantId;
NSString *testInAppMessagePageId;
NSString *testLiveActivityId;
NSString *testLiveActivityToken;
NSString *testNotificationId;
OSOutcomeEvent *testOutcome;
NSNumber *testDeviceType;
Expand Down Expand Up @@ -84,6 +86,8 @@ - (void)setUp {
testInAppMessageAppId = @"test_in_app_message_app_id";
testInAppMessageVariantId = @"test_in_app_message_variant_id";
testInAppMessagePageId = @"test_in_app_message_page_id";
testLiveActivityId = @"test_live_activity_id";
testLiveActivityToken = @"test_live_activity_token";
testNotificationId = @"test_notification_id";

testOutcome = [[OSOutcomeEvent new] initWithSession:UNATTRIBUTED
Expand Down Expand Up @@ -763,6 +767,39 @@ - (void)testSendTrackUsageRequest {
}));
}

- (void)testEnterLiveActivity {
let request = [OSRequestLiveActivityEnter withUserId:testUserId appId:testAppId activityId:testLiveActivityId token:testLiveActivityToken];

let testEnterLiveActivityUrlPath = [NSString stringWithFormat:@"apps/%@/live_activities/%@/token",
testAppId,
testLiveActivityId];

let correctUrl = correctUrlWithPath(testEnterLiveActivityUrlPath);

XCTAssertTrue([correctUrl isEqualToString:request.urlRequest.URL.absoluteString]);
XCTAssertTrue(checkHttpBody(request.urlRequest.HTTPBody, @{@"push_token" : testLiveActivityToken, @"subscription_id" : testUserId }));

XCTAssertEqualObjects(request.urlRequest.HTTPMethod, @"POST");
XCTAssertEqualObjects(request.urlRequest.allHTTPHeaderFields[@"Accept"], @"application/vnd.onesignal.v1+json");
}

- (void)testExitLiveActivity {
let request = [OSRequestLiveActivityExit withUserId:testUserId appId:testAppId activityId:testLiveActivityId];

let testExitLiveActivityUrlPath = [NSString stringWithFormat:@"apps/%@/live_activities/%@/token/%@",
testAppId,
testLiveActivityId,
testUserId];

let correctUrl = correctUrlWithPath(testExitLiveActivityUrlPath);

XCTAssertTrue([correctUrl isEqualToString:request.urlRequest.URL.absoluteString]);

XCTAssertEqualObjects(request.urlRequest.HTTPBody, nil);
XCTAssertEqualObjects(request.urlRequest.HTTPMethod, @"DELETE");
XCTAssertEqualObjects(request.urlRequest.allHTTPHeaderFields[@"Accept"], @"application/vnd.onesignal.v1+json");
}

- (void)testAdditionalHeaders {
// Create a fake request
let request = [OneSignalRequest new];
Expand Down
Loading

0 comments on commit 32de71b

Please sign in to comment.