Tải bản đầy đủ (.pdf) (27 trang)

Lập trình ứng dụng cho iPhone part 20

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (826.11 KB, 27 trang )

396
The web: web views
and internet protocols
We started this book with a look at the web. Chapters 3 through 8 offered an exten-
sive discussion of building iPhone web apps using
HTML
,
CSS
, JavaScript, and the
dynamic programming language of your choice. As we said at the time, web devel-
opment is one of two major ways that you can program for the iPhone, the other
being the
SDK
that we’ve spent the last ten chapters on.
We’ve generally suggested web apps as the proper platform for creating inter-
net-related programs. This chapter will present some solutions for when that’s not
the case. Even if you’re depending heavily on the web, there are numerous reasons
that you might want to program using the
SDK
. You might want to make use of its
more extensive graphic capabilities. You could be designing something of sufficient
complexity that you want to use a well-organized object-oriented environment. You
might want to monetize your app without having to depend on ads. For whatever
This chapter covers

Using web views

Parsing XML

Accessing other protocols
397Low-level networking


reason, you’ve decided to design an
SDK
web app, not an
HTML
-based web app, and
now you need to know how to do so.
In this chapter, we’re going to cover the major ways to access the internet from the
SDK
. You can do so in a variety of ways, and we’ll outline their hierarchy in our first section.
20.1 The hierarchy of the internet
internet programming involves a hierarchy of protocols. At the lowest level, you have
the sockets that you use to connect from one computer to another. Above them are a
variety of more sophisticated technologies, such as
FTP
, Bonjour, and
HTML
.
HTML
is
a critical protocol, represented on the iPhone by both low-level access and the high-
level
UIWebView
. Recently an increasing number of protocols have been built on top
of
HTML
, forming what we call the social network.
This hierarchy of internet protocols is shown in figure 20.1, along with iPhone
OS
classes of note.
In this chapter, we’re going to cover all of these protocols, starting with the lowest

level, but our real focus will be on the higher-level internet and social network proto-
cols, because they’re the protocols that are best supported by the iPhone, and they’re
the ones you’re most likely to want to interact with.
20.2 Low-level networking
We’ve opted not to pay much attention to
BSD
sockets and the lower-level networking
classes, because we expect they’ll be of little interest to most iPhone programmers. If
you need to work with
BSD
sockets, you should look at Apple’s “Introduction to
CFN
et-
work Programming Guide.”
Low-level
Networking
BSD
sockets
CFNetworking
Unabstracted
protocols
Raw HTML access
Raw Host Connections
CFHost
CFHTTPMessage
NSData
NSURLRequest
Web
views
Abstracted HTML display UIWebView

Web
protocols
Social networking
Ajax, JSON, RSS,
SOAP, XML
NSXMLParser
Third-Party libraries
Figure 20.1 internet protocols are arranged in a hierarchy.
398
C
HAPTER
20
The web: web views and internet protocols
If you need to work with the lower-level protocols,
CFNetwork
provides a variety of
classes that you’ll find useful. You can find more information on them in the “Net-
working & internet” topic in the Apple docs. In particular, the “
CFN
etwork Framework
Reference” will give you an overview of the various classes. Among the classes are
CFFTPStream
, which lets you communicate with
FTP
servers, and
CFNetServices
,
which gives you access to Bonjour—Apple’s service discovery protocol. There are also
two low-level
HTTP

-related classes,
CFHTTPMessage
and
CFHTTPStream
. We’re going to
leave these classes alone, as our
HTML
work will be related to the higher-level
NSURL
,
NSURLRequest
,
UIWebView
,
NSMutableURLRequest
, and
NSURLConnection
classes.
Rather than skipping over these low-level and unabstracted protocols entirely, we’ll
take a look at one of them,
CFHost
. It’s the easiest to work with and perhaps the most
immediately useful.
20.2.1 The CFHost class
CFHost
allows your program to request information about an internet host, such as its
name, its address, and whether it’s reachable. Listing 20.1 shows a sample of how to
determine whether a host name exists or not.
-(IBAction)reportStatus:(id)sender {
CFStreamError errorTest;

if (myInput.text) {
CFHostRef myHost = CFHostCreateWithName(kCFAllocatorDefault,
(CFStringRef)myInput.text);
if (myHost) {
if (CFHostStartInfoResolution(myHost, kCFHostAddresses,
&errorTest)) {
myOutput.text = [myInput.text stringByAppendingString:
@" COULD be resolved."];
} else {
myOutput.text = [myInput.text stringByAppendingFormat:
@" could NOT be resolved (Error: %i).",
errorTest.error];
}
}
CFRelease(myHost);
}
}
Our sample method,
reportStatus:
, is activated by a button push. It reads a host
name from a
UITextField
called
myInput
and reports out to a
UITextView
called
myOutput
.
All uses of the

