Introducing Core Data763sqlite .dump BEGIN TRANSACTION; CREATE TABLE ZDEPARTMENT ( Z_PK INTEGER doc

96 316 0
Introducing Core Data763sqlite .dump BEGIN TRANSACTION; CREATE TABLE ZDEPARTMENT ( Z_PK INTEGER doc

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

ptg 763 Introducing Core Data sqlite> .dump BEGIN TRANSACTION; CREATE TABLE ZDEPARTMENT ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZMANAGER INTEGER, ZGROUPNAME VARCHAR ); INSERT INTO "ZDEPARTMENT" VALUES(1,1,1,1,’Office of Personnel Management’); CREATE TABLE ZPERSON ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZDEPARTMENT INTEGER, ZBIRTHDAY TIMESTAMP, ZNAME VARCHAR ); INSERT INTO "ZPERSON" VALUES(1,2,1,1,-3126877200,’John Smith’); INSERT INTO "ZPERSON" VALUES(2,2,1,1,-2484234000,’Jane Doe’); CREATE TABLE Z_PRIMARYKEY (Z_ENT INTEGER PRIMARY KEY, Z_NAME VARCHAR, Z_SUPER INTEGER, Z_MAX INTEGER); INSERT INTO "Z_PRIMARYKEY" VALUES(1,’Department’,0,1); INSERT INTO "Z_PRIMARYKEY" VALUES(2,’Person’,0,2); CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY KEY, Z_UUID VARCHAR(255), Z_PLIST BLOB); INSERT INTO "Z_METADATA" VALUES(1,’A4ADDA90-5C26-4E01-8E68- 1C4BB7A910B1’,X’62706C6973743030D60102030405060708090A0B105F10204E5353746F72654D6F 64656C56657273696F6E48617368657356657273696F6E5F101E4E5353746F72654D6F64656C566572 73696F6E4964656E746966696572735B4E5353746F7265547970655F101D4E5350657273697374656E 63654672616D65776F726B56657273696F6E5F10194E5353746F72654D6F64656C56657273696F6E48 61736865735F10125F4E534175746F56616375756D4C6576656C1003A05653514C69746510F1D20C0D 0E0F5A4465706172746D656E7456506572736F6E4F10203B34C3D1DAC08316B2656664A26C9EAC82FE 04E4C34FC75A3E0981E678F3909B4F1020DE27A38E814A2A5F72418573563732F83CBC1CACAADF39FA 559420B155E5A973513200080015003800590065008500A100B600B800B900C000C200C700D200D900 FC011F0000000000000201000000000000001100000000000000000000000000000121’); CREATE INDEX ZDEPARTMENT_ZMANAGER_INDEX ON ZDEPARTMENT (ZMANAGER); CREATE INDEX ZPERSON_ZDEPARTMENT_INDEX ON ZPERSON (ZDEPARTMENT); COMMIT; sqlite> Querying the Data Base Retrieve objects from the database by performing fetch requests.A fetch request describes your search criteria. It’s passed through and used to initialize a results object that contains a pointer to the managed objects that meet those criteria.The results controller executes the fetch before passing back the array of managed object results. The following fetchObjects method creates a new request, setting its entity type to Person.This search looks for Person objects in the shared managed store. Each request must contain at least one sort descriptor. For this example, the search returns a list of Person records sorted in ascending order by their name field.Although you can produce more complicated queries, this example shows the simplest “please return all managed items of a given type” request. - (void) fetchObjects { // Create a basic fetch request NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; [fetchRequest setEntity:[NSEntityDescription entityForName:@"Person" inManagedObjectContext:self.context]]; ptg 764 Chapter 19 A Taste of Core Data // Add a sort descriptor. Mandatory. NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:nil]; NSArray *descriptors = [NSArray arrayWithObject:sortDescriptor]; [fetchRequest setSortDescriptors:descriptors]; [sortDescriptor release]; // Init the fetched results controller NSError *error; self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.context sectionNameKeyPath:nil cacheName:@"Root"]; if (![self.fetchedResultsController performFetch:&error]) NSLog(@"Error %@", [error localizedDescription]); [self.fetchedResultsController release]; [fetchRequest release]; } - (void) action: (UIBarButtonItem *) bbi { [self fetchObjects]; for (Person *person in self.fetchedResultsController.fetchedObjects) NSLog(@"%@ : %@", person.name, person.department.groupName); } The fetch request is used to initialize an NSFetchedResultsController object.This class manages the results returned from a Core Data fetch.The results controller is kept on hand via a retained class property (self.fetchedResultsController). Fetch results pro- vide concrete access to the data model objects.After fetching the data, this action: method lists out each person by name and department. It uses the results’ fetchedObjects property to do so. Detecting Changes Fetched results might be used as a table data source or to fill out an object settings form, or for any other purpose you might think of.Whether you’re retrieving just one object or many, the fetched results controller offers you direct access to those managed objects on request. So how do you make sure that your fetched data remains current? After adding new objects or otherwise changing the data store, you want to fetch a fresh set of results. Sub- scribe to the results controller’s controllerDidChangeContent: callback.This method notifies your class when changes affect your fetched objects.To subscribe, declare the ptg 765 Introducing Core Data NSFetchedResultsControllerDelegate protocol and assign the controller’s delegate as follows.After setting the results’ delegate, you receive a callback each time the data store updates. self.fetchedResultsController.delegate = self. Removing Objects Removing objects, especially those that use relationships in addition to simple properties, can prove harder than you might first expect. Consider the following code. It goes through each person in the fetched object results and deletes them before saving the con- text.This method fails as it tries to save the context to file. - (void) removeObjects { NSError *error = nil; for (Person *person in self.fetchedResultsController.fetchedObjects) [self.context deleteObject:person]; if (![self.context save:&error]) NSLog(@"Error %@ (%@)", [error localizedDescription]); [self fetchObjects]; } That’s because Core Data ensures internal consistency before writing data out, throwing an error if it cannot.The managed model from Figure 19-1 uses several cross-references. Each person may belong to a department, which stores a list of its members and its manager.These references must be cleared before the object can safely be removed from the persistent store. If not, objects may point to deleted items, a situation that can lead to bad references. The following is another version of the same method, one that saves without errors. This updated version removes all references from the department object. It checks whether a person is a manager, removing that connection if it exists. It also filters the per- son out of its department members using a predicate to return an updated set. Once these connections are removed, the context will save out properly. - (void) removeObjects { NSError *error = nil; if (!self.fetchedResultsController.fetchedObjects.count) { NSLog(@"No one to delete"); return; } ptg 766 Chapter 19 A Taste of Core Data // Remove each person for (Person *person in self.fetchedResultsController.fetchedObjects) { // Remove person as manager if necessary if (person.department.manager == person) person.department.manager = nil; // Remove person from department NSPredicate *pred = [NSPredicate predicateWithFormat: @"SELF != %@", person]; if (person.department.members) person.department.members = [person.department.members filteredSetUsingPredicate:pred]; // Delete the person object [self.context deleteObject:person]; } // Save if (![self.context save:&error]) NSLog(@"Error %@", [error localizedDescription]); } In addition to this kind of manual disconnection, you can set Core Data delete rules in the data model editor. Delete rules control how an object responds to an attempted delete.You can Deny delete requests, ensuring that a relationship has no connection before allowing object deletion. Nullify resets inverse relationships before deleting an object. Cascade deletes an object plus all its relationships; for example, you could delete an entire department (including its members) all at once with a cascade. No Action provides that the objects pointed to by a relationship remain unaffected, even if those objects point back to the item about to be deleted. In the sample code that accompanies this chapter, the introductory project (essentially Recipe 0 for this chapter) nullifies its connections.The department/members relationship represents an inverse relationship. By using Nullify, the default delete rule, you do not need to remove the member from the department list before deleting a person. On the other hand, the department’s manager relationship is not reciprocal.As there is no inverse relationship, you cannot delete objects without resetting that manager.Taking these delete rules into account, the remove objects method for this example can be short- ened to the following. - (void) removeObjects { NSError *error = nil; // Remove all people (if they exist) ptg 767 Recipe: Using Core Data for a Table Data Source [self fetchObjects]; if (!self.fetchedResultsController.fetchedObjects.count) { NSLog(@"No one to delete"); return; } // Remove each person for (Person *person in self.fetchedResultsController.fetchedObjects) { // Remove person as manager if necessary if (person.department.manager == person) person.department.manager = nil; // Delete the person object [self.context deleteObject:person]; } // Save if (![self.context save:&error]) NSLog(@"Error %@", [error localizedDescription]); } Xcode issues warnings when it detects nonreciprocal relationships.Avoid these unbal- anced relationships to simplify your code and provide better internal consistency. If you cannot avoid nonreciprocal items, you need to take them into account when you create your delete methods, as was done here. Recipe: Using Core Data for a Table Data Source Core Data on the iPhone works closely with table views.The NSFetchedResults ➥Controller class includes features that simplify the integration of Core Data objects with table data sources.As you can see in the following list, many of the fetched results class’s properties and methods are designed for table support. n Index path access—The fetched results class offers object-index path integration in two directions.You can recover objects from a fetched object array using index paths by calling objectAtIndexPath:.You can query for the index path associated with a fetched object by calling indexPathForObject:.These two methods work with both sectioned tables and those tables that are flat—that is, that only use a single section for all their data. n Section key path—The sectionNameKeyPath property links a managed object attribute to section names.This property helps determine which section each managed object belongs to.You can set this property directly at any time or you initialize it when you set up your fetched results controller. ptg 768 Chapter 19 A Taste of Core Data Recipe 19-1 uses an attribute named section to distinguish sections, although you can use any attribute name for this key path. For this example, this attribute uses the first character of each object name to assign a managed object to a section. Set the key path to nil to produce a flat table without sections. n Section groups—Recover section subgroups with the sections property.This property returns an array of sections, each of which stores the managed objects whose section attribute maps to the same letter. Each returned section implements the NSFetchedResultsSectionInfo protocol. This protocol ensures that sections can report their objects and numberOfObjects, their name, and an indexTitle, that is, the title that appears on the quick reference index optionally shown above and at the right of the table. n Index titles—The sectionIndexTitles property generates a list of section titles from the sections within the fetched data. For Recipe 19-1, that array includes sin- gle letter titles.The default implementation uses the value of each section key to return a list of all known sections. Two further instance methods, sectionIndexTitleForSectionName: and sectionForSectionIndexTitle:atIndex:, provide section title lookup features. The first returns a title for a section name.The second looks up a section via its title. Override these to use section titles that do not match the data stored in the section name key. As these properties and methods reveal, fetched results instances are both table aware and table ready for use. Recipe 19-1 uses these features to duplicate the indexed color name table first introduced in Chapter 11,“Creating and Managing Table Views.”The code in this recipe recovers data from the fetched results using index paths, as shown in the method that produces a cell for a given row and the method that tints the navigation bar with the color from the selected row. Each method used for creating and managing sections is tiny.The built-in Core Data access features reduce these methods to one or two lines each.That’s because all the work in creating and accessing the sections is handed over directly to Core Data.The call that initializes each fetched data request specifies what data attribute to use for the sections. Core Data then takes over and does the rest of the work. self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.context sectionNameKeyPath:@"section" cacheName:@"Root"]; Caching reduces overhead associated with producing data that’s structured with sections and indices. Multiple fetch requests are ignored when the data has not changed, minimiz- ing the cost associated with fetch requests over the lifetime of an application.The name used for the cache is completely arbitrary. Either use nil to prevent caching or supply a ptg 769 Recipe: Using Core Data for a Table Data Source name in the form of an NSString.The snippet above uses " Root " , but there’s no reason you can’t use another string. Recipe 19-1 Building a Sectioned Table with Core Data - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Retrieve or create a cell UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"basic cell"]; if (!cell) cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"basic cell"] autorelease]; // Recover object from fetched results NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath]; cell.textLabel.text = [managedObject valueForKey:@"name"]; UIColor *color = [self getColor:[managedObject valueForKey:@"color"]]; cell.textLabel.textColor = ([[managedObject valueForKey:@"color"] hasPrefix:@"FFFFFF"]) ? [UIColor blackColor] : color; return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // When a row is selected, color the navigation bar accordingly NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath]; UIColor *color = [self getColor:[managedObject valueForKey:@"color"]]; self.navigationController.navigationBar.tintColor = color; } #pragma mark Sections - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Use the fetched results section count return [[self.fetchedResultsController sections] count]; } ptg 770 Chapter 19 A Taste of Core Data - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the count for each section return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects]; } - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)aTableView { // Return the array of section index titles return self.fetchedResultsController.sectionIndexTitles; } - (NSString *)tableView:(UITableView *)aTableView titleForHeaderInSection:(NSInteger)section { // Return the title for a given section NSArray *titles = [self.fetchedResultsController sectionIndexTitles]; if (titles.count <= section) return @"Error"; return [titles objectAtIndex:section]; } - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { // Query the titles for the section associated with an index title return [self.fetchedResultsController.sectionIndexTitles indexOfObject:title]; } Get This Recipe’s Code To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 19 and open the project for this recipe. Recipe: Search Tables and Core Data Core Data stores are designed to work efficiently with NSPredicates. Predicates allow you to create fetch requests that select only those managed objects that match the predi- cate’s rule or rules.Adding a predicate to a fetch request limits the fetched results to matching objects. Recipe 19-2 adapts the search table from Recipe 11-16 to build a Core Data-based solution.This recipe uses a search bar to select data from the persistent store, displaying the results in a table’s search sheet. Figure 19-2 shows a search in progress. ptg 771 Recipe: Search Tables and Core Data Figure 19-2 To power this search table with Core Data, the fetched results must update each time the text in the search box changes. As the text in the search bar at the top of the table changes, the search bar’s delegate receives a searchBar:textDidChange: callback. In turn, that method performs a new fetch. Recipe 11-16 shows that fetch method, which builds a restrictive predicate. The recipe’s performFetch method creates that simple predicate based on the text in the search bar. It sets the request’s predicate property to limit matches to names that contain the text, using a case insensitive match. contains matches text anywhere in a string.The [cd] after contains refers to case and diacritic insensitive matching. Diacritics are small marks that accompany a letter, such as the dots of an umlaut or the tilde above a Spanish n. For more complex queries, assign a compound predicate. Compound predicates allow you to combine simple predicates together using standard logical operations like AND, OR, and NOT. Use the NSCompoundPredicate class to build a compound predicate out of a series of component predicates, or include the AND, OR, and NOT notation directly in NSPredicate text, as was done in Chapter 18,“Connecting to the Address Book.” None of the methods from Recipe 19-1 need updating for use with Recipe 19-2’s performFetch method.All the cell and section methods are tied to the results object and its properties, simplifying implementation even when adding these search table features. ptg 772 Chapter 19 A Taste of Core Data Recipe 19-2 Using Fetch Requests with Predicates - (void) performFetch { // Init a fetch request NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Crayon" inManagedObjectContext:self.context]; [fetchRequest setEntity:entity]; // Apply an ascending sort for the color items NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:nil]; NSArray *descriptors = [NSArray arrayWithObject:sortDescriptor]; [fetchRequest setSortDescriptors:descriptors]; // Recover query NSString *query = self.searchBar.text; if (query && query.length) fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name contains[cd] %@", query]; // Init the fetched results controller NSError *error; self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.context sectionNameKeyPath:@"section" cacheName:@"Root"]; self.fetchedResultsController.delegate = self; [self.fetchedResultsController release]; if (![[self fetchedResultsController] performFetch:&error]) NSLog(@"Error %@", [error localizedDescription]); [fetchRequest release]; [sortDescriptor release]; } Get This Recipe’s Code To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 19 and open the project for this recipe. [...]... reordering allowed } - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { // Delete request if (editingStyle == UITableViewCellEditingStyleDelete) { NSError *error = nil; [self.context deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]]; if (! [self.context save:&error]) NSLog(@"Error %@", [error... deselectRowAtIndexPath: [self.tableView indexPathForSelectedRow] animated:YES]; [self.tableView setEditing:YES animated:YES]; [self setBarButtonItems]; } 773 774 Chapter 19 A Taste of Core Data -(void)leaveEditMode { // Finish editing [self.tableView setEditing:NO animated:YES]; [self setBarButtonItems]; } - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { return... courtesy of Core Data Be aware that any undo/redo data will not survive quitting your application.This works just as you’d expect with manual undo/redo support Recipe 19-4 Expanding Cell Management for Undo/Redo Support - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { [self.context.undoManager beginUndoGrouping];... Callback Methods - (void)request:(SKRequest *)request didFailWithError:(NSError *)error { [self doLog: @"Error: Could not contact App Store properly, %@", [error localizedDescription]]; } - (void)requestDidFinish:(SKRequest *)request { // Release the request [request release]; [self doLog:@"Request finished."]; } - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse...Recipe: Integrating Core Data Tables with Live Data Edits Recipe: Integrating Core Data Tables with Live Data Edits Recipe 19-3 demonstrates how to move basic table editing tasks into the Core Data world Its code is based on the basic edits of Recipe 11-12.There are, however, real changes that must be made to provide its Core Data solution.These changes include the following... handleFailedTransaction: (SKPaymentTransaction *) transaction { if (transaction.error.code != SKErrorPaymentCancelled) [ModalAlert say:@"Transaction Error Please try again later."]; [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; } - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState)... add methods for the table. To make this happen, it brackets the core data access with an undo grouping.The beginUndoGrouping and endUndoGrouping calls appear before and after the context updates and saves with changes.An action name describes the operation that just took place These three calls (begin, undo, and setting the action name) comprise all the work needed to ensure that Core Data can reverse... Core Data interaction simplifies the integration between the data model and the user interface.And that’s due in large part to Apple’s thoughtful class designs that handle the managed object responsibilities Recipe 19-3 highlights this design, showcasing the code parsimony that results from using Core Data Recipe 19-3 Adapting Table Edits to Core Data -(void)enterEditMode { // Start editing [self.tableView... Support with Core Data item.sectionName = [[todoAction substringToIndex:1] uppercaseString]; // Save the new item NSError *error; if (! [self.context save:&error]) NSLog(@"Error %@", [error localizedDescription]); // Update buttons after add [self setBarButtonItems]; // Update sections [self performFetch]; } - (void)controllerDidChangeContent: (NSFetchedResultsController *)controller { // Update table when... Responding to Payments - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions { } - (void) completedPurchaseTransaction: (SKPaymentTransaction *) transaction { // PERFORM THE SUCCESS ACTION THAT UNLOCKS THE FEATURE HERE // Finish transaction [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; [ModalAlert say:@"Thank you for your purchase."]; } - (void) handleFailedTransaction: . ptg 763 Introducing Core Data sqlite> .dump BEGIN TRANSACTION; CREATE TABLE ZDEPARTMENT ( Z_ PK INTEGER PRIMARY KEY, Z_ ENT INTEGER, Z_ OPT INTEGER, ZMANAGER INTEGER, ZGROUPNAME VARCHAR. " ;ZDEPARTMENT& quot; VALUES(1,1,1,1,’Office of Personnel Management’); CREATE TABLE ZPERSON ( Z_ PK INTEGER PRIMARY KEY, Z_ ENT INTEGER, Z_ OPT INTEGER, ZDEPARTMENT INTEGER, ZBIRTHDAY TIMESTAMP, ZNAME. VALUES(1,’A4ADDA90-5C26-4E01-8E68- 1C4BB7A910B1’,X’62706C6973743030D60102030405060708090A0B105F10204E5353746F72654D6F 64656C56657273696F6E48617368657356657273696F6E5F101E4E5353746F72654D6F64656C566572 73696F6E4964656E746966696572735B4E5353746F7265547970655F101D4E5350657273697374656E 63654672616D65776F726B56657273696F6E5F10194E5353746F72654D6F64656C56657273696F6E48 61736865735F10125F4E534175746F56616375756D4C6576656C1003A05653514C69746510F1D20C0D 0E0F5A4465706172746D656E7456506572736F6E4F10203B34C3D1DAC08316B2656664A26C9EAC82FE 04E4C34FC75A3E0981E678F3909B4F1020DE27A38E814A2A5F72418573563732F83CBC1CACAADF39FA 559420B155E5A973513200080015003800590065008500A100B600B800B900C000C200C700D200D900 FC011F0000000000000201000000000000001100000000000000000000000000000121’); CREATE INDEX ZDEPARTMENT_ ZMANAGER_INDEX ON ZDEPARTMENT (ZMANAGER); CREATE INDEX ZPERSON _ZDEPARTMENT_ INDEX ON ZPERSON (ZDEPARTMENT) ; COMMIT; sqlite> Querying

Ngày đăng: 13/08/2014, 18:20

Mục lục

    19 A Taste of Core Data

    Recipe: Using Core Data for a Table Data Source

    Recipe: Search Tables and Core Data

    Recipe: Integrating Core Data Tables with Live Data Edits

    Recipe: Implementing Undo-Redo Support with Core Data

    Getting Started with StoreKit

    Creating New In-App Purchase Items

    21 Accessibility and Other iPhone OS Services

    Adding VoiceOver Accessibility to Your Apps

    Recipe: Adding Custom Settings Bundles

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan