Skip to content

Commit 2d460c7

Browse files
authored
fix: update unattributed outcomes to match attributed outcomes and Android SDK (#1655)
* Align iOS session duration reporting with Android SDK behavior Both attributed and unattributed focus time processors now behave like the Android SDK: session time accumulates silently across background events, and both the user property update and the outcomes/measure call are sent together only after a 30-second timer fires (confirming the user has left). If the user returns within 30 seconds, the timer is cancelled and no calls are made. Changes to OSAttributedFocusTimeProcessor: - Move sendSessionTime from sendOnFocusCall to the actual send method so it only fires when the 30s timer fires or the session ends, sending the full accumulated total instead of each interval Changes to OSUnattributedFocusTimeProcessor: - Add 30s NSTimer delay before sending to outcomes/measure - Send immediately (no delay) when session ends via influence change - Implement cancelDelayedJob to invalidate timer on foreground return - Clear unsent time only on outcomes success (retry on failure) - Move sendSessionTime to the actual send method (same as attributed) - Reduce min session threshold from 60s to 1s to match attributed Made-with: Cursor * Remove unused min session time threshold Both processors now use a unified `< 1` guard in sendOnFocusCallWithParams: instead of the old getMinSessionTime/hasMinSyncTime dispatch through the base class.
1 parent 5f2ad5b commit 2d460c7

4 files changed

Lines changed: 53 additions & 44 deletions

File tree

iOS_SDK/OneSignalSDK/Source/OSAttributedFocusTimeProcessor.m

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ @implementation OSAttributedFocusTimeProcessor {
3939
NSTimer* restCallTimer;
4040
}
4141

42-
static let ATTRIBUTED_MIN_SESSION_TIME_SEC = 1;
4342
static let DELAY_TIME = 30;
4443

4544
- (instancetype)init {
@@ -48,10 +47,6 @@ - (instancetype)init {
4847
return self;
4948
}
5049

51-
- (int)getMinSessionTime {
52-
return ATTRIBUTED_MIN_SESSION_TIME_SEC;
53-
}
54-
5550
- (NSString*)unsentActiveTimeUserDefaultsKey {
5651
return OSUD_UNSENT_ACTIVE_TIME_ATTRIBUTED;
5752
}
@@ -61,11 +56,8 @@ - (void)sendOnFocusCall:(OSFocusCallParams *)params {
6156
let totalTimeActive = unsentActive + params.timeElapsed;
6257
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG
6358
message:[NSString stringWithFormat:@"sendOnFocusCall attributed with totalTimeActive %f", totalTimeActive]];
64-
59+
6560
[super saveUnsentActiveTime:totalTimeActive];
66-
67-
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"OSAttributedFocusTimeProcessor:sendSessionTime of %@", @(params.timeElapsed)]];
68-
[OneSignalUserManagerImpl.sharedInstance sendSessionTime:@(params.timeElapsed)];
6961

7062
[self sendOnFocusCallWithParams:params totalTimeActive:totalTimeActive];
7163
}
@@ -110,6 +102,9 @@ - (void)sendBackgroundAttributedSessionTimeWithNSTimer:(NSTimer*)timer {
110102
- (void)sendBackgroundAttributedSessionTimeWithParams:(OSFocusCallParams *)params withTotalTimeActive:(NSNumber*)totalTimeActive {
111103
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:@"OSAttributedFocusTimeProcessor:sendBackgroundAttributedSessionTimeWithParams start"];
112104

105+
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"OSAttributedFocusTimeProcessor:sendSessionTime of %@", totalTimeActive]];
106+
[OneSignalUserManagerImpl.sharedInstance sendSessionTime:totalTimeActive];
107+
113108
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
114109
[OneSignal sendSessionEndOutcomes:totalTimeActive params:params onSuccess:^(NSDictionary *result) {
115110
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:@"sendBackgroundAttributed succeed"];

iOS_SDK/OneSignalSDK/Source/OSBaseFocusTimeProcessor.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@
3434

3535
@property (nonatomic, readonly) BOOL onFocusCallEnabled;
3636

37-
- (int)getMinSessionTime;
3837
- (NSString*)unsentActiveTimeUserDefaultsKey;
39-
- (BOOL)hasMinSyncTime:(NSTimeInterval)activeTime;
4038

4139
- (void)resetUnsentActiveTime;
4240
- (void)sendOnFocusCall:(OSFocusCallParams *)params;

iOS_SDK/OneSignalSDK/Source/OSBaseFocusTimeProcessor.m

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,6 @@ @implementation OSBaseFocusTimeProcessor {
3333
NSNumber* unsentActiveTime;
3434
}
3535

36-
// Must override
37-
- (int)getMinSessionTime {
38-
@throw [NSException exceptionWithName:NSInternalInconsistencyException
39-
reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] userInfo:nil];
40-
}
41-
42-
- (BOOL)hasMinSyncTime:(NSTimeInterval)activeTime {
43-
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"OSBaseFocusTimeProcessor hasMinSyncTime getMinSessionTime: %d activeTime: %f", [self getMinSessionTime], activeTime]];
44-
return activeTime >= [self getMinSessionTime];
45-
}
46-
4736
- (void)resetUnsentActiveTime {
4837
unsentActiveTime = nil;
4938
}

iOS_SDK/OneSignalSDK/Source/OSUnattributedFocusTimeProcessor.m

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,67 +36,94 @@ @interface OneSignal ()
3636
+ (void)sendSessionEndOutcomes:(NSNumber*)totalTimeActive params:(OSFocusCallParams *)params onSuccess:(OSResultSuccessBlock _Nonnull)successBlock onFailure:(OSFailureBlock _Nonnull)failureBlock;
3737
@end
3838

