Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 254 lines (145 sloc) 11.663 kb
1a4f8d6 @casademora Removing references to ActiveRecord, renaming to MagicalRecord
casademora authored
1 # MagicalRecord for Core Data
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
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
6992aeb @casademora Some working tests verifying entity relationships are set properly
casademora authored
9 Magical Record for Core Data was inspired by the ease of Ruby on Rails' Active Record fetching. The goals of this code are:
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
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
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
17 1. In your XCode Project, add all the .h and .m files from the *Source* folder into your project.
b9e3546 @casademora Updating documentation
casademora authored
18 2. Add *CoreData+MagicalRecord.h* file to your PCH file or your AppDelegate file.
feffba8 @tonyxiao Update Readme.md to reflect recent changes, especially the MR_SHORTHA…
tonyxiao authored
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!
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
21
d66b419 @casademora Updated some docs
casademora authored
22 # ARC Support
23
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
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.
d66b419 @casademora Updated some docs
casademora authored
25
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
26 # Usage
27
28 ## Setting up the Core Data Stack
29
1a4f8d6 @casademora Removing references to ActiveRecord, renaming to MagicalRecord
casademora authored
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.
b9e3546 @casademora Updating documentation
casademora authored
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:
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
32
33 + (void) setupCoreDataStack;
34 + (void) setupAutoMigratingDefaultCoreDataStack;
35 + (void) setupCoreDataStackWithInMemoryStore;
36 + (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName;
37 + (void) setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName;
cce797d @casademora Update threading instructions
casademora authored
38
b9e3546 @casademora Updating documentation
casademora authored
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".
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
40
41 And, before your app exits, you can use the clean up method:
42
1a4f8d6 @casademora Removing references to ActiveRecord, renaming to MagicalRecord
casademora authored
43 [MagicalRecordHelpers cleanUp];
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
44
45 ### Default Managed Object Context
46
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
47 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:
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
48
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
49 [NSManagedObjectContext MR_defaultContext];
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
50
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
51 This context will be used if a find or request method (described below) is not specifying a specific context using the **inContext:** method overload.
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
52
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
53 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:
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
54
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
55 NSManagedObjectContext *myNewContext = [NSManagedObjectContext context];
56
57 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.
58
59 And, if you want to make *myNewContext* the default for all fetch requests on the main thread:
b9e3546 @casademora Updating documentation
casademora authored
60
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
61 [NSManagedObjectContext setDefaultContext:myNewContext];
62
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
63 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:
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
64
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
65 [NSManagedObjectContext MR_contextForCurrentThread];
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
66
b9e3546 @casademora Updating documentation
casademora authored
67 **It is *highly* recommended that the default context is created and set using the main thread**
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
68
69 ### Fetching
70
71 #### Basic Finding
b9e3546 @casademora Updating documentation
casademora authored
72
73 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:
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
74
feffba8 @tonyxiao Update Readme.md to reflect recent changes, especially the MR_SHORTHA…
tonyxiao authored
75 //In order for this to work you need to add "#define MR_SHORTHAND" to your PCH file
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
76 NSArray *people = [Person findAll];
77
feffba8 @tonyxiao Update Readme.md to reflect recent changes, especially the MR_SHORTHA…
tonyxiao authored
78 // Otherwise you can use the longer, namespaced version
79 NSArray *people = [Person MR_findAll];
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
80
81 Or, to have the results sorted by a property:
82
83 NSArray *peopleSorted = [Person findAllSortedByProperty:@"LastName" ascending:YES];
84
26d74ec @casademora Adding aggregate operation support submitted from Duane Fields. Thank…
casademora authored
85 Or, to have the results sorted by multiple properties:
86
87 NSArray *peopleSorted = [Person findAllSortedByProperty:@"LastName,FirstName" ascending:YES];
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
88
89 If you have a unique way of retrieving a single object from your data store, you can get that object directly:
90
91 Person *person = [Person findFirstByAttribute:@"FirstName" withValue:@"Forrest"];
92
93 #### Advanced Finding
94
95 If you want to be more specific with your search, you can send in a predicate:
96
97 NSArray *departments = [NSArray arrayWithObjects:dept1, dept2, ..., nil];
98 NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
99
100 NSArray *people = [Person findAllWithPredicate:peopleFilter];
101
b9e3546 @casademora Updating documentation
casademora authored
102 #### Returning an NSFetchRequest
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
103
104 NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
105
106 NSArray *people = [Person fetchAllWithPredicate:peopleFilter];
107
108 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.
109
b9e3546 @casademora Updating documentation
casademora authored
110 #### Customizing the Request
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
111
112 NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
113
114 NSFetchRequest *peopleRequest = [Person requestAllWithPredicate:peopleFilter];
115 [peopleRequest setReturnsDistinctResults:NO];
116 [peopleRequest setReturnPropertiesNamed:[NSArray arrayWithObjects:@"FirstName", @"LastName", nil]];
117 ...
118
119 NSArray *people = [Person executeFetchRequest:peopleRequest];
120
121 #### Find the number of entities
122
123 You can also perform a count of entities in your Store, that will be performed on the Store
124
b9e3546 @casademora Updating documentation
casademora authored
125 NSNumber *count = [Person numberOfEntities];
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
126
127 Or, if you're looking for a count of entities based on a predicate or some filter:
128
b9e3546 @casademora Updating documentation
casademora authored
129 NSNumber *count = [Person numberOfEntitiesWithPredicate:...];
130
131 There are also counterpart methods which return NSUInteger rather than NSNumbers:
132
133 * countOfEntities
134 * countOfEntitiesWithContext:(NSManagedObjectContext *)
135 * countOfEntitiesWithPredicate:(NSPredicate *)
136 * countOfEntitiesWithPredicate:(NSPredicate *) inContext:(NSManagedObjectContext *)
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
137
26d74ec @casademora Adding aggregate operation support submitted from Duane Fields. Thank…
casademora authored
138 #### Aggregate Operations
139
140 NSPredicate *prediate = [NSPredicate predicateWithFormat:@"diaryEntry.date == %@", today];
141 int totalFat = [[CTFoodDiaryEntry aggregateOperation:@"sum:" onAttribute:@"fatColories" withPredicate:predicate] intValue];
142 int fattest = [[CTFoodDiaryEntry aggregateOperation:@"max:" onAttribute:@"fatColories" withPredicate:predicate] intValue];
143
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
144 #### Finding from a different context
145
146 All find, fetch and request methods have an inContext: method parameter
147
148 NSManagedObjectContext *someOtherContext = ...;
149
150 NSArray *peopleFromAnotherContext = [Person findAllInContext:someOtherContext];
151
152 ...
153
154 Person *personFromContext = [Person findFirstByAttribute:@"lastName" withValue:@"Gump" inContext:someOtherContext];
155
156 ...
157
158 NSUInteger count = [Person numberOfEntitiesWithContext:someOtherContext];
159
160
161 ## Creating new Entities
162
163 When you need to create a new instance of an Entity, use:
164
165 Person *myNewPersonInstance = [Person createEntity];
166
167 or, to specify a context:
168
169 NSManagedObjectContext *otherContext = ...;
170
171 Person *myPerson = [Person createInContext:otherContext];
172
173
174 ## Deleting Entities
175
176 To delete a single entity:
177
178 Person *p = ...;
179 [p deleteEntity];
180
181 or, to specify a context:
182
183 NSManagedObjectContext *otherContext = ...;
184 Person *deleteMe = ...;
185
186 [deleteMe deleteInContext:otherContext];
187
188 There is no delete *All Entities* or *truncate* operation in core data, so one is provided for you with Active Record for Core Data:
189
190 [Person truncateAll];
191
192 or, with a specific context:
193
194 NSManagedObjectContext *otherContext = ...;
195 [Person truncateAllInContext:otherContext];
196
197 ## Performing Core Data operations on Threads
198
199 Available only on iOS 4.0 and Mac OS 10.6
200
201 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:
202
203 * Use a new, dedicated NSManagedObjectContext instance for every thread
204 * Use an instance of your NSManagedObjects that is local for the new NSManagedObjectContext
205 * Notify other contexts that the background is updated or saved
206
1a4f8d6 @casademora Removing references to ActiveRecord, renaming to MagicalRecord
casademora authored
207 The Magical Record library is trying to make these steps more reusable with the following methods:
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
208
209 + (void) performSaveDataOperationWithBlock:(CoreDataBlock)block;
210 + (void) performSaveDataOperationInBackgroundWithBlock:(CoreDataBlock)block;
211
212 CoreDataBlock is typedef'd as:
213
214 typedef void (^CoreDataBlock)(NSManagedObjectContext *);
215
216 All the boilerplate operations that need to be done when saving are done in these methods. To use this method from the *main thread*:
217
cce797d @casademora Update threading instructions
casademora authored
218 Person *person = ...;
6992aeb @casademora Some working tests verifying entity relationships are set properly
casademora authored
219 [MRCoreDataAction saveDataInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
cce797d @casademora Update threading instructions
casademora authored
220 Person *localPerson = [person inContext:localContext];
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
221
222 localPerson.firstName = @"Chuck";
223 localPerson.lastName = @"Smith";
224 }];
225
226 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.
cce797d @casademora Update threading instructions
casademora authored
227
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
228 To perform an action after this save block is completed, you can fill in a completion block:
229
230 Person *person = ...;
231 [MRCoreDataAction saveDataInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
232 Person *localPerson = [person inContext:localContext];
233
234 localPerson.firstName = @"Chuck";
235 localPerson.lastName = @"Smith";
236 } completion:^{
237
238 self.everyoneInTheDepartment = [Person findAll];
239 }];
240
241 This completion block is called on the main thread (queue), so this is also safe for triggering UI updates.
242
6992aeb @casademora Some working tests verifying entity relationships are set properly
casademora authored
243 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.
d66b419 @casademora Updated some docs
casademora authored
244
245 # Data Import
246
247 *Experimental*
248
5470cc6 @casademora Made the default managed object context section more clear.
casademora authored
249 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.
250 This feature is currently under development, and is undergoing updates. Feel free to try it out, add tests and send in your feedback.
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
251
252 # Extra Bits
253 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.