Merge branch 'feature/feed_cache' into 'release/11_5_0'

Feature/feed cache

### Summary
Persistently Cached Discover

### JIRA Ticket
https://onejira.verizon.com/browse/MVAPCT-48

Co-authored-by: Scott Pfeil <Scott.Pfeil3@verizonwireless.com>
Co-authored-by: Hedden, Kyle Matthew <kyle.hedden@verizonwireless.com>

See merge request https://gitlab.verizon.com/BPHV_MIPS/mvm_core/-/merge_requests/319
This commit is contained in:
Bruce, Matt R 2024-04-05 20:17:36 +00:00
commit f6f272f727
15 changed files with 401 additions and 54 deletions

View File

@ -96,6 +96,7 @@
AF43A7411FC5FA6F008E9347 /* MVMCoreViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; AF43A7411FC5FA6F008E9347 /* MVMCoreViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF43A74C1FC6109F008E9347 /* MVMCoreSessionObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; AF43A74C1FC6109F008E9347 /* MVMCoreSessionObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF43A74D1FC6109F008E9347 /* MVMCoreSessionObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */; }; AF43A74D1FC6109F008E9347 /* MVMCoreSessionObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */; };
AF4955E22BAB1EB200567276 /* MVMCoreCache+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4955E12BAB1EB200567276 /* MVMCoreCache+Extension.swift */; };
AF60A7F2289212CA00919EEB /* MVMError.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F1289212CA00919EEB /* MVMError.swift */; }; AF60A7F2289212CA00919EEB /* MVMError.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F1289212CA00919EEB /* MVMError.swift */; };
AF60A7F4289212EB00919EEB /* MVMCoreError.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F3289212EB00919EEB /* MVMCoreError.swift */; }; AF60A7F4289212EB00919EEB /* MVMCoreError.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F3289212EB00919EEB /* MVMCoreError.swift */; };
AF686FDA2A8A876A008F666A /* NavigationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF686FD92A8A876A008F666A /* NavigationOperation.swift */; }; AF686FDA2A8A876A008F666A /* NavigationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF686FD92A8A876A008F666A /* NavigationOperation.swift */; };
@ -252,6 +253,7 @@
AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewProtocol.h; sourceTree = "<group>"; }; AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewProtocol.h; sourceTree = "<group>"; };
AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreSessionObject.h; sourceTree = "<group>"; }; AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreSessionObject.h; sourceTree = "<group>"; };
AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreSessionObject.m; sourceTree = "<group>"; }; AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreSessionObject.m; sourceTree = "<group>"; };
AF4955E12BAB1EB200567276 /* MVMCoreCache+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreCache+Extension.swift"; sourceTree = "<group>"; };
AF60A7F1289212CA00919EEB /* MVMError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMError.swift; sourceTree = "<group>"; }; AF60A7F1289212CA00919EEB /* MVMError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMError.swift; sourceTree = "<group>"; };
AF60A7F3289212EB00919EEB /* MVMCoreError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreError.swift; sourceTree = "<group>"; }; AF60A7F3289212EB00919EEB /* MVMCoreError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreError.swift; sourceTree = "<group>"; };
AF686FD92A8A876A008F666A /* NavigationOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationOperation.swift; sourceTree = "<group>"; }; AF686FD92A8A876A008F666A /* NavigationOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationOperation.swift; sourceTree = "<group>"; };
@ -656,6 +658,7 @@
children = ( children = (
AF43A7091FC4F415008E9347 /* MVMCoreCache.h */, AF43A7091FC4F415008E9347 /* MVMCoreCache.h */,
AF43A7081FC4F415008E9347 /* MVMCoreCache.m */, AF43A7081FC4F415008E9347 /* MVMCoreCache.m */,
AF4955E12BAB1EB200567276 /* MVMCoreCache+Extension.swift */,
605A9A292ABD712F00487E47 /* MVMCoreLoggingHandler.swift */, 605A9A292ABD712F00487E47 /* MVMCoreLoggingHandler.swift */,
6042E8FB2B3094680031644B /* MVMCoreLoggingHandlerHelper.h */, 6042E8FB2B3094680031644B /* MVMCoreLoggingHandlerHelper.h */,
D288D5F426C6EFE000A5C365 /* MVMCoreLoggingHandler+Extension.swift */, D288D5F426C6EFE000A5C365 /* MVMCoreLoggingHandler+Extension.swift */,
@ -902,6 +905,7 @@
AFBB96351FBA34310008D868 /* MVMCoreErrorConstants.m in Sources */, AFBB96351FBA34310008D868 /* MVMCoreErrorConstants.m in Sources */,
AF43A5881FBB67D6008E9347 /* MVMCoreActionUtility.m in Sources */, AF43A5881FBB67D6008E9347 /* MVMCoreActionUtility.m in Sources */,
AFED77A61FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.m in Sources */, AFED77A61FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.m in Sources */,
AF4955E22BAB1EB200567276 /* MVMCoreCache+Extension.swift in Sources */,
016CF36925FA6DD400B82A1F /* ClientParameterHandler.swift in Sources */, 016CF36925FA6DD400B82A1F /* ClientParameterHandler.swift in Sources */,
5846ABF42B44BB9000FA6C76 /* Collection+Safe.swift in Sources */, 5846ABF42B44BB9000FA6C76 /* Collection+Safe.swift in Sources */,
AF69D4F7286EA0B800BC6862 /* ActionPreviousSubmitHandler.swift in Sources */, AF69D4F7286EA0B800BC6862 /* ActionPreviousSubmitHandler.swift in Sources */,

View File

