diff --git a/RHExtendedManagedObject.h b/RHExtendedManagedObject.h new file mode 100644 index 0000000..6d7232a --- /dev/null +++ b/RHExtendedManagedObject.h @@ -0,0 +1,31 @@ +// +// RHExtendedManagedObject.h +// SimplifiedCoreDataExample +// +// Created by David De Bels on 22/11/13. +// +// + +#import "RHManagedObject.h" + +@interface RHExtendedManagedObject : RHManagedObject +{ +@protected + + // Internal boolean to track whether or not to update the additionalData on save + BOOL _needsUpdate; + +@private + + NSMutableDictionary* _additionalProperties; +} + +@property (nonatomic, retain) NSData * additionalData; + +-(id)additionalPropertyForKey:(NSString*)key; + +-(void)setAdditionalDateProperty:(NSDate*)date forKey:(NSString*)key; +-(void)setAdditionalNumberProperty:(NSNumber*)number forKey:(NSString*)key; +-(void)setAdditionalStringProperty:(NSString*)string forKey:(NSString*)key; + +@end diff --git a/RHExtendedManagedObject.m b/RHExtendedManagedObject.m new file mode 100644 index 0000000..e28a0cc --- /dev/null +++ b/RHExtendedManagedObject.m @@ -0,0 +1,120 @@ +// +// RHExtendedManagedObject.m +// SimplifiedCoreDataExample +// +// Created by David De Bels on 22/11/13. +// +// + +#import "RHExtendedManagedObject.h" + +static NSString* kRHRuntimePropertiesKey = @"kRHRuntimePropertiesKey"; + + +@interface RHExtendedManagedObject () + +@property (nonatomic, readonly) NSMutableDictionary* additionalProperties; + +@end + + +@implementation RHExtendedManagedObject + +@dynamic additionalData; + +#pragma mark - Get or set additional properties + +-(NSMutableDictionary*)additionalProperties +{ + if (!_additionalProperties) + { + _additionalProperties = [self readAdditionalData]; + + if (!_additionalProperties) + _additionalProperties = [[NSMutableDictionary alloc] init]; + } + + return _additionalProperties; +} + +-(void)setAdditionalDateProperty:(NSDate*)date forKey:(NSString*)key +{ + if (date == nil || [date isKindOfClass:[NSDate class]]) + [self setAdditionalValue:date forKey:key]; +} + +-(void)setAdditionalNumberProperty:(NSNumber*)number forKey:(NSString*)key +{ + if (number == nil || [number isKindOfClass:[NSNumber class]]) + [self setAdditionalValue:number forKey:key]; +} + +-(void)setAdditionalStringProperty:(NSString*)string forKey:(NSString*)key +{ + if (string == nil || [string isKindOfClass:[NSString class]]) + [self setAdditionalValue:string forKey:key]; +} + +-(void)setAdditionalValue:(id)value forKey:(NSString*)key +{ + if (!key) + return; + + if (value) + [self.additionalProperties setObject:value forKey:key]; + else + [self.additionalProperties removeObjectForKey:key]; + + _needsUpdate = YES; +} + +-(id)additionalPropertyForKey:(NSString*)key +{ + return [self.additionalProperties objectForKey:key]; +} + +-(void)willSave +{ + [super willSave]; + [self writeAdditionalData]; +} + + + +#pragma mark - Read or write the additionalData + +-(NSMutableDictionary*)readAdditionalData +{ + if ([self respondsToSelector:@selector(additionalData)]) + { + NSDictionary* dataDictionary = nil; + + NSData* data = [self performSelector:@selector(additionalData)]; + if (data) + { + NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; + dataDictionary = [unarchiver decodeObjectForKey:kRHRuntimePropertiesKey]; + [unarchiver finishDecoding]; + } + + return [dataDictionary mutableCopy]; + } + + return nil; +} + +-(void)writeAdditionalData +{ + if (_needsUpdate && [self respondsToSelector:@selector(setAdditionalData:)]) + { + NSMutableData* data = [[NSMutableData alloc] init]; + NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; + [archiver encodeObject:self.additionalProperties forKey:kRHRuntimePropertiesKey]; + [archiver finishEncoding]; + + [self performSelector:@selector(setAdditionalData:) withObject:data]; + _needsUpdate = NO; + } +} + +@end diff --git a/RHManagedObject.h b/RHManagedObject.h index dc10b99..47093b7 100644 --- a/RHManagedObject.h +++ b/RHManagedObject.h @@ -77,6 +77,40 @@ typedef void (^RHDidDeleteBlock)(); withLimit:(NSUInteger)limit error:(NSError **)error; ++(NSArray *)fetchWithPredicate:(NSPredicate *)predicate + sortDescriptors:(NSArray *)descriptors + withLimit:(NSUInteger)limit + error:(NSError **)error; + ++(void)fetchInBackgroundWithPredicate:(NSPredicate *)predicate + sortDescriptors:(NSArray *)descriptors + withLimit:(NSUInteger)limit + completion:(void (^)(NSArray* fetchedObjects, NSError* error))completion; + ++(NSDictionary*)fetchAllAsDictionaryWithKeyProperty:(NSString*)keyProperty + error:(NSError **)error; + ++(NSDictionary*)fetchAsDictionaryWithKeyProperty:(NSString*)keyProperty + withPredicate:(NSPredicate*)predicate + error:(NSError **)error; + ++(NSDictionary*)fetchAsDictionaryWithKeyProperty:(NSString*)keyProperty + withPredicate:(NSPredicate*)predicate + withSortDescriptor:(NSSortDescriptor*)descriptor + error:(NSError **)error; + ++(NSDictionary*)fetchAsDictionaryWithKeyProperty:(NSString*)keyProperty + withPredicate:(NSPredicate*)predicate + withSortDescriptors:(NSArray*)descriptors + withLimit:(NSUInteger)limit + error:(NSError **)error; + ++(void)fetchInBackgroundAsDictionaryWithKeyProperty:(NSString*)keyProperty + withPredicate:(NSPredicate*)predicate + withSortDescriptors:(NSArray*)descriptors + withLimit:(NSUInteger)limit + completion:(void (^)(NSDictionary* fetchedObjects, NSError* error))completion; + +(NSUInteger)countWithError:(NSError **)error; +(NSUInteger)countWithPredicate:(NSPredicate *)predicate error:(NSError **)error; @@ -112,6 +146,11 @@ typedef void (^RHDidDeleteBlock)(); -(void)didUpdate; -(void)didDelete; + ++(NSArray*)arrayInCurrentThreadContext:(NSArray*)array; ++(NSDictionary*)dictionaryInCurrentThreadContext:(NSDictionary*)dictionary; ++(NSSet*)setInCurrentThreadContext:(NSSet*)set; + @end @interface ImageToDataTransformer : NSValueTransformer diff --git a/RHManagedObject.m b/RHManagedObject.m index 56c86df..a107e6d 100644 --- a/RHManagedObject.m +++ b/RHManagedObject.m @@ -152,6 +152,140 @@ +(NSArray *)fetchWithPredicate:(NSPredicate *)predicate return [[self managedObjectContextForCurrentThreadWithError:error] executeFetchRequest:fetch error:error]; } ++(NSArray *)fetchWithPredicate:(NSPredicate *)predicate + sortDescriptors:(NSArray *)descriptors + withLimit:(NSUInteger)limit + error:(NSError **)error { + NSFetchRequest *fetch = [[NSFetchRequest alloc] init]; + + [fetch setEntity:[self entityDescriptionWithError:error]]; + + if (predicate) { + [fetch setPredicate:predicate]; + } + + if (descriptors) { + [fetch setSortDescriptors:descriptors]; + } + + if (limit > 0) { + [fetch setFetchLimit:limit]; + } + + [fetch setIncludesPendingChanges:YES]; + + return [[self managedObjectContextForCurrentThreadWithError:error] executeFetchRequest:fetch error:error]; +} + ++(void)fetchInBackgroundWithPredicate:(NSPredicate *)predicate + sortDescriptors:(NSArray *)descriptors + withLimit:(NSUInteger)limit + completion:(void (^)(NSArray* fetchedObjects, NSError* error))completion { + + if (![NSThread isMainThread]) + { + NSError* error = nil; + NSArray* fetchedObjects = [self fetchWithPredicate:predicate sortDescriptors:descriptors withLimit:limit error:&error]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + NSArray* convertedFetchedObjects = [self arrayInCurrentThreadContext:fetchedObjects]; + completion(convertedFetchedObjects, error); + }); + } + else + { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + + NSError* error = nil; + NSArray* fetchedObjects = [self fetchWithPredicate:predicate sortDescriptors:descriptors withLimit:limit error:&error]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + NSArray* convertedFetchedObjects = [self arrayInCurrentThreadContext:fetchedObjects]; + completion(convertedFetchedObjects, error); + }); + }); + } +} + ++(NSDictionary*)fetchAllAsDictionaryWithKeyProperty:(NSString*)keyProperty + error:(NSError **)error { + + return [self fetchAsDictionaryWithKeyProperty:keyProperty withPredicate:nil withSortDescriptors:nil withLimit:0 error:error]; +} + ++(NSDictionary*)fetchAsDictionaryWithKeyProperty:(NSString*)keyProperty + withPredicate:(NSPredicate*)predicate + error:(NSError **)error { + + return [self fetchAsDictionaryWithKeyProperty:keyProperty withPredicate:predicate withSortDescriptors:nil withLimit:0 error:error]; +} + ++(NSDictionary*)fetchAsDictionaryWithKeyProperty:(NSString*)keyProperty + withPredicate:(NSPredicate*)predicate + withSortDescriptor:(NSSortDescriptor*)descriptor + error:(NSError **)error { + + NSArray* descriptors = nil; + if (descriptor) + descriptors = [NSArray arrayWithObject:descriptor]; + + return [self fetchAsDictionaryWithKeyProperty:keyProperty withPredicate:predicate withSortDescriptors:descriptors withLimit:0 error:error]; +} + ++(NSDictionary*)fetchAsDictionaryWithKeyProperty:(NSString*)keyProperty + withPredicate:(NSPredicate*)predicate + withSortDescriptors:(NSArray*)descriptors + withLimit:(NSUInteger)limit + error:(NSError **)error { + + NSArray* fetchedObjects = [self fetchWithPredicate:predicate sortDescriptors:descriptors withLimit:limit error:error]; + + NSMutableDictionary* dictionary = [NSMutableDictionary new]; + for (RHManagedObject* managedObject in fetchedObjects) + { + if ([managedObject valueForKey:keyProperty]) { + [dictionary setObject:managedObject forKey:[managedObject valueForKey:keyProperty]]; + } + } + + return [dictionary copy]; +} + ++(void)fetchInBackgroundAsDictionaryWithKeyProperty:(NSString*)keyProperty + withPredicate:(NSPredicate*)predicate + withSortDescriptors:(NSArray*)descriptors + withLimit:(NSUInteger)limit + completion:(void (^)(NSDictionary* fetchedObjects, NSError* error))completion { + + if (![NSThread isMainThread]) + { + NSError* error = nil; + NSDictionary* fetchedObjects = [self fetchAsDictionaryWithKeyProperty:keyProperty withPredicate:predicate withSortDescriptors:descriptors withLimit:limit error:&error]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + NSDictionary* convertedFetchedObjects = [self dictionaryInCurrentThreadContext:fetchedObjects]; + completion(convertedFetchedObjects, error); + }); + } + else + { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + + NSError* error = nil; + NSDictionary* fetchedObjects = [self fetchAsDictionaryWithKeyProperty:keyProperty withPredicate:predicate withSortDescriptors:descriptors withLimit:limit error:&error]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + NSDictionary* convertedFetchedObjects = [self dictionaryInCurrentThreadContext:fetchedObjects]; + completion(convertedFetchedObjects, error); + }); + }); + } +} + +(NSUInteger)countWithError:(NSError **)error { return [self countWithPredicate:nil error:error]; } @@ -288,6 +422,61 @@ -(id)objectInCurrentThreadContext { return [currentMoc objectWithID:self.objectID]; } ++(NSArray*)arrayInCurrentThreadContext:(NSArray*)array { + if (!array) + return nil; + + NSMutableArray* tempArray = [NSMutableArray new]; + for (RHManagedObject* managedObject in array) + { + if ([managedObject isKindOfClass:[RHManagedObject class]]) + { + RHManagedObject* convertedManagedObject = [managedObject objectInCurrentThreadContext]; + if (convertedManagedObject) + [tempArray addObject:convertedManagedObject]; + } + else + { + // Some other object, don't do anything with it + [tempArray addObject:managedObject]; + } + } + + return [tempArray copy]; +} + ++(NSDictionary*)dictionaryInCurrentThreadContext:(NSDictionary*)dictionary { + if (!dictionary) + return nil; + + NSMutableDictionary* tempDictionary = [NSMutableDictionary new]; + for (NSString* key in [dictionary allKeys]) + { + RHManagedObject* managedObject = [dictionary objectForKey:key]; + if ([managedObject isKindOfClass:[RHManagedObject class]]) + { + RHManagedObject* convertedManagedObject = [managedObject objectInCurrentThreadContext]; + if (convertedManagedObject) + [tempDictionary setObject:convertedManagedObject forKey:key]; + } + else + { + // Some other object, don't do anything with it + [tempDictionary setObject:managedObject forKey:key]; + } + } + + return [NSDictionary dictionaryWithDictionary:tempDictionary]; +} + ++(NSSet*)setInCurrentThreadContext:(NSSet*)set { + if (!set) + return nil; + + NSArray* array = [self arrayInCurrentThreadContext:[set allObjects]]; + return [NSSet setWithArray:array]; +} + // This function needs work -(NSDictionary *)serialize { NSMutableDictionary *dict = [NSMutableDictionary dictionary];