CFHost
commands follow the same pattern. First you create a
CFHostRef
object with
CFHostCreateCopy
,
CFHostCreateWithAddress
, or
CFHost-
CreateWithName
B
. Then you use
CFHostStartInfoResolution
to request a certain
Listing 20.1 A simple host name lookup
B
C
399Working with URLs
type of information, which can be
kCFHostAddresses
,
kCFHostNames
, or
kCFHost-
Reachability
C
. This example omits a final step where you retrieve your information
with
CFHostGetAddressing
,

CFHostGetNames
, or
CFHostReachability
—something
that wasn’t necessary here because the point was to see if the request for an address
resolved correctly at all.
You can find more information on these functions, and on how to use a callback
function to make the host resolution asynchronous, in the
CFHost
reference.
We consider this look at low-level networking—and
CFHost
—an aside, meant only
to hint at what is possible if you must do lower-level networking work. Now we’ll move
on to higher-level
HTML
-related network work that’s more likely to be the focus of
your iPhone network programming. The first thing you’ll need to know is how to use
the iPhone’s
URL
objects.
20.3 Working with URLs
With HTTP being the basis of most iPhone internet programming, it shouldn’t be a
surprise that
URL
s are a foundational technique for internet-based programming.
You’ll use them whether you’re calling up
UIImageView
s, accessing content by hand,
or parsing

XML
. As a result, we’re going to spend a bit of time on the two fundamental
URL
classes:
NSURL
and
NSURLRequest
.
20.3.1 Creating an NSURL
An
NSURL
is an object that contains a
URL
. It can reference a web site or a local file,
like any
URL
can. You’ve used it in the past to access Apple’s stock page and to load up
local media files for play.
As noted in the
NSURL
class reference, there are numerous methods that you can use
to create an
NSURL
. The most important ones are listed in table 20.1.
Table 20.1 A variety of
NSURL
creation methods
Method Summary
fileURLWithPath:
Creates a URL from a local file path

URLWithString:
Creates a URL from a string; equivalent to
initWithString:
URLWithString:relativeToURL:
Adds a string to a base URL; equivalent to
initWithString:relativeToURL:
NSURL
and
CFURLRef
NSURL
is a toll-free bridge to
CFURL
, making an
NSURL *
and a
CFURLRef
equivalent.
We took advantage of this in chapter 18 when dealing with the
MPMoviePlayerCon-
troller
and with sounds. Whenever you need to create a
CFURLRef
, you can do so
using the standard methods for
NSURL
creation that are described in this chapter.
400
C
HAPTER
20

The web: web views and internet protocols
Once you’ve got an
NSURL
in hand, you can do any number of things with it:

You can pass it on to functions that require a bare
NSURL
, as was the case with
those media functions in chapter 18.

You can query its properties to easily break down the
URL
to its parts. As usual,
you can find a complete list of properties in the Apple reference, but properties
like
baseURL
,
fragment
,
host
,
path
,
port
, and
query
might be particularly useful.

You can use the
NSURL

to load up a
UIWebView
.
The first two possibilities require only the use of an
NSURL
, but when you’re working with
a
UIWebView
, you must first create an
NSURL
and then turn it into an
NSURLRequest
.
20.3.2 Building an NSURLRequest
The
NSURLRequest
class contains two parts: a
URL
and a specific policy for dealing
with cached responses. As noted in table 20.2, there are four ways to create an
NSURL-
Request
, though we expect that you’ll usually fall back on the simple factory method,
requestWithURL:
.
By default, an
NSURLRequest
is built with a caching policy that’s dependent upon the
protocol, and a timeout value of 60 seconds, which should be sufficient for most of
your programming needs. If you need to get more specific about how things are