@ -83,7 +83,7 @@ open class ActionOpenPageHandler: MVMCoreJSONActionHandlerProtocol {
} }
// Adds any client parameters to the request parameters. // Adds any client parameters to the request parameters.
if let parametersToFetch = model.clientParameters, if let parametersToFetch = model.clientParameters,
let fetchedParameters = try await ClientParameterHandler().getClientParameters( let fetchedParameters = await ClientParameterHandler().getClientParameters(
with: parametersToFetch, with: parametersToFetch,
requestParameters: requestParameters.parameters as? [String : Any] ?? [:], requestParameters: requestParameters.parameters as? [String : Any] ?? [:],
actionId: MVMCoreActionHandler.getUUID(additionalData: additionalData) ?? "unknown", actionId: MVMCoreActionHandler.getUUID(additionalData: additionalData) ?? "unknown",

View File

@ -385,6 +385,7 @@
} else { } else {
MVMCoreLoadRequestOperation *loadOperation = [[MVMCoreLoadRequestOperation alloc] initWithRequestParameters:requestParameters dataForPage:dataForPage delegateObject:delegateObject backgroundLoad:NO]; MVMCoreLoadRequestOperation *loadOperation = [[MVMCoreLoadRequestOperation alloc] initWithRequestParameters:requestParameters dataForPage:dataForPage delegateObject:delegateObject backgroundLoad:NO];
loadOperation.identifier = requestParameters.identifier; loadOperation.identifier = requestParameters.identifier;
[loadOperation startLoadingAnimationIfNeeded];
[self.blockingLoadQueue addOperation:loadOperation]; [self.blockingLoadQueue addOperation:loadOperation];
return loadOperation; return loadOperation;
} }
@ -399,6 +400,8 @@
- (MVMCoreLoadRequestOperation *)loadObject:(nonnull MVMCoreLoadObject *)loadObject { - (MVMCoreLoadRequestOperation *)loadObject:(nonnull MVMCoreLoadObject *)loadObject {
MVMCoreLoadRequestOperation *loadOperation = [[MVMCoreLoadRequestOperation alloc] initWithLoadObject:loadObject backgroundLoad:NO]; MVMCoreLoadRequestOperation *loadOperation = [[MVMCoreLoadRequestOperation alloc] initWithLoadObject:loadObject backgroundLoad:NO];
loadOperation.identifier = loadObject.requestParameters.identifier;
[loadOperation startLoadingAnimationIfNeeded];
[self.blockingLoadQueue addOperation:loadOperation]; [self.blockingLoadQueue addOperation:loadOperation];
return loadOperation; return loadOperation;
} }

View File

