Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/Core-Data-Purge-And-Update'
Browse files Browse the repository at this point in the history
* origin/Core-Data-Purge-And-Update:
  - check for an existing key before trying to find an existing managed object
  - initial checkin for core data update/purge support
  • Loading branch information
aryaxt committed Jan 6, 2014
2 parents 87011e6 + f5c156f commit 90de93d
Show file tree
Hide file tree
Showing 13 changed files with 425 additions and 33 deletions.
3 changes: 2 additions & 1 deletion OCMapper.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@
15B554A2171B7B3B0058E159 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0450;
LastUpgradeCheck = 0500;
ORGANIZATIONNAME = "Aryan Ghassemi";
};
buildConfigurationList = 15B554A5171B7B3B0058E159 /* Build configuration list for PBXProject "OCMapper" */;
Expand Down Expand Up @@ -669,6 +669,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
Expand Down
1 change: 1 addition & 0 deletions OCMapper/Models/CDUser.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

@interface CDUser : NSManagedObject

@property (nonatomic, strong) NSNumber *userId;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSDate *dateOfBirth;
Expand Down
1 change: 1 addition & 0 deletions OCMapper/Models/CDUser.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#import "CDUser.h"

@implementation CDUser
@dynamic userId;
@dynamic firstName;
@dynamic lastName;
@dynamic age;
Expand Down
1 change: 1 addition & 0 deletions OCMapper/Models/Core Data/CDAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

@interface CDAddress : NSManagedObject

@property (nonatomic, strong) NSNumber *addressId;
@property (nonatomic, strong) NSString *city;
@property (nonatomic, strong) NSString *country;

Expand Down
1 change: 1 addition & 0 deletions OCMapper/Models/Core Data/CDAddress.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#import "CDAddress.h"

@implementation CDAddress
@dynamic addressId;
@dynamic city;
@dynamic country;

Expand Down
2 changes: 1 addition & 1 deletion OCMapper/Models/CoreDataManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ - (NSPersistentStoreCoordinator *)persistentStoreCoordinator

NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:storeURL options:options error:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="3400" systemVersion="13A603" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="CDAddress" representedClassName="CDAddress" syncable="YES">
<attribute name="addressId" optional="YES" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
<attribute name="city" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="country" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="user" optional="YES" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="CDUser" inverseName="address" inverseEntity="CDUser" syncable="YES"/>
Expand All @@ -18,13 +19,14 @@
<attribute name="dateOfBirth" optional="YES" attributeType="Date" syncable="YES"/>
<attribute name="firstName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="lastName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="userId" optional="YES" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
<relationship name="address" optional="YES" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="CDAddress" inverseName="user" inverseEntity="CDAddress" syncable="YES"/>
<relationship name="posts" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="CDPost" inverseName="user" inverseEntity="CDPost" syncable="YES"/>
</entity>
<elements>
<element name="CDAddress" positionX="0" positionY="0" width="0" height="0"/>
<element name="CDPost" positionX="0" positionY="0" width="0" height="0"/>
<element name="CDUser" positionX="0" positionY="0" width="0" height="0"/>
<element name="CDSpecialUser" positionX="0" positionY="0" width="0" height="0"/>
<element name="CDUser" positionX="0" positionY="0" width="0" height="0"/>
</elements>
</model>
5 changes: 3 additions & 2 deletions OCMapper/Source/Instance Provider/InstanceProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@

@protocol InstanceProvider <NSObject>

- (id)emptyInstanceFromClass:(Class)class;
- (id)emptyInstanceOfCollectionObject;
- (id)emptyInstanceForClass:(Class)class;
- (id)emptyCollectionInstance;
- (id)upsertObject:(NSObject *)object error:(NSError **)error;
- (NSString *)propertyNameForObject:(NSObject *)object byCaseInsensitivePropertyName:(NSString *)caseInsensitivePropertyName;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@
#import <CoreData/CoreData.h>
#import "InstanceProvider.h"

@interface ManagedObjectInstanceProvider : NSObject <InstanceProvider>
typedef enum {
UpsertModeUpdateExistingObject,
UpsertModePurgeExistingObject
}UpsertMode;

@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
@interface ManagedObjectInstanceProvider : NSObject <InstanceProvider>

- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
- (id)initWithManagedObjectContext:(NSManagedObjectContext *)aManagedObjectContext;
- (void)setUniqueKeys:(NSArray *)keys forClass:(Class)class withUpsertMode:(UpsertMode)upsertMode;

@end
122 changes: 119 additions & 3 deletions OCMapper/Source/Instance Provider/ManagedObjectInstanceProvider.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,19 @@

#import "ManagedObjectInstanceProvider.h"

@interface UpsertInfo : NSObject
@property (nonatomic, strong) NSArray *keys;
@property (nonatomic, assign) UpsertMode upsertMode;
@end
@implementation UpsertInfo
@end

@interface ManagedObjectInstanceProvider()
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) NSMutableDictionary *uniqueKeysDictionary;
@end

@implementation ManagedObjectInstanceProvider
@synthesize managedObjectContext;

#pragma mark - Initialization -

Expand All @@ -37,24 +48,129 @@ - (id)initWithManagedObjectContext:(NSManagedObjectContext *)aManagedObjectConte
if (self = [super init])
{
self.managedObjectContext = aManagedObjectContext;
self.uniqueKeysDictionary = [NSMutableDictionary dictionary];
}

return self;
}

- (id)init
{
@throw ([NSException exceptionWithName:@"InvalidInitializer"
reason:@"Use initWithManagedObjectContext to initialize this provider"
userInfo:nil]);
}

#pragma mark - Public Methods -

- (void)setUniqueKeys:(NSArray *)keys forClass:(Class)class withUpsertMode:(UpsertMode)upsertMode
{
for (id key in keys)
{
if (![key isKindOfClass:[NSString class]])
@throw ([NSException exceptionWithName:@"InvalidArgumentException" reason:@"Method setUniqueKeys takes string only" userInfo:nil]);
}

UpsertInfo *upsertInfo = [[UpsertInfo alloc] init];
upsertInfo.keys = keys;
upsertInfo.upsertMode = upsertMode;

[self.uniqueKeysDictionary setObject:upsertInfo forKey:NSStringFromClass(class)];
}

#pragma mark - InstanceProvider Methods -

- (id)emptyInstanceFromClass:(Class)class
- (id)emptyInstanceForClass:(Class)class
{
NSEntityDescription *entity = [NSEntityDescription entityForName:NSStringFromClass(class) inManagedObjectContext:self.managedObjectContext];
return (entity) ? [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext] : nil;
}

- (id)emptyInstanceOfCollectionObject
- (id)emptyCollectionInstance
{
return [NSMutableSet set];
}

- (id)upsertObject:(NSManagedObject *)object error:(NSError **)error
{
UpsertInfo *upsertInfo = [self.uniqueKeysDictionary objectForKey:NSStringFromClass([object class])];

if (!upsertInfo || !upsertInfo.keys.count)
return object;

NSMutableArray *predicates = [NSMutableArray array];
[predicates addObject:[NSPredicate predicateWithFormat:@"SELF != %@", object]];

for (int i=0 ; i<upsertInfo.keys.count ; i++)
{
NSString *key = [upsertInfo.keys objectAtIndex:i];
id value = [object valueForKey:key];

if (key && value)
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@", key, value];
[predicates addObject:predicate];
}
else
{
*error = [NSError errorWithDomain:[NSString stringWithFormat:@"Value for property '%@' is null. Keys should not be nullable", key] code:0 userInfo:nil];
return object;
}
}

NSPredicate *compundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:NSStringFromClass([object class]) inManagedObjectContext:self.managedObjectContext]];
[request setPredicate:compundPredicate];

NSError *fetchError;
NSArray *existingObjects = [self.managedObjectContext executeFetchRequest:request error:&fetchError];

if (fetchError)
{
*error = fetchError;
return object;
}
else
{
if (existingObjects.count == 0)
{
return object;
}
else if (existingObjects.count == 1)
{
if (upsertInfo.upsertMode == UpsertModeUpdateExistingObject)
{
NSManagedObject *existingObject = [existingObjects firstObject];

for (NSAttributeDescription *attributeDescription in existingObject.entity.properties)
{
[existingObject setValue:[object valueForKey:attributeDescription.name] forKey:attributeDescription.name];
}

[self.managedObjectContext deleteObject:object];
return existingObject;
}
else if (upsertInfo.upsertMode == UpsertModePurgeExistingObject)
{
NSManagedObject *existingObject = [existingObjects firstObject];
[self.managedObjectContext deleteObject:existingObject];
return object;
}
else
{
*error = [NSError errorWithDomain:[NSString stringWithFormat:@"Invalid upsertMode for class (%@)", NSStringFromClass([object class])] code:0 userInfo:nil];
return object;
}
}
else
{
*error = [NSError errorWithDomain:@"Multiple instances were found based on given key(s)" code:0 userInfo:nil];
return object;
}
}
}