loaded, you can call
requestWithURL:cachePolicy:timeoutInterval:
, giving it an
NSURLRequestCachePolicy
for the policy and an
NSTimeInterval
for the timeout.
You can also create a more interactive
NSURLRequest
by using the
NSMutableURL-
Request
class, which allows you to more carefully form and modify the request that
you’re sending. We’ll talk about this in section 20.6, when we examine how to send
POST
requests from an iPhone.
The
NSURLRequest
will get you through most web page work. As with the
NSURL
,
there are a few different things that you can do with an
NSURLRequest
. You can hand it
off to a
UIImageView
, or you can use it to read in the contents of a web page, to later
manipulate it by hand.
20.3.3 Manipulating HTML data by hand
To read the contents of a web page manually, you need to access an

NSURLRequest
’s
properties. Table 20.3 lists some of the most important ones, though, as usual, more
information can be found in the class reference.
Table 20.2 The related
NSURLRequest
init methods
Method Summary
requestWithURL:
Creates a default request from the URL; equiva-
lent to
initWithURL:
requestWithURL:cachePolicy:timeoutInterval:
Creates a request with specific caching
choices; equivalent to
initWithURL:
cachePolicy:timeoutInterval:
401Using UIWebView
The catch with these properties is that you can only work with well-defined
HTML
pages.
Most notably, the
NSURLRequest
properties can’t read fragments, such as would be gen-
erated by Ajax or
JSON
, nor can they parse other sorts of content, such as
XML
or
RSS

.
You may also discover that you need a more interactive way to deal with
HTML
data. In
this case, you’ll probably use an
NSURLConnection
object; but as with the
NSMut-
ableURLRequest
, we’re going to save that for later, because you’ll typically only need
to use it when you’re
POST
ing information to a web page rather than just retrieving it.
For the moment, we’re going to put all of these complexities aside and instead
look at how to display straight
HTML
data using the
SDK
’s
UIWebView
.
20.4 Using UIWebView
One of the easiest ways to connect up to the internet is to use the
UIWebView
class,
which gives you full access to web pages of any sort. In some ways, this class is of lim-
ited utility, because it largely duplicates Safari, and Apple isn’t interested in approving
applications that just duplicate their existing technology. But there are clearly situa-
tions where you’ll want a program to be able to refer to some specific web pages, and
that’s what

UIWebView
is for.
The class is easy to use—we included it in simple examples way back in chapters 11
and 12. The only real complexity is in building an
NSURL
or
NSURLRequest
object to
get your web view started, but that process follows the methods we’ve already seen.
Table 20.3
NSURLRequest
can give access to a page’s content
Property Summary
allHTTPHeaderFields
Returns an
NSDictionary
of the header
HTTPBody
Returns an
NSData
with the body
valueforHTTPHeaderField:
Returns an
NSString
with the header
Other ways to read HTTP content
If you’re not reading data that meets the HTML protocol, you can’t use
NSURLRe-
quest
’s properties to access the data. Instead, you must fall back on other functions

that let you read in data from an
NSURL
.
We’re already met functions that read data that follows other protocol specifications,
such as the
MPMoviePlayerController
and the sound players from chapter 18.
Similarly, in this chapter we’ll talk about an XML parser. All of these classes can read
directly from a URL.
If you need to capture raw data that isn’t set in HTML, the best way to do so is with
an init or factory method that reads from a URL, such as
NSData
’s
dataWithCon-
tentsOfURL:
. We’ll look at an example of that in the last section of this chapter.
402
C
HAPTER
20
The web: web views and internet protocols
20.4.1 Calling up the web view
There are two main ways to fill a web view once you’ve created it, as listed in table 20.4.
Most frequently, you’ll start with an
NSURLRequest
, which you must have created using
the two-step process that we described in the previous section, but you can also load a
web view with an
NSURL
and an

