1. Trang chủ
  2. » Công Nghệ Thông Tin

Phát triển ứng dụng cho iPhone và iPad - part 32 potx

10 144 0

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 3,44 MB

Nội dung

{ NSLog (@”connection:willSendRequest:redirectResponse:”); return request; } RSSSampleViewController.m The next delegate method is connection:didReceiveAuthenticationChallenge: . You will receive this message if the web server that you are calling requires authentication to complete the request. Because I am trying to keep this example simple to show the basics of using the URL Loading System, the example will not use any authentication. However, in a production application, you will often be required to authenticate to retrieve data from a URL. When the connection:didReceiveAuthenticationChallenge: method is called, you can respond in one of three ways: Provide the necessary credentials by creating an NSURLCredential object and populating it with the data that the server expects. You can determine what the server expects by interrogating the NSURLAuthenticationChallenge object that you receive as a parameter to the didReceiveAuthenticationChallenge: method. Attempt to continue without passing credentials. You can do this by calling the continueWithoutCredentialsForAuthenticationChallenge: method on the sender of the challenge. Doing this will typically cause the request to fail, but it may allow the user to retrieve a URL that does not require authentication. Cancel the request by calling the cancelAuthenticationChallenge method on the sender of the challenge. Doing this will send the connection: didCancelAuthenticationChallenge: message to your delegate. You can fi nd more information about authentication in the “ URL Loading System Programming Guide ” in the Xcode help and on the Apple developer web site. In this implementation, you will just log that the NSURLconnection object invoked this method: // Called when the server requires authentication - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { NSLog (@”connection:didReceiveAuthenticationChallenge:”); } RSSSampleViewController.m If you cancel the authentication challenge on the connection, the connection object calls the next delegate method, connection:didCancelAuthenticationChallenge: . Again, you implement this method by simply logging the call: // Called when the authentication challenge is cancelled on the connection - (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge ➤ ➤ ➤ iPhone SDK and the Web ❘ 279 CH010.indd 279CH010.indd 279 9/18/10 10:01:15 AM9/18/10 10:01:15 AM 280 ❘ CHAPTER 10 WORKING WITH XML ON THE IPHONE { NSLog (@”connection:didCancelAuthenticationChallenge:”); } RSSSampleViewController.m The next delegate method, connection:didReceiveResponse: , provides some useful information about the response received from the server. The connection object calls this method when the connection has enough data to create an NSURLResponse object. You can interrogate the response for some useful information such as encoding type and expected length: // Called when the connection has enough data to create an NSURLResponse - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { NSLog (@”connection:didReceiveResponse:”); NSLog(@”expectedContentLength: %qi”, [response expectedContentLength] ); NSLog(@”textEncodingName: %@”, [response textEncodingName]); [self.responseData setLength:0]; } RSSSampleViewController.m It is possible to receive this method more than once, so to be safe, you discard the data in your received buffer when you receive a call to this method by setting the length of the responseData buffer to 0. The next delegate method that you will implement is connection:didReceiveData: . The connection object calls this method each time the connection receives a chunk of data. You will receive calls to this method multiple times, as you receive data from the server. In the implementation, you append the data received in this method to the responseData NSMutableData instance. Remember that you need to use the mutable version of NSData because you will be appending data to the object each time this method runs. Here is the implementation: // Called each time the connection receives a chunk of data - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSLog (@”connection:didReceiveData:”); // Append the received data to our responseData property [self.responseData appendData:data]; } RSSSampleViewController.m You will receive the connection:willCacheResponse: message before the connection caches the response. This method gives you a chance to change the cached response. There is a userInfo CH010.indd 280CH010.indd 280 9/18/10 10:01:15 AM9/18/10 10:01:15 AM property that you can use to add information to the cached response before the connection object caches the response. You can implement this method to return nil if you do not want to cache the response. In the example, you will simply pass along the cached response: // Called before the response is cached - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { NSLog (@”connection:willCacheResponse:”); // Simply return the response to cache return cachedResponse; } RSSSampleViewController.m The last call that you receive will be either connectionDidFinishLoading: or connection: didFailWithError: . In the case of connectionDidFinishLoading: , the connection object calls this method when the connection has successfully received the complete response. The implementation takes the response data and converts it to a string for display in the console: // Called when the connection has successfully received the complete response - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog (@”connectionDidFinishLoading:”); // Convert the data to a string and log the response string NSString *responseString = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; NSLog(@”Response String: \n%@”, responseString); [responseString release]; // Free the connection [connection release]; } RSSSampleViewController.m The connection object calls the connection:didFailWithError: method when there is an error in receiving the response. This method simply dumps the error description to the log. In a production application, you will probably want to handle the error a little more robustly by possibly allowing the user to retry the operation, or at least providing some feedback as to why the operation failed. Here is the implementation: // Called when an error occurs in loading the response - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog (@”connection:didFailWithError:”); iPhone SDK and the Web ❘ 281 CH010.indd 281CH010.indd 281 9/18/10 10:01:16 AM9/18/10 10:01:16 AM 282 ❘ CHAPTER 10 WORKING WITH XML ON THE IPHONE NSLog (@”%@”,[error localizedDescription]); // Free the connection [connection release]; } RSSSampleViewController.m Finishing Up You are just about ready to build and run your application. First, however, you need to implement viewDidUnload and dealloc to do some memory cleanup: - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; self.responseData = nil; } - (void)dealloc { [responseData release]; [super dealloc]; } RSSSampleViewController.m Build and run the application. When the application starts, tap the Send Request button. Make sure that you have the console displayed by selecting Run ➪ Console from the menu bar or by typing Command+Shift+R. If you have an active Internet connection, you should see a log entry in the console log for each delegate method as it runs. When the connection has fi nished downloading the data from the server, you should see the XML document that you retrieved from the CNN.com RSS feed. You need to have an active Internet connection for this code to work correctly. XML AND THE IPHONE SDK In this section, you learn about XML and how to integrate XML content into your application. You will extend the sample project to parse the XML contained in the response from the CNN.com web server using the NSXMLParser class. Brief Overview of XML XML (the eXtensible Markup Language) defi nes rules for the electronic transmission and storage of data. The W3C web standards body defi nes the XML 1.0 specifi cation. You can fi nd the entire specifi cation at http://www.w3.org/TR/REC-xml/ . CH010.indd 282CH010.indd 282 9/18/10 10:01:17 AM9/18/10 10:01:17 AM The goal of XML is to provide software engineers with a tool that they could use and extend to defi ne arbitrary data. XML is extensible in that, unlike HTML or other markup languages, the designer can introduce and include arbitrary tags that make sense for his or her application. In conjunction with an arbitrary schema, the developer can provide a formal defi nition of the schema via XSD (XML Schema Defi nition) or DTD (Document Type Defi nition) fi les. Users of the XML data can then use these schema defi nitions to validate the XML document and ensure that it conforms to the defi ned schema. There are many standardized schemas currently in use, including RSS (which you used in the chapter example), SOAP, Atom, and XHTML. Additionally, software vendors such as Apple and Microsoft use XML as the fi le storage format for many of their document - based software packages. XML fi les are plain text and human readable, which helps make them easy for developers to deal with. XML fi les typically defi ne data in a tree structure, as shown in Figure 10 - 4. Because the tree is a common software paradigm, programmers typically have little problem parsing and using the data contained in an XML fi le with this structure. The fact that the designer can make up his own tags makes XML extremely fl exible to use while you are designing the structure of your data and messages. rss channel title link image title title title title link link link link description description description description item item item item FIGURE 10 - 4: CNN.com RSS feed XML tree You can use XML to store data locally. However, it is equally if not more common to use XML as a way to transfer data over the Internet. Many data transfer schemas, such as RSS and SOAP, rely on XML to exchange data. In the sample in the previous section, you received an XML response from the server in response to your request. In this section, you will learn how to parse that response to obtain the headlines contained in the feed. Parsing XML with NSXML Parser After you make a call to a web service or, in our case, an RSS feed, you often need to parse the XML response to get the data that you need for your application. There are a couple of different programming models when it comes to parsing XML, specifi cally, DOM and SAX. XML and the iPhone SDK ❘ 283 CH010.indd 283CH010.indd 283 9/18/10 10:01:22 AM9/18/10 10:01:22 AM 284 ❘ CHAPTER 10 WORKING WITH XML ON THE IPHONE DOM stands for Document Object Model. The DOM programming model represents the tree structure of XML as a tree of objects that you can access in your code. The objects in the DOM model represent Nodes, Attributes, Comments, and other XML elements. In your code, you can recursively navigate the tree or perform queries to return all nodes with a particular name, attribute, and so on. When you use a DOM parser, the entire XML document that you are parsing is loaded into memory at one time. For large XML documents, a DOM model can consume quite a bit of memory. Because memory is such a scarce resource on mobile devices such as the iPhone and iPad, Apple chose not to provide a DOM parser with the SDK. However, if you need the functionality provided by a DOM parser, you can use the C library libxml2 . I cover the use of libxml2 later in this chapter in the section “ Generating XML with libxml. ” Before you settle on using DOM, make sure that you consider the performance and memory implications of loading the entire XML document into memory. The parser provided by Apple in the iPhone SDK is a SAX - based parser. SAX stands for Serial Access Parser. SAX implements an event - driven parser that calls delegate methods as the parser moves through an XML document. The parser navigates the XML document sequentially and calls methods in a defi ned order when it fi nds things like tags, attributes, and text characters. Apple has provided an event - driven SAX parser with the NSXMLParser class. While implementing the delegate methods to use a SAX parser can be cumbersome, it is far less resource intensive in terms of memory usage than using a DOM parser. When you want to parse XML, you will typically follow a few specifi c steps. First, you need to locate the XML that you want to parse. You can obtain the XML from the Web through a URL, from a text fi le on a network or on the device, or from somewhere else and stored in an NSData object. Once you have located your XML, you will create an instance of the NSXMLParser class. When you initialize your instance, you will pass in the XML data that you want to parse. Once you have this code in place, you will need to code the delegate methods that the NSXMLParser calls as it encounters certain elements in the XML fi le. The parser calls the parserDidStartDocument and parserDidEndDocument methods when it begins and fi nishes parsing the document respectively. You can add code to parserDidStartDocument to set up any variables that you intend to use during parsing. Likewise, you will want to clean up any of these variables in the parserDidEndDocument method. When the parser encounters a start element or tag, it will call the parser:didStartElement: namespaceURI:qualifiedName:attributes: method. In this method, you will typically check to see which element was started and prepare to process the data within the element accordingly. The parser passes element attributes as a dictionary with the name of the attribute as the key in the dictionary and the value of the attribute as the value. The corresponding method that the parser calls when an element ends is parser:didEndElement:namespaceURI:qualifiedName: . In this method, you will typically take some action when an element that you are interested in has ended. When the parser fi nds characters contained within an element, it calls the parser:foundCharacters: method. The parser may call this method multiple times for a single element, so you should append any characters sent to this method to a mutable string until you receive a didEndElement call. This is similar to the way that you handled the connection:didReceiveData: delegate method call from the NSURLConnection object earlier in this chapter. CH010.indd 284CH010.indd 284 9/18/10 10:01:23 AM9/18/10 10:01:23 AM If the parser encounters an error, it will call the parser:parseErrorOccurred: method. You should handle this method and provide some information to the user about the error that occurred. Extending the Example, Parsing the XML In this section, you will extend the example code that you developed in the fi rst part of this chapter to get the CNN.com RSS feed from the Internet. You will take the XML document that you retrieve from the call to the web site, parse it, and display the titles of the headline stories. Starting Out Before you begin, you need to know what you are looking for in the XML that you retrieve from the server. Figure 10 - 5 shows a representative sample of the XML that you retrieve when you call the CNN.com RSS feed web site. You can download the complete XML fi le from this book’s web site. FIGURE 10 - 5: CNN.com RSS feed XML You are interested in the item entities that encapsulate the information about a particular story item. Within the item , you only really want to grab the text inside the title element because your application will only display the title of each story. If you were building a more intricate application, you could create an item class with title, link, publication date, and description properties as well. However, for simplicity, you will only capture and display the title of the article. XML and the iPhone SDK ❘ 285 CH010.indd 285CH010.indd 285 9/18/10 10:01:23 AM9/18/10 10:01:23 AM 286 ❘ CHAPTER 10 WORKING WITH XML ON THE IPHONE Setting Up to Parse If you do not have the RSSSample project open, open it now. You need to add an instance variable to the RSSSampleViewController to hold the characters captured from the XML for the title . In the RSSSampleViewController.h header, add an instance variable: NSMutableString *capturedCharacters; Notice that you use an object of type NSMutableString . You must use a mutable string because you will be appending to the current string each time you get a delegate method call to the parser: foundCharacters: method. Keep in mind that the NSString class is immutable, meaning that you cannot change it after its initial creation. Next, you will add a Boolean fl ag variable so that you will know when you are inside an item element. You need to know this because the title element occurs in multiple places, but you are only interested in extracting the characters if you fi nd a title element inside of an item element. Here is the declaration: BOOL inItemElement; While you are in the header, you need to add a method that will start parsing the XML. You will call this method once you are fi nished loading the URL from the web site. Call the new method parseXML : - (void) parseXML; Instead of just logging the results, you can show the output of your parsing in your application. To keep this example simple, just add a UITextView outlet to the interface so that you can access the property in Interface Builder. You will then stuff all of the headlines in that TextView. Add an instance variable for a UITextView to the header: IBOutlet UITextView *textView; Next, add a property for the UITextView : @property (nonatomic, retain) UITextView *textView; The following is the completed header fi le for the RSSSampleViewController class: #import < UIKit/UIKit.h > @interface RSSSampleViewController : UIViewController { NSMutableData *responseData; NSMutableString *capturedCharacters; BOOL inItemElement; IBOutlet UITextView *textView; } - (IBAction)buttonTapped:(id)sender; - (void) parseXML; @property (nonatomic,retain) NSMutableData *responseData; CH010.indd 286CH010.indd 286 9/18/10 10:01:24 AM9/18/10 10:01:24 AM @property (nonatomic, retain) UITextView *textView; @end RSSSampleViewController.h Now let ’ s move on to the implementation fi le. In RSSSampleViewController.m , synthesize the textView property: @synthesize responseData,textView; Add code to the viewDidUnload and dealloc methods to clean up the UITextView : - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; self.responseData = nil; self.textView = nil; } - (void)dealloc { [responseData release]; [textView release]; [super dealloc]; } RSSSampleViewController.m Next, modify the connectionDidFinishLoading delegate method to call the new parseXML method. When you fi nish loading all of the data from the URL, you want to parse and display it. Here is the connectionDidFinishLoading delegate method: // Called when the connection has successfully received the complete response - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog (@”connectionDidFinishLoading:”); // Convert the data to a string and log the response string NSString *responseString = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; NSLog(@”Response String: \n%@”, responseString); [responseString release]; [connection release]; [self parseXML]; } RSSSampleViewController.m XML and the iPhone SDK ❘ 287 CH010.indd 287CH010.indd 287 9/18/10 10:01:25 AM9/18/10 10:01:25 AM 288 ❘ CHAPTER 10 WORKING WITH XML ON THE IPHONE The next step is to implement the parseXML method to instantiate the parser, set the delegate, and begin parsing the XML: - (void) parseXML { NSLog (@”parseXML”); // Initialize the parser with our NSData from the RSS feed NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:self.responseData]; // Set the delegate to self [xmlParser setDelegate:self]; // Start the parser if (![xmlParser parse]) { NSLog (@”An error occurred in the parsing”); } // Release the parser because we are done with it [xmlParser release]; } RSSSampleViewController.m Modifying the Interface You need to add a TextView to the user interface using Interface Builder. You will use this TextView to display the headlines that you retrieve from the RSS feed. Open Interface Builder and add a UITextView to the interface, as shown in Figure 10 - 6. Clear the default text from the TextView and move the Send Request button to the top of the screen. Finally, hook the UITextView up to the File ’ s Owner textView property. Implementing the Parser Delegate Methods As the NSXMLParser works its way through the XML document, it calls a series of delegate methods. You need to implement these methods to get the information that you need from the XML document. First, you implement the start and end document methods: // Called when the parser begins parsing the document - (void)parserDidStartDocument:(NSXMLParser *)parser { NSLog (@”parserDidStartDocument”); inItemElement = NO; } FIGURE 10 - 6: RSSSample user interface CH010.indd 288CH010.indd 288 9/18/10 10:01:25 AM9/18/10 10:01:25 AM . of memory. Because memory is such a scarce resource on mobile devices such as the iPhone and iPad, Apple chose not to provide a DOM parser with the SDK. However, if you need the functionality. memory. The parser provided by Apple in the iPhone SDK is a SAX - based parser. SAX stands for Serial Access Parser. SAX implements an event - driven parser that calls delegate methods as. to do some memory cleanup: - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; self.responseData = nil; } - (void)dealloc { [responseData

Ngày đăng: 04/07/2014, 21:20

w