39-
@implementation OSUnattributedFocusTimeProcessor
39+
@implementation OSUnattributedFocusTimeProcessor {
40+
NSTimer* restCallTimer;
41+
}
4042

41-
static let UNATTRIBUTED_MIN_SESSION_TIME_SEC = 60;
43+
static let DELAY_TIME = 30;
4244

4345
- (instancetype)init {
4446
self = [super init];
4547
[OSBackgroundTaskManager setTaskInvalid:SESSION_OUTCOMES_TASK];
4648
return self;
4749
}
4850

49-
- (int)getMinSessionTime {
50-
return UNATTRIBUTED_MIN_SESSION_TIME_SEC;
51-
}
52-
5351
- (NSString*)unsentActiveTimeUserDefaultsKey {
5452
return OSUD_UNSENT_ACTIVE_TIME;
5553
}
5654

5755
- (void)sendOnFocusCall:(OSFocusCallParams *)params {
5856
let unsentActive = [super getUnsentActiveTime];
5957
let totalTimeActive = unsentActive + params.timeElapsed;
60-
58+
6159
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"sendOnFocusCall unattributed with totalTimeActive %f", totalTimeActive]];
62-
63-
if (![super hasMinSyncTime:totalTimeActive]) {
64-
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"unattributed influence saveUnsentActiveTime %f", totalTimeActive]];
65-
[super saveUnsentActiveTime:totalTimeActive];
66-
return;
67-
}
60+
61+
[super saveUnsentActiveTime:totalTimeActive];
6862

6963
[self sendOnFocusCallWithParams:params totalTimeActive:totalTimeActive];
7064
}
7165

7266
- (void)sendUnsentActiveTime:(OSFocusCallParams *)params {
7367
let unsentActive = [super getUnsentActiveTime];
7468
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"sendUnsentActiveTime unattributed with unsentActive %f", unsentActive]];
75-
69+
7670
[self sendOnFocusCallWithParams:params totalTimeActive:unsentActive];
7771
}
7872

7973
- (void)sendOnFocusCallWithParams:(OSFocusCallParams *)params totalTimeActive:(NSTimeInterval)totalTimeActive {
80-
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"OSUnattributedFocusTimeProcessor:sendSessionTime of %@", @(totalTimeActive)]];
81-
[OneSignalUserManagerImpl.sharedInstance sendSessionTime:@(totalTimeActive)];
82-
[super saveUnsentActiveTime:0];
83-
74+
if (totalTimeActive < 1) {
75+
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"sendSessionEndOutcomes not sending active time %f", totalTimeActive]];
76+
return;
77+
}
78+
8479
[OSBackgroundTaskManager beginBackgroundTask:SESSION_OUTCOMES_TASK];
80+
81+
if (params.onSessionEnded) {
82+
[self sendBackgroundUnattributedSessionTimeWithParams:params withTotalTimeActive:@(totalTimeActive)];
83+
return;
84+
}
85+
86+
restCallTimer = [NSTimer
87+
scheduledTimerWithTimeInterval:DELAY_TIME
88+
target:self
89+
selector:@selector(sendBackgroundUnattributedSessionTimeWithNSTimer:)
90+
userInfo:@{@"params": params, @"time": @(totalTimeActive)}
91+
repeats:false];
92+
}
93+
94+
- (void)sendBackgroundUnattributedSessionTimeWithNSTimer:(NSTimer*)timer {
95+
let userInfo = (NSDictionary<NSString*, id>*)timer.userInfo;
96+
let params = (OSFocusCallParams*)userInfo[@"params"];
97+
let totalTimeActive = (NSNumber*)userInfo[@"time"];
98+
[self sendBackgroundUnattributedSessionTimeWithParams:params withTotalTimeActive:totalTimeActive];
99+
}
100+
101+
- (void)sendBackgroundUnattributedSessionTimeWithParams:(OSFocusCallParams *)params withTotalTimeActive:(NSNumber*)totalTimeActive {
102+
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:@"OSUnattributedFocusTimeProcessor:sendBackgroundUnattributedSessionTimeWithParams start"];
103+
104+
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"OSUnattributedFocusTimeProcessor:sendSessionTime of %@", totalTimeActive]];
105+
[OneSignalUserManagerImpl.sharedInstance sendSessionTime:totalTimeActive];
106+
85107
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
86-
[OneSignal sendSessionEndOutcomes:@(totalTimeActive) params:params onSuccess:^(NSDictionary *result) {
108+
[OneSignal sendSessionEndOutcomes:totalTimeActive params:params onSuccess:^(NSDictionary *result) {
87109
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:@"sendUnattributed session end outcomes succeed"];
110+
[super saveUnsentActiveTime:0];
88111
[OSBackgroundTaskManager endBackgroundTask:SESSION_OUTCOMES_TASK];
89112
} onFailure:^(NSError *error) {
90-
[OneSignalLog onesignalLog:ONE_S_LL_ERROR message:@"sendUnattributed session end outcomes failed"];
113+
[OneSignalLog onesignalLog:ONE_S_LL_ERROR message:@"sendUnattributed session end outcomes failed, will retry on next open"];
91114
[OSBackgroundTaskManager endBackgroundTask:SESSION_OUTCOMES_TASK];
92115
}];
93116
});
94117
}
95118

96119
- (void)cancelDelayedJob {
97-
// No job to cancel, network call is made right away.
98-
}
120+
if (!restCallTimer)
121+
return;
99122

123+
[restCallTimer invalidate];
124+
restCallTimer = nil;
125+
[OSBackgroundTaskManager endBackgroundTask:SESSION_OUTCOMES_TASK];
126+
}
100127

101128
@end
102129

0 commit comments

Comments
 (0)