NSString
. A few other init methods can be found in
the class reference.
Assuming you use the more common
NSURLRequest
method, you can put all the les-
sons you’ve learned so far together, which is just what you did back in chapter 11 when
you created your first
UIWebView
:
[myWebView loadRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:url]]];
Once you’ve got a
UIWebView
, you can start working with it. There are five
UIWebView
methods and properties of particular note, which are summarized in table 20.5.
The most exciting options in our mind are the
goBack
,
goForward
, and
reload
methods,
which can give you some control over how the
UIWebView
moves among pages. Similarly,
the
loadRequest:

method can be continually rerun if you want to move a user through
multiple pages, treating the
UIWebView
more like a web slideshow than a browser.
In our opinion, the
scalesPageToFit
property does not work correctly at
the current time. It always scales the page as if the
UIWebView
were full
screen, and it leaves a less than optimal view if you create a small
UIWeb-
View
, as we will do in our next example. We expect this to be resolved in
a future version of the
SDK
.
Table 20.4 Methods for loading
UIWebView
Method Summary
loadHTMLString:baseURL:
Loads a page from a URL and a string
loadRequest:
Loads a page from an
NSURLRequest
Table 20.5 Some sterling
UIWebView
options
Method/Property Type Summary
detectsPhoneNumbers

Property Boolean that determines whether phone numbers become links
goBack
Method
Moves back a page; check
canGoBack
property first
goForward
Method
Moves forward a page; check
canGoForward
property first
reload
Method Reloads the current page
scalesPageToFit
Property Boolean that determines whether the page is zoomed into a
viewport and whether user zooming is allowed
WARNING
403Using UIWebView
As we wrote in chapter 12, the biggest gotcha in using a
UIWebView
is that you can’t load
a URL straight from Interface Builder. Expect to always use the
NSURL
-
NSURLRequest
-
loadRequest:
process that we’ve laid out here to load up pages into your web views.
20.4.2 Managing the web view delegate
There’s one other element that we didn’t discuss when we talked about web views in

chapters 11 and 12: you can set a delegate to manage a few common responses. You
must follow the
UIWebViewDelegate
protocol, which lists four methods, described in
table 20.6.
Together with the
UIWebView
methods, these delegate methods give you considerable
power. You could use them to load alternative web pages if the preferred ones don’t
load. Or, continuing our slideshow analogy, you could use them to continuously load
new pages when old ones finish. All those possibilities highlight the ways that you
might be able to use the
UIWebView
as more than just a Safari clone.
20.4.3 Thumbnails: a web view example
As we’ve previously stated,
UIWebView
s are pretty easy to set up, and we’re not going to
spend a lot of time on a coding sample. Listing 20.2 presents a simple example that
creates a set of web page thumbnails, similar to the startup page of the Google
Chrome browser. It uses delegates first to get rid of
UIWebView
s that don’t load, and
later to zoom in on the one the user selects.
It should be initially created in Interface Builder by laying out four
UIWebView
s.
Make sure that they’re set to scale, and set their delegates to be the view controller.
- (void)viewDidLoad {
[super viewDidLoad];

webArray = [[NSArray alloc]
initWithObjects:webView1,webView2,webView3,webView4,nil];
NSString *paths = [[NSBundle mainBundle] resourcePath];
NSString *filePath = [paths
stringByAppendingPathComponent:@"weblist.txt"];
NSString *webList = [NSString stringWithContentsOfFile:filePath];
NSArray *webListArray = [webList componentsSeparatedByString:@"\n"];
for (int i = 0 ; i < [webArray count] ; i++) {
Table 20.6 Managing
UIWebView
s with delegate methods
Method Summary
webView:shouldStartLoadWithRequest:navigationType:
Called prior to content loading
webViewDidStartLoad:
Called after content begins loading
webViewDidFinishLoad:
Called after content finishes loading
webView:didFailLoadWithError:
Called after content fails to load
Listing 20.2 A thumbnail web viewer
Sets up web
views
B
404
C
HAPTER
20
The web: web views and internet protocols
[[webArray objectAtIndex:i] loadRequest:

[NSURLRequest requestWithURL:
[NSURL URLWithString:
[webListArray objectAtIndex:i]]]];
}
}
- (void)webView:(UIWebView *)webView
didFailLoadWithError:(NSError *)thiserror {
NSLog(@"Web Thumbs Error: %@",thiserror);
if (thiserror.code == -1003) {
[webView removeFromSuperview];
}
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (webView.canGoBack == YES) {
for (int i = 0 ; i < [webArray count] ; i ++) {
if ([webArray objectAtIndex:i] != webView) {
[[webArray objectAtIndex:i] removeFromSuperview];
} else {
webView.frame = [[UIScreen mainScreen] bounds];
}
}
}
}
To start with, you read a set of (exactly) four
URL
s from a file and use the
NSString
method
componentsSeparatedByString:
to turn them into an

