iPhone SDK 3 Programming Advanced Mobile Development for Apple iPhone and iPod touc phần 8 ppsx

68 213 0
iPhone SDK 3 Programming Advanced Mobile Development for Apple iPhone and iPod touc phần 8 ppsx

Đ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

Custom UI Components 455 Since the UI component instance is the delegate of the alert view, it defines the alertView:clickedButtonAtIndex: We simply cancel the thread by sending a cancel message to it - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ [thread cancel]; } The isCancelled simply returns the cancellation status of the thread -(BOOL)isCancelled{ return [thread isCancelled]; } Using this UI component is simple The following code fragment creates the ProgressAlertView instance, sets its delegate to self, sets its task to the method compute and sends it a start message self.progress = [[[ProgressAlertView alloc] init] autorelease]; progress.delegate = self; progress.task = @selector(compute); [progress start]; Our demo compute method (shown in Listing 16.10) doesn’t anything useful It simply fakes several computation phases and updates the view at the end of each phase The delay is achieved using the sleepForTimeInterval: NSThread class method When this method returns, the progress alert view disappears Listing 16.10 The compute method used in demonstrating the usage of the ProgressAlertView component -(void)compute{ [self updateUIWithProgress:0.0 andMessage:@"Initializing "]; [NSThread sleepForTimeInterval:1]; if([progress isCancelled]){ self.progress = nil; return; } [self updateUIWithProgress:0.2 andMessage:@"Preparing data "]; [NSThread sleepForTimeInterval:2]; if([progress isCancelled]){ self.progress = nil; return; } [self updateUIWithProgress:0.4 andMessage:@"Crunching numbers "]; [NSThread sleepForTimeInterval:1]; if([progress isCancelled]){ self.progress = nil; 456 iPhone SDK Programming return; } [self updateUIWithProgress:0.8 andMessage:@"Almost done!"]; [NSThread sleepForTimeInterval:2]; if([progress isCancelled]){ self.progress = nil; return; } [self updateUIWithProgress:1.0 andMessage:@"Done!"]; } The following is a convenience method for updating the UI -(void)updateUIWithProgress:(float)_progress andMessage:(NSString*)_message{ NSMutableDictionary *_progressData = [NSMutableDictionary dictionary]; [_progressData setObject:[NSNumber numberWithFloat:_progress] forKey:PROGRESS_PERCENTAGE_KEY]; [_progressData setObject:_message forKey:PROGRESS_MESSAGE_KEY]; [progress updateProgress:_progressData]; } See the ProgressAlertView project in the source downloads for a complete application utilizing this component 16.4 Summary The SDK provides basic UI components for everyday use Sometimes during the developments of your projects, you need a UI component that is not provided by the SDK In this chapter, we showed how to marry various UI components and build custom reusable ones First, we showed how to build an alert view with a text field in it, in Section 16.1 Next, Section 16.2 presented a table view inside an alert view Finally, Section 16.3 showed how to build a progress alert view Problems (1) Build a tabbed control UI component For each tab, the user should be able to provide just the title and the view In addition, an optional title for the control should be accommodated The UI component manages the switching from one view to the other The UI component should support a variable number of tabs and variable length of the titles The UI component Custom UI Components 457 should support scrolling if a given view has a height that exceeds the available space for it See Figure 16.6 for an example Figure 16.6 The tabbed control UI component 17 Advanced Networking This chapter addresses several advanced networking topics We start by looking in Section 17.1 at how we can determine the network connectivity of the device This is important for several reasons First, your application needs to determine the status of network connectivity and alert the user of connectivity problems instead of presenting an empty view to the user In addition, some applications require WiFi connection for specific services (e.g., downloading large files) You should be able to enable such services dynamically, based on the connectivity of the device After that, we tackle the issue of uploading multimedia content (e.g., photos) to remote servers, in Section 17.2 Next, In Section 17.3, we present a category on NSString that allows you to easily compute the MD5 digest of a string This is important as some services, such as Flickr, require posting parameters with the appropriate signature Section 17.4 then shows you how to present a responsive table view whose data rows are fed from the Internet without sacrificing the user experience Next, Section 17.5 addresses the topic of push notification Section 17.6 discusses sending email from within your iPhone application Finally, Section 17.7 summarizes the chapter 17.1 Determining Network Connectivity In this section, we look at a mechanism that allows you to determine the network connectivity of the device We develop the following three methods in a category on UIDevice class: • cellularConnected This method is used to determine whether the device is connected to the network via EDGE or GPRS • wiFiConnected This method is used to determine whether the device is connected to the network on a WiFi • networkConnected This method is used to determine network connectivity in general Listing 17.1 shows the declaration of the category 460 iPhone SDK Programming Listing 17.1 A category on UIDevice for network connectivity @interface UIDevice (DeviceConnectivity) +(BOOL)cellularConnected; +(BOOL)wiFiConnected; +(BOOL)networkConnected; @end In order to use the methods in this section, you need to add the SystemConfiguration framework as explained in Section D.4 In addition, you need to add the following import statement to your code: #import 17.1.1 Determining network connectivity via EDGE or GPRS Listing 17.2 shows the cellularConnected method which determines whether the device is connected via EDGE or GPRS Listing 17.2 The cellularConnected method for determining connectivity to the network via EDGE or GPRS +(BOOL)cellularConnected{// EDGE or GPRS SCNetworkReachabilityFlags flags = 0; SCNetworkReachabilityRef netReachability; netReachability = SCNetworkReachabilityCreateWithName (CFAllocatorGetDefault(), [EXTERNAL_HOST UTF8String]); if(netReachability){ SCNetworkReachabilityGetFlags(netReachability, &flags); CFRelease(netReachability); } if(flags & kSCNetworkReachabilityFlagsIsWWAN){ return YES; } return NO; } The method first creates a network reachability object using the function SCNetworkReachabilityCreateWithName() This function is declared as follows: SCNetworkReachabilityRef SCNetworkReachabilityCreateWithName ( CFAllocatorRef allocator, const char *nodename ); Advanced Networking 461 You pass in the allocator in the first argument An allocator is used throughout Core Foundation for allocating and deallocating Core Foundation objects In our case, we just use the default allocator by obtaining it using the CFAllocatorGetDefault() function The second parameter is the node name (e.g., google.com) that you want to test reachability to After obtaining the reachability reference, the method determines network connectivity to the host by calling the SCNetworkReachabilityGetFlags() function You pass in the network reference and a reference to the flags local variable (32-bit number) The method checks the flags by looking for a in bit 18 If it is 1, that means the device is reachable via a cellular connection, such as EDGE or GPRS 17.1.2 Determining network connectivity in general Listing 17.3 shows a method that determines network connectivity in general Listing 17.3 The method networkConnected that determines network connectivity +(BOOL)networkConnected{ SCNetworkReachabilityFlags flags = 0; SCNetworkReachabilityRef netReachability; BOOL retrievedFlags = NO; netReachability = SCNetworkReachabilityCreateWithName( CFAllocatorGetDefault(), [EXTERNAL_HOST UTF8String]); if(netReachability){ retrievedFlags = SCNetworkReachabilityGetFlags(netReachability, &flags); CFRelease(netReachability); } if (!retrievedFlags || !flags){ return NO; } return YES; } It uses the same procedure above, except that we simply check that the return value is YES and flags is not If that is the case, we report network connectivity by returning YES; otherwise, we return NO 17.1.3 Determining network connectivity via WiFi Finally, the following method determines WiFi connectivity by checking for network connectivity via a transport other than EDGE/GPRS +(BOOL)wiFiConnected{ if([self cellularConnected]){ 462 iPhone SDK Programming return NO; } return [self networkConnected]; } 17.2 Uploading Multimedia Content In this section, we will see how we can use the NSURLConnection class to upload multimedia content to a remote server For demonstration purposes, we will use the example of uploading a photo along with several other parameters to a remote server The procedure presented here also applies to uploading other types of multimedia content (e.g., audio) To communicate with a server, you follow these steps: Create a request (instance of NSURLRequest class or its subclasses) Configure that request with the url (an instance of NSURL class) Specify the HTTP method (Post, Get, etc.) Set the values of some of the request headers Build the request body (instance of NSData class) and assign it to the request Use either a synchronous or asynchronous version of the NSURLConnection to send the request to the server Obtain the response from the server and act according to the HTTP status code and/or application-level return values Listing 17.4 shows a method that posts a photo to a server Listing 17.4 A method that posts a photo to a server -(void)postPhoto:(UIImage *)_photo withLat:(float)_lat withLng:(float)_lng andCaption:(NSString*)_caption{ NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString:@"http://192.168.1.100:3000/photos"]]; [req setHTTPMethod:@"POST"]; NSString *contentType = [NSString stringWithFormat: @"multipart/form-data; boundary=%@", boundary]; [req setValue:contentType forHTTPHeaderField:@"Content-type"]; NSMutableData *postBody = [NSMutableData data]; [postBody appendData:[[NSString stringWithFormat: @" %@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [self appendPhoto:_photo withParamName:@"photo[uploaded_data]" toData:postBody]; [self appendParamValue:_caption withParamName:@"photo[caption]" Advanced Networking 463 toData:postBody]; [self appendParamValue:[NSNumber numberWithFloat:_lat] withParamName:@"photo[lat]" toData:postBody]; [self appendParamValue:[NSNumber numberWithFloat:_lng] withParamName:@"photo[lng]" toData:postBody]; [postBody appendData:[[NSString stringWithFormat: @"\r\n %@ \r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [req setHTTPBody:postBody]; NSHTTPURLResponse * returnResponse = nil; NSError * returnError = nil; NSData *returnData = [NSURLConnection sendSynchronousRequest:req returningResponse:&returnResponse error:&returnError]; int statusCode = returnResponse.statusCode; NSString *str = [[[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding] autorelease]; if([str isEqualToString:@"OK"] &&(statusCode == 200) && !returnError){ NSLog(@"Photo uploaded successfully! Status code: 200"); } else{ NSLog(@"Failed to upload photo error: %@ Status code: %d Error:%@", str, statusCode, [returnError localizedDescription]); } } The first argument is the photo in a UIImage instance The second and third arguments are the latitude and longitude, respectively The fourth and last argument is the caption for this photo First, the method creates a mutable request and initializes it with an NSURL instance representing the post address In this example, we are posting to a Rails server available from the source downloads Nevertheless, this method should generally work with any other service (such as Flickr) with minimal changes to accommodate the service Next, we set the HTTP method type to Post and the Content-type to: multipart/form-data; boundary= 75023658052007 The value for the boundary should be a unique pattern that does not occur within the post data.1 After that, we add the parts of this post one after the other to a mutable NSData object The separators are needed between these parameters and should be used literally, otherwise the post will be invalid Listing 17.5 shows the method used to add the caption, and the geo-data to a post body by the postPhoto:withLat:withLng:andCaption: method RFC1867 – Form-based File Upload in HTML, http://www.faqs.org/rfcs/rfc1867.html 464 iPhone SDK Programming Listing 17.5 A method that adds a parameter to a post data -(void)appendParamValue:(id)_value withParamName:(NSString*)_param toData:(NSMutableData*)_data{ NSString *_tmp = [NSString stringWithFormat: @"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", _param]; [_data appendData:[[NSString stringWithFormat: @"\r\n %@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [_data appendData:[_tmp dataUsingEncoding:NSUTF8StringEncoding]]; [_data appendData:[[[_value description] urlEncodedVersion] dataUsingEncoding:NSUTF8StringEncoding]]; } Again, the separator is defined by the RFC and should be used as is The NSNumber and NSString classes define the description method in a way suitable for our purpose If you would like to use this method to add a parameter of different type, make sure that its description method is defined in the correct way Listing 17.6 shows the method for adding a photo parameter to the post body Listing 17.6 Appending a photo to a post body after compression -(void)appendPhoto:(UIImage*)_photo withParamName:(NSString*)_param toData:(NSMutableData*)_data{ NSData *_photoData = UIImageJPEGRepresentation(_photo, 0.6); NSString *_tmp = [NSString stringWithFormat: @"Content-Disposition: form-data; name=\"%@\"; filename=\"p.jpg\"\r\n", _param]; [_data appendData:[_tmp dataUsingEncoding:NSUTF8StringEncoding]]; [_data appendData:[@"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [_data appendData:_photoData]; } The method first compresses the image to a reasonable size; the post of an iPhone 320 × 320 photo should take around 10 seconds over EDGE The compressed data is then appended to the post body (after the headers, of course) One last comment about communicating with a server You need to encode the data Listing 17.7 shows a category on NSString to allow strings to produce encoded versions of their content The method urlEncodedVersion simply replaces all occurrences of an encodable character with the encoded version of that character Listing 17.7 A category on NSString to extend strings with the ability to produce encoded versions of themselves @interface NSString(URL_ENCODE) -(NSString *)urlEncodedVersion; @end 508 iPhone SDK Programming Figure 18.2 People picker controller showing a record details If you want the default action to be performed (e.g., dialing a phone number), you should return YES and possibly dismiss the controller If, on the other hand, you’re just interested in the value the user has selected, you should return NO and dismiss the controller The method is passed the person record and the value identifier in a multi-value property that was selected For example, if you show phone numbers, and the person record contains three phone numbers, you get to know which of these phone numbers was selected (by its unique identifier) The following shows a possible implementation ABMultiValueRef phoneProperty = ABRecordCopyValue(person, property); NSInteger phoneIndex = ABMultiValueGetIndexForIdentifier(phoneProperty, identifier); NSString *phone = (NSString *)ABMultiValueCopyValueAtIndex(phoneProperty, phoneIndex); NSLog(@"The phone number selected is %@:", phone); CFRelease(phoneProperty); [phone release]; [self dismissModalViewControllerAnimated:YES]; return NO; Working with the Address Book Database 509 In the above method, we retrieve the value of the property Since this property is multi-value, we retrieve its index After that, we retrieve the value chosen by the user using this index We release memory, as we have learned before, and dismiss the controller The project PersonPicker, available from the source downloads, contains a complete application using the people picker 18.11 Using the ABPersonViewController Class The ABPersonViewController class allows you to present the details of a specific record to the user As with its other sisters, you need to create it and initialize it Next, you set its delegate (one method to implement) After that, you configure some of its options The allowsEditing property is set to YES if you want the user to edit the record NO is the default The displayedPerson must be set to the person record you want to show The displayedProperties is an array of all properties you want to show If you allow editing, then the other properties will show as well while editing If you want to highlight a given property, use the setHighlightedItemForProperty:withIdentifier: method which is declared as follows: - (void)setHighlightedItemForProperty:(ABPropertyID)property withIdentifier:(ABMultiValueIdentifier)identifier; The property parameter specifies the property identifier you want to highlight If the property is single-value, the second parameter is ignored If, on the other hand, the property is multi-value (e.g., phone), the identifier specifies which value to highlight You can highlight as many values as you want Listing 18.6 shows how one can setup and invoke a person view controller In this example, we retrieve the first person record and show it The displayed properties are the phone and email properties The method also highlights the phone value with identifier Listing 18.6 Showing a person record using the ABPersonViewController class -(void)buttonPushed{ ABAddressBookRef addressBook = ABAddressBookCreate(); NSArray *allPeople = (NSArray*) ABAddressBookCopyArrayOfAllPeople(addressBook); if(allPeople.count){ ABRecordRef person = (ABRecordRef)[allPeople objectAtIndex:0]; ABPersonViewController *personViewController = [[[ABPersonViewController alloc] init] autorelease]; personViewController.personViewDelegate = self; personViewController.displayedPerson = person; personViewController.displayedProperties = [NSArray arrayWithObjects: [NSNumber numberWithInt:kABPersonEmailProperty], 510 iPhone SDK Programming [NSNumber numberWithInt:kABPersonPhoneProperty], nil]; [personViewController setHighlightedItemForProperty:kABPersonPhoneProperty withIdentifier:0]; [self presentModalViewController:personViewController animated:YES]; } [allPeople release]; CFRelease(addressBook); } Listing 18.7 shows the delegate method of the person view controller If the property selected by the user is an email property, we log it, disallow default action, and dismiss the controller Otherwise, we dismiss the controller and allow default action to occur Listing 18.7 The delegate method of a person view controller - (BOOL) personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue{ if(property == kABPersonEmailProperty){ ABMultiValueRef emailProperty = ABRecordCopyValue(person, property); NSInteger emailndex = ABMultiValueGetIndexForIdentifier(emailProperty, identifierForValue); NSString *email = (NSString *)ABMultiValueCopyValueAtIndex(emailProperty, emailndex); NSLog(@"The email selected is %@:", email); CFRelease(emailProperty); [email release]; [self dismissModalViewControllerAnimated:YES]; return NO; } else{ [self dismissModalViewControllerAnimated:YES]; return YES; } } Figure 18.3 shows a view from the person view controller 18.12 Using the ABNewPersonViewController Class If you want the user to create a new person record, you can use the ABNewPersonViewController controller You create and initialize the controller and set the delegate The controller is then pushed on the stack The following shows a typical invocation: Working with the Address Book Database Figure 18.3 511 A view from a person view controller ABNewPersonViewController *newPersonViewController = [[[ABNewPersonViewController alloc] init] autorelease]; newPersonViewController.newPersonViewDelegate = self; [self.navigationController pushViewController:newPersonViewController animated:YES]; The only delegate method is newPersonViewController:didCompleteWithNewPerson: You should dismiss the controller and inspect the new person record Note that the person will be added to the address book by the time this method is called If the user cancelled, the person parameter is NULL - (void) newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person{ [self.navigationController popViewControllerAnimated:YES]; } Figure 18.4 shows the view of ABNewPersonViewController controller 512 iPhone SDK Programming Figure 18.4 Adding a new contact using the ABNewPersonViewController class 18.13 Summary In this chapter, we discussed the foundation of the address book API and several UI elements that can be used to modify the contacts database In Section 18.1, we provided a brief introduction to the subject Next, Section 18.2 discussed property types After that, Sections 18.3 and 18.4 showed how to access single- and multi-value properties, respectively Next, Sections 18.5 and 18.6 went into the details of the person record and the address book, respectively Issues related to multithreading and identifiers were addressed in Section 18.7 After covering the foundation of the address book API, we provided several sample applications Section 18.8 showed how to use a query to retrieve the photo of a given record Next, Section 18.9 showed how to use the ABUnknownPersonViewController class After that, Section 18.10 covered the ABPeoplePickerNavigationController class The ABPersonViewController class was covered in Section 18.11 Finally, Section 18.12 covered the ABNewPersonViewController class Working with the Address Book Database 513 Problems (1) Study the ABRecordRef type in the ABRecordRef.h header file and the documentation (2) Write a view controller that lists the contacts in the address book and allows the user to perform multiple selections of person records (3) Enhance the controller in (2) by adding search capability 19 Core Data In this chapter, you learn how to use the Core Data framework in your application In Section 19.1, you learn about the main components in the Core Data application Next, in Section 19.2, we talk about the major classes in the Core Data framework In Section 19.3, you learn how to use the graphical modeling tool to build a data model After that, Section 19.4 addresses the basic operations in persistence storage using Core Data Next, Section 19.5 shows how to use relationships in the Core Data model After that, Section 19.6 presents a search application that utilizes Core Data for storage Finally, we summarize the chapter in Section 19.7 19.1 Core Data Application Components Data models in a Core Data application are represented by entities Entities are composed of attributes (such as salary) and relationships (such as manager) Attributes and relationships are called properties For entities to come to life, they need to be represented by a managed object In the relational database world, an entity can be thought of as a table and a managed object as a specific row in that table In the object world, an entity can be thought of as a class, and a managed object as an instance of that class A collection of entities with their attributes and relationships represents a schema in a Core Data application This schema is represented by a managed object model You can create the model using Objective-C code (not recommended) or using the more convenient graphical modeling tool within XCode In addition to an entity, a managed object needs a managed object context Object contexts manage the life cycle of a collection of managed objects A managed object context needs a persistence store coordinator to manage the persistence of managed objects as well as their retrieval from the store A store can be a SQLite database, in-memory, or binary file storage 516 iPhone SDK Programming 19.2 Key Players In this section, we talk about the major classes in the Core Data framework In essence, we present the Core Data stack 19.2.1 Entity Entities are represented by an instance of the NSEntityDescription class As a minimum, this instance needs a name The following code fragment shows how to create an instance of this class: NSEntityDescription *userEntity = [[[NSEntityDescription alloc] init] autorelease]; [userEntity setName:@"User"]; Attributes are represented by the NSAttributeDescription class To add an attribute, such as name, to an entity, you first create it as follows: NSAttributeDescription *nameAttribute; nameAttribute = [[[NSAttributeDescription alloc] init] autorelease]; [nameAttribute setName:@"name"]; [nameAttribute setAttributeType:NSStringAttributeType]; [nameAttribute setOptional:NO]; In the above code fragment, we set the name of this attribute, provide its type (a string), and specify that is mandatory After creating the attribute(s), the properties are added as follows: [userEntity setProperties: [NSArray arrayWithObjects: dobAttribute, nameAttribute, nil]]; In the above code, we set the properties of the User entity to two attributes 19.2.2 Managed object model A managed object model is an instance of the NSManagedObjectModel class There are two options available to create an instance of this class: • Objective-C code You start by creating entities and then adding these entities to an instance of NSManagedObjectModel using the setEntities: method • Data modeling You use a data modeling tool in XCode to graphically build your data model, save it to a file, and load it at run time into an instance of NSManagedObjectModel class The easiest initializer is the mergedModelFromBundles: method If you pass in nil as the argument, all models in the main bundle are loaded and used to initialize the instance as shown below: Core Data 517 NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain]; 19.2.3 Persistent store coordinator A persistence store coordinator is an instance of the NSPersistentStoreCoordinator class You first create an instance of this class and then initialize it with the managed object model instance After that, you add a persistence store to it The following code shows a typical setup: NSURL *storeUrl = [NSURL fileURLWithPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Documents/database.sqlite"]]; NSError *error; NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel]; if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) { NSLog(@"error"); } In the above code, we specify the SQLite store type and the location of the database file in the home directory of the application 19.2.4 Managed object context Managed object contexts are instances of the NSManagedObjectContext class The following code fragment shows the setup of an instance of this class: managedObjectContext = [[NSManagedObjectContext alloc] init]; [managedObjectContext setPersistentStoreCoordinator:persistentStoreCoordinator]; Notice how a managed object context needs a persistence store coordinator in order to retrieve and persist the managed objects it manages 19.2.5 Managed object In an MVC Core Data application, the model is captured by the managed object; an instance of NSManagedObject To create a managed object, you can use the following class method of NSEntityDescription: + (id)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context; 518 iPhone SDK Programming Here, you attach a managed object instance configured to work with a specific entity to a managed object context The method returns the managed object autoreleased The following shows a typical example: User *user = (User *)[NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:self.managedObjectContext]; user.dob = [NSDate date]; user.name = @"Kate"; user.social = @"555-12-9898"; To save this object into the persistence store (e.g., a database table), you must send its context a save: message passing a reference to an NSError object, or NULL if you are not interested in receiving potential error messages 19.2.6 Summary If the previous description seems somewhat abstract, this section will make things concrete by giving a complete Core Data wrapper class that you can find in the CoreDataBasic1 project which can be found in the source downloads The class interface is shown in Listing 19.1 An application needs one instance of this class It provides a basic Core Data stack that can be used The three needed main objects (context, coordinator, and model) are created and initialized for you as we shall see shortly In addition, it provides a save method for saving all managed objects to the store Listing 19.1 Interface for the Core Data wrapper #import #import "User.h" @interface CoreDataWrapper : NSObject { NSPersistentStoreCoordinator *persistentStoreCoordinator; NSManagedObjectModel *managedObjectModel; NSManagedObjectContext *managedObjectContext; } @property(nonatomic, readonly, retain) NSPersistentStoreCoordinator *persistentStoreCoordinator; @property(nonatomic, readonly, retain) NSManagedObjectModel *managedObjectModel; @property(nonatomic, readonly, retain) NSManagedObjectContext *managedObjectContext; -(id)init; -(BOOL)save; @end Core Data 519 Listing 19.2 shows some of the implementation of the wrapper class We’ve talked about most of these methods The init method simply starts the stack build process by retrieving the value of the context The method managedObjectContext gets called and it checks to see if the object was initialized before Since all instance variables are initialized to nil, the method proceeds by retrieving the persistence coordinator and attaching this coordinator to the context The persistentStoreCoordinator method checks to see if the coordinator was created before If not, it creates it and initializes it as we saw before in Section 19.2.3 The initialization uses a managed object model instance The managedObjectModel checks to see if the model was created before If not, it creates from either code (shown in Listing 19.3) or loading it from file Listing 19.2 Some of the implementation of the wrapper class #import "CoreDataWrapper.h" @implementation CoreDataWrapper -(id)init{ if(self = [super init]){ self.managedObjectContext; } return self; } -(BOOL)save{ NSError *error; if ([self.managedObjectContext save:&error]) { return YES; } return NO; } - (NSManagedObjectContext *) managedObjectContext { if (managedObjectContext) { return managedObjectContext; } NSPersistentStoreCoordinator *coordinator = self.persistentStoreCoordinator; if (coordinator) { managedObjectContext = [[NSManagedObjectContext alloc] init]; [managedObjectContext setPersistentStoreCoordinator:coordinator]; } return managedObjectContext; } -(NSManagedObjectModel*) managedObjectModel{ #if FALSE return [self managedObjectModelFromCode]; #else if(managedObjectModel){ return managedObjectModel; } 520 iPhone SDK Programming managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain]; return managedObjectModel; #endif } -(NSPersistentStoreCoordinator*) persistentStoreCoordinator{ if (persistentStoreCoordinator) { return persistentStoreCoordinator; } NSURL *storeUrl = [NSURL fileURLWithPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Documents/database.sqlite"]]; NSError *error; persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel]; if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]){ NSLog(@"error"); } return persistentStoreCoordinator; } -(void)dealloc{ [persistentStoreCoordinator release]; [managedObjectModel release]; [managedObjectContext release]; [super dealloc]; } @end Listing 19.3 Creating a managed object model with a User entity from code -(NSManagedObjectModel*) managedObjectModelFromCode{ if(managedObjectModel){ return managedObjectModel; } managedObjectModel = [[NSManagedObjectModel alloc] init]; NSEntityDescription *userEntity = [[[NSEntityDescription alloc] init] autorelease]; [userEntity setName:@"User"]; [userEntity setManagedObjectClassName:@"User"]; [managedObjectModel setEntities:[NSArray arrayWithObject:userEntity]]; NSAttributeDescription *dobAttribute; dobAttribute = [[[NSAttributeDescription alloc] init] autorelease]; [dobAttribute setName:@"dob"]; [dobAttribute setAttributeType:NSDateAttributeType]; [dobAttribute setOptional:NO]; Core Data 521 NSAttributeDescription *nameAttribute; nameAttribute = [[[NSAttributeDescription alloc] init] autorelease]; [nameAttribute setName:@"name"]; [nameAttribute setAttributeType:NSStringAttributeType]; [nameAttribute setOptional:NO]; NSAttributeDescription *socialAttribute; socialAttribute = [[[NSAttributeDescription alloc] init] autorelease]; [socialAttribute setName:@"social"]; [socialAttribute setAttributeType:NSStringAttributeType]; [socialAttribute setOptional:NO]; [userEntity setProperties: [NSArray arrayWithObjects:dobAttribute, nameAttribute, socialAttribute, nil]]; return managedObjectModel; } In Listing 19.3, we associated the User entity with the Objective-C class User using the following statement: [userEntity setManagedObjectClassName:@"User"]; The User class is shown in Listing 19.4 It simply inherits all the behavior from NSManagedObject class and declares three attributes Since the Core Data framework will generate the accessor methods, you are encouraged to use the @dynamic directive This directive will simply stop compiler warnings Listing 19.4 The User managed object class #import @interface User : NSManagedObject { } @property (retain) NSString *social; @property (retain) NSString *name; @property (retain) NSDate *dob; @end @implementation User @dynamic dob, social, name; @end 19.3 Using the Modeling Tool In this section, we show how to build a simple model using the graphical modeling tool The model will consist of two entities: User and Comment A user can have many comments, and every comment belongs to a specific user The model resides in a file in the bundle To create the model, start by creating this file 522 iPhone SDK Programming Right-click on the Resources group and choose Add and then choose New File Select Resource, choose Data Model and hit Next (see Figure 19.1) Figure 19.1 Selecting a data model resource Choose the model name and hit Next (see Figure 19.2) Hit Finish On the diagram window, right-click and choose Add Entity (Figure 19.3) Enter the name of the entity and the name of its class as shown in Figure 19.4 Select the User entity and click on the + icon to add an attribute as shown in Figure 19.5 Name the attribute name and choose the type to be String as shown in Figure 19.6 Repeat the process and add a dob attribute of type Date, and a social attribute of type String The state of the User entity at this stage is shown in Figure 19.7 Now, go ahead and add another model, Comment Comment has a lat attribute of type Double, a lon attribute of type Double, and a text attribute of type String All are mandatory Our data model declares that a user has many comments and each comment belongs to one user Let’s declare these relationships using the tool Select the User entity and choose Add Relationship as shown in Figure 19.8 ... extra logging information) is as follows: MD5 of zero-length string is D41D8CD98F00B204E 980 0998ECF8427E MD5(The quick brown fox jumps over the lazy dog) = \ 9E107D9D372BB 682 6BD81D3542A419D6 MD5(The... will see it available for download and listed under Development Provisioning Profiles as shown in Figure 17.21 482 iPhone SDK Programming Figure 17.20 Creation of the iPhone development provisioning... After that, Sections 18 .3 and 18. 4 show how to access single- and multi-value properties, respectively Next, Sections 18. 5 and 18. 6 go into the details of the person record and the address book,

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

Từ khóa liên quan

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

Tài liệu liên quan