2727
2828#import " ManagedObjectInstanceProvider.h"
2929
30+ @interface UpsertInfo : NSObject
31+ @property (nonatomic , strong ) NSArray *keys;
32+ @property (nonatomic , assign ) UpsertMode upsertMode;
33+ @end
34+ @implementation UpsertInfo
35+ @end
36+
37+ @interface ManagedObjectInstanceProvider ()
38+ @property (nonatomic , strong ) NSManagedObjectContext *managedObjectContext;
39+ @property (nonatomic , strong ) NSMutableDictionary *uniqueKeysDictionary;
40+ @end
41+
3042@implementation ManagedObjectInstanceProvider
31- @synthesize managedObjectContext;
3243
3344#pragma mark - Initialization -
3445
@@ -37,24 +48,129 @@ - (id)initWithManagedObjectContext:(NSManagedObjectContext *)aManagedObjectConte
3748 if (self = [super init ])
3849 {
3950 self.managedObjectContext = aManagedObjectContext;
51+ self.uniqueKeysDictionary = [NSMutableDictionary dictionary ];
4052 }
4153
4254 return self;
4355}
4456
57+ - (id )init
58+ {
59+ @throw ([NSException exceptionWithName: @" InvalidInitializer"
60+ reason: @" Use initWithManagedObjectContext to initialize this provider"
61+ userInfo: nil ]);
62+ }
63+
64+ #pragma mark - Public Methods -
65+
66+ - (void )setUniqueKeys : (NSArray *)keys forClass : (Class )class withUpsertMode : (UpsertMode)upsertMode
67+ {
68+ for (id key in keys)
69+ {
70+ if (![key isKindOfClass: [NSString class ]])
71+ @throw ([NSException exceptionWithName: @" InvalidArgumentException" reason: @" Method setUniqueKeys takes string only" userInfo: nil ]);
72+ }
73+
74+ UpsertInfo *upsertInfo = [[UpsertInfo alloc ] init ];
75+ upsertInfo.keys = keys;
76+ upsertInfo.upsertMode = upsertMode;
77+
78+ [self .uniqueKeysDictionary setObject: upsertInfo forKey: NSStringFromClass (class)];
79+ }
80+
4581#pragma mark - InstanceProvider Methods -
4682
47- - (id )emptyInstanceFromClass : (Class )class
83+ - (id )emptyInstanceForClass : (Class )class
4884{
4985 NSEntityDescription *entity = [NSEntityDescription entityForName: NSStringFromClass (class) inManagedObjectContext: self .managedObjectContext];
5086 return (entity) ? [[NSManagedObject alloc ] initWithEntity: entity insertIntoManagedObjectContext: self .managedObjectContext] : nil ;
5187}
5288
53- - (id )emptyInstanceOfCollectionObject
89+ - (id )emptyCollectionInstance
5490{
5591 return [NSMutableSet set ];
5692}
5793
94+ - (id )upsertObject : (NSManagedObject *)object error : (NSError **)error
95+ {
96+ UpsertInfo *upsertInfo = [self .uniqueKeysDictionary objectForKey: NSStringFromClass ([object class ])];
97+
98+ if (!upsertInfo || !upsertInfo.keys .count )
99+ return object;
100+
101+ NSMutableArray *predicates = [NSMutableArray array ];
102+ [predicates addObject: [NSPredicate predicateWithFormat: @" SELF != %@" , object]];
103+
104+ for (int i=0 ; i<upsertInfo.keys .count ; i++)
105+ {
106+ NSString *key = [upsertInfo.keys objectAtIndex: i];
107+ id value = [object valueForKey: key];
108+
109+ if (key && value)
110+ {
111+ NSPredicate *predicate = [NSPredicate predicateWithFormat: @" %K == %@" , key, value];
112+ [predicates addObject: predicate];
113+ }
114+ else
115+ {
116+ *error = [NSError errorWithDomain: [NSString stringWithFormat: @" Value for property '%@ ' is null. Keys should not be nullable" , key] code: 0 userInfo: nil ];
117+ return object;
118+ }
119+ }
120+
121+ NSPredicate *compundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: predicates];
122+ NSFetchRequest *request = [[NSFetchRequest alloc ] init ];
123+ [request setEntity: [NSEntityDescription entityForName: NSStringFromClass ([object class ]) inManagedObjectContext: self .managedObjectContext]];
124+ [request setPredicate: compundPredicate];
125+
126+ NSError *fetchError;
127+ NSArray *existingObjects = [self .managedObjectContext executeFetchRequest: request error: &fetchError];
128+
129+ if (fetchError)
130+ {
131+ *error = fetchError;
132+ return object;
133+ }
134+ else
135+ {
136+ if (existingObjects.count == 0 )
137+ {
138+ return object;
139+ }
140+ else if (existingObjects.count == 1 )
141+ {
142+ if (upsertInfo.upsertMode == UpsertModeUpdateExistingObject)
143+ {
144+ NSManagedObject *existingObject = [existingObjects firstObject ];
145+
146+ for (NSAttributeDescription *attributeDescription in existingObject.entity .properties )
147+ {
148+ [existingObject setValue: [object valueForKey: attributeDescription.name] forKey: attributeDescription.name];
149+ }
150+
151+ [self .managedObjectContext deleteObject: object];
152+ return existingObject;
153+ }
154+ else if (upsertInfo.upsertMode == UpsertModePurgeExistingObject)
155+ {
156+ NSManagedObject *existingObject = [existingObjects firstObject ];
157+ [self .managedObjectContext deleteObject: existingObject];
158+ return object;
159+ }
160+ else
161+ {
162+ *error = [NSError errorWithDomain: [NSString stringWithFormat: @" Invalid upsertMode for class (%@ )" , NSStringFromClass ([object class ])] code: 0 userInfo: nil ];
163+ return object;
164+ }
165+ }
166+ else
167+ {
168+ *error = [NSError errorWithDomain: @" Multiple instances were found based on given key(s)" code: 0 userInfo: nil ];
169+ return object;
170+ }
171+ }
172+ }
173+
58174- (NSString *)propertyNameForObject : (NSManagedObject *)object byCaseInsensitivePropertyName : (NSString *)caseInsensitivePropertyName
59175{
60176 for (NSAttributeDescription *attributeDescription in object.entity .properties )
0 commit comments