NSArray
that you use to
seed your web views
B
. After that, it’s a question of
responding to delegation messages.
The
webView:didFailLoadWithError:
method
C
shows off some valuable techniques for both debug-
ging and error management.
NSLog
is what you want to
use when you want to do a
printf
-style reporting of
runtime variables. It’ll output to
/var/log/system.log
when you run it inside the iPhone Simulator.
Within a
UIWebView
, there are two error codes that
will come up with some frequency: -1003 is “Can’t find
host” and -999 is “Operation could not be completed.”
This example ignores -999 (which usually means that the
user clicked a link before the page finished loading), but
in the case of a -1003 failure, you dismiss the web view.
Finally, the
webViewDidFinishLoad:

method
D
zooms in on an individual web view (dismissing the
rest) once a user clicks on a link. Realistically, this
should occur whenever the user touches the web view,
but we wanted to show the
UIWebView
delegate meth-
ods, so we chose this slightly more circuitous route.
And that’s it—a simple web thumbnail program, as
shown in figure 20.2. It could be improved by giving the
Resolves
errors
C
Zooms active
view
D
Figure 20.2 The thumbnail
program removes web views
that fail to load.
405Using UIWebView
user the ability to manage the selected
URL
s and by polishing up the way the user selects
an individual page (including an option to return to the thumbnail page afterward).
For our purposes, though, it does a great job of demonstrating some of the intricacies
of the
UIWebView
.
Before we finish with web views entirely, we’re going to look at one more example.

Back in chapter 17, we talked about how Core Location would be better served once
we got into the world of the internet. We’re going to look at the first of two Core Loca-
tion internet examples.
20.4.4 Google Maps: a Core Location example
Google Maps should be an ideal way to show off Core Location, because it’s already
built into the iPhone and because it can take longitude and latitude as
URL
arguments. Unfortunately, the reality falls a little short of that ideal at the time of
this writing.
There are two problems. Most notably, as a programmer, you have no access to the
proprietary Google Maps interface built into the iPhone. Instead, you have to use a
UIWebView
to show the map results (which is why we’re covering this example in this
section). That leads to the second problem, which is that Google Maps often sizes
itself in weird ways when displayed in a
UIWebView
. This is probably due to some com-
bination of
UIWebView
always assuming that it will appear full screen—which we’ve
already discussed—and Google Maps trying to do the right thing when it detects that
you’re on the iPhone. We expect Google Maps’ presentation will slowly improve
through future releases of the
SDK
.
Debugging
We haven’t talked about debugging your SDK program much in this book, primarily
for reasons of space. Here’s a short overview of our favorite techniques:
Xcode itself provides the best debugging. Pay careful attention to autocompletion of
words and note when an expected autocompletion doesn’t occur, because that usu-

ally means you didn’t set a variable correctly.
The warnings and errors that appear on compilation should always be carefully
considered.
During Simulator runtime, we suggest keeping an eye on /var/log/system.log on your
Mac. You can do this by opening a terminal and running
tail -f /var/log/sys-
tem.log
. You can log to the system log by hand with
NSLog
. If a program crashes at
runtime, you’ll usually see an error here, and then you can go to Xcode to step back
through a trace to see exactly where the crash occurred.
Finally, once you’re done with your program you should run it through Instruments to
check for memory leaks.
For more information, take a look at the “Xcode Debugging Guide,” “Debugging with
GDB,” and the “Instruments User Guide,” Apple articles which contain comprehen-
sive explanations of those subjects.

×