@ -38,6 +38,41 @@ public enum PopBackError: MVMError, CustomStringConvertible {
@objc @objc
public extension MVMCoreLoadRequestOperation { public extension MVMCoreLoadRequestOperation {
/// Attempt to navigate to the controller with the given load object. Return the controller that we navigated to if successful.
@objc
@MainActor
func goToViewController(loadObject: MVMCoreLoadObject) async -> UIViewController? {
guard loadObject.requestParameters?.replaceViewIfOnStackElseLoadWithStyle == true,
let pageType = loadObject.pageType else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "\(type(of: self)): A new controller should be made and any existing shouldn't be replaced. pageType:\(String(describing: loadObject.pageType))")
return nil
}
let template = loadObject.pageJSON?.optionalStringForKey("template")
guard let controllerMappingObject = MVMCoreViewControllerMappingObject.shared()?.getViewControllerMapping(forTemplate: template, pageType: pageType) else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "\(type(of: self)): Failed to create a new controller. template:\(String(describing: template)) page:\(pageType)")
return nil
}
var controllerType: AnyClass?
if let programmaticMapping = controllerMappingObject as? MVMCoreViewControllerProgrammaticMappingObject {
controllerType = programmaticMapping.viewControllerClass
} else if let newVC = MVMCoreViewControllerMappingObject.shared()?.createMFViewController(ofTemplate: loadObject.pageJSON?.optionalStringForKey("template"), pageType: pageType) {
// Need to create the view controller to fetch the type.
controllerType = type(of: newVC)
} else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "\(type(of: self)): Failed to create a new controller. template:\(String(describing: template)) page:\(pageType)")
return nil
}
guard let viewController = await NavigationHandler.shared().navigateToViewController(of: pageType, controllerType: controllerType) else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "\(type(of: self)): No matching controller found in the hierarchy. Will need to create a new controller. pageType:\(pageType) controllerType:\(String(describing: controllerType)).")
return nil
}
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "\(type(of: self)): Navigated to controller. pageType:\(pageType) controllerType:\(String(describing: controllerType))")
stopLoadingAnimationIfNeeded()
return viewController
}
@objc @objc
func popBackToPage(for loadObject: MVMCoreLoadObject) { func popBackToPage(for loadObject: MVMCoreLoadObject) {
Task(priority: .high) { Task(priority: .high) {
@ -71,13 +106,9 @@ public extension MVMCoreLoadRequestOperation {
func navigate(with navigationOperation: NavigationOperation, loadObject: MVMCoreLoadObject?) async { func navigate(with navigationOperation: NavigationOperation, loadObject: MVMCoreLoadObject?) async {
// stop any loading animation we may have started if we are about to display // stop any loading animation we may have started if we are about to display
cancellable = NavigationHandler.shared().onNavigation cancellable = NavigationHandler.shared().onNavigation
.filter { $0.0 == .willNavigate } .filter { $0.0 == .willNavigate && navigationOperation == $0.1 }
.sink { (event, operation) in .sink { [weak self] (event, operation) in
if navigationOperation == operation, self?.stopLoadingAnimationIfNeeded()
!self.backgroundLoad,
!(loadObject?.requestParameters?.noloadingOverlay ?? false) {
MVMCoreLoadingOverlayHandler.sharedLoadingOverlay()?.stopLoading(false)
}
} }
await NavigationHandler.shared().navigate(with: navigationOperation) await NavigationHandler.shared().navigate(with: navigationOperation)
cancellable = nil cancellable = nil

View File

@ -19,7 +19,6 @@
@interface MVMCoreLoadRequestOperation : MVMCoreOperation @interface MVMCoreLoadRequestOperation : MVMCoreOperation
@property (nullable, strong, nonatomic) MVMCoreRequestParameters *requestParameters; @property (nullable, strong, nonatomic) MVMCoreRequestParameters *requestParameters;
/// For load objects as in input parameter. Does not attach self generated load objects.
@property (nullable, strong, nonatomic) MVMCoreLoadObject *loadObject; @property (nullable, strong, nonatomic) MVMCoreLoadObject *loadObject;
@property (nullable, strong, nonatomic) NSDictionary *dataForPage; @property (nullable, strong, nonatomic) NSDictionary *dataForPage;
@property (nullable, strong, nonatomic) DelegateObject *delegateObject; @property (nullable, strong, nonatomic) DelegateObject *delegateObject;
@ -42,6 +41,12 @@
// Initializes the operation with the load object, data for page, and mvm view controller to handle the loading with. Can be used for loading a screen without going to the cache or server. // Initializes the operation with the load object, data for page, and mvm view controller to handle the loading with. Can be used for loading a screen without going to the cache or server.
- (nullable instancetype)initWithLoadObject:(nullable MVMCoreLoadObject *)loadObject backgroundLoad:(BOOL)backgroundLoad; - (nullable instancetype)initWithLoadObject:(nullable MVMCoreLoadObject *)loadObject backgroundLoad:(BOOL)backgroundLoad;
/// Begins the loading animation if needed.
- (void)startLoadingAnimationIfNeeded;
/// Ends the loading animation if needed.
- (void)stopLoadingAnimationIfNeeded;
/* Checks the cache for the data and calls the completion handler with any found data. /* Checks the cache for the data and calls the completion handler with any found data.
* @param completionHandler The block that gets called with any fetched data. */ * @param completionHandler The block that gets called with any fetched data. */
+ (void)checkCacheForDataForRequest:(nonnull MVMCoreRequestParameters *)requestParameters completionHandler:(nonnull void (^)(NSDictionary * _Nullable pageFromCache, NSDictionary * _Nullable modulesFromCache))completionHandler; + (void)checkCacheForDataForRequest:(nonnull MVMCoreRequestParameters *)requestParameters completionHandler:(nonnull void (^)(NSDictionary * _Nullable pageFromCache, NSDictionary * _Nullable modulesFromCache))completionHandler;
@ -90,6 +95,8 @@
*/ */
+ (void)removeCaches:(nullable NSDictionary *)cacheDictionary; + (void)removeCaches:(nullable NSDictionary *)cacheDictionary;
+ (void)notifyListenersOfNewResponse:(nullable NSDictionary *)pages modules:(nullable NSDictionary *)modules systemParameters:(nullable NSDictionary *)systemParameters loadObject:(nonnull MVMCoreLoadObject *)loadObject;
/** Creates the view controller based on the load object passed in. /** Creates the view controller based on the load object passed in.
* @param loadObject The load data from the cache or server. * @param loadObject The load data from the cache or server.
* @param completionHandler The completion handler to load once finished. Returns any loaded view controller and the load.*/ * @param completionHandler The completion handler to load once finished. Returns any loaded view controller and the load.*/

View File

@ -34,6 +34,8 @@
@property (nonatomic, readwrite) BOOL alertToShow; @property (nonatomic, readwrite) BOOL alertToShow;
@property (strong, nonatomic, nullable) MVMCoreErrorObject *errorForAlertToShow; @property (strong, nonatomic, nullable) MVMCoreErrorObject *errorForAlertToShow;
@property (nonatomic, readwrite) BOOL loadingAnimationRunning;
@end @end
@implementation MVMCoreLoadRequestOperation @implementation MVMCoreLoadRequestOperation
@ -64,14 +66,13 @@
- (void)cancel { - (void)cancel {
[super cancel]; [super cancel];
[self.sessionTask cancel]; [self.sessionTask cancel];
[self stopLoadingAnimationIfNeeded];
} }
- (void)start { - (void)start {
// Adds a loading overlay if necessary. // Adds a loading overlay if necessary.
if (!self.backgroundLoad && !self.requestParameters.noloadingOverlay) { [self startLoadingAnimationIfNeeded];
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] startLoading];
}
[super start]; [super start];
} }
@ -83,9 +84,7 @@
- (void)markAsFinished { - (void)markAsFinished {
// stop any loading animation we may have started // stop any loading animation we may have started
if (!self.backgroundLoad && !self.requestParameters.noloadingOverlay) { [self stopLoadingAnimationIfNeeded];
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:NO];
}
MVMCoreLog(@"Load Operation finished for page type %@, background load %@", self.requestParameters.pageType, @(self.backgroundLoad)); MVMCoreLog(@"Load Operation finished for page type %@, background load %@", self.requestParameters.pageType, @(self.backgroundLoad));
[super markAsFinished]; [super markAsFinished];
@ -148,6 +147,7 @@
// Create a load object from any data we fetched. // Create a load object from any data we fetched.
MVMCoreLoadObject *loadObject = [self createLoadObjectWithPageFromCache:pageFromCache modulesFromCache:modulesFromCache]; MVMCoreLoadObject *loadObject = [self createLoadObjectWithPageFromCache:pageFromCache modulesFromCache:modulesFromCache];
self.loadObject = loadObject;
// Check if we need to go to server for missing data. // Check if we need to go to server for missing data.
MVMCoreRequestParameters *requestForMissingData = [MVMCoreLoadRequestOperation createRequestForDataWithLoadObject:loadObject]; MVMCoreRequestParameters *requestForMissingData = [MVMCoreLoadRequestOperation createRequestForDataWithLoadObject:loadObject];
@ -162,22 +162,22 @@
if(!self.backgroundLoad && loadObject.requestParameters.pageType) { if(!self.backgroundLoad && loadObject.requestParameters.pageType) {
[[MVMCoreLoggingHandler sharedLoggingHandler] logPageLoadStartedFor:loadObject.requestParameters.pageType requestUUID:loadObject.identifier requestURL:loadObject.requestParameters.URL.absoluteString]; [[MVMCoreLoggingHandler sharedLoggingHandler] logPageLoadStartedFor:loadObject.requestParameters.pageType requestUUID:loadObject.identifier requestURL:loadObject.requestParameters.URL.absoluteString];
} }
// Send a new request to the server. // Send a new request to the server.
[MVMCoreLoadRequestOperation sendRequest:requestForMissingData loadObject:loadObject completionHandler:^(NSDictionary * _Nullable json) { [MVMCoreLoadRequestOperation sendRequest:requestForMissingData loadObject:loadObject completionHandler:^(NSDictionary * _Nullable json) {
#if ENABLE_HARD_CODED_RESPONSE #if ENABLE_HARD_CODED_RESPONSE
if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(modifyJSON:)]) { if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(modifyJSON:)]) {
json = [[MVMCoreObject sharedInstance].globalLoadDelegate modifyJSON:json]; json = [[MVMCoreObject sharedInstance].globalLoadDelegate modifyJSON:json];
} }
#endif #endif
NSString *serverProcessTime = [(NSDictionary *)json objectChainOfKeysOrIndexes:@[@"ResponseInfo", @"timeStamp"]] ?: @"0"; NSString *serverProcessTime = [(NSDictionary *)json objectChainOfKeysOrIndexes:@[@"ResponseInfo", @"timeStamp"]] ?: @"0";
if(!self.backgroundLoad && loadObject.requestParameters.pageType && serverProcessTime) { if(!self.backgroundLoad && loadObject.requestParameters.pageType && serverProcessTime) {
[[MVMCoreLoggingHandler sharedLoggingHandler] logPageLoadCompleteFor:loadObject.requestParameters.pageType serverProcessingTime:serverProcessTime requestURL:loadObject.requestParameters.URL.absoluteString requestUUID:loadObject.identifier isFromCache:loadObject.pageDataFromCache]; [[MVMCoreLoggingHandler sharedLoggingHandler] logPageLoadCompleteFor:loadObject.requestParameters.pageType serverProcessingTime:serverProcessTime requestURL:loadObject.requestParameters.URL.absoluteString requestUUID:loadObject.identifier isFromCache:loadObject.pageDataFromCache];
} }
// Process the data retrieved from the server. // Process the data retrieved from the server.
[MVMCoreLoadRequestOperation processJSONFromServer:json loadObject:loadObject completionHandler:^(MVMCoreLoadObject * _Nonnull loadObject, MVMCoreErrorObject * _Nullable error) { [MVMCoreLoadRequestOperation processJSONFromServer:json loadObject:loadObject completionHandler:^(MVMCoreLoadObject * _Nonnull loadObject, MVMCoreErrorObject * _Nullable error) {
@ -202,6 +202,20 @@
#pragma mark - Load Functions #pragma mark - Load Functions
- (void)startLoadingAnimationIfNeeded {
if (self.loadingAnimationRunning) { return; }
if (self.backgroundLoad) { return; }
if (self.requestParameters.noloadingOverlay) { return; }
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] startLoading];
self.loadingAnimationRunning = YES;
}
- (void)stopLoadingAnimationIfNeeded {
if (!self.loadingAnimationRunning) { return; }
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:YES];
self.loadingAnimationRunning = NO;
}
+ (void)checkCacheForDataForRequest:(nonnull MVMCoreRequestParameters *)requestParameters completionHandler:(nonnull void (^)(NSDictionary * _Nullable pageFromCache, NSDictionary * _Nullable modulesFromCache))completionHandler { + (void)checkCacheForDataForRequest:(nonnull MVMCoreRequestParameters *)requestParameters completionHandler:(nonnull void (^)(NSDictionary * _Nullable pageFromCache, NSDictionary * _Nullable modulesFromCache))completionHandler {
if (requestParameters.neverLoadFromCache) { if (requestParameters.neverLoadFromCache) {
@ -430,6 +444,10 @@
NSDictionary *systemParameters = [jsonDictionary dict:KeySystemParameters]; NSDictionary *systemParameters = [jsonDictionary dict:KeySystemParameters];
loadObject.systemParametersJSON = systemParameters.count > 0 ? systemParameters : nil; loadObject.systemParametersJSON = systemParameters.count > 0 ? systemParameters : nil;
if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(willProcessLoadObject:)]) {
[[MVMCoreObject sharedInstance].globalLoadDelegate willProcessLoadObject:loadObject];
}
// module items are cached. // module items are cached.
MVMCoreErrorObject *moduleCachingError = nil; MVMCoreErrorObject *moduleCachingError = nil;
BOOL shouldContinue = [MVMCoreLoadRequestOperation cacheModules:modules loadObject:loadObject error:&moduleCachingError]; BOOL shouldContinue = [MVMCoreLoadRequestOperation cacheModules:modules loadObject:loadObject error:&moduleCachingError];
@ -581,9 +599,18 @@
}; };
if (!error.nativeDrivenErrorScreen) { if (!error.nativeDrivenErrorScreen) {
// Server driven screen, create normally // Server driven screen, create normally
[MVMCoreLoadRequestOperation createViewControllerWithLoadObject:loadObject completionHandler:completionHandler]; [MVMCoreDispatchUtility performBlockOnMainThread:^{
[loadObject.operation goToViewControllerWithLoadObject:loadObject completionHandler:^(UIViewController * _Nullable viewController) {
[MVMCoreDispatchUtility performBlockInBackground:^{
if (viewController) {
[MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:viewController errorObject:nil];
} else {
[MVMCoreLoadRequestOperation createViewControllerWithLoadObject:loadObject completionHandler:completionHandler];
}
}];
}];
}];
} else { } else {
// Get the proper native error screen from the delegate // Get the proper native error screen from the delegate
[MVMCoreDispatchUtility performBlockOnMainThread:^{ [MVMCoreDispatchUtility performBlockOnMainThread:^{

View File

@ -41,6 +41,9 @@
/// Checks to see if the operation has content to show. /// Checks to see if the operation has content to show.
- (BOOL)hasContentToShow:(nonnull MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error; - (BOOL)hasContentToShow:(nonnull MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error;
/// Notifies the delegate we are about to process the load object.
- (void)willProcessLoadObject:(nonnull MVMCoreLoadObject *)loadObject;
#if ENABLE_HARD_CODED_RESPONSE #if ENABLE_HARD_CODED_RESPONSE
- (nullable NSDictionary *)getJSONForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters; - (nullable NSDictionary *)getJSONForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters;
- (nonnull NSDictionary *)modifyJSON:(nonnull NSDictionary *)json; - (nonnull NSDictionary *)modifyJSON:(nonnull NSDictionary *)json;

View File

@ -19,6 +19,9 @@
- (nullable NSArray*)getAccessibilityElements; //AccessibilityElements that are owned by Manager. - (nullable NSArray*)getAccessibilityElements; //AccessibilityElements that are owned by Manager.
/// Attempt to navigate to the controller. Return the controller that we navigated to if successful.
- (void)navigateToViewControllerOfPageType:(nonnull NSString *)pageType controllerType:(_Nullable Class)controllerType completionHandler:(void (^ __nullable)(UIViewController * _Nullable viewController))completionHandler;
@optional @optional
/// Notifies the manager that the controller received new data. /// Notifies the manager that the controller received new data.

View File

@ -0,0 +1,106 @@
//
// Cache.swift
// JSONCreator
//
// Created by Matt Bruce on 3/20/24.
// Copyright © 2024 Verizon Wireless. All rights reserved.
//
import Foundation
public enum CacheError: Error {
case serializationFailed
case deserializationFailed
case dataNotFound
case dataExpired
case saveFailed(Error)
case loadFailed(Error)
}
public class CachedData: Codable {
public var data: [String: AnyHashable]
public var expirationDate: Date
enum CodingKeys: CodingKey {
case data, expirationDate
}
public init(data: [String: AnyHashable], expirationDate: Date) {
self.data = data
self.expirationDate = expirationDate
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(expirationDate, forKey: .expirationDate)
let dataAsData = try JSONSerialization.data(withJSONObject: data, options: [])
try container.encode(dataAsData, forKey: .data)
}
required public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
expirationDate = try values.decode(Date.self, forKey: .expirationDate)
let dataAsData = try values.decode(Data.self, forKey: .data)
guard let jsonData = try JSONSerialization.jsonObject(with: dataAsData, options: []) as? [String: AnyHashable] else {
throw CacheError.deserializationFailed
}
data = jsonData
}
}
@objc public class PersistentCacheManager: NSObject {
@objc public static let shared = PersistentCacheManager()
private let fileManager = FileManager.default
@objc public lazy var cacheDirectory = { fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("Atomic")}()
private override init() {}
@objc public func save(data: [String: AnyHashable], forKey key: String, path: URL, expirationDate: Date) throws {
let cachedData = CachedData(data: data, expirationDate: expirationDate)
do {
try FileManager.default.createDirectory(atPath: path.deletingLastPathComponent().relativePath, withIntermediateDirectories: true, attributes: [.protectionKey: FileProtectionType.complete])
let dataToSave = try JSONEncoder().encode(cachedData)
try dataToSave.write(to: path, options: [.atomic, .completeFileProtection])
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "CACHEDFEED: SAVED TO PERSISTENT CACHE, key:\(key), path:\(path)")
} catch is EncodingError {
throw CacheError.serializationFailed
} catch {
throw CacheError.saveFailed(error)
}
}
@objc public func load(forKey key: String, path: URL) throws -> [String: AnyHashable] {
do {
let data = try Data(contentsOf: path)
let decodedCachedData = try JSONDecoder().decode(CachedData.self, from: data)
if Date() < decodedCachedData.expirationDate {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "CACHEDFEED: LOADED FROM PERSISTENT CACHE, key:\(key), path:\(path)")
return decodedCachedData.data
} else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "CACHEDFEED: EXPIRED, key:\(key), path:\(path)")
throw CacheError.dataExpired
}
} catch {
// Remove item from the cache on any failure.
try fileManager.removeItem(at: path)
switch error {
case is DecodingError:
throw CacheError.deserializationFailed
default:
throw CacheError.loadFailed(error)
}
}
}
@objc public func remove(forKey key: String) throws {
let filePath = self.filePath(forKey: key)
try fileManager.removeItem(at: filePath)
}
@objc public func removeAll() throws {
try FileManager.default.removeItem(at: cacheDirectory)
}
private func filePath(forKey key: String) -> URL {
return cacheDirectory.appendingPathComponent("\(key).json")
}
}

View File

@ -12,6 +12,9 @@
#import <AVKit/AVKit.h> #import <AVKit/AVKit.h>
@class MVMCoreErrorObject; @class MVMCoreErrorObject;
extern NSString * _Nonnull const KeyCachePolicy;
extern NSString * _Nonnull const KeyCacheExpiry;
//block returned when getting image //block returned when getting image
//parameters are UIImage object for the image, NSData for gif images, UIImage object for the image, A BOOL to indicate if it is a fall back image. //parameters are UIImage object for the image, NSData for gif images, UIImage object for the image, A BOOL to indicate if it is a fall back image.
typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOOL); typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOOL);
@ -31,10 +34,28 @@ typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOO
#pragma mark - Page and Module Handling #pragma mark - Page and Module Handling
// Checks the set of pageTypes to be cached for the given pageType. // Checks the set of pageTypes to be cached for the given pageType.
- (BOOL)shouldCacheJSONWithPageType:(nonnull NSString *)pageType; - (BOOL)shouldCachePageJSON:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType;
// Checks the set of modules to be cached for the given module // Checks the set of modules to be cached for the given module
- (BOOL)shouldCacheJSONWithModule:(nonnull NSString *)module; - (BOOL)shouldCacheModuleJSON:(nonnull NSDictionary *)jsonDictionary moduleName:(nonnull NSString *)moduleName;
/// Returns if the json is expired or not.
- (BOOL)isJSONExpired:(nonnull NSDictionary *)jsonDictionary;
/// Returns the expiry time for the object.
- (nonnull NSDate *)getExpirationDateForJSON:(nonnull NSDictionary *)jsonDictionary;
/// Checks if the page is to be persistently cached.
- (BOOL)shouldPersistentlyCachePage:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType;
/// Checks if the module is to be persistently cached.
- (BOOL)shouldPersistentlyCacheModule:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module;
/// Can override the path for the page to be cached. Currently Cache/Atomic/Pages/pageType
- (nullable NSURL *)getPathForPersistentCachePage:(nonnull NSString *)pageType;
/// Can override the path for the page to be cached. Currently Cache/Atomic/Modules/moduleName
- (nullable NSURL *)getPathForPersistentCacheModule:(nonnull NSString *)moduleName;
// For pages external to the mobile first framework to be added to the list to not cache // For pages external to the mobile first framework to be added to the list to not cache
- (void)addPageTypesToNotCache:(nullable NSArray <NSString *>*)array; - (void)addPageTypesToNotCache:(nullable NSArray <NSString *>*)array;
@ -53,6 +74,12 @@ typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOO
// Gets a json dictionary from the cache with all the requested modules. Pass in the block that you want to run once the dictionary is received. This will be run on a background thread. // Gets a json dictionary from the cache with all the requested modules. Pass in the block that you want to run once the dictionary is received. This will be run on a background thread.
- (void)fetchJSONForModules:(nullable NSArray *)modules completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler; - (void)fetchJSONForModules:(nullable NSArray *)modules completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler;
/// Returns a page JSON from the persistent cache.
- (NSDictionary * _Nullable)fetchPageFromPersistentCache:(nonnull NSString *)pageType;
/// Returns a module JSON from the persistent cache.
- (NSDictionary * _Nullable)fetchModuleFromPersistentCache:(nonnull NSString *)moduleName;
#pragma mark - Advanced Fetch #pragma mark - Advanced Fetch
// Pass in the block that you want to run once the dictionary is received and which queue to run it on. Pass in if you'd like the current thread to wait. // Pass in the block that you want to run once the dictionary is received and which queue to run it on. Pass in if you'd like the current thread to wait.
@ -75,6 +102,12 @@ typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOO
// Adds a json dictionary to the cache by modules. // Adds a json dictionary to the cache by modules.
- (void)addModulesToCache:(nonnull NSDictionary *)jsonDictionary; - (void)addModulesToCache:(nonnull NSDictionary *)jsonDictionary;
/// Adds the json to the persistent cache by pageType.
- (void)addPageToPersistentCache:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType expirationDate:(nonnull NSDate *)expirationDate;
/// Adds the json to the persistent cache by module.
- (void)addModuleToPersistentCache:(nonnull NSDictionary *)jsonDictionary moduleName:(nonnull NSString *)moduleName expirationDate:(nonnull NSDate *)expirationDate;
#pragma mark - Advanced Insertion #pragma mark - Advanced Insertion
// Adds a json dictionary to the cache by pageType. Pass in the block that you want to run once the dictionary is received and which queue to run it on. Pass in if you'd like the current thread to wait. // Adds a json dictionary to the cache by pageType. Pass in the block that you want to run once the dictionary is received and which queue to run it on. Pass in if you'd like the current thread to wait.
@ -111,6 +144,9 @@ typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOO
// Removes the json for modules. Pass in the block that you want to run once the dictionary is received and which queue to run it on. // Removes the json for modules. Pass in the block that you want to run once the dictionary is received and which queue to run it on.
- (void)removeJSONForModules:(nonnull NSArray *)modules queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock; - (void)removeJSONForModules:(nonnull NSArray *)modules queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock;
/// Clears the persistent JSON cache
- (void)clearPersistentJSONCache;
#pragma mark Image Functions #pragma mark Image Functions
/// Register a bundle as one to search for images in. /// Register a bundle as one to search for images in.

View File

@ -17,6 +17,9 @@
#import "MVMCoreErrorConstants.h" #import "MVMCoreErrorConstants.h"
#import "MVMCoreLoggingHandlerHelper.h" #import "MVMCoreLoggingHandlerHelper.h"
NSString * _Nonnull const KeyCachePolicy = @"cachePolicy";
NSString * _Nonnull const KeyCacheExpiry = @"expiry";
@interface MVMCoreCache () @interface MVMCoreCache ()
// The cache for json. // The cache for json.
@ -91,12 +94,55 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
return _modulesToNotCache; return _modulesToNotCache;
} }
- (BOOL)shouldCacheJSONWithPageType:(nonnull NSString *)pageType { - (BOOL)shouldCachePageJSON:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType {
return ![self.pageTypesToNotCache containsObject:pageType]; if ([self.pageTypesToNotCache containsObject:pageType]) { return NO; }
NSNumber *shouldCache = [jsonDictionary optionalNumberForKey:@"cache"];
return shouldCache == nil || shouldCache.boolValue;
} }
- (BOOL)shouldCacheJSONWithModule:(nonnull NSString *)module { - (BOOL)shouldCacheModuleJSON:(nonnull NSDictionary *)jsonDictionary moduleName:(nonnull NSString *)moduleName {
return ![self.modulesToNotCache containsObject:module]; if ([self.modulesToNotCache containsObject:moduleName]) { return NO; }
NSNumber *shouldCache = [jsonDictionary optionalNumberForKey:@"cache"];
return shouldCache == nil || shouldCache.boolValue;
}
- (BOOL)isJSONExpired:(nonnull NSDictionary *)jsonDictionary {
NSDate *expirationDate = [self getExpirationDateForJSON:jsonDictionary];
NSDate *now = [NSDate date];
if ([now compare:expirationDate] == NSOrderedDescending) {
[MVMCoreLoggingHandler logDebugMessageWithDelegate:[NSString stringWithFormat:@"CACHEDFEED: NEW DATA ALREADY EXPIRED %@ now:%@ expirationDate:%@",jsonDictionary,now,expirationDate]];
}
return [now compare:expirationDate] == NSOrderedDescending;
}
- (nonnull NSDate *)getExpirationDateForJSON:(nonnull NSDictionary *)jsonDictionary {
NSDictionary *cachePolicy = [jsonDictionary dict:KeyCachePolicy];
NSTimeInterval interval = [[cachePolicy string:KeyCacheExpiry] doubleValue] / 1000;
return [NSDate dateWithTimeIntervalSince1970:interval];
}
- (BOOL)shouldPersistentlyCacheJSON:(nonnull NSDictionary *)jsonDictionary {
NSDictionary *cachePolicy = [jsonDictionary dict:KeyCachePolicy];
if (!cachePolicy || ![cachePolicy boolForKey:@"persist"]) {
return NO;
}
return ![self isJSONExpired:jsonDictionary];
}
- (BOOL)shouldPersistentlyCachePage:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType {
return [self shouldPersistentlyCacheJSON:jsonDictionary];
}
- (BOOL)shouldPersistentlyCacheModule:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module {
return [self shouldPersistentlyCacheJSON:jsonDictionary];
}
- (nullable NSURL *)getPathForPersistentCachePage:(nonnull NSString *)pageType {
return [[[[PersistentCacheManager shared].cacheDirectory URLByAppendingPathComponent:@"Pages"] URLByAppendingPathComponent:pageType] URLByAppendingPathExtension:@"json"];
}
- (nullable NSURL *)getPathForPersistentCacheModule:(nonnull NSString *)moduleName {
return [[[[PersistentCacheManager shared].cacheDirectory URLByAppendingPathComponent:@"Modules"] URLByAppendingPathComponent:moduleName]URLByAppendingPathExtension:@"json"];
} }
- (void)addPageTypesToNotCache:(nullable NSArray <NSString *>*)array { - (void)addPageTypesToNotCache:(nullable NSArray <NSString *>*)array {
@ -125,6 +171,16 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
[self fetchJSONForModules:modules queue:self.completionQueue waitUntilFinished:NO completionHandler:completionHandler]; [self fetchJSONForModules:modules queue:self.completionQueue waitUntilFinished:NO completionHandler:completionHandler];
} }
- (NSDictionary * _Nullable)fetchPageFromPersistentCache:(nonnull NSString *)pageType {
NSError *error = nil;
return [[PersistentCacheManager shared] loadForKey:pageType path:[self getPathForPersistentCachePage:pageType] error:&error];
}
- (NSDictionary * _Nullable)fetchModuleFromPersistentCache:(nonnull NSString *)moduleName {
NSError *error = nil;
return [[PersistentCacheManager shared] loadForKey:moduleName path:[self getPathForPersistentCacheModule:moduleName] error:&error];
}
#pragma mark - Advanced Fetch #pragma mark - Advanced Fetch
- (void)fetchJSONForPageType:(nullable NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler { - (void)fetchJSONForPageType:(nullable NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler {
@ -140,6 +196,9 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
// First checks the cache by page type. // First checks the cache by page type.
dictionary = [weakSelf.pageTypeCache objectForKey:pageType]; dictionary = [weakSelf.pageTypeCache objectForKey:pageType];
if (!dictionary) {
dictionary = [self fetchPageFromPersistentCache:pageType];
}
} else { } else {
// If no pagetype, return whole cache // If no pagetype, return whole cache
@ -176,6 +235,11 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
NSDictionary *moduleDictionary = [weakSelf.moduleCache objectForKey:module]; NSDictionary *moduleDictionary = [weakSelf.moduleCache objectForKey:module];
if (moduleDictionary) { if (moduleDictionary) {
[modulesDictionary setObject:moduleDictionary forKey:module]; [modulesDictionary setObject:moduleDictionary forKey:module];
} else {
moduleDictionary = [self fetchModuleFromPersistentCache:module];
if (moduleDictionary) {
[modulesDictionary setObject:moduleDictionary forKey:module];
}
} }
} }
@ -206,16 +270,37 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
[self addModulesToCache:jsonDictionary queue:nil waitUntilFinished:NO completionBlock:NULL]; [self addModulesToCache:jsonDictionary queue:nil waitUntilFinished:NO completionBlock:NULL];
} }
- (void)addPageToPersistentCache:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType expirationDate:(nonnull NSDate *)expirationDate {
NSError *error = nil;
[[PersistentCacheManager shared] saveWithData:jsonDictionary forKey:pageType path:[self getPathForPersistentCachePage:pageType] expirationDate:expirationDate error:&error];
if (error) {
[[MVMCoreLoggingHandler sharedLoggingHandler] addErrorToLog:[MVMCoreErrorObject createErrorObjectForNSError:error location:[NSString stringWithFormat:@"%s_%@",__PRETTY_FUNCTION__,pageType]]];
}
}
- (void)addModuleToPersistentCache:(nonnull NSDictionary *)jsonDictionary moduleName:(nonnull NSString *)moduleName expirationDate:(nonnull NSDate *)expirationDate {
NSError *error = nil;
[[PersistentCacheManager shared] saveWithData:jsonDictionary forKey:moduleName path:[self getPathForPersistentCacheModule:moduleName] expirationDate:expirationDate error:&error];
if (error) {
[[MVMCoreLoggingHandler sharedLoggingHandler] addErrorToLog:[MVMCoreErrorObject createErrorObjectForNSError:error location:[NSString stringWithFormat:@"%s_%@",__PRETTY_FUNCTION__,moduleName]]];
}
}
#pragma mark - Advanced Insertion #pragma mark - Advanced Insertion
- (void)addPageToCache:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock { - (void)addPageToCache:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
if (![self shouldCachePageJSON:jsonDictionary pageType:pageType]) {
if (completionBlock) {
completionBlock();
}
return;
}
NSBlockOperation *addOperation = [[NSBlockOperation alloc] init]; NSBlockOperation *addOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = addOperation; __weak NSBlockOperation *weakOperation = addOperation;
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
[addOperation addExecutionBlock:^{ [addOperation addExecutionBlock:^{
if (!weakOperation.isCancelled && [[MVMCoreCache sharedCache] shouldCacheJSONWithPageType:pageType]) { if (!weakOperation.isCancelled) {
// There must be a dictionary and page type to cache. // There must be a dictionary and page type to cache.
if (jsonDictionary && pageType) { if (jsonDictionary && pageType) {
@ -227,10 +312,13 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
} }
// Adds json to cache with page type key. // Adds json to cache with page type key.
NSNumber *shouldCache = [jsonDictionary optionalNumberForKey:@"cache"]; [weakSelf.pageTypeCache setObject:jsonDictionary forKey:pageType];
if (shouldCache == nil || shouldCache.boolValue) {
[weakSelf.pageTypeCache setObject:jsonDictionary forKey:pageType]; if (![self shouldPersistentlyCachePage:jsonDictionary pageType:pageType]) {
[[PersistentCacheManager shared] removeForKey:pageType error:nil];
return;
} }
[self addPageToPersistentCache:jsonDictionary pageType:pageType expirationDate:[self getExpirationDateForJSON:jsonDictionary]];
} }
} }
if (completionBlock) { if (completionBlock) {
@ -241,11 +329,12 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
} }
- (void)addModuleToCache:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock { - (void)addModuleToCache:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
[self addModulesToCache:@{module:jsonDictionary} queue:queue waitUntilFinished:waitUntilFinished completionBlock:completionBlock]; if (![self shouldCacheModuleJSON:jsonDictionary moduleName:module]) {
} if (completionBlock) {
completionBlock();
- (void)addModulesToCache:(nonnull NSDictionary *)jsonDictionary queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock { }
return;
}
NSBlockOperation *addOperation = [[NSBlockOperation alloc] init]; NSBlockOperation *addOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = addOperation; __weak NSBlockOperation *weakOperation = addOperation;
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
@ -253,23 +342,19 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
if (!weakOperation.isCancelled) { if (!weakOperation.isCancelled) {
[jsonDictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { if (!weakSelf.moduleCache) {
if ([[MVMCoreCache sharedCache] shouldCacheJSONWithModule:key]) { // Create the cache if necessary.
weakSelf.moduleCache = [NSMutableDictionary dictionary];
if (!weakSelf.moduleCache) { }
// Create the cache if necessary. [weakSelf.moduleCache setObject:jsonDictionary forKey:module];
weakSelf.moduleCache = [NSMutableDictionary dictionary];
} if (![self shouldPersistentlyCacheModule:jsonDictionary module:module]) {
[[PersistentCacheManager shared] removeForKey:module error:nil];
// Adds json to cache with page type key. return;
NSNumber *shouldCache = [jsonDictionary optionalNumberForKey:@"cache"]; }
if (shouldCache == nil || shouldCache.boolValue) { [self addModuleToPersistentCache:jsonDictionary moduleName:module expirationDate:[self getExpirationDateForJSON:jsonDictionary]];
[weakSelf.moduleCache setObject:obj forKey:key];
}
}
}];
} }
if (completionBlock) { if (completionBlock) {
[(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:completionBlock]] waitUntilFinished:waitUntilFinished]; [(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:completionBlock]] waitUntilFinished:waitUntilFinished];
@ -278,6 +363,18 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
[self.moduleQueue addOperations:@[addOperation] waitUntilFinished:waitUntilFinished]; [self.moduleQueue addOperations:@[addOperation] waitUntilFinished:waitUntilFinished];
} }
- (void)addModulesToCache:(nonnull NSDictionary *)jsonDictionary queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
[jsonDictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
[self addModuleToCache:obj module:key];
}];
__weak typeof(self) weakSelf = self;
[self.moduleQueue addOperations:@[[NSBlockOperation blockOperationWithBlock:^{
if (completionBlock) {
[(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:completionBlock]] waitUntilFinished:waitUntilFinished];
}
}]] waitUntilFinished:waitUntilFinished];
}
#pragma mark - Simple Deletion #pragma mark - Simple Deletion
- (void)removeJSONForPageType:(nonnull NSString *)pageType { - (void)removeJSONForPageType:(nonnull NSString *)pageType {
@ -292,6 +389,10 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
[self removeJSONForModules:modules queue:nil waitUntilFinished:NO completionBlock:NULL]; [self removeJSONForModules:modules queue:nil waitUntilFinished:NO completionBlock:NULL];
} }
- (void)clearPersistentJSONCache {
[[PersistentCacheManager shared] removeAllAndReturnError:nil];
}
- (void)clearMFCache { - (void)clearMFCache {
[self.pageTypeQueue cancelAllOperations]; [self.pageTypeQueue cancelAllOperations];
[self.moduleQueue cancelAllOperations]; [self.moduleQueue cancelAllOperations];
@ -329,6 +430,7 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
} }
}]; }];
[self.pageTypeQueue addOperations:@[removeOperation] waitUntilFinished:waitUntilFinished]; [self.pageTypeQueue addOperations:@[removeOperation] waitUntilFinished:waitUntilFinished];
[[PersistentCacheManager shared] removeForKey:pageType error:nil];
} }
- (void)removeJSONForModule:(nonnull NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock { - (void)removeJSONForModule:(nonnull NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
@ -352,6 +454,7 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
} }
}]; }];
[self.moduleQueue addOperations:@[removeOperation] waitUntilFinished:waitUntilFinished]; [self.moduleQueue addOperations:@[removeOperation] waitUntilFinished:waitUntilFinished];
[[PersistentCacheManager shared] removeForKey:module error:nil];
} }
- (void)removeJSONForModules:(nonnull NSArray *)modules queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock { - (void)removeJSONForModules:(nonnull NSArray *)modules queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
@ -371,6 +474,7 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
// Removes json from cache with module key. // Removes json from cache with module key.
[weakSelf.moduleCache removeObjectForKey:obj]; [weakSelf.moduleCache removeObjectForKey:obj];
} }
[[PersistentCacheManager shared] removeForKey:obj error:nil];
} }
}]; }];
if (completionBlock) { if (completionBlock) {

View File

@ -189,6 +189,13 @@ public class NavigationHandler {
await navigate(with: .pop(navigationController: navigationController, animated: animated), delegateObject: delegateObject) await navigate(with: .pop(navigationController: navigationController, animated: animated), delegateObject: delegateObject)
} }
} }
/// Attempts to go to navigate to a viewcontroller of pageType and controllerType. Returns the view controller if successful
@MainActor
func navigateToViewController(of pageType: String, controllerType: AnyClass?) async -> UIViewController? {
// TODO: Need to manage for present view controllers.
return await MVMCoreObject.sharedInstance()?.viewControllerManager?.navigate(toViewControllerOfPageType: pageType, controllerType: controllerType)
}
} }
extension UINavigationController { extension UINavigationController {

View File

@ -23,6 +23,9 @@ public class MVMCoreObject: NSObject {
public var globalLoadDelegate: MVMCoreGlobalLoadProtocol? public var globalLoadDelegate: MVMCoreGlobalLoadProtocol?
public var loadingProtocol: MVMCoreLoadingOverlayDelegateProtocol? public var loadingProtocol: MVMCoreLoadingOverlayDelegateProtocol?
public var loggingDelegate: MVMCoreLoggingDelegateProtocol? public var loggingDelegate: MVMCoreLoggingDelegateProtocol?
/// The main manager of the view controllers in the application.
public weak var viewControllerManager: MVMCoreViewManagerProtocol?
/// A reference to the calling application delegate that should be set. For a normal app, could be the UIApplicationDelegate. For watch, could be WKExtensionDelegate. For iMessage, could be MSMessagesAppViewController. etc, etc. Useful for the framework to call delegate specific functions. /// A reference to the calling application delegate that should be set. For a normal app, could be the UIApplicationDelegate. For watch, could be WKExtensionDelegate. For iMessage, could be MSMessagesAppViewController. etc, etc. Useful for the framework to call delegate specific functions.
public weak var applicationDelegate: AnyObject? public weak var applicationDelegate: AnyObject?

View File

@ -47,6 +47,9 @@
// For pages external to the mobile first framework to be added to the view controller mapping. // For pages external to the mobile first framework to be added to the view controller mapping.
- (void)addToTemplateViewControllerMapping:(nullable NSDictionary <NSString *,NSObject <MVMCoreViewControllerMappingProtocol>*>*)map; - (void)addToTemplateViewControllerMapping:(nullable NSDictionary <NSString *,NSObject <MVMCoreViewControllerMappingProtocol>*>*)map;
// Transition function: A mix of new and legacy.
- (nullable NSObject <MVMCoreViewControllerMappingProtocol>*)getViewControllerMappingForTemplate:(nullable NSString *)templateID pageType:(nullable NSString *)pageType;
// Transition function: A mix of new and legacy. // Transition function: A mix of new and legacy.
- (nullable UIViewController <MVMCoreViewControllerProtocol> *)createMFViewControllerOfTemplate:(nullable NSString *)templateID pageType:(nullable NSString *)pageType; - (nullable UIViewController <MVMCoreViewControllerProtocol> *)createMFViewControllerOfTemplate:(nullable NSString *)templateID pageType:(nullable NSString *)pageType;

View File

@ -46,6 +46,16 @@
} }
} }
- (nullable NSObject <MVMCoreViewControllerMappingProtocol>*)getViewControllerMappingForTemplate:(nullable NSString *)templateID pageType:(nullable NSString *)pageType {
if (templateID) {
return [self getViewControllerMappingForTemplate:templateID];
} else if (pageType) {
return [self getViewControllerMappingForPageType:pageType];
} else {
return nil;
}
}
// Transition function: A mix of new and legacy. // Transition function: A mix of new and legacy.
- (nullable UIViewController <MVMCoreViewControllerProtocol> *)createMFViewControllerOfTemplate:(nullable NSString *)templateID pageType:(nullable NSString *)pageType { - (nullable UIViewController <MVMCoreViewControllerProtocol> *)createMFViewControllerOfTemplate:(nullable NSString *)templateID pageType:(nullable NSString *)pageType {
if (templateID) { if (templateID) {