story: story: ONEAPP-4067 Swiftify sessionTimeHandler
This commit is contained in:
parent
cdff607cf1
commit
c21e4b680b
@ -41,8 +41,7 @@
|
||||
1DAD0FFE26AAB40000216E83 /* ActionRunJavaScriptModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DAD0FFD26AAB3FF00216E83 /* ActionRunJavaScriptModel.swift */; };
|
||||
2723337B28BD534D004EAEE0 /* MVMCoreEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2723337A28BD534D004EAEE0 /* MVMCoreEvent.swift */; };
|
||||
2723337D28BD53C2004EAEE0 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2723337C28BD53C2004EAEE0 /* Date+Extension.swift */; };
|
||||
30349BF11FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 30349BEF1FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
30349BF21FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 30349BF01FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m */; };
|
||||
60CBD0542A02397A00056CB0 /* MVMCoreSessionTimeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CBD0532A02397A00056CB0 /* MVMCoreSessionTimeHandler.swift */; };
|
||||
881D26931FCC9D180079C521 /* MVMCoreErrorObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 881D268F1FCC9D180079C521 /* MVMCoreErrorObject.m */; };
|
||||
881D26941FCC9D180079C521 /* MVMCoreOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 881D26901FCC9D180079C521 /* MVMCoreOperation.m */; };
|
||||
881D26951FCC9D180079C521 /* MVMCoreErrorObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 881D26911FCC9D180079C521 /* MVMCoreErrorObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@ -199,8 +198,7 @@
|
||||
1DAD0FFD26AAB3FF00216E83 /* ActionRunJavaScriptModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionRunJavaScriptModel.swift; sourceTree = "<group>"; };
|
||||
2723337A28BD534D004EAEE0 /* MVMCoreEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreEvent.swift; sourceTree = "<group>"; };
|
||||
2723337C28BD53C2004EAEE0 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = "<group>"; };
|
||||
30349BEF1FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreSessionTimeHandler.h; sourceTree = "<group>"; };
|
||||
30349BF01FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreSessionTimeHandler.m; sourceTree = "<group>"; };
|
||||
60CBD0532A02397A00056CB0 /* MVMCoreSessionTimeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreSessionTimeHandler.swift; sourceTree = "<group>"; };
|
||||
881D268F1FCC9D180079C521 /* MVMCoreErrorObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreErrorObject.m; sourceTree = "<group>"; };
|
||||
881D26901FCC9D180079C521 /* MVMCoreOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreOperation.m; sourceTree = "<group>"; };
|
||||
881D26911FCC9D180079C521 /* MVMCoreErrorObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreErrorObject.h; sourceTree = "<group>"; };
|
||||
@ -516,8 +514,7 @@
|
||||
AF43A70C1FC4F42B008E9347 /* Session */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
30349BEF1FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h */,
|
||||
30349BF01FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m */,
|
||||
60CBD0532A02397A00056CB0 /* MVMCoreSessionTimeHandler.swift */,
|
||||
AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */,
|
||||
AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */,
|
||||
);
|
||||
@ -737,7 +734,6 @@
|
||||
AFBB96EC1FBA4A260008D868 /* MFHardCodedServerResponse.h in Headers */,
|
||||
AFBB96B21FBA3B590008D868 /* MVMCoreGetterUtility.h in Headers */,
|
||||
8876D5F21FB50AB000EB2E3D /* UIFont+MFSpacing.h in Headers */,
|
||||
30349BF11FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h in Headers */,
|
||||
AFBB96931FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.h in Headers */,
|
||||
AFBB96961FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.h in Headers */,
|
||||
AFBB96911FBA3A9A0008D868 /* MVMCoreNavigationOperation.h in Headers */,
|
||||
@ -915,7 +911,6 @@
|
||||
94C014D124211869005811A9 /* ActionRestartModel.swift in Sources */,
|
||||
D288D5F526C6EFE000A5C365 /* MVMCoreLoggingHandler+Extension.swift in Sources */,
|
||||
946EE1B0237B5EF70036751F /* JSONHelper.swift in Sources */,
|
||||
30349BF21FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m in Sources */,
|
||||
D2DEDCB923C6400600C44CC4 /* UnitInterval.swift in Sources */,
|
||||
94C014D3242119E6005811A9 /* ActionPreviousSubmitModel.swift in Sources */,
|
||||
AF60A7F2289212CA00919EEB /* MVMError.swift in Sources */,
|
||||
@ -973,6 +968,7 @@
|
||||
AF69D4F1286E9D8000BC6862 /* ActionNoopHandler.swift in Sources */,
|
||||
016FF6F2259A4FCC00F5E4AA /* ClientParameterModel.swift in Sources */,
|
||||
D2DEDCBB23C65BC300C44CC4 /* Percent.swift in Sources */,
|
||||
60CBD0542A02397A00056CB0 /* MVMCoreSessionTimeHandler.swift in Sources */,
|
||||
AFBB966A1FBA3A570008D868 /* MVMCoreLoadRequestOperation.m in Sources */,
|
||||
D268D82C26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.m in Sources */,
|
||||
0AEBB84625FA75C000EA80EE /* ActionOpenSMSModel.swift in Sources */,
|
||||
|
||||
@ -15,21 +15,23 @@ open class ActionRestartHandler: MVMCoreActionHandlerProtocol {
|
||||
guard let model = model as? ActionRestartModel else { return }
|
||||
|
||||
let _: Void = try await withCheckedThrowingContinuation { continuation in
|
||||
|
||||
|
||||
// Invalidates the session before restarting.
|
||||
MVMCoreSessionTimeHandler.sharedSession()?.invalidateSession({ error in
|
||||
if let error = error {
|
||||
guard error.code != NSURLErrorCancelled else {
|
||||
Task { @MainActor in
|
||||
MVMCoreSessionTimeHandler.shared().invalidateSession({ error in
|
||||
if let error = error {
|
||||
guard error.code != NSURLErrorCancelled else {
|
||||
continuation.resume()
|
||||
return
|
||||
}
|
||||
continuation.resume(throwing: MVMCoreError.errorObject(error))
|
||||
} else {
|
||||
// Restarts the app (forcing any passed in page types).
|
||||
MVMCoreSessionObject.sharedGlobal()?.restartSession(withPageType: model.pageType, request: model.requestURL, parameters: model.extraParameters?.toJSON(), clearAllVariables: true)
|
||||
continuation.resume()
|
||||
return
|
||||
}
|
||||
continuation.resume(throwing: MVMCoreError.errorObject(error))
|
||||
} else {
|
||||
// Restarts the app (forcing any passed in page types).
|
||||
MVMCoreSessionObject.sharedGlobal()?.restartSession(withPageType: model.pageType, request: model.requestURL, parameters: model.extraParameters?.toJSON(), clearAllVariables: true)
|
||||
continuation.resume()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
#import "MVMCoreLoadHandler.h"
|
||||
#import "MVMCoreLoadingOverlayHandler.h"
|
||||
#import "MVMCoreCache.h"
|
||||
#import "MVMCoreSessionTimeHandler.h"
|
||||
#import "MVMCoreLoggingHandler.h"
|
||||
#import "MVMCoreSessionObject.h"
|
||||
#import "MVMCoreViewControllerMappingObject.h"
|
||||
@ -392,7 +391,9 @@
|
||||
|
||||
// Update the session timer on a good response.
|
||||
if (loadObject.extendsAppSession) {
|
||||
[[MVMCoreSessionTimeHandler sharedSessionHandler] startSessionTimer];
|
||||
[MVMCoreDispatchUtility performBlockOnMainThread:^{
|
||||
[[MVMCoreSessionTimeHandler sharedSessionHandler] startSessionTimer];
|
||||
}];
|
||||
}
|
||||
|
||||
// Adds the modules received from server to any modules we grabbed from the cache.
|
||||
|
||||
@ -81,5 +81,4 @@ FOUNDATION_EXPORT const unsigned char MVMCoreVersionString[];
|
||||
|
||||
// Singletons
|
||||
#import <MVMCore/MVMCoreCache.h>
|
||||
#import <MVMCore/MVMCoreSessionTimeHandler.h>
|
||||
#import <MVMCore/MVMCoreSessionObject.h>
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
//
|
||||
// MVMCoreSessionTimeHandler.h
|
||||
// myverizon
|
||||
//
|
||||
// Created by Scott Pfeil on 2/25/14.
|
||||
// Copyright (c) 2014 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
// Handles the session timer.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class MVMCoreErrorObject;
|
||||
|
||||
@interface MVMCoreSessionTimeHandler : NSObject
|
||||
|
||||
/// The number of seconds ago that we started the last session timer. A value of -1 will be returned for sessions not started.
|
||||
@property (nonatomic, readonly) NSTimeInterval timeSinceStart;
|
||||
|
||||
/// The number of seconds remaining until a warning will occur. A value of 0 will be returned for sessions not started or warning tracking is not enabled.
|
||||
@property (nonatomic, readonly) NSTimeInterval remainingTimeUntilWarning;
|
||||
|
||||
/// The number of seconds remaining until a timeout will occur. A value of 0 will be returned for sessions not started or timeout tracking is not enabled.
|
||||
@property (nonatomic, readonly) NSTimeInterval remainingTimeUntiTimeout;
|
||||
|
||||
/// Keeps track of if the session is currently being timed. Used for entering from the background.
|
||||
@property (assign, nonatomic, readonly) BOOL sessionBeingTimed;
|
||||
|
||||
/// Keeps track of if the session has already timed out.
|
||||
@property (assign, nonatomic, readonly) BOOL sessionTimedOut;
|
||||
|
||||
#pragma mark - functions to override
|
||||
|
||||
/// Can override to provide a time until the warning shows in seconds. Set to 0 if there should be no warning. Default is 0.
|
||||
- (NSTimeInterval)timeUntilWarning;
|
||||
|
||||
/// Can override to provide a time until the timeout happens in seconds. If there is a warning, then this value is used after the warning happens. Set to 0 if there should be no timeout. Default is 0.
|
||||
- (NSTimeInterval)timeUntilTimeout;
|
||||
|
||||
/// Starts the timeout timer. Override to handle what happens on timeout warning. Should call super if want the timeout timer going.
|
||||
- (void)sessionTimeoutWarning NS_REQUIRES_SUPER;
|
||||
|
||||
/// Called when the session has timed out. Override to handle what happens on timeout. Should call super. Can be called to force timeout... should never need to call unless simulating timout.
|
||||
- (void)sessionTimeout:(BOOL)whileInBackground NS_REQUIRES_SUPER;
|
||||
|
||||
/// Keeps the session alive. A boolean for if we should show the alert if there is an error. Does nothing by default. Can override to do something.
|
||||
- (void)sendKeepAliveToServer:(BOOL)notifyUserIfError;
|
||||
|
||||
/// Invalidates the server session and then calls the completion handler. Error may or may not populate. By default this only calls the completion handler, override to invalidate your server session as you see fit then call completion.
|
||||
- (void)invalidateSession:(void (^ __nullable)(MVMCoreErrorObject * _Nullable error))completion;
|
||||
|
||||
#pragma mark - Session timer functions
|
||||
|
||||
/// Returns the shared instance of this singleton
|
||||
+ (nullable instancetype)sharedSessionHandler;
|
||||
|
||||
/// Starts the session timer. Should be called after every response from the server. Happens on the main thread.
|
||||
- (void)startSessionTimer;
|
||||
|
||||
/// Completely stop the session timer. Should only be used in rare occassions, like on the original wifi screen.
|
||||
- (void)stopSessionTimer;
|
||||
|
||||
/// Returns whether the app is in session.
|
||||
- (BOOL)isAppInSession;
|
||||
|
||||
/// Resets everything.
|
||||
- (void)resetState;
|
||||
|
||||
@end
|
||||
@ -1,232 +0,0 @@
|
||||
//
|
||||
// MVMCoreSessionTimeHandler.m
|
||||
// myverizon
|
||||
//
|
||||
// Created by Scott Pfeil on 2/25/14.
|
||||
// Copyright (c) 2014 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MVMCoreSessionTimeHandler.h"
|
||||
#import "MVMCoreLoggingHandler.h"
|
||||
#import "MVMCoreSessionObject.h"
|
||||
#import "MVMCoreLoadHandler.h"
|
||||
#import "MVMCoreLoadingOverlayHandler.h"
|
||||
#import "MVMCoreGetterUtility.h"
|
||||
#import "MVMCoreErrorConstants.h"
|
||||
#import "MVMCoreErrorObject.h"
|
||||
#import "MVMCoreRequestParameters.h"
|
||||
#import "NSDictionary+MFConvenience.h"
|
||||
#import "MVMCoreObject.h"
|
||||
#import "MVMCoreActionUtility.h"
|
||||
|
||||
@interface MVMCoreSessionTimeHandler ()
|
||||
|
||||
@property (strong, nonatomic) NSTimer *sessionWarningTimer;
|
||||
@property (strong, nonatomic) NSTimer *sessionTimer;
|
||||
|
||||
/// The time that we started the last session timer.
|
||||
@property (assign, nonatomic, readwrite) NSTimeInterval timeTimerStarted;
|
||||
|
||||
/// Keeps track of if the session is currently being timed. Used for entering from the background.
|
||||
@property (assign, nonatomic, readwrite) BOOL sessionBeingTimed;
|
||||
|
||||
/// Keeps track of if the session has already timed out.
|
||||
@property (assign, nonatomic, readwrite) BOOL sessionTimedOut;
|
||||
|
||||
@property (assign, nonatomic) NSTimeInterval secondsUntilWarning;
|
||||
@property (assign, nonatomic) NSTimeInterval secondsUntilTimeout;
|
||||
|
||||
/// Should be called when the app enters the background.
|
||||
- (void)appEnteredBackground;
|
||||
|
||||
/// Should be called when the app enters the foreground.
|
||||
- (void)appEnteredForeground;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MVMCoreSessionTimeHandler
|
||||
|
||||
+ (nullable instancetype)sharedSessionHandler {
|
||||
return [MVMCoreActionUtility initializerClassCheck:[MVMCoreObject sharedInstance].sessionHandler classToVerify:self];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
// Adds notifications for if the app entered the background/foreground.
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnteredBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnteredForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
|
||||
[self resetStartTime];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
|
||||
}
|
||||
|
||||
#pragma mark - functions to override
|
||||
|
||||
- (NSTimeInterval)timeUntilWarning {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)timeUntilTimeout {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)sessionTimeoutWarning {
|
||||
}
|
||||
|
||||
- (void)sessionTimeout:(BOOL)whileInBackground {
|
||||
self.sessionTimedOut = YES;
|
||||
[self stopSessionTimer];
|
||||
}
|
||||
|
||||
- (void)sendKeepAliveToServer:(BOOL)notifyUserIfError {
|
||||
}
|
||||
|
||||
#pragma mark - State
|
||||
|
||||
- (void)markStartTime {
|
||||
self.timeTimerStarted = [NSDate timeIntervalSinceReferenceDate];
|
||||
}
|
||||
|
||||
- (void)resetStartTime {
|
||||
self.timeTimerStarted = -1;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)timeSinceStart {
|
||||
if (self.timeTimerStarted < 0) {
|
||||
return -1;
|
||||
}
|
||||
return [NSDate timeIntervalSinceReferenceDate] - self.timeTimerStarted;
|
||||
}
|
||||
|
||||
- (BOOL)isWarningEnabled {
|
||||
return [self isTimeoutEnabled] && !fequal(0, self.secondsUntilWarning);
|
||||
}
|
||||
|
||||
- (BOOL)isTimeoutEnabled {
|
||||
return !fequal(0, self.secondsUntilTimeout);
|
||||
}
|
||||
|
||||
- (NSTimeInterval)remainingTimeUntilWarning {
|
||||
if (self.timeTimerStarted < 0 || ![self isWarningEnabled]) {
|
||||
return 0;
|
||||
}
|
||||
return self.secondsUntilWarning - self.timeSinceStart;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)remainingTimeUntiTimeout {
|
||||
if (self.timeTimerStarted < 0 || ![self isTimeoutEnabled]) {
|
||||
return 0;
|
||||
}
|
||||
return self.secondsUntilTimeout - self.timeSinceStart;
|
||||
}
|
||||
|
||||
#pragma mark - Session timer functions
|
||||
|
||||
- (void)startSessionTimer {
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
|
||||
if (!self.sessionTimedOut) {
|
||||
|
||||
[self.sessionWarningTimer invalidate];
|
||||
[self.sessionTimer invalidate];
|
||||
|
||||
self.secondsUntilWarning = [self timeUntilWarning];
|
||||
self.secondsUntilTimeout = [self timeUntilTimeout];
|
||||
|
||||
if (self.isTimeoutEnabled) {
|
||||
self.sessionBeingTimed = YES;
|
||||
[self markStartTime];
|
||||
[self resumeSessionTimer];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Resume the session timers if they are stopped.
|
||||
- (void)resumeSessionTimer {
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
// Only start physical timer if active, otherwise will begin once entering foreground. FaceId prompt is considered UIApplicationStateInactive.
|
||||
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.isTimeoutEnabled && ![self.sessionTimer isValid]) {
|
||||
// Session timeout, implicitly whileInBackround as no. Note based on scheduledTimerWithTimeInterval, if the remaining time < 0, this will fire near immediately.
|
||||
self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:[self remainingTimeUntiTimeout] target:self selector:@selector(sessionTimeout:) userInfo:nil repeats:NO];
|
||||
MVMCoreLog(@"Session timeout will fire at: %@", self.sessionTimer.fireDate);
|
||||
|
||||
// Only setup a warning timer if there is a timeout timer. Note based on scheduledTimerWithTimeInterval, if the remaining time < 0, this will fire near immediately.
|
||||
if (self.isWarningEnabled && ![self.sessionWarningTimer isValid]) {
|
||||
self.sessionWarningTimer = [NSTimer scheduledTimerWithTimeInterval:[self remainingTimeUntilWarning] target:self selector:@selector(sessionTimeoutWarning) userInfo:nil repeats:NO];
|
||||
MVMCoreLog(@"Session warning will fire at: %@", self.sessionWarningTimer.fireDate);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)stopSessionTimer {
|
||||
|
||||
// nil timer, session no longer timed.
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
self.sessionBeingTimed = NO;
|
||||
[self.sessionWarningTimer invalidate];
|
||||
self.sessionWarningTimer = nil;
|
||||
[self.sessionTimer invalidate];
|
||||
self.sessionTimer = nil;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)appEnteredBackground {
|
||||
|
||||
// Session is still being timed. Invalidates here, will start up again on enter foreground if need be.
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
[self.sessionWarningTimer invalidate];
|
||||
self.sessionWarningTimer = nil;
|
||||
[self.sessionTimer invalidate];
|
||||
self.sessionTimer = nil;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)appEnteredForeground {
|
||||
// Special return logic to cancel when returning in warning track. This is not in the revalidateSessionTimestamp in order to prevent API checks to isAppInSession from timing out the session from under the user warning period.
|
||||
if (self.isWarningEnabled && self.remainingTimeUntilWarning < 0) {
|
||||
[self sessionTimeout:YES];
|
||||
}
|
||||
|
||||
// Revalidate first to check if we should be in the timeout state.
|
||||
[self revalidateSessionTimestamp];
|
||||
[self sendKeepAliveToServer:NO];
|
||||
}
|
||||
|
||||
- (void)invalidateSession:(void (^ __nullable)(MVMCoreErrorObject * _Nullable error))completion {
|
||||
completion(nil);
|
||||
}
|
||||
|
||||
// Checks to make sure session is still valid and that the timer is running.
|
||||
- (void)revalidateSessionTimestamp {
|
||||
if (self.sessionBeingTimed || self.sessionTimedOut) {
|
||||
if (self.isTimeoutEnabled && self.remainingTimeUntiTimeout < 0) {
|
||||
// Force a timeout if we are passed the timeout interval.
|
||||
[self sessionTimeout:YES];
|
||||
} else {
|
||||
[self resumeSessionTimer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isAppInSession {
|
||||
[self revalidateSessionTimestamp]; // Possible race condition hazard. Session timeouts will happen as if they are in background. If isAppInSession is called right before foreground trigger, it could potentially suppress the dialog.
|
||||
return self.sessionBeingTimed && !self.sessionTimedOut;
|
||||
}
|
||||
|
||||
- (void)resetState {
|
||||
[self stopSessionTimer];
|
||||
self.sessionTimedOut = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
202
MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.swift
Normal file
202
MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.swift
Normal file
@ -0,0 +1,202 @@
|
||||
//
|
||||
// MVMCoreSessionTimeHandler.swift
|
||||
// MVMCore
|
||||
//
|
||||
// Created by Nandhini Rajendran on 03/05/23.
|
||||
// Copyright © 2023 myverizon. All rights reserved.
|
||||
//
|
||||
// Managed on the main thread because of the run loop requirements of the timers.
|
||||
|
||||
@MainActor
|
||||
@objc open class MVMCoreSessionTimeHandler: NSObject {
|
||||
/// Keeps track of if the session is currently being timed. Used for entering from the background.
|
||||
public var sessionBeingTimed: Bool = false
|
||||
/// Keeps track of if the session has already timed out.
|
||||
public var sessionTimedOut: Bool = false
|
||||
private var sessionWarningTimer: Timer?
|
||||
private var sessionTimer: Timer?
|
||||
/// The time that we started the last session timer.
|
||||
private var timeTimerStarted: TimeInterval?
|
||||
private var secondsUntilWarning: TimeInterval = 0
|
||||
private var secondsUntilTimeout: TimeInterval = 0
|
||||
|
||||
@objc(sharedSessionHandler)
|
||||
public static func shared() -> Self {
|
||||
return MVMCoreActionUtility.fatalClassCheck(object: MVMCoreObject.sharedInstance()?.sessionHandler)
|
||||
}
|
||||
|
||||
public override init() {
|
||||
super.init()
|
||||
// Adds notifications for if the app entered the background/foreground.
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(appEnteredBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(appEnteredForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
|
||||
resetStartTime()
|
||||
}
|
||||
|
||||
deinit {
|
||||
Task { @MainActor in
|
||||
NotificationCenter.default.removeObserver(self, name: UIApplication.didEnterBackgroundNotification, object: nil)
|
||||
NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - functions to override
|
||||
|
||||
/// Can override to provide a time until the warning shows in seconds. Set to 0 if there should be no warning. Default is 0.
|
||||
open func timeUntilWarning() -> TimeInterval {
|
||||
return 0
|
||||
}
|
||||
|
||||
/// Can override to provide a time until the timeout happens in seconds. If there is a warning, then this value is used after the warning happens. Set to 0 if there should be no timeout. Default is 0.
|
||||
open func timeUntilTimeout() -> TimeInterval {
|
||||
return 0
|
||||
}
|
||||
|
||||
/// Starts the timeout timer. Override to handle what happens on timeout warning. Should call super if want the timeout timer going.
|
||||
@objc open func sessionTimeoutWarning() { }
|
||||
|
||||
/// Called when the session has timed out. Override to handle what happens on timeout. Should call super. Can be called to force timeout... should never need to call unless simulating timout.
|
||||
@objc open func sessionTimeout(_ whileInBackground: Bool) {
|
||||
sessionTimedOut = true
|
||||
stopSessionTimer()
|
||||
}
|
||||
|
||||
/// Keeps the session alive. A boolean for if we should show the alert if there is an error. Does nothing by default. Can override to do something.
|
||||
@objc(sendKeepAliveToServer:)
|
||||
open func sendKeepAlive(toServer notifyUserIfError: Bool) { }
|
||||
|
||||
// MARK: - State
|
||||
func markStartTime() {
|
||||
timeTimerStarted = Date().timeIntervalSinceReferenceDate
|
||||
}
|
||||
|
||||
func resetStartTime() {
|
||||
timeTimerStarted = nil
|
||||
}
|
||||
|
||||
/// The number of seconds ago that we started the last session timer. A value of nil will be returned for sessions not started.
|
||||
public func timeSinceStart() -> TimeInterval? {
|
||||
guard let timeTimerStarted = timeTimerStarted else { return nil }
|
||||
return Date().timeIntervalSinceReferenceDate - timeTimerStarted
|
||||
}
|
||||
|
||||
func isWarningEnabled() -> Bool {
|
||||
return isTimeoutEnabled() && !MVMCoreGetterUtility.fequal(a: 0, b: Float(secondsUntilWarning))
|
||||
}
|
||||
|
||||
func isTimeoutEnabled() -> Bool {
|
||||
return !MVMCoreGetterUtility.fequal(a: 0, b: Float(secondsUntilTimeout))
|
||||
}
|
||||
|
||||
/// The number of seconds remaining until a warning will occur. A value of nil will be returned for sessions not started or warning tracking is not enabled.
|
||||
func remainingTimeUntilWarning() -> TimeInterval? {
|
||||
guard isWarningEnabled(),
|
||||
let timeTimerStarted = timeSinceStart() else { return nil }
|
||||
return secondsUntilWarning - timeTimerStarted
|
||||
}
|
||||
|
||||
/// The number of seconds remaining until a timeout will occur. A value of nil will be returned for sessions not started or timeout tracking is not enabled.
|
||||
public func remainingTimeUntiTimeout() -> TimeInterval? {
|
||||
guard isTimeoutEnabled(),
|
||||
let timeTimerStarted = timeSinceStart() else { return nil }
|
||||
return secondsUntilTimeout - timeTimerStarted
|
||||
}
|
||||
|
||||
// MARK: - Session timer functions
|
||||
|
||||
/// Starts the session timer. Should be called after every response from the server. Happens on the main thread.
|
||||
@objc public func startSessionTimer() {
|
||||
guard !sessionTimedOut else { return }
|
||||
sessionWarningTimer?.invalidate()
|
||||
sessionTimer?.invalidate()
|
||||
|
||||
secondsUntilWarning = timeUntilWarning()
|
||||
secondsUntilTimeout = timeUntilTimeout()
|
||||
|
||||
guard isTimeoutEnabled() else { return }
|
||||
sessionBeingTimed = true
|
||||
markStartTime()
|
||||
resumeSessionTimer()
|
||||
}
|
||||
|
||||
/// Resume the session timers if they are stopped.
|
||||
func resumeSessionTimer() {
|
||||
// Only start physical timer if active, otherwise will begin once entering foreground. FaceId prompt is considered UIApplicationStateInactive.
|
||||
guard UIApplication.shared.applicationState != UIApplication.State.background,
|
||||
let remainingTimeUntiTimeout = remainingTimeUntiTimeout(),
|
||||
!(sessionTimer?.isValid ?? false) else { return }
|
||||
// Session timeout, implicitly whileInBackround as no. Note based on scheduledTimerWithTimeInterval, if the remaining time < 0, this will fire near immediately.
|
||||
sessionTimer = Timer.scheduledTimer(timeInterval: remainingTimeUntiTimeout, target: self, selector: #selector(sessionTimeout(_:)), userInfo: nil, repeats: false)
|
||||
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "Session timeout will fire at: \(String(describing: sessionTimer?.fireDate)))")
|
||||
|
||||
// Only setup a warning timer if there is a timeout timer. Note based on scheduledTimerWithTimeInterval, if the remaining time < 0, this will fire near immediately.
|
||||
guard let remainingTimeUntilWarning = remainingTimeUntilWarning(),
|
||||
!(self.sessionWarningTimer?.isValid ?? false) else { return }
|
||||
sessionWarningTimer = Timer.scheduledTimer(timeInterval: remainingTimeUntilWarning, target: self, selector: #selector(sessionTimeoutWarning), userInfo: nil, repeats: false)
|
||||
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "Session warning will fire at: \(String(describing: sessionWarningTimer?.fireDate)))")
|
||||
}
|
||||
|
||||
/// Completely stop the session timer. Should only be used in rare occassions, like on the original wifi screen.
|
||||
@objc public func stopSessionTimer() {
|
||||
// nil timer, session no longer timed.
|
||||
sessionBeingTimed = false
|
||||
sessionWarningTimer?.invalidate()
|
||||
sessionWarningTimer = nil
|
||||
sessionTimer?.invalidate()
|
||||
sessionTimer = nil
|
||||
}
|
||||
|
||||
/// Should be called when the app enters the background.
|
||||
@objc nonisolated func appEnteredBackground() {
|
||||
// Session is still being timed. Invalidates here, will start up again on enter foreground if need be.
|
||||
Task { @MainActor in
|
||||
self.sessionWarningTimer?.invalidate()
|
||||
self.sessionWarningTimer = nil
|
||||
self.sessionTimer?.invalidate()
|
||||
self.sessionTimer = nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Should be called when the app enters the foreground.
|
||||
@objc nonisolated func appEnteredForeground() {
|
||||
// Special return logic to cancel when returning in warning track. This is not in the revalidateSessionTimestamp in order to prevent API checks to isAppInSession from timing out the session from under the user warning period.
|
||||
Task { @MainActor in
|
||||
if let remainingTimeUntilWarning = self.remainingTimeUntilWarning(),
|
||||
remainingTimeUntilWarning < 0 {
|
||||
self.sessionTimeout(true)
|
||||
}
|
||||
|
||||
// Revalidate first to check if we should be in the timeout state.
|
||||
self.revalidateSessionTimestamp()
|
||||
self.sendKeepAlive(toServer: false)
|
||||
}
|
||||
}
|
||||
|
||||
@objc open func invalidateSession(_ completion: ((MVMCoreErrorObject?) -> Void)? = nil) {
|
||||
completion?(nil)
|
||||
}
|
||||
|
||||
/// Checks to make sure session is still valid and that the timer is running.
|
||||
func revalidateSessionTimestamp() {
|
||||
guard sessionBeingTimed || sessionTimedOut else { return }
|
||||
if let remainingTimeUntiTimeout = remainingTimeUntiTimeout(),
|
||||
remainingTimeUntiTimeout < 0 {
|
||||
// Force a timeout if we are passed the timeout interval.
|
||||
sessionTimeout(true)
|
||||
} else {
|
||||
resumeSessionTimer()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the app is in session.
|
||||
@objc open func isAppInSession() -> Bool {
|
||||
revalidateSessionTimestamp() // Possible race condition hazard. Session timeouts will happen as if they are in background. If isAppInSession is called right before foreground trigger, it could potentially suppress the dialog.
|
||||
return sessionBeingTimed && !sessionTimedOut
|
||||
}
|
||||
|
||||
/// Resets everything.
|
||||
public func resetState() {
|
||||
stopSessionTimer()
|
||||
sessionTimedOut = false
|
||||
}
|
||||
}
|
||||
@ -10,13 +10,13 @@
|
||||
#import <MVMCore/MVMCoreSessionObject.h>
|
||||
#import <MVMCore/MVMCoreCache.h>
|
||||
#import <MVMCore/MVMCoreViewControllerMappingObject.h>
|
||||
#import <MVMCore/MVMCoreSessionTimeHandler.h>
|
||||
#import <MVMCore/MVMCoreGlobalLoadProtocol.h>
|
||||
#import <MVMCore/MVMCoreLoadingOverlayDelegateProtocol.h>
|
||||
#import <MVMCore/MVMCoreLoggingDelegateProtocol.h>
|
||||
#import <MVMCore/MVMCoreLoggingHandler.h>
|
||||
#import <MVMCore/MVMCoreLoadHandler.h>
|
||||
@class MVMCoreActionHandler;
|
||||
@class MVMCoreSessionTimeHandler;
|
||||
|
||||
@interface MVMCoreObject : NSObject
|
||||
|
||||
|
||||
@ -25,7 +25,9 @@
|
||||
self.session = [[MVMCoreSessionObject alloc] init];
|
||||
self.cache = [[MVMCoreCache alloc] init];
|
||||
self.viewControllerMapping = [[MVMCoreViewControllerMappingObject alloc] init];
|
||||
self.sessionHandler = [[MVMCoreSessionTimeHandler alloc] init];
|
||||
[MVMCoreDispatchUtility performSyncBlockOnMainThread:^{
|
||||
self.sessionHandler = [[MVMCoreSessionTimeHandler alloc] init];
|
||||
}];
|
||||
self.actionHandler = [[MVMCoreActionHandler alloc] init];
|
||||
self.loggingDelegate = [[MVMCoreLoggingHandler alloc] init];
|
||||
self.loadHandler = [[MVMCoreLoadHandler alloc] init];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user