Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/mvm_core into feature/ONEAPP-3998

This commit is contained in:
Scott Pfeil 2023-05-30 10:18:07 -04:00
commit a5c5397c92
9 changed files with 228 additions and 326 deletions

View File

@ -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 */,

View File

@ -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()
}
})
}
})
}
}
}
}

View File

@ -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"
@ -396,7 +395,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.

View File

@ -81,5 +81,4 @@ FOUNDATION_EXPORT const unsigned char MVMCoreVersionString[];
// Singletons
#import <MVMCore/MVMCoreCache.h>
#import <MVMCore/MVMCoreSessionTimeHandler.h>
#import <MVMCore/MVMCoreSessionObject.h>

View File

@ -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

View File

@ -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

View 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
}
}

View File

@ -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

View File

@ -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];