- (NSString *)propertyNameForObject:(NSManagedObject *)object byCaseInsensitivePropertyName:(NSString *)caseInsensitivePropertyName
{
for (NSAttributeDescription *attributeDescription in object.entity.properties)
Expand Down
12 changes: 10 additions & 2 deletions OCMapper/Source/Instance Provider/ObjectInstanceProvider.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,24 @@ @implementation ObjectInstanceProvider

#pragma mark - InstanceProvider Methods -

- (id)emptyInstanceFromClass:(Class)class
- (id)emptyInstanceForClass:(Class)class
{
return [[class alloc] init];
}

- (id)emptyInstanceOfCollectionObject
- (id)emptyCollectionInstance
{
return [NSMutableArray array];
}

- (id)upsertObject:(NSObject *)object error:(NSError **)error;
{
// Object not stored anywhere and therefore no need to upsert
// This is specifically made for NSManagedObjects used by ManagedObjectInstanceProvider

return object;
}

- (NSString *)propertyNameForObject:(NSObject *)object byCaseInsensitivePropertyName:(NSString *)caseInsensitivePropertyName
{
NSString *result = nil;
Expand Down
40 changes: 22 additions & 18 deletions OCMapper/Source/ObjectMapper.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,6 @@ @interface ObjectMapper()
@end

@implementation ObjectMapper
@synthesize defaultDateFormatter;
@synthesize commonDateFormaters;
@synthesize instanceProvider;
@synthesize mappingProvider;
@synthesize loggingProvider;

#pragma mark - initialization -

Expand Down Expand Up @@ -78,10 +73,10 @@ - (id)init

- (id)objectFromSource:(id)source toInstanceOfClass:(Class)class
{
if (!mappingProvider)
if (!_mappingProvider)
@throw ([NSException exceptionWithName:@"MissingMappingProvider" reason:@"Mapping provider is not set" userInfo:nil]);

if (!instanceProvider)
if (!_instanceProvider)
@throw ([NSException exceptionWithName:@"MissingInstanceProvider" reason:@"Instance provider is not set" userInfo:nil]);

if ([source isKindOfClass:[NSDictionary class]])
Expand Down Expand Up @@ -244,7 +239,7 @@ - (id)processDictionary:(NSDictionary *)source forClass:(Class)class
{
NSDictionary *normalizedSource = [self normalizedDictionaryFromDictionary:source forClass:class];

id object = [self.instanceProvider emptyInstanceFromClass:class];
id object = [self.instanceProvider emptyInstanceForClass:class];

for (NSString *key in normalizedSource)
{
Expand Down Expand Up @@ -317,12 +312,21 @@ - (id)processDictionary:(NSDictionary *)source forClass:(Class)class
}
}

NSError *error;
object = [self.instanceProvider upsertObject:object error:&error];

if (error)
ELog(@"Attempt to update existing instance failed with error '%@' for class (%@) and object %@",
error.localizedDescription,
NSStringFromClass(class),
object);

return object;
}

- (id)processArray:(NSArray *)value forClass:(Class)class
{
id collection = [self.instanceProvider emptyInstanceOfCollectionObject];
id collection = [self.instanceProvider emptyCollectionInstance];

for (id objectInArray in value)
{
Expand Down Expand Up @@ -409,36 +413,36 @@ - (NSDate *)dateFromString:(NSString *)string forProperty:(NSString *)property a

- (NSMutableArray *)commonDateFormaters
{
if (!commonDateFormaters)
if (!_commonDateFormaters)
{
commonDateFormaters = [NSMutableArray array];
_commonDateFormaters = [NSMutableArray array];

NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
[formatter1 setDateFormat:@"yyyy-MM-dd"];
[commonDateFormaters addObject:formatter1];
[_commonDateFormaters addObject:formatter1];

NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
[formatter2 setDateFormat:@"MM/dd/yyyy"];
[commonDateFormaters addObject:formatter2];
[_commonDateFormaters addObject:formatter2];

NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init];
[formatter3 setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSSSSSZ"];
[commonDateFormaters addObject:formatter3];
[_commonDateFormaters addObject:formatter3];

NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init];
[formatter4 setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
[commonDateFormaters addObject:formatter4];
[_commonDateFormaters addObject:formatter4];

NSDateFormatter *formatter5 = [[NSDateFormatter alloc] init];
[formatter5 setDateFormat:@"MM/dd/yyyy HH:mm:ss aaa"];
[commonDateFormaters addObject:formatter5];
[_commonDateFormaters addObject:formatter5];

NSDateFormatter *formatter6 = [[NSDateFormatter alloc] init];
[formatter6 setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"];
[commonDateFormaters addObject:formatter6];
[_commonDateFormaters addObject:formatter6];
}

return commonDateFormaters;
return _commonDateFormaters;
}

- (NSString *)typeForProperty:(NSString *)property andClass:(Class)class
Expand Down
Loading

0 comments on commit 90de93d

Please sign in to comment.