Skip to content
This repository
Newer
Older
100644 268 lines (153 sloc) 13.173 kb
1a4f8d6e »
2011-06-29 Removing references to ActiveRecord, renaming to MagicalRecord
1 # MagicalRecord for Core Data
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
2
3 In software engineering, the active record pattern is a design pattern found in software that stores its data in relational databases. It was named by Martin Fowler in his book Patterns of Enterprise Application Architecture. The interface to such an object would include functions such as Insert, Update, and Delete, plus properties that correspond more-or-less directly to the columns in the underlying database table.
4
5 > Active record is an approach to accessing data in a database. A database table or view is wrapped into a class; thus an object instance is tied to a single row in the table. After creation of an object, a new row is added to the table upon save. Any object loaded gets its information from the database; when an object is updated, the corresponding row in the table is also updated. The wrapper class implements accessor methods or properties for each column in the table or view.
6
7 > *- [Wikipedia]("http://en.wikipedia.org/wiki/Active_record_pattern")*
8
6992aeb1 »
2011-07-24 Some working tests verifying entity relationships are set properly
9 Magical Record for Core Data was inspired by the ease of Ruby on Rails' Active Record fetching. The goals of this code are:
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
10
11 * Clean up my Core Data related code
12 * Allow for clear, simple, one-line fetches
13 * Still allow the modification of the NSFetchRequest when request optimizations are needed
14
15 # Installation
16
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
17 1. In your XCode Project, add all the .h and .m files from the *Source* folder into your project.
b9e35462 »
2011-09-16 Updating documentation
18 2. Add *CoreData+MagicalRecord.h* file to your PCH file or your AppDelegate file.
feffba86 »
2011-12-28 Update Readme.md to reflect recent changes, especially the MR_SHORTHA…
19 * Optionally add `#define MR_SHORTHAND` to your PCH file if you want to use shorthand like `findAll` instead of `MR_findAll`
20 4. Start writing code! ... There is no step 3!
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
21
d66b4191 »
2011-07-24 Updated some docs
22 # ARC Support
23
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
24 MagicalRecord fully supports ARC *and* non-ARC modes out of the box, there is no configuration necessary. This is great for legacy applications or projects that still need to compile with GCC. ARC support has been tested with the Apple LLVM 3.0 compiler.
d66b4191 »
2011-07-24 Updated some docs
25
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
26 # Usage
27
28 ## Setting up the Core Data Stack
29
1a4f8d6e »
2011-06-29 Removing references to ActiveRecord, renaming to MagicalRecord
30 To get started, first, import the header file *CoreData+MagicalRecord.h* in your project's pch file. This will allow a global include of all the required headers.
b9e35462 »
2011-09-16 Updating documentation
31 Next, somewhere in your app delegate, in either the applicationDidFinishLaunching:(UIApplication *) withOptions:(NSDictionary *) method, or awakeFromNib, use **one** of the following setup calls with the MagicalRecordHelpers class:
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
32
33 + (void) setupCoreDataStack;
34 + (void) setupAutoMigratingDefaultCoreDataStack;
35 + (void) setupCoreDataStackWithInMemoryStore;
36 + (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName;
37 + (void) setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName;
cce797d0 »
2011-04-20 Update threading instructions
38
b9e35462 »
2011-09-16 Updating documentation
39 Each call instantiates one of each piece of the Core Data stack, and provides getter and setter methods for these instances. These well known instances to MagicalRecord, and are recognized as "defaults".
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
40
41 And, before your app exits, you can use the clean up method:
42
1a4f8d6e »
2011-06-29 Removing references to ActiveRecord, renaming to MagicalRecord
43 [MagicalRecordHelpers cleanUp];
9dddc5d2 »
2012-02-15 [README] Provide iCloud details
44
45 ## iCloud Support
46
47 Apps built for iOS5+ and OSX Lion 10.7.2+ can take advantage of iCloud to sync Core Data stores. To implement this functionality with Magical Record, use **one** of the following setup calls instead of those listed in the previous section:
48
051c8fb7 »
2012-02-15 Update README.md
49 + (void) setupCoreDataStackWithiCloudContainer:(NSString *)icloudBucket localStoreNamed:(NSString *)localStore;
50 + (void) setupCoreDataStackWithiCloudContainer:(NSString *)containerID contentNameKey:(NSString *)contentNameKey localStoreNamed:(NSString *)localStoreName cloudStorePathComponent:(NSString *)pathSubcomponent;
51 + (void) setupCoreDataStackWithiCloudContainer:(NSString *)containerID contentNameKey:(NSString *)contentNameKey localStoreNamed:(NSString *)localStoreName cloudStorePathComponent:(NSString *)pathSubcomponent completion:(void(^)(void))completion;
9dddc5d2 »
2012-02-15 [README] Provide iCloud details
52
adad5249 »
2012-02-15 Update README.md
53 For further details, and to ensure that your application is suitable for iCloud, please see [Apple's iCloud Notes](https://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-iCloudCoreData/_index.html).
54
55 In particular note that the first helper method, + (void) setupCoreDataStackWithiCloudContainer:(NSString *)icloudBucket localStoreNamed:(NSString *)localStore, automatically generates the **NSPersistentStoreUbiquitousContentNameKey** based on your application's Bundle Identifier.
56
57 If you are managing multiple different iCloud stores it is highly recommended that you use one of the other helper methods to specify your own **contentNameKey**
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
58
59 ### Default Managed Object Context
60
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
61 When using Core Data, you will deal with two types of objects the most: *NSManagedObject* and *NSManagedObjectContext*. MagicalRecord gives you a place for a default NSManagedObjectContext for use within your app. This is great for single threaded apps. You can easily get to this default context by calling:
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
62
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
63 [NSManagedObjectContext MR_defaultContext];
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
64
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
65 This context will be used if a find or request method (described below) is not specifying a specific context using the **inContext:** method overload.
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
66
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
67 If you need to create a new Managed Object Context for use in other threads, based on the default persistent store that was creating using one of the setup methods, use:
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
68
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
69 NSManagedObjectContext *myNewContext = [NSManagedObjectContext context];
70
71 This will use the same object model and persistent store, but create an entirely new context for use with threads other than the main thread.
72
73 And, if you want to make *myNewContext* the default for all fetch requests on the main thread:
b9e35462 »
2011-09-16 Updating documentation
74
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
75 [NSManagedObjectContext setDefaultContext:myNewContext];
76
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
77 Magical Record also has a helper method to hold on to a Managed Object Context in a thread's threadDictionary. This lets you access the correct NSManagedObjectContext instance no matter which thread you're calling from. This methods is:
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
78
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
79 [NSManagedObjectContext MR_contextForCurrentThread];
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
80
b9e35462 »
2011-09-16 Updating documentation
81 **It is *highly* recommended that the default context is created and set using the main thread**
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
82
83 ### Fetching
84
85 #### Basic Finding
b9e35462 »
2011-09-16 Updating documentation
86
87 Most methods in MagicalRecord return an NSArray of results. So, if you have an Entity called Person, related to a Department (as seen in various Apple Core Data documentation), to get all the Person entities from your Persistent Store:
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
88
feffba86 »
2011-12-28 Update Readme.md to reflect recent changes, especially the MR_SHORTHA…
89 //In order for this to work you need to add "#define MR_SHORTHAND" to your PCH file
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
90 NSArray *people = [Person findAll];
91
feffba86 »
2011-12-28 Update Readme.md to reflect recent changes, especially the MR_SHORTHA…
92 // Otherwise you can use the longer, namespaced version
93 NSArray *people = [Person MR_findAll];
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
94
95 Or, to have the results sorted by a property:
96
97 NSArray *peopleSorted = [Person findAllSortedByProperty:@"LastName" ascending:YES];
98
26d74eca »
2011-11-15 Adding aggregate operation support submitted from Duane Fields. Thank…
99 Or, to have the results sorted by multiple properties:
100
101 NSArray *peopleSorted = [Person findAllSortedByProperty:@"LastName,FirstName" ascending:YES];
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
102
103 If you have a unique way of retrieving a single object from your data store, you can get that object directly:
104
105 Person *person = [Person findFirstByAttribute:@"FirstName" withValue:@"Forrest"];
106
107 #### Advanced Finding
108
109 If you want to be more specific with your search, you can send in a predicate:
110
111 NSArray *departments = [NSArray arrayWithObjects:dept1, dept2, ..., nil];
112 NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
113
114 NSArray *people = [Person findAllWithPredicate:peopleFilter];
115
b9e35462 »
2011-09-16 Updating documentation
116 #### Returning an NSFetchRequest
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
117
118 NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
119
120 NSArray *people = [Person fetchAllWithPredicate:peopleFilter];
121
122 For each of these single line calls, the full stack of NSFetchRequest, NSSortDescriptors and a simple default error handling scheme (ie. logging to the console) is created.
123
b9e35462 »
2011-09-16 Updating documentation
124 #### Customizing the Request
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
125
126 NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
127
128 NSFetchRequest *peopleRequest = [Person requestAllWithPredicate:peopleFilter];
129 [peopleRequest setReturnsDistinctResults:NO];
130 [peopleRequest setReturnPropertiesNamed:[NSArray arrayWithObjects:@"FirstName", @"LastName", nil]];
131 ...
132
133 NSArray *people = [Person executeFetchRequest:peopleRequest];
134
135 #### Find the number of entities
136
137 You can also perform a count of entities in your Store, that will be performed on the Store
138
b9e35462 »
2011-09-16 Updating documentation
139 NSNumber *count = [Person numberOfEntities];
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
140
141 Or, if you're looking for a count of entities based on a predicate or some filter:
142
b9e35462 »
2011-09-16 Updating documentation
143 NSNumber *count = [Person numberOfEntitiesWithPredicate:...];
144
145 There are also counterpart methods which return NSUInteger rather than NSNumbers:
146
147 * countOfEntities
148 * countOfEntitiesWithContext:(NSManagedObjectContext *)
149 * countOfEntitiesWithPredicate:(NSPredicate *)
150 * countOfEntitiesWithPredicate:(NSPredicate *) inContext:(NSManagedObjectContext *)
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
151
26d74eca »
2011-11-15 Adding aggregate operation support submitted from Duane Fields. Thank…
152 #### Aggregate Operations
153
154 NSPredicate *prediate = [NSPredicate predicateWithFormat:@"diaryEntry.date == %@", today];
155 int totalFat = [[CTFoodDiaryEntry aggregateOperation:@"sum:" onAttribute:@"fatColories" withPredicate:predicate] intValue];
156 int fattest = [[CTFoodDiaryEntry aggregateOperation:@"max:" onAttribute:@"fatColories" withPredicate:predicate] intValue];
157
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
158 #### Finding from a different context
159
160 All find, fetch and request methods have an inContext: method parameter
161
162 NSManagedObjectContext *someOtherContext = ...;
163
164 NSArray *peopleFromAnotherContext = [Person findAllInContext:someOtherContext];
165
166 ...
167
168 Person *personFromContext = [Person findFirstByAttribute:@"lastName" withValue:@"Gump" inContext:someOtherContext];
169
170 ...
171
172 NSUInteger count = [Person numberOfEntitiesWithContext:someOtherContext];
173
174
175 ## Creating new Entities
176
177 When you need to create a new instance of an Entity, use:
178
179 Person *myNewPersonInstance = [Person createEntity];
180
181 or, to specify a context:
182
183 NSManagedObjectContext *otherContext = ...;
184
185 Person *myPerson = [Person createInContext:otherContext];
186
187
188 ## Deleting Entities
189
190 To delete a single entity:
191
192 Person *p = ...;
193 [p deleteEntity];
194
195 or, to specify a context:
196
197 NSManagedObjectContext *otherContext = ...;
198 Person *deleteMe = ...;
199
200 [deleteMe deleteInContext:otherContext];
201
202 There is no delete *All Entities* or *truncate* operation in core data, so one is provided for you with Active Record for Core Data:
203
204 [Person truncateAll];
205
206 or, with a specific context:
207
208 NSManagedObjectContext *otherContext = ...;
209 [Person truncateAllInContext:otherContext];
210
211 ## Performing Core Data operations on Threads
212
213 Available only on iOS 4.0 and Mac OS 10.6
214
215 Paraphrasing the [Apple documentation on Core Data and Threading]("http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdConcurrency.html#//apple_ref/doc/uid/TP40003385-SW1"), you should always do the following:
216
217 * Use a new, dedicated NSManagedObjectContext instance for every thread
218 * Use an instance of your NSManagedObjects that is local for the new NSManagedObjectContext
219 * Notify other contexts that the background is updated or saved
220
1a4f8d6e »
2011-06-29 Removing references to ActiveRecord, renaming to MagicalRecord
221 The Magical Record library is trying to make these steps more reusable with the following methods:
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
222
223 + (void) performSaveDataOperationWithBlock:(CoreDataBlock)block;
224 + (void) performSaveDataOperationInBackgroundWithBlock:(CoreDataBlock)block;
225
226 CoreDataBlock is typedef'd as:
227
228 typedef void (^CoreDataBlock)(NSManagedObjectContext *);
229
230 All the boilerplate operations that need to be done when saving are done in these methods. To use this method from the *main thread*:
231
cce797d0 »
2011-04-20 Update threading instructions
232 Person *person = ...;
6992aeb1 »
2011-07-24 Some working tests verifying entity relationships are set properly
233 [MRCoreDataAction saveDataInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
cce797d0 »
2011-04-20 Update threading instructions
234 Person *localPerson = [person inContext:localContext];
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
235
236 localPerson.firstName = @"Chuck";
237 localPerson.lastName = @"Smith";
238 }];
239
240 In this method, the CoreDataBlock provides you with the proper context in which to perform your operations, you don't need to worry about setting up the context so that it tells the Default Context that it's done, and should update because changes were performed on another thread.
cce797d0 »
2011-04-20 Update threading instructions
241
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
242 To perform an action after this save block is completed, you can fill in a completion block:
243
244 Person *person = ...;
245 [MRCoreDataAction saveDataInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
246 Person *localPerson = [person inContext:localContext];
247
248 localPerson.firstName = @"Chuck";
249 localPerson.lastName = @"Smith";
250 } completion:^{
251
252 self.everyoneInTheDepartment = [Person findAll];
253 }];
254
255 This completion block is called on the main thread (queue), so this is also safe for triggering UI updates.
256
6992aeb1 »
2011-07-24 Some working tests verifying entity relationships are set properly
257 All MRCoreDataActions have a dedicated GCD queue on which they operate. This means that throughout your app, you only really have 2 queues (sort of like threads) performing Core Data actions at any one time: one on the main queue, and another on this dedicated GCD queue.
d66b4191 »
2011-07-24 Updated some docs
258
259 # Data Import
260
261 *Experimental*
262
5470cc64 »
2011-11-15 Made the default managed object context section more clear.
263 MagicalRecord will now import data from NSDictionaries into your Core Data store. [Documentation](https://github.com/magicalpanda/MagicalRecord/wiki/Data-Import) for this feature will be added to the wiki.
264 This feature is currently under development, and is undergoing updates. Feel free to try it out, add tests and send in your feedback.
dc6ab2c5 »
2010-11-29 updated readme, changed to markdown
265
266 # Extra Bits
267 This Code is released under the MIT License by [Magical Panda Software, LLC.](http://www.magicalpanda.com)
Something went wrong with that request. Please try again.