Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 197 lines (108 sloc) 8.444 kB
dc6ab2c @casademora updated readme, changed to markdown
casademora authored
1 # ActiveRecord Fetching for Core Data
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
9 Active Record for Core Data was inspired by the ease of Ruby on Rails' Active Record fetching. The goals of this code are:
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
17 In your XCode Project, add all the .h and .m files into your project.
18 Add the proper import states for the .h files either to your specific files using Core Data, or in your pre-compiled header file
19 Start writing code! ... There is no step 3!
20
21 # Usage
22
23 ## Setting up the Core Data Stack
24
25 To get started, first, import the header file *CoreData+ActiveRecordFetching.h* in your project's pch file. This will allow a global include of all the required headers.
26 Next, somewhere in your app's startup, say in the applicationDidFinishLaunching:(UIApplication *) withOptions:(NSDictionary *) method, use one of the following setup calls with the ActiveRecordHelpers class:
27
28 + (void) setupCoreDataStack;
29 + (void) setupAutoMigratingDefaultCoreDataStack;
30 + (void) setupCoreDataStackWithInMemoryStore;
31 + (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName;
32 + (void) setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName;
33
34 And, before your app exits, you can use the clean up method:
35
36 [ActiveRecordHelpers cleanUp];
37
38 ### Default Managed Object Context
39
40 When using Core Data, you will deal with two types of objects the most: NSManagedObject and NSManagedObjectContext. ActiveRecord for Core Data gives you a place for a default NSManagedObjectContext for use within your app. This is great for single threaded apps. If you need to create a new Managed Object Context for use in other threads, based on your single persistent store, use:
41
42 NSManagedObjectContext *myNewContext = [NSManagedObjectContext context];
43
44
45 This default context will be used for all fetch requests, unless otherwise specified in the methods ending with **inContext:**.
46 If you want to make *myNewContext* the default for all fetch requests on the main thread:
47
48 [NSManagedObjectContext setDefaultContext:myNewContext];
49
50
51 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.
52
53 **It is recommended that the default context is created and set using the main thread**
54
55 ### Fetching
56
57 #### Basic Finding
58 Most methods in the ActiveRecord for Core Data library 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:
59
60
61 NSArray *people = [Person findAll];
62
63
64 Or, to have the results sorted by a property:
65
66 NSArray *peopleSorted = [Person findAllSortedByProperty:@"LastName" ascending:YES];
67
68
69 If you have a unique way of retrieving a single object from your data store, you can get that object directly:
70
71 Person *person = [Person findFirstByAttribute:@"FirstName" withValue:@"Forrest"];
72
73 #### Advanced Finding
74
75 If you want to be more specific with your search, you can send in a predicate:
76
77 NSArray *departments = [NSArray arrayWithObjects:dept1, dept2, ..., nil];
78 NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
79
80 NSArray *people = [Person findAllWithPredicate:peopleFilter];
81
82 Returning an NSFetchRequest
83
84 NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
85
86 NSArray *people = [Person fetchAllWithPredicate:peopleFilter];
87
88 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.
89
90 Customizing the Request
91
92 NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
93
94 NSFetchRequest *peopleRequest = [Person requestAllWithPredicate:peopleFilter];
95 [peopleRequest setReturnsDistinctResults:NO];
96 [peopleRequest setReturnPropertiesNamed:[NSArray arrayWithObjects:@"FirstName", @"LastName", nil]];
97 ...
98
99 NSArray *people = [Person executeFetchRequest:peopleRequest];
100
101 #### Find the number of entities
102
103 You can also perform a count of entities in your Store, that will be performed on the Store
104
105 NSUInteger count = [Person numberOfEntities];
106
107 Or, if you're looking for a count of entities based on a predicate or some filter:
108
109 NSUInteger count = [Person numberOfEntitiesWithPredicate:...];
110
111 #### Finding from a different context
112
113 All find, fetch and request methods have an inContext: method parameter
114
115 NSManagedObjectContext *someOtherContext = ...;
116
117 NSArray *peopleFromAnotherContext = [Person findAllInContext:someOtherContext];
118
119 ...
120
121 Person *personFromContext = [Person findFirstByAttribute:@"lastName" withValue:@"Gump" inContext:someOtherContext];
122
123 ...
124
125 NSUInteger count = [Person numberOfEntitiesWithContext:someOtherContext];
126
127
128 ## Creating new Entities
129
130 When you need to create a new instance of an Entity, use:
131
132 Person *myNewPersonInstance = [Person createEntity];
133
134 or, to specify a context:
135
136 NSManagedObjectContext *otherContext = ...;
137
138 Person *myPerson = [Person createInContext:otherContext];
139
140
141 ## Deleting Entities
142
143 To delete a single entity:
144
145 Person *p = ...;
146 [p deleteEntity];
147
148 or, to specify a context:
149
150 NSManagedObjectContext *otherContext = ...;
151 Person *deleteMe = ...;
152
153 [deleteMe deleteInContext:otherContext];
154
155 There is no delete *All Entities* or *truncate* operation in core data, so one is provided for you with Active Record for Core Data:
156
157 [Person truncateAll];
158
159 or, with a specific context:
160
161 NSManagedObjectContext *otherContext = ...;
162 [Person truncateAllInContext:otherContext];
163
164 ## Performing Core Data operations on Threads
165
166 Available only on iOS 4.0 and Mac OS 10.6
167
168 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:
169
170 * Use a new, dedicated NSManagedObjectContext instance for every thread
171 * Use an instance of your NSManagedObjects that is local for the new NSManagedObjectContext
172 * Notify other contexts that the background is updated or saved
173
174 The Active Record fetching library is trying to make these steps more reusable with the following methods:
175
176 + (void) performSaveDataOperationWithBlock:(CoreDataBlock)block;
177 + (void) performSaveDataOperationInBackgroundWithBlock:(CoreDataBlock)block;
178
179 CoreDataBlock is typedef'd as:
180
181 typedef void (^CoreDataBlock)(NSManagedObjectContext *);
182
183 All the boilerplate operations that need to be done when saving are done in these methods. To use this method from the *main thread*:
184
185 Person *p = ...;
186 [ActiveRecordHelpers performSaveDataOperationWithBlock:^(NSManagedObjectContext *localContext){
187 Person *localPerson = [localContext objectWithID:p.objectID];
188
189 localPerson.firstName = @"Chuck";
190 localPerson.lastName = @"Smith";
191 }];
192
193 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.
194
195 # Extra Bits
196 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.