From f5f51d79fd6604d74ec36a177a25cf9e933802e2 Mon Sep 17 00:00:00 2001 From: Alex Gray Date: Fri, 4 Oct 2013 11:18:38 -0400 Subject: [PATCH] Minor refactor to enhance readability, foldability, verbosity, etc. --- BaseModel/BaseModel.h | 176 +++++----- BaseModel/BaseModel.m | 771 +++++++++++++++++------------------------- 2 files changed, 383 insertions(+), 564 deletions(-) diff --git a/BaseModel/BaseModel.h b/BaseModel/BaseModel.h index 52c7fba..4dbdb20 100755 --- a/BaseModel/BaseModel.h +++ b/BaseModel/BaseModel.h @@ -1,111 +1,99 @@ -// -// BaseModel.h -// -// Version 2.4.3 -// -// Created by Nick Lockwood on 25/06/2011. -// Copyright 2011 Charcoal Design -// -// Distributed under the permissive zlib license -// Get the latest version from here: -// -// https://github.com/nicklockwood/BaseModel -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// +/*** + BaseModel.h + Version 2.4.4 -#import + Created by Nick Lockwood on 25/06/2011. + Copyright 2011 Charcoal Design + Distributed under the permissive zlib license + Get the latest version from here: -extern NSString *const BaseModelSharedInstanceUpdatedNotification; + https://github.com/nicklockwood/BaseModel + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. -//the BaseModel protocol defines optional methods that -//you can define on your BaseModel subclasses to extend their functionality + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: -@protocol BaseModel -@optional + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. -//loading sequence: -//setUp called first -//then setWithDictionary/Array/String/etc if resource file exists -//then setWithCoder if save file exists + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. -- (void)setUp; -- (void)setWithDictionary:(NSDictionary *)dict; -- (void)setWithArray:(NSArray *)array; -- (void)setWithString:(NSString *)string; -- (void)setWithCoder:(NSCoder *)coder; + 3. This notice may not be removed or altered from any source distribution. */ -//coding +#import -- (void)encodeWithCoder:(NSCoder *)coder; +extern NSString *const BaseModelSharedInstanceUpdatedNotification; -@end +// The BaseModel protocol defines optional methods +// that you can define on your BaseModel subclasses to extend their functionality +@protocol BaseModel @optional -//use the BaseModel class as the base class for any of your -//model objects. BaseModels can be standalone objects, or -//act as sub-properties of a larger object +/* loading sequence: 1. setUp + 2. setWithDictionary/Array/String/etc if resource file exists + 3. setWithCoder if save file exists */ +- (void) setUp; +- (void) setWithDictionary:(NSDictionary*)dict; +- (void) setWithArray: (NSArray*)array; +- (void) setWithString: (NSString*)string; +- (void) setWithCoder: (NSCoder*)coder; + +- (void)encodeWithCoder:(NSCoder *)coder; //coding + +@end + +// Use the BaseModel class as the base class for any of your model objects. +// BaseModels can be standalone objects, or act as sub-properties of a larger object @interface BaseModel : NSObject -//new autoreleased instance -+ (instancetype)instance; - -//shared (singelton) instance -+ (instancetype)sharedInstance; -+ (BOOL)hasSharedInstance; -+ (void)setSharedInstance:(BaseModel *)instance; -+ (void)reloadSharedInstance; - -//creating instances from collection or string -+ (instancetype)instanceWithObject:(id)object; -- (instancetype)initWithObject:(id)object; -+ (NSArray *)instancesWithArray:(NSArray *)array; - -//creating an instance using NSCoding -+ (instancetype)instanceWithCoder:(NSCoder *)decoder; -- (instancetype)initWithCoder:(NSCoder *)decoder; - -//loading and saving the model from a plist file -+ (instancetype)instanceWithContentsOfFile:(NSString *)path; -- (instancetype)initWithContentsOfFile:(NSString *)path; -- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)atomically; -- (BOOL)useHRCoderIfAvailable; - -//resourceFile is a file, typically within the resource bundle that -//is used to initialise any BaseModel instance -//saveFile is a path, typically within application support that -//is used to save the shared instance of the model -//saveFileForID is a path, typically within application support that -//is used to save any instance of the model -+ (NSString *)resourceFile; -+ (NSString *)saveFile; - -//save the model -- (void)save; - -//generate unique identifier -//useful for creating universally unique -//identifiers and filenames for model objects -+ (NSString *)newUniqueIdentifier; - -@end \ No newline at end of file ++ (instancetype) instance; //new autoreleased instance + +// shared (singelton) instance ++ (instancetype) sharedInstance; ++ (BOOL) hasSharedInstance; ++ (void) setSharedInstance:(BaseModel*)instance; ++ (void) reloadSharedInstance; + +// creating instances from collection or string ++ (instancetype) instanceWithObject:(id)object; +- (instancetype) initWithObject: (id)object; ++ (NSArray*) instancesWithArray:(NSArray*)array; + +// creating an instance using NSCoding ++ (instancetype) instanceWithCoder:(NSCoder*)decoder; +- (instancetype) initWithCoder: (NSCoder*)decoder; + +// loading and saving the model from a plist file ++ (instancetype) instanceWithContentsOfFile:(NSString*)path; +- (instancetype) initWithContentsOfFile: (NSString*)path; +- (BOOL) writeToFile: (NSString*)path atomically:(BOOL)atomically; +- (BOOL) useHRCoderIfAvailable; + +/* + + resourceFile is a file, typically within the resource bundle, + that is used to initialise any BaseModel instance + + saveFile is a path, typically within application support, + that is used to save the shared instance of the model + + saveFileForID is a path, typically within application support, + that is used to save any instance of the model. */ + ++ (NSString*) resourceFile; ++ (NSString*) saveFile; + +- (void) save; //save the model + +// Generates a UUID - useful for creating universally unique id's / filenames for model objects + ++ (NSString*)newUniqueIdentifier; + +@end diff --git a/BaseModel/BaseModel.m b/BaseModel/BaseModel.m index ed9684d..5e3d2e7 100755 --- a/BaseModel/BaseModel.m +++ b/BaseModel/BaseModel.m @@ -1,39 +1,37 @@ -// -// BaseModel.m -// -// Version 2.4.3 -// -// Created by Nick Lockwood on 25/06/2011. -// Copyright 2011 Charcoal Design -// -// Distributed under the permissive zlib license -// Get the latest version from here: -// -// https://github.com/nicklockwood/BaseModel -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// +/** + BaseModel.m + Version 2.4.4 -#import "BaseModel.h" -#import + Created by Nick Lockwood on 25/06/2011. + Copyright 2011 Charcoal Design + + Distributed under the permissive zlib license + Get the latest version from here: + + https://github.com/nicklockwood/BaseModel + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. */ + + +#import "BaseModel.h" +#include #import #if !__has_feature(objc_arc) @@ -43,500 +41,333 @@ NSString *const BaseModelSharedInstanceUpdatedNotification = @"BaseModelSharedInstanceUpdatedNotification"; +static NSString *const BaseModelSharedInstanceKey = @"sharedInstance", + *const BaseModelLoadingFromResourceFileKey = @"loadingFromResourceFile"; -static NSString *const BaseModelSharedInstanceKey = @"sharedInstance"; -static NSString *const BaseModelLoadingFromResourceFileKey = @"loadingFromResourceFile"; +@implementation BaseModel +#pragma mark - Private utility methods -@implementation BaseModel ++ (NSString*) BaseModel_resourceFilePath:(NSString*)path { -#pragma mark - -#pragma mark Private utility methods - -+ (NSString *)BaseModel_resourceFilePath:(NSString *)path -{ - //check if the path is a full path or not - if (![path isAbsolutePath]) - { - return [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:path]; - } - return path; + return path.isAbsolutePath ? path //check if the path is a full path or not + : [NSBundle.mainBundle.resourcePath stringByAppendingPathComponent:path]; } ++ (NSString*) BaseModel_resourceFilePath { -+ (NSString *)BaseModel_resourceFilePath -{ - return [self BaseModel_resourceFilePath:[self resourceFile]]; + return [self BaseModel_resourceFilePath:self.resourceFile]; } ++ (NSString*) BaseModel_saveFilePath: (NSString*)path { + + if (path.isAbsolutePath) return path; //check if the path is a full path or not + //get the path to the application support folder + NSString *folder = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) lastObject]; -+ (NSString *)BaseModel_saveFilePath:(NSString *)path -{ - //check if the path is a full path or not - if (![path isAbsolutePath]) - { - //get the path to the application support folder - NSString *folder = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) lastObject]; - #ifndef __IPHONE_OS_VERSION_MAX_ALLOWED - - //append application name on Mac OS - NSString *identifier = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey]; - folder = [folder stringByAppendingPathComponent:identifier]; - + + //append application name on Mac OS + NSString *identifier = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString*)kCFBundleNameKey]; + folder = [folder stringByAppendingPathComponent:identifier]; + #endif - - //create the folder if it doesn't exist - if (![[NSFileManager defaultManager] fileExistsAtPath:folder]) - { - [[NSFileManager defaultManager] createDirectoryAtPath:folder - withIntermediateDirectories:YES - attributes:nil - error:NULL]; - } - - return [folder stringByAppendingPathComponent:path]; - } - return path; -} -+ (NSString *)BaseModel_saveFilePath -{ - return [self BaseModel_saveFilePath:[self saveFile]]; + //create the folder if it doesn't exist + if (![NSFileManager.defaultManager fileExistsAtPath:folder]) + [NSFileManager.defaultManager createDirectoryAtPath:folder + withIntermediateDirectories:YES + attributes:nil + error:NULL]; + + return [folder stringByAppendingPathComponent:path]; } ++ (NSString*) BaseModel_saveFilePath { return [self BaseModel_saveFilePath:self.saveFile]; } static NSMutableDictionary *classValues = nil; -+ (id)BaseModel_classPropertyForKey:(NSString *)key -{ - NSString *className = NSStringFromClass(self); - return classValues[className][key]; -} ++ (id) BaseModel_classPropertyForKey: (NSString*)key { -+ (void)BaseModel_setClassProperty:(id)property forKey:(NSString *)key -{ - @synchronized ([BaseModel class]) - { - NSString *className = NSStringFromClass(self); - if (!classValues) - { - classValues = [[NSMutableDictionary alloc] init]; - } - NSMutableDictionary *values = classValues[className]; - if (!values) - { - values = [NSMutableDictionary dictionary]; - classValues[className] = values; - } - if (property) - { - values[key] = property; - } - else - { - [values removeObjectForKey:key]; - } - } + return classValues[NSStringFromClass(self)][key]; +} ++ (void) BaseModel_setClassProperty:(id)property forKey:(NSString*)key { + + @synchronized (BaseModel.class) + { + if (!classValues) classValues = NSMutableDictionary.new; + NSMutableDictionary *values = classValues[NSStringFromClass(self)]; + if (!values) + classValues[NSStringFromClass(self)] = (values = NSMutableDictionary.new); + if (property) values[key] = property; + else [values removeObjectForKey:key]; + } } +#pragma mark - Singleton behaviour -#pragma mark - -#pragma mark Singleton behaviour - -+ (void)setSharedInstance:(BaseModel *)instance -{ - @synchronized ([self class]) - { - if (instance && ![instance isKindOfClass:self]) - { - [NSException raise:NSGenericException format:@"setSharedInstance: instance class does not match"]; - } - id oldInstance = [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey]; - [self BaseModel_setClassProperty:instance forKey:BaseModelSharedInstanceKey]; - if (oldInstance) - { - [[NSNotificationCenter defaultCenter] postNotificationName:BaseModelSharedInstanceUpdatedNotification object:oldInstance]; - } - } -} ++ (instancetype) sharedInstance { -+ (BOOL)hasSharedInstance -{ - return [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey] != nil; + @synchronized (self.class) + { + id instance = [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey]; + if (instance != nil) return instance; + [self reloadSharedInstance]; //load or create instance + //get loaded instance + return [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey]; + } } - -+ (instancetype)sharedInstance -{ - @synchronized ([self class]) - { - id instance = [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey]; - if (instance == nil) - { - //load or create instance - [self reloadSharedInstance]; - - //get loaded instance - instance = [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey]; - } - return instance; - } ++ (void) setSharedInstance:(BaseModel*)instance { + + @synchronized (self.class) + { + if (instance && ![instance isKindOfClass:self]) + [NSException raise:NSGenericException format:@"setSharedInstance: instance class does not match"]; + + id oldInstance = [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey]; + [self BaseModel_setClassProperty:instance forKey:BaseModelSharedInstanceKey]; + if (oldInstance) + [NSNotificationCenter.defaultCenter postNotificationName:BaseModelSharedInstanceUpdatedNotification + object:oldInstance]; + } } ++ (BOOL) hasSharedInstance { -+ (void)reloadSharedInstance -{ - @synchronized ([self class]) - { - id instance = nil; - - //try loading previously saved version - instance = [self instanceWithContentsOfFile:[self BaseModel_saveFilePath]]; - if (instance == nil) - { - //construct a new instance - instance = [self instance]; - } - - //set singleton - [self setSharedInstance:instance]; - } + return [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey] != nil; } - -+ (NSString *)resourceFile -{ - //used for every instance - return [NSStringFromClass(self) stringByAppendingPathExtension:@"plist"]; ++ (void) reloadSharedInstance { + + @synchronized (self.class) + { + id instance = nil; + //try loading previously saved version + instance = [self instanceWithContentsOfFile:self.BaseModel_saveFilePath]; + [self setSharedInstance:instance ?: self.instance]; + //set singleton + } } ++ (NSString*) resourceFile { -+ (NSString *)saveFile -{ - //used to save shared (singleton) instance - return [NSStringFromClass(self) stringByAppendingPathExtension:@"plist"]; + //used for every instance + return [NSStringFromClass(self) stringByAppendingPathExtension:@"plist"]; } ++ (NSString*) saveFile { -- (BOOL)useHRCoderIfAvailable -{ - return YES; + //used to save shared (singleton) instance + return [NSStringFromClass(self) stringByAppendingPathExtension:@"plist"]; } - -- (void)save -{ - if ([[self class] BaseModel_classPropertyForKey:BaseModelSharedInstanceKey] == self) - { - //shared (singleton) instance - [self writeToFile:[[self class] BaseModel_saveFilePath] atomically:YES]; - } - else - { - //no save implementation - [NSException raise:NSGenericException format:@"Unable to save object, save method not implemented"]; - } +- (BOOL) useHRCoderIfAvailable { return YES; } +- (void) save { + + [self.class BaseModel_classPropertyForKey:BaseModelSharedInstanceKey] == self + ? //shared (singleton) instance + [self writeToFile:self.class.BaseModel_saveFilePath atomically:YES] + : //no save implementation + [NSException raise:NSGenericException format:@"Unable to save object, save method not implemented"]; } +#pragma mark - Default constructors -#pragma mark - -#pragma mark Default constructors +- (void) setUp { -- (void)setUp -{ - //override this + //override this } - -+ (instancetype)instance -{ - return [[self alloc] init]; ++ (instancetype) instance { return self.new; } +- (instancetype) init { + + @synchronized (self.class) + { + if (![[self.class BaseModel_classPropertyForKey:BaseModelLoadingFromResourceFileKey] boolValue]) + { + //attempt to load from resource file + [self.class BaseModel_setClassProperty:@YES forKey:BaseModelLoadingFromResourceFileKey]; + id object = [self.class.alloc initWithContentsOfFile:[self.class BaseModel_resourceFilePath]]; + [self.class BaseModel_setClassProperty:nil forKey:BaseModelLoadingFromResourceFileKey]; + if (object) return ((self = object)); + } + if ((self = super.init)) + { + if (self.class == BaseModel.class) + [NSException raise:NSGenericException format:@"BaseModel class is abstract and should be subclassed rather than instantiated directly"]; + [self setUp]; + } + return self; + } } ++ (instancetype) instanceWithObject:(id)object { -- (instancetype)init -{ - @synchronized ([self class]) - { - if (![[[self class] BaseModel_classPropertyForKey:BaseModelLoadingFromResourceFileKey] boolValue]) - { - //attempt to load from resource file - [[self class] BaseModel_setClassProperty:@YES forKey:BaseModelLoadingFromResourceFileKey]; - id object = [[[self class] alloc] initWithContentsOfFile:[[self class] BaseModel_resourceFilePath]]; - [[self class] BaseModel_setClassProperty:nil forKey:BaseModelLoadingFromResourceFileKey]; - if (object) - { - return ((self = object)); - } - } - if ((self = [super init])) - { - if ([self class] == [BaseModel class]) - { - [NSException raise:NSGenericException format:@"BaseModel class is abstract and should be subclassed rather than instantiated directly"]; - } - - [self setUp]; - } - return self; - } + return object ? [self.alloc initWithObject:object]: nil; //return nil if object is nil } +- (NSString*) setterNameForClass:(Class)class { -+ (instancetype)instanceWithObject:(id)object -{ - //return nil if object is nil - return object? [[self alloc] initWithObject:object]: nil; -} + //get class name + NSString *className = NSStringFromClass(class); -- (NSString *)setterNameForClass:(Class)class -{ - //get class name - NSString *className = NSStringFromClass(class); - - //strip NS prefix - if ([className hasPrefix:@"NS"]) - { - className = [className substringFromIndex:2]; - } - - //return setter name - return [NSString stringWithFormat:@"setWith%@:", className]; + //strip NS prefix + if ([className hasPrefix:@"NS"]) + className = [className substringFromIndex:2]; + //return setter name + return [NSString stringWithFormat:@"setWith%@:", className]; } - -- (instancetype)initWithObject:(id)object -{ - if ((self = [self init])) - { - Class class = [object class]; - while (true) - { - SEL setter = NSSelectorFromString([self setterNameForClass:class]); - if ([self respondsToSelector:setter]) - { - ((void (*)(id, SEL, id))objc_msgSend)(self, setter, object); - return self; - } - if ([class superclass] == [NSObject class]) break; - class = [class superclass]; - } - [NSException raise:NSGenericException - format:@"%@ not implemented", [self setterNameForClass:class]]; - } - return self; +- (instancetype) initWithObject: (id)object { + + if ((self = self.init)) + { + Class class = [object class]; + while (true) + { + SEL setter = NSSelectorFromString([self setterNameForClass:class]); + if ([self respondsToSelector:setter]) + { + ((void (*)(id, SEL, id))objc_msgSend)(self, setter, object); + return self; + } + if (class.superclass == NSObject.class) break; + class = class.superclass; + } + [NSException raise:NSGenericException + format:@"%@ not implemented", [self setterNameForClass:class]]; + } + return self; } ++ (NSArray*) instancesWithArray:(NSArray*)array { -+ (NSArray *)instancesWithArray:(NSArray *)array -{ - NSMutableArray *result = [NSMutableArray array]; - for (id object in array) - { - [result addObject:[self instanceWithObject:object]]; - } - return result; + NSMutableArray *result = NSMutableArray.new; + for (id object in array) [result addObject:[self instanceWithObject:object]]; + return result; } ++ (instancetype) instanceWithCoder: (NSCoder*)decoder { -+ (instancetype)instanceWithCoder:(NSCoder *)decoder -{ - //return nil if coder is nil - return decoder? [[self alloc] initWithCoder:decoder]: nil; + return decoder? [self.alloc initWithCoder:decoder]: nil; //return nil if coder is nil } +- (instancetype) initWithCoder: (NSCoder*)decoder { -- (instancetype)initWithCoder:(NSCoder *)decoder -{ - if ((self = [self init])) - { - if ([self respondsToSelector:@selector(setWithCoder:)]) - { - [self setWithCoder:decoder]; - } - else - { - [NSException raise:NSGenericException format:@"setWithCoder: not implemented"]; - } - } - return self; + if ((self = self.init)) + [self respondsToSelector:@selector(setWithCoder:)] + ? [self setWithCoder:decoder] + : [NSException raise:NSGenericException format:@"setWithCoder: not implemented"]; + return self; } - -+ (instancetype)instanceWithContentsOfFile:(NSString *)filePath -{ - //check if the path is a full path or not - NSString *path = filePath; - if (![path isAbsolutePath]) - { - //try resources - path = [self BaseModel_resourceFilePath:filePath]; - if (![[NSFileManager defaultManager] fileExistsAtPath:path]) - { - //try application support - path = [self BaseModel_saveFilePath:filePath]; - } - } - - return [[self alloc] initWithContentsOfFile:path]; ++ (instancetype) instanceWithContentsOfFile:(NSString*)filePath { + + NSString *path = filePath; // check if the path is a full path or not + if (!path.isAbsolutePath) { // try resources + path = [self BaseModel_resourceFilePath:filePath]; + if (![NSFileManager.defaultManager fileExistsAtPath:path]) + path = [self BaseModel_saveFilePath:filePath]; //try application support + } + return [self.alloc initWithContentsOfFile:path]; } +- (instancetype) initWithContentsOfFile: (NSString*)filePath { + + id object = nil; + NSData *data = nil; + + static NSCache *cachedResourceFiles = nil; + cachedResourceFiles = cachedResourceFiles ?: NSCache.new; + + //check cache for existing instance - only cache files inside the main bundle as they are immutable + BOOL isResourceFile = [filePath hasPrefix:NSBundle.mainBundle.bundlePath]; + if (isResourceFile) + { + object = [cachedResourceFiles objectForKey:filePath]; + if (object == NSNull.null) object = nil; + else if ([object isKindOfClass:NSData.class]) + { + data = object; + object = nil; + } + } + if (!object) //load object if no cached version found + { + data = data ?: [NSData dataWithContentsOfFile:filePath]; //load the file + if (data) + { + NSString *extension = filePath.pathExtension.lowercaseString; + if ([extension isEqualToString:@"json"] || [extension isEqualToString:@"js"]) + { + //attempt to deserialise data as json + Class FXJSONClass = NSClassFromString(@"FXJSON"); + if (FXJSONClass) + object = ((id (*)(id, SEL, id))objc_msgSend)(FXJSONClass, @selector(objectWithJSONData:), data); + else + { -- (instancetype)initWithContentsOfFile:(NSString *)filePath -{ - id object = nil; - NSData *data = nil; - - static NSCache *cachedResourceFiles = nil; - if (cachedResourceFiles == nil) - { - cachedResourceFiles = [[NSCache alloc] init]; - } - - //check cache for existing instance - //only cache files inside the main bundle as they are immutable - BOOL isResourceFile = [filePath hasPrefix:[[NSBundle mainBundle] bundlePath]]; - if (isResourceFile) - { - object = [cachedResourceFiles objectForKey:filePath]; - if (object == [NSNull null]) - { - object = nil; - } - else if ([object isKindOfClass:[NSData class]]) - { - data = object; - object = nil; - } - } - - //load object if no cached version found - if (!object) - { - if (!data) - { - //load the file - data = [NSData dataWithContentsOfFile:filePath]; - } - - if (data) - { - NSString *extension = [[filePath pathExtension] lowercaseString]; - if ([extension isEqualToString:@"json"] || [extension isEqualToString:@"js"]) - { - //attempt to deserialise data as json - Class FXJSONClass = NSClassFromString(@"FXJSON"); - if (FXJSONClass) - { - object = ((id (*)(id, SEL, id))objc_msgSend)(FXJSONClass, @selector(objectWithJSONData:), data); - } - else - { - #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_5_0 || __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_7 - object = [NSJSONSerialization JSONObjectWithData:data - options:NSJSONReadingAllowFragments - error:NULL]; + object = [NSJSONSerialization JSONObjectWithData:data + options:NSJSONReadingAllowFragments + error:NULL]; #else - [NSException raise:NSGenericException format:@"NSJSONSerialization is not available on some of the platforms you are targeting. Add FXJSON (https://github.com/nicklockwood/FXJSON) to the project to resolve this."]; + [NSException raise:NSGenericException format:@"NSJSONSerialization is not available on some of the platforms you are targeting. Add FXJSON (https://github.com/nicklockwood/FXJSON) to the project to resolve this."]; #endif - } - } - else - { - //attempt to deserialise data as a plist - NSPropertyListFormat format; - NSPropertyListReadOptions options = NSPropertyListMutableContainersAndLeaves; - object = [NSPropertyListSerialization propertyListWithData:data options:options format:&format error:NULL]; - } - if (!object) - { - //data is not a known serialisation format - object = data; - } - } - } - - //success? - if (object) - { - //check if object is an NSCoded archive - if ([object respondsToSelector:@selector(objectForKey:)]) - { - if (object[@"$archiver"]) - { - if (isResourceFile) - { - //cache data for next time - [cachedResourceFiles setObject:data forKey:filePath]; - } - - //unarchive object - Class coderClass = NSClassFromString(@"CryptoCoder"); - if (!coderClass) - { - coderClass = [NSKeyedUnarchiver class]; - } - object = [coderClass unarchiveObjectWithData:data]; - } - else - { - if (isResourceFile) - { - //cache object for next time - [cachedResourceFiles setObject:object forKey:filePath]; - } - - //unarchive object - Class HRCoderClass = NSClassFromString(@"HRCoder"); - NSString *classNameKey = [HRCoderClass valueForKey:@"classNameKey"]; - if (object[classNameKey]) - { - object = ((id (*)(id, SEL, id))objc_msgSend)(HRCoderClass, NSSelectorFromString(@"unarchiveObjectWithPlist:"), object); - } - } - - if ([object isKindOfClass:[self class]]) - { - //return object - return ((self = object)); - } - } - else if (isResourceFile) - { - //cache object for next time - [cachedResourceFiles setObject:object forKey:filePath]; - } - - //load with object - return ((self = [self initWithObject:object])); - } - else if (isResourceFile) - { - //store null for non-existent files to improve performance next time - [cachedResourceFiles setObject:[NSNull null] forKey:filePath]; - } - - //failed to load - return ((self = nil)); + } + } + else + { + //attempt to deserialise data as a plist + NSPropertyListFormat format; + NSPropertyListReadOptions options = NSPropertyListMutableContainersAndLeaves; + object = [NSPropertyListSerialization propertyListWithData:data options:options format:&format error:NULL]; + } + object = object ?: data; //data is not a known serialisation format + } + } + if (object) //success? + { + if ([object respondsToSelector:@selector(objectForKey:)]) //check if object is an NSCoded archive + { + if (object[@"$archiver"]) + { + if (isResourceFile) [cachedResourceFiles setObject:data forKey:filePath]; //cache data for next time + //unarchive object + Class coderClass = NSClassFromString(@"CryptoCoder") ?: NSKeyedUnarchiver.class; + object = [coderClass unarchiveObjectWithData:data]; + } + else + { + if (isResourceFile) [cachedResourceFiles setObject:object forKey:filePath]; //cache object for next time + //unarchive object + Class HRCoderClass = NSClassFromString(@"HRCoder"); + NSString *classNameKey = [HRCoderClass valueForKey:@"classNameKey"]; + if (object[classNameKey]) + object = ((id (*)(id, SEL, id))objc_msgSend)(HRCoderClass, NSSelectorFromString(@"unarchiveObjectWithPlist:"), object); + } + if ([object isKindOfClass:self.class]) return ((self = object)); // return object + } + else if (isResourceFile) + [cachedResourceFiles setObject:object forKey:filePath]; // cache object for next time + + return ((self = [self initWithObject:object])); // load with object + } + else if (isResourceFile) + //store null for non-existent files to improve performance next time + [cachedResourceFiles setObject:NSNull.null forKey:filePath]; + + return ((self = nil)); //failed to load } - -- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)atomically -{ - NSData *data = nil; - Class CryptoCoderClass = NSClassFromString(@"CryptoCoder"); - Class HRCoderClass = NSClassFromString(@"HRCoder"); - if (CryptoCoderClass && [[self class] respondsToSelector:NSSelectorFromString(@"CCPassword")]) - { - data = [CryptoCoderClass archivedDataWithRootObject:self]; - } - else if (HRCoderClass && [self useHRCoderIfAvailable]) - { - id plist = ((id (*)(id, SEL, id))objc_msgSend)(HRCoderClass, NSSelectorFromString(@"archivedPlistWithRootObject:"), self); - NSPropertyListFormat format = NSPropertyListBinaryFormat_v1_0; - data = [NSPropertyListSerialization dataWithPropertyList:plist format:format options:0 error:NULL]; - } - else - { - data = [NSKeyedArchiver archivedDataWithRootObject:self]; - } - return [data writeToFile:[[self class] BaseModel_saveFilePath:path] atomically:atomically]; +- (BOOL) writeToFile:(NSString*)path atomically:(BOOL)atomically { + + NSData *data = nil; + Class CryptoCoderClass = NSClassFromString(@"CryptoCoder"); + Class HRCoderClass = NSClassFromString(@"HRCoder"); + if (CryptoCoderClass && [self.class respondsToSelector:NSSelectorFromString(@"CCPassword")]) + data = [CryptoCoderClass archivedDataWithRootObject:self]; + else if (HRCoderClass && self.useHRCoderIfAvailable) + { + id plist = ((id (*)(id, SEL, id))objc_msgSend)(HRCoderClass, NSSelectorFromString(@"archivedPlistWithRootObject:"), self); + NSPropertyListFormat format = NSPropertyListBinaryFormat_v1_0; + data = [NSPropertyListSerialization dataWithPropertyList:plist format:format options:0 error:NULL]; + } + else data = [NSKeyedArchiver archivedDataWithRootObject:self]; + return [data writeToFile:[self.class BaseModel_saveFilePath:path] atomically:atomically]; } +#pragma mark - Unique identifier generation -#pragma mark - -#pragma mark Unique identifier generation ++ (NSString*)newUniqueIdentifier { -+ (NSString *)newUniqueIdentifier -{ - CFUUIDRef uuid = CFUUIDCreate(NULL); - CFStringRef identifier = CFUUIDCreateString(NULL, uuid); - CFRelease(uuid); - return CFBridgingRelease(identifier); + CFUUIDRef uuid = CFUUIDCreate(NULL); + CFStringRef identifier = CFUUIDCreateString(NULL, uuid); + CFRelease(uuid); + return CFBridgingRelease(identifier); } -@end \ No newline at end of file +@end