diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m index 4e95c40..0dbc16d 100644 --- a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m @@ -36,8 +36,7 @@ NSString * const KeyActionTypeOpen = @"openPage"; @implementation MVMCoreActionHandler + (nullable instancetype)sharedActionHandler { - [MVMCoreActionUtility classIsInstanceTypeOfClass:[MVMCoreObject sharedInstance].actionHandler.class otherClass:self throwException:YES]; - return [MVMCoreObject sharedInstance].actionHandler; + return [MVMCoreActionUtility initializerClassCheck:[MVMCoreObject sharedInstance].actionHandler classToVerify:self]; } - (void)handleActionWithDictionary:(nullable NSDictionary *)dictionary additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { diff --git a/MVMCore/MVMCore/MainProtocols/MVMCoreGlobalLoadProtocol.h b/MVMCore/MVMCore/MainProtocols/MVMCoreGlobalLoadProtocol.h index fbca045..ddf3db9 100644 --- a/MVMCore/MVMCore/MainProtocols/MVMCoreGlobalLoadProtocol.h +++ b/MVMCore/MVMCore/MainProtocols/MVMCoreGlobalLoadProtocol.h @@ -14,11 +14,11 @@ @protocol MVMCoreGlobalLoadProtocol -// Provides the desired error screen for the native error. If null, will fail silently. Called on the main thread. -- (nullable UIViewController *)getNativeScreenForRequestError:(nonnull MVMCoreErrorObject *)errorObject requestObject:(nonnull MVMCoreRequestParameters *)requestObject; - @optional +// Provides the desired error screen for the native error. Called on the main thread. If set, any native errors or NSURLErrorDomain errors will use a native screen. +- (nullable UIViewController *)getNativeScreenForRequestError:(nonnull MVMCoreErrorObject *)errorObject requestObject:(nonnull MVMCoreRequestParameters *)requestObject; + /* Can choose to handle global error scenarios here... This will happen for every request so only put things in here that aren't controller specific. Must call completion handler. @param completionHandler must be called when finished handling errors. Pass if we should continue or not, and any error object that you want to be handled. */ - (void)handleGlobalErrorsForLoadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(BOOL shouldContinueLoad, MVMCoreErrorObject * _Nullable error))completionHandler; diff --git a/MVMCore/MVMCore/OtherHandlers/MVMCoreCache.m b/MVMCore/MVMCore/OtherHandlers/MVMCoreCache.m index 116c352..68cc85d 100644 --- a/MVMCore/MVMCore/OtherHandlers/MVMCoreCache.m +++ b/MVMCore/MVMCore/OtherHandlers/MVMCoreCache.m @@ -42,8 +42,7 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt"; + (nullable instancetype)sharedCache { - [MVMCoreActionUtility classIsInstanceTypeOfClass:[MVMCoreObject sharedInstance].cache.class otherClass:self throwException:YES]; - return [MVMCoreObject sharedInstance].cache; + return [MVMCoreActionUtility initializerClassCheck:[MVMCoreObject sharedInstance].cache classToVerify:self]; } - (nullable instancetype)init { diff --git a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.h b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.h index 557ac8e..4cb1301 100644 --- a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.h +++ b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.h @@ -10,12 +10,13 @@ #import #import #import +#import @class MVMCoreAlertController; #define MVMCoreLog(fmt, ...) \ [MVMCoreLoggingHandler logDebugMessageWithDelegate:[NSString stringWithFormat:(@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__]]; -@interface MVMCoreLoggingHandler : NSObject +@interface MVMCoreLoggingHandler : NSObject + (nullable instancetype)sharedLoggingHandler; diff --git a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.m b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.m index 82ad77a..84865ad 100644 --- a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.m +++ b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.m @@ -13,7 +13,7 @@ @implementation MVMCoreLoggingHandler + (nullable instancetype)sharedLoggingHandler { - if ([MVMCoreActionUtility classIsInstanceTypeOfClass:[MVMCoreObject sharedInstance].loggingDelegate.class otherClass:self throwException:NO]) { + if ([MVMCoreObject sharedInstance].loggingDelegate && [MVMCoreActionUtility classIsInstanceTypeOfClass:[MVMCoreObject sharedInstance].loggingDelegate.class otherClass:self throwException:NO]) { return (MVMCoreLoggingHandler *)[MVMCoreObject sharedInstance].loggingDelegate; } else { return nil; @@ -50,5 +50,12 @@ } } +#pragma mark - logging delegate + +- (void)handleDebugMessage:(nullable NSString *)message { +#ifdef DEBUG + NSLog(@"%@", message); +#endif +} @end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m index b6aa0aa..0feddeb 100644 --- a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m @@ -276,12 +276,13 @@ - (void)presentViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated delegate:(nullable NSObject*)delegate completionHandler:(nullable void (^)(void))completionBlock { - if (!self.viewControllerToPresentOn) { + UIViewController *controllerToPresentOn = self.viewControllerToPresentOn ?: [UIApplication sharedApplication].keyWindow.rootViewController; + if (!controllerToPresentOn) { // Log if we are attempting to add an alert to nothing. MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:nil message:@"The viewControllerToPresent on property is nil and an attempt was made to present." code:ErrorCodeNoViewControllerToPresentOn domain:ErrorDomainNative location:[NSString stringWithFormat:@"ViewController:%@|Delegate:%@",viewController.description,delegate]]; [MVMCoreLoggingHandler addErrorToLog:error]; } else { - MVMCorePresentViewControllerOperation *operation = [[MVMCorePresentViewControllerOperation alloc] initWithPresentingViewController:self.viewControllerToPresentOn presentedViewController:viewController animated:animated]; + MVMCorePresentViewControllerOperation *operation = [[MVMCorePresentViewControllerOperation alloc] initWithPresentingViewController:controllerToPresentOn presentedViewController:viewController animated:animated]; operation.delegate = delegate; operation.completionBlock = completionBlock; [self.presentationQueue addOperation:operation]; diff --git a/MVMCore/MVMCore/Session/MVMCoreSessionObject.h b/MVMCore/MVMCore/Session/MVMCoreSessionObject.h index c2a85e6..ceb4e23 100644 --- a/MVMCore/MVMCore/Session/MVMCoreSessionObject.h +++ b/MVMCore/MVMCore/Session/MVMCoreSessionObject.h @@ -26,6 +26,9 @@ // Returns the shared instance of this singleton + (nullable instancetype)sharedGlobal; +// Creates the nsurlsession with default configuration and no delegate. Can subclass for different session. +- (nonnull NSURLSession *)createNSURLSession; + // Gets inital parameters for request parameters. - (nullable NSDictionary *)getInitialParameters; // Gets inital parameters for request parameters excluding any items given a list of key names. @@ -37,7 +40,7 @@ // Redirect, leaving the current app experience. - (void)redirectWithInfo:(nullable NSDictionary *)dictionary; -// Clears the session singleton. +// Clears the session singleton. Creates a new session NSURLSession also. - (void)clearSessionObject; @end diff --git a/MVMCore/MVMCore/Session/MVMCoreSessionObject.m b/MVMCore/MVMCore/Session/MVMCoreSessionObject.m index 5deb1e4..d094b0b 100644 --- a/MVMCore/MVMCore/Session/MVMCoreSessionObject.m +++ b/MVMCore/MVMCore/Session/MVMCoreSessionObject.m @@ -12,9 +12,19 @@ @implementation MVMCoreSessionObject +- (instancetype)init { + if (self = [super init]) { + self.session = [self createNSURLSession]; + } + return self; +} + + (nullable instancetype)sharedGlobal { - [MVMCoreActionUtility classIsInstanceTypeOfClass:[MVMCoreObject sharedInstance].session.class otherClass:self throwException:YES]; - return [MVMCoreObject sharedInstance].session; + return [MVMCoreActionUtility initializerClassCheck:[MVMCoreObject sharedInstance].session classToVerify:self]; +} + +- (nonnull NSURLSession *)createNSURLSession { + return [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:nil delegateQueue:nil]; } - (nullable NSDictionary *)getInitialParameters { @@ -37,6 +47,8 @@ } - (void)clearSessionObject { + [self.session invalidateAndCancel]; + self.session = [self createNSURLSession]; } @end diff --git a/MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.m b/MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.m index a18113f..fad63f3 100644 --- a/MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.m +++ b/MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.m @@ -47,8 +47,7 @@ @implementation MVMCoreSessionTimeHandler + (nullable instancetype)sharedSessionHandler { - [MVMCoreActionUtility classIsInstanceTypeOfClass:[MVMCoreObject sharedInstance].sessionHandler.class otherClass:self throwException:YES]; - return [MVMCoreObject sharedInstance].sessionHandler; + return [MVMCoreActionUtility initializerClassCheck:[MVMCoreObject sharedInstance].sessionHandler classToVerify:self]; } - (instancetype)init { diff --git a/MVMCore/MVMCore/Singletons/MVMCoreObject.h b/MVMCore/MVMCore/Singletons/MVMCoreObject.h index ebdf435..26013a3 100644 --- a/MVMCore/MVMCore/Singletons/MVMCoreObject.h +++ b/MVMCore/MVMCore/Singletons/MVMCoreObject.h @@ -27,10 +27,10 @@ @property (nullable, strong, nonatomic) MVMCoreSessionTimeHandler *sessionHandler; // The delegates -@property (nullable, weak, nonatomic) id globalLoadDelegate; -@property (nullable, weak, nonatomic) id loadingProtocol; -@property (nullable, weak, nonatomic) NSObject *loggingDelegate; -@property (nullable, weak, nonatomic) id globalTopAlertDelegate; +@property (nullable, strong, nonatomic) id globalLoadDelegate; +@property (nullable, strong, nonatomic) id loadingProtocol; +@property (nullable, strong, nonatomic) NSObject *loggingDelegate; +@property (nullable, strong, nonatomic) id globalTopAlertDelegate; // 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. @property (nullable, weak, nonatomic) id applicationDelegate; @@ -38,4 +38,7 @@ // A singleton. + (nullable instancetype)sharedInstance; +// Sets up with a default session, cache, view controller mapping, action handler, session handler, and logging delegate. +- (void)defaultInitialSetup; + @end diff --git a/MVMCore/MVMCore/Singletons/MVMCoreObject.m b/MVMCore/MVMCore/Singletons/MVMCoreObject.m index 6a44fde..40d0732 100644 --- a/MVMCore/MVMCore/Singletons/MVMCoreObject.m +++ b/MVMCore/MVMCore/Singletons/MVMCoreObject.m @@ -19,4 +19,13 @@ return sharedInstance; } +- (void)defaultInitialSetup { + self.session = [[MVMCoreSessionObject alloc] init]; + self.cache = [[MVMCoreCache alloc] init]; + self.viewControllerMapping = [[MVMCoreViewControllerMappingObject alloc] init]; + self.sessionHandler = [[MVMCoreSessionTimeHandler alloc] init]; + self.actionHandler = [[MVMCoreActionHandler alloc] init]; + self.loggingDelegate = [[MVMCoreLoggingHandler alloc] init]; +} + @end diff --git a/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.h b/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.h index 76f1d8e..6e7f4ef 100644 --- a/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.h +++ b/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.h @@ -26,4 +26,7 @@ // returns if the class is not the same or a subclass of the other class. Can pass throw an exception as well + (BOOL)classIsInstanceTypeOfClass:(Class)theClass otherClass:(Class)otherClass throwException:(BOOL)throwException; +// Calls the above function with throw exception yes if the object is not nil. Returns the object. Convenience function for one liner in initializer. ++ (id)initializerClassCheck:(nullable NSObject *)object classToVerify:(Class)classToVerify; + @end diff --git a/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.m b/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.m index f350b6c..2c6c8f1 100644 --- a/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.m +++ b/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.m @@ -101,14 +101,7 @@ } + (void)openURL:(nullable NSURL *)url completionHandler:(void (^ __nullable)(BOOL success))completion{ - if (@available(iOS 10.0, *)) { - [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:completion]; - } else { - BOOL successful = [[UIApplication sharedApplication] openURL:url]; - if (completion != nil){ - completion(successful); - } - } + [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:completion]; } + (void)displayViewController:(nonnull UIViewController *)viewController forLoadObject:(nullable MVMCoreLoadObject *)loadObject presentationDelegate:(nullable NSObject*)delegate completionHandler:(nullable void (^)(void))completionBlock { @@ -147,4 +140,11 @@ return YES; } ++ (id)initializerClassCheck:(nullable NSObject *)object classToVerify:(Class)classToVerify { + if (object) { + [self classIsInstanceTypeOfClass:object.class otherClass:classToVerify throwException:YES]; + } + return object; +} + @end diff --git a/MVMCore/MVMCore/Utility/MVMCoreErrorObject.m b/MVMCore/MVMCore/Utility/MVMCoreErrorObject.m index 552893a..9354326 100644 --- a/MVMCore/MVMCore/Utility/MVMCoreErrorObject.m +++ b/MVMCore/MVMCore/Utility/MVMCoreErrorObject.m @@ -7,6 +7,7 @@ // #import "MVMCoreErrorObject.h" +#import "MVMCoreObject.h" #import "MVMCoreErrorConstants.h" #import "MVMCoreGetterUtility.h" #import "NSDictionary+MFConvenience.h" @@ -32,8 +33,10 @@ self.logError = YES; // Native and system errors have an error screen. - self.errorScreenError = YES; - self.nativeDrivenErrorScreen = YES; + if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(getNativeScreenForRequestError:requestObject:)]) { + self.errorScreenError = YES; + self.nativeDrivenErrorScreen = YES; + } } else { self.logError = NO; } @@ -58,8 +61,10 @@ self.logError = YES; // Native and system errors have an error screen. - self.errorScreenError = YES; - self.nativeDrivenErrorScreen = YES; + if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(getNativeScreenForRequestError:requestObject:)]) { + self.errorScreenError = YES; + self.nativeDrivenErrorScreen = YES; + } } else { self.logError = NO; } @@ -98,9 +103,11 @@ MVMCoreErrorObject *errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[error localizedDescription] code:[error code] domain:ErrorDomainSystem location:location]; if ([error.domain isEqualToString:NSURLErrorDomain]) { - errorObject.errorScreenError = YES; - errorObject.nativeDrivenErrorScreen = YES; errorObject.systemDomain = error.domain; + if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(getNativeScreenForRequestError:requestObject:)]) { + errorObject.errorScreenError = YES; + errorObject.nativeDrivenErrorScreen = YES; + } } return errorObject; } diff --git a/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.m b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.m index d39193e..99fc136 100644 --- a/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.m +++ b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.m @@ -27,8 +27,7 @@ @implementation MVMCoreViewControllerMappingObject + (nullable instancetype)sharedViewControllerMappingObject { - [MVMCoreActionUtility classIsInstanceTypeOfClass:[MVMCoreObject sharedInstance].viewControllerMapping.class otherClass:self throwException:YES]; - return [MVMCoreObject sharedInstance].viewControllerMapping; + return [MVMCoreActionUtility initializerClassCheck:[MVMCoreObject sharedInstance].viewControllerMapping classToVerify:self]; } #pragma mark - View Controller Mapping