Finally, add an instance method called parseXML that you will invoke to start the XML processing: - IBAction extractTerms:idsender; - void parseXML; The complete header should look lik
Trang 1Next, you defi ne an MKCoordinateRegion struct You will pass this struct to the mapView to defi ne the region that you want to display An MKCoordinateRegion consists of a span and a center point
You will center the map on the coordinate that you receive from Core Location
Next, you tell the mapView to set the region displayed on the map and to animate the transition to your new region Finally, you tell the Core Location manager to stop getting location updates from the GPS Because your application does not need extremely accurate resolution, nor do you need constant updates from the GPS, you can conserve power by turning the GPS off
For the next step, you need to make a couple of additions to the viewDidLoad method Because you will be customizing the pin colors for your annotations, you need to set the mapView delegate
to self In addition, for illustrative purposes, you will display the user ’ s location on the map by setting the map view ’ s showsUserLocation property to YES When using the showsUserLocation property, you need to be aware that this will cause the map view to use Core Location to
retrieve and maintain the user ’ s location on the map This forces the GPS receiver to remain on, consuming valuable battery power You should carefully consider if your application needs this functionality or not before using it This example uses this feature to demonstrate a capability
of the map view to display the user ’ s current location Here is the complete implementation of viewDidLoad :
- (void)viewDidLoad { [super viewDidLoad];
// Create the results array self.results = [[NSMutableArray alloc] init];
// Create the Core Location CLLocationManager CLLocationManager *locationManager = [[CLLocationManager alloc] init];
// Set the delegate to self [locationManager setDelegate:self];
// Tell the location manager to start updating the location [locationManager startUpdatingLocation];
// Set the delegate for the searchbar [self.searchBar setDelegate:self];
// Set the delegate for the mapView [self.mapView setDelegate:self];
// Use Core Location to find the user’s location and display it on the map // Be careful when using this because it causes the mapview to continue to // use Core Location to keep the user’s position on the map up to date self.mapView.showsUserLocation = YES;
}
LocationSearchViewController.m
Trang 2330 ❘ CHAPTER 11 INTEGRATING WITH WEB SERVICES
When the user clears the text from the search bar, you want to clear the old annotations from the
map You can do this by implementing the searchBar:textDidChange: delegate method like this:
// Called when the searchbar text changes
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
NSLog (@”textDidChange”);
// If the text was cleared, clear the map annotations
if ([searchText isEqualToString:@””])
{
// Clear the annotations
[self.mapView removeAnnotations:self.mapView.annotations];
// Clear the results array
[self.results removeAllObjects];
}
}
LocationSearchViewController.m
You implement this code to check to see if the user has cleared the search string If he has, you
remove the annotations from the map and clear your results array
In the last bit of code, you will implement the mapView:viewForAnnotation: delegate method
The map view will call this method when the map needs the view for an annotation If you wanted
to implement a custom view for your annotations, you would do it in this method Instead of
implementing a custom view, you will use the MKPinAnnotationView ; however, you could easily
replace this with your own view You will change the color of the pin based on the user rating of the
business that you are plotting on the map Here is the code:
- (MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id < MKAnnotation > )annotation
{
// If we are displaying the user’s location, return nil
// to use the default view
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
// Try to dequeue an existing pin
MKPinAnnotationView *pinAnnotationView =
(MKPinAnnotationView *)
[self.mapView dequeueReusableAnnotationViewWithIdentifier:@”location”];
if (!pinAnnotationView) {
// We could not get a pin from the queue
pinAnnotationView=[[[MKPinAnnotationView alloc]
initWithAnnotation:annotation
Trang 3reuseIdentifier:@”location”] autorelease];
pinAnnotationView.animatesDrop=TRUE;
pinAnnotationView.canShowCallout = YES;
} // We need to get the rating from the annotation object // to color the pin based on rating
Result *resultAnnotation = (Result*) annotation;
if (resultAnnotation.rating > 4.5) { pinAnnotationView.pinColor = MKPinAnnotationColorGreen;
} else if (resultAnnotation.rating > 3.5) { pinAnnotationView.pinColor = MKPinAnnotationColorPurple;
} else { pinAnnotationView.pinColor = MKPinAnnotationColorRed;
} return pinAnnotationView;
}
LocationSearchViewController.m
The fi rst line of the method checks to see if the annotation is for the user location view If it is, you simply return nil to tell the map to use the default annotation
Next, you will see the attempt to dequeue an existing annotation view The MapView works very much like the TableView in this respect It doesn ’ t make sense to keep invisible map annotations
in memory Therefore, the MapView creates and releases annotations as they become visible or disappear from the map respectively Instead of creating new annotation instances every time, the MapView maintains an internal queue of annotation objects that it can reuse Therefore, you fi rst try to dequeue an annotation If you cannot, you create a new pin annotation view with the correct reuse identifi er Then, you set the attributes of this view
Next, you cast the annotation that the MapView is asking for to a Result object Then, you use the rating property of the Result to set the color of the pin Finally, you return the pinAnnotationView
Finishing Up
The code is now complete You should be able to successfully build and run the application If you attempt to run the application in the simulator, you will see that the device thinks that it is at Apple headquarters, regardless of where you are actually located This is by design Enter a search term
in the search bar and watch the pins drop to show you the results You can view the XML returned
by the web service in the console
Trang 4332 ❘ CHAPTER 11 INTEGRATING WITH WEB SERVICES
EXAMPLE 2: TERM EXTRACTION
When making calls to a web service, you will often use the HTTP GET
method to send parameters to the service When dealing with REST
based web services, you use GET to indicate that you are performing a
query for some data from the server There are occasions where you will
need to POST data to the server Many SOAP - based web services use
POST to send data REST uses the POST method to indicate that you are
sending data to the server and intend to modify the database
Sending a POST request is very similar to sending a GET request with some
minor exceptions, as you will see in the example code
In this example, you will make a call to the Yahoo! Term Extraction
service This service returns a list of what it deems to be the signifi cant
words and phrases in the text passage that you submit There is no
defi nition of what Yahoo! determines to be “ signifi cant, ” nor is their
algorithm to determine signifi cance public Because of the length of the
string that you can submit to the service, it is not practical to use the GET
method; therefore, the service requires that you use POST to send your
string into the web service You can apply the same principles that you
use here to any web service that requires you to submit data using the
POST method The completed example will look like Figure 11 - 5
Getting Started
To get started, open Xcode and create a new View - based application called TermExtract In the
TermExtractViewController.h header fi le, add instance variables for two UITextView variables:
UITextView *textToExtractTextView;
UITextView *extractedTermsTextView;
Next, add properties for these UITextView s:
@property (nonatomic, retain) IBOutlet UITextView *textToExtractTextView;
@property (nonatomic, retain) IBOutlet UITextView *extractedTermsTextView;
Also, add instance variables for the response data that you will receive from the server in response
to your request, and the characters that you will capture during XML parsing:
NSMutableData *responseData;
NSMutableString *capturedCharacters;
Now, add a property for the responseData :
@property (nonatomic, retain) NSMutableData *responseData;
FIGURE 11 - 5: Complete term extraction application
Trang 5Next, add an IBAction method called extractTerms that you will call after the user enters the text
to send to the service Finally, add an instance method called parseXML that you will invoke to start the XML processing:
- (IBAction) extractTerms:(id)sender;
- (void) parseXML;
The complete header should look like this:
#import < UIKit/UIKit.h >
@interface TermExtractViewController : UIViewController { UITextView *textToExtractTextView;
UITextView *extractedTermsTextView;
NSMutableData *responseData;
NSMutableString *capturedCharacters;
}
@property (nonatomic, retain) IBOutlet UITextView *textToExtractTextView;
@property (nonatomic, retain) IBOutlet UITextView *extractedTermsTextView;
@property (nonatomic, retain) NSMutableData *responseData;
- (IBAction) extractTerms:(id)sender;
- (void) parseXML;
@end
TermExtractViewController.h
In the implementation fi le, synthesize the textToExtractTextView,extractedTermsTextView, responseData properties:
@synthesize textToExtractTextView,extractedTermsTextView,responseData;
Then, add the code to clean up the properties in the viewDidUnload method:
- (void)viewDidUnload { // Release any retained subviews of the main view.
// e.g self.myOutlet = nil;
self.textToExtractTextView=nil;
self.extractedTermsTextView=nil;
self.responseData=nil;
}
TermExtractViewController.m
Trang 6334 ❘ CHAPTER 11 INTEGRATING WITH WEB SERVICES
Finally, release your instance variables and call the superclass ’ s dealloc method in dealloc :
- (void)dealloc {
[textToExtractTextView release];
[extractedTermsTextView release];
[responseData release];
[super dealloc];
}
TermExtractViewController.m
Building the User Interface
You will now build the user interface for the
application using Interface Builder Double - click on
the TermExtractViewController.xib fi le in Xcode to open the
fi le in Interface Builder Once the interface view is open, add
two UITextView s, two UILabel s, and a UIButton , as shown
in Figure 11 - 6
Change the title attribute of the UIButton to read “ Extract
Terms ” Change the text of the top UILabel to read “ Text to
extract: ” and the bottom UILabel to “ Extracted Terms: ”
As default text in the “ Text to extract: ” TextView, I set the text
to the Declaration of Independence I have included a text fi le
containing the declaration, or you could use your own text or
just provide text at runtime Delete the default text from the
extracted terms TextView
Next, you need to hook up the TextViews to the proper
outlets in Interface Builder Hook up the Extract Terms
button to the IBAction extractTerms in File ’ s Owner In the
TermExtractViewController.m implementation fi le, implement
a stub extractTerms method to log when someone calls the
method You will use this to verify that you have correctly wired up the button in Interface Builder
Here is the stub code:
- (IBAction) extractTerms:(id)sender
{
NSLog (@”extractTerms”);
}
Build and run the application Click the extractTerms button and verify that you see the log
message in the console This shows that you have correctly wired the button to the method If you
do not see the message in the console, make sure that you have properly wired the button to the
message in Interface Builder
FIGURE 11 - 6: Term extract user interface
Trang 7You are fi nished with the user interface, so you can close Interface Builder
Implementing the POST Call
You will implement the extractTerms method to POST the request to the web service
The fi rst thing that you do in this method is to dismiss the keyboard by calling the resignFirstResponder method on the TextView Next, you clear the list of extracted terms
to eliminate old results:
- (IBAction) extractTerms:(id)sender {
NSLog (@”extractTerms”);
// Hide the keyboard [self.textToExtractTextView resignFirstResponder];
// Clear the old extracted terms self.extractedTermsTextView.text = @””;
TermExtractViewController.m
Now, you create a string to hold the URL that you plan to call This is the address of the Yahoo!
Term Extraction web service Next, you create an NSURL object with this string:
// Create a string for the URL NSString *urlString = @”http://search.yahooapis.com/ContentAnalysisService/V1/termExtraction”;
// Create the NSURL NSURL *url = [NSURL URLWithString:urlString];
TermExtractViewController.m
The next line is where using the POST method differs from using GET If you recall, when using the GET method, you simply set the URL string, set the parameter values inline, and sent the request off through the NSURLConnection When using the POST method, you need to do things a little differently After you create the NSURLRequest , you will need to modify some of its properties
Therefore, you must use an NSMutableURLRequest instead:
// Create a mutable request because we will append data to it.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval: 30.0];
TermExtractViewController.m
Trang 8336 ❘ CHAPTER 11 INTEGRATING WITH WEB SERVICES
The fi rst change that you will make to the request is to set the HTTP method that you plan to use
Remember that you are using the POST method The default method is GET , so you have to change
this in the request to POST using the setHTTPMethod method:
// Set the HTTP method of the request to POST
[request setHTTPMethod:@”POST”];
TermExtractViewController.m
Next, you will build a string to hold your parameters In this example, there is only one parameter,
but many parameters can optionally be passed using the POST method You should note that
parameters must be passed using the HTML parameter passing syntax name=value just like
when using the GET method In your implementation, make sure that you replace the appid with
the actual appid that you receive from Yahoo! after you register your application Here is your
parameter string:
// Build a string for the parameters
NSString *parameters = [[NSString alloc] initWithFormat:
@”appid=YOUR_ID_GOES_HERE & context=%@”,
self.textToExtractTextView.text];
TermExtractViewController.m
When you use the GET method to call a web service, you pass the parameters in the query string
of the HTTP request However, when you use POST , you pass those parameters in the body of the
HTTP message Therefore, you have to set the HTTP body using the setHTTPBody method:
// Set the body of the request
[request setHTTPBody:[parameters dataUsingEncoding:NSUTF8StringEncoding]];
TermExtractViewController.m
The rest of the code for the method is the same as you have seen before First, you create the
NSURLConnection :
NSURLConnection *connection =
[[NSURLConnection alloc] initWithRequest:request delegate:self];
TermExtractViewController.m
Next, you instantiate your responseData property:
// Make sure that the connection is good
if (connection) {
// Instantiate the responseData data structure to store to response
self.responseData = [NSMutableData data];
}
Trang 9else { NSLog (@”The connection failed”);
}
TermExtractViewController.m
Finally, you need to clean up your local variables:
// Clean up our local variables [urlString release];
[parameters release];
TermExtractViewController.m
Here is the complete method implementation:
- (IBAction) extractTerms:(id)sender {
NSLog (@”extractTerms”);
// Hide the keyboard [self.textToExtractTextView resignFirstResponder];
// Clear the old extracted terms self.extractedTermsTextView.text = @””;
// Create a string for the URL NSString *urlString =
@”http://search.yahooapis.com/ContentAnalysisService/V1/termExtraction”;
// Create the NSURL NSURL *url = [NSURL URLWithString:urlString];
// Create a mutable request because we will append data to it.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval: 30.0];
// Set the HTTP method of the request to POST [request setHTTPMethod:@”POST”];
// Build a string for the parameters NSString *parameters = [[NSString alloc] initWithFormat:
@”appid=YOUR_ID_GOES_HERE & context=%@”, self.textToExtractTextView.text];
// Set the body of the request [request setHTTPBody:[parameters dataUsingEncoding:NSUTF8StringEncoding]];
// Create the connection and send the request
Trang 10338 ❘ CHAPTER 11 INTEGRATING WITH WEB SERVICES
NSURLConnection *connection =
[[NSURLConnection alloc] initWithRequest:request delegate:self];
// Make sure that the connection is good
if (connection) {
// Instantiate the responseData data structure to store to response
self.responseData = [NSMutableData data];
}
else {
NSLog (@”The connection failed”);
}
// Clean up our local variables
[urlString release];
[parameters release];
}
TermExtractViewController.m
Receiving the XML Response
In order to receive the response from the web service, you need to implement the NSURLConnection
delegate methods as you did in the previous example
First, you will implement the connection:didReceiveResponse: method This delegate method is
called when the NSURLConnection creates the response The connection could call this method
multiple times, so you need to reset your response data by setting its length to zero each time this
method runs Here is the implementation:
// 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];
}
TermExtractViewController.m
Next, you need to implement the connection:didReceiveData: delegate method The connection
calls this method each time it receives data so you need to append the received data to your
responseData buffer:
// Called each time the connection receives a chunk of data
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{