Chapter    7

Consuming Web Content

This chapter covers how to use web content with Objective-C.

The recipes in this chapter will show you how to:

  • Download files with NSURL
  • Use web services with XML and JSON
  • Parse both XML and JSON data
  • Use NSURLConnection to asynchronously consume web content

7.1 Downloading a File

Problem

You want to download a file from the Internet.

Solution

Use NSURL to specify a URL for a file and then use NSData to download the contents of that file into your file system.

NOTE: URL stands for Uniform Resource Locator. A URL is a character string that specifies the location of an Internet resource. NSURL is a Foundation class that lets you use URLs in Objective-C.

How It Works

For this solution you must have a file available on the Internet that you can download. I posted a text file to my blog to use in this example. The URL of that file is:

http://howtomakeiphoneapps.com/wp-content/uploads/2012/03/objective-c-recipes-Image
example-file.txt

The first part of this process requires you to create a new NSURL object with the URL of the resource that you want to download. Using the URL just provided, it looks like this:

NSURL *remoteTextFileURL = [NSURL Image
URLWithString:@"http://howtomakeiphoneapps.com/wp-Image
content/uploads/2012/03/objective-c-recipes-example-file.txt"];

Next, create a new NSData object with the contents of the NSURL object.

NSData *remoteTextFileData = [NSData dataWithContentsOfURL:remoteTextFileURL];

You can use the NSData object right in your Objective-C program (see Recipe 4.11 for examples of working with NSData) or save it to the file system like this:

[remoteTextFileData writeToFile:@"/Users/Shared/objective-c-recipes-example-file.txt"
                     atomically:YES];

See Listing 7-1 for the code.

The Code

Listing 7-1. main.m

#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]){
    @autoreleasepool {
        NSURL *remoteTextFileURL = [NSURL URLWithString:Image
@"http://howtomakeiphoneapps.com/wp-content/uploads/2012/03/objective-c-recipes-Image
example-file.txt"];
        NSData *remoteTextFileData = [NSData dataWithContentsOfURL:remoteTextFileURL];
        [remoteTextFileData writeToFile:@"/Users/Shared/objective-c-recipes-Image
example-file.txt"
                             atomically:YES];

    }
    return 0;
}

Usage

To try this recipe out, set up a Mac command-line Xcode project and change the code in your main.m file to look like the code in Listing 7-1. Build and run the command-line app to download the file into this location on your Mac:

/Users/Shared/objective-c-recipes-example-file.txt

Locate and open this file to see if the download was successful.

7.2 Consuming a Web Service Using XML

Problem

You would like to add web services that use XML data to your application.

NOTE: Internet companies publish web services to allow developers to include their services in the developer’s applications. Web services work like a web browser. In a web browser, you type in a web address (the request), hit return, and wait for a response from a remote computer on the Internet. When that response comes back, the web browser uses the rules and content in the response to present a web page to you. Web services work the same way except that the application sends the request and gets the response.

Internet companies do their best to formulate web service requests and responses using standard formats that make it easier for the applications to use their services. Web requests are strings of characters (like a web address) while web responses are strings of characters formatted as XML or JSON. XML and JSON will be discussed in full later.

Solution

Formulate a request string based on the documentation that the publisher of the web service provides. Create an NSURL object based on the request string and NSData to download the response from the web service. Use NSXMLParser to go through the XML document that you get back.

How It Works

For this recipe, you are going to learn how to consume a web service that is provided by a company called bitly. This company publishes a web service that you can use to shorten a long URL. All you have to do is send a request to the bitly web service with the long URL along with your bitly credentials in the format that they expect; bitly sends you an XML file with the shortened URL included in the contents.

NOTE: To follow along with this recipe, you need to create a (free) account with bitly and get your own API key and API username. Go to https://bit.ly to get your account.

I’m going to work through this recipe with a command line-based Mac application in Xcode, but you can follow with any project type that you like. Since NSXMLParser uses the Delegation design pattern, you need to locate your code in a class that can adopt a protocol and otherwise support Delegation. Add a new class to your project by going to File Image New File Image Objective-C class. Name the class LinkShortener.

The interface for LinkShortener needs to include a forward declaration for an NSMutableString named recorderString that will record the data you get from the web service. LinkShortener also needs a string for keeping track of the area in the XML file where the XML parser is currently looking. Call that variable currentElement and make both currentElement and recorderString private. Also, you need a forward declaration for the function that you call when you want this object to shorten a URL; call this function getTheShortUrlVersionOfThisLongURL. The interface for LinkShortener should look like this:

#import <Foundation/Foundation.h>

@interface LinkShortener : NSObject{
    @private
    NSMutableString *recorderString;
    NSString *currentElement;
}

-(NSString *)getTheShortURLVersionOfThisLongURL:(NSString *)longURL;

@end
How XML Parsing Works

Before we move on, let’s discuss what XML is and how NSXMLParser reads XML documents. XML stands for EXtensible Markup Language and it’s used to store and transport data. XML works by enclosing data with opening and closing tags. Opening tags are characters surrounded by the characters < and >. Closing tags are characters surrounded by the characters </ and >. The tags and data together are referred to as an element.

For example, if I had an XML data type for a person, I might use an opening tag like <Person>. The closing tag would look like </Person>. The characters in the middle are the data. The whole thing together looks like this:

<Person>Matthew J. Campbell</Person>

XML tags are intended to be descriptive so it’s obvious what the tags mean. An entire document will have many tags with data and can be arranged in a hierarchy. So you may have tagged data within other tagged data, like this:

<Person>
        <Name>Matthew J. Campbell</Name>
        <Gender>Male</Gender>
</Person>

NSXMLParser reads through an XML document starting from the beginning, reading element by element until it reaches the end. If NSXMLParser was reading the XML above, it would start with the Person element, and then move on to the Name element, and then the Gender element. This method of parsing XML is called Simple API for XML (SAX).

You’re going to use delegation to parse the XML data that you get from bitly. As the parser looks at each element in the document, it sends a message to the delegate LinkShortener object, giving you a chance to extract the data from each element.

So LinkShortener needs to be able to act as a delegate for NSXMLParser and this means that you need LinkShortener to adopt the NSXMLParserDelegate protocol. Do this by including the protocol name right after the NSObject superclass.

#import <Foundation/Foundation.h>

@interface LinkShortener : NSObject<NSXMLParserDelegate>{
    @private
    NSMutableString *recorderString;
    NSString *currentElement;
}

-(NSString *)getTheShortURLVersionOfThisLongURL:(NSString *)longURL;

@end

Now that you have adopted this protocol, you need to implement at least these two delegate methods: parser:didStartElement:namespaceURI:qualifiedName:attributes: and parser:foundCharacters:.

Since your application only needs data from one element in the XML file, you can get away with only these two delegate methods. But, if necessary, you can always implement parser:didEndElement:namespaceURI:qualifiedName: if you want to be notified when the parser encounters a closing tag in the XML data.

Open LinkShortener.m to implement the first delegate method.

- (void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
   namespaceURI:(NSString *)namespaceURI
  qualifiedName:(NSString *)qName
     attributes:(NSDictionary *)attributeDict{

        currentElement = [elementName copy];
        if ([elementName isEqualToString:@"shortUrl"]) {
                recorderString = [[NSMutableString alloc] init];
        }
}

Remember that this delegate method executes each time a new XML element is reached (most XML files have lots of elements in them). For this reason, make a copy of the parameter elementName and put it into currentElement. You want to be able to keep track of what element you are in when the other delegate methods execute.

The other significant part of this code is the if statement where you test to see if you are in the element that corresponds to shortUrl. This is the element where bitly puts the shortened URL string. If you do encounter the shortULR element, you will create and initialize a new NSMutableString to be used later to record what is found in the element.

Now you can implement the next delegate method. This delegate method executes each time characters are encountered in an XML element in the file. You can test to see if the XML parser is in the shortUrl element. If the answer is yes, append the characters that are found to the recorderString NSMutableString.

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
        if ([currentElement isEqualToString:@"shortUrl"])
                [recorderString appendString:string];

}

All of this prepares LinkShortener to receive the XML data. Now you can prepare the request and send that off to the web server to download the response. All of this takes place in the function getTheShortUrlVersionOfThisLongURL:, which you can start coding in the LinkShortener implementation file.

-(NSString *)getTheShortUrlVersionOfThisLongURL:(NSString *)longURL{

}

The first thing you want to do in this function is to compose the request string. To do this, you compose a string based on the request string that bitly requires along with the longURL parameter and your own API key and API login.

#warning Get your API Login from https://bitly.com/a/your_api_key and put it here before Image
running
NSString *APILogin = @"[YOUR API LOGIN]";
#warning Get your API key from https://bitly.com/a/your_api_key and put it here before Image
running
NSString *APIKey = @"[YOUR API KEY]";

NSString *requestString = [[NSString alloc] initWithFormat:Image
@"http://api.bit.ly/shorten?version=2.0.1&longUrl=%@&login=%@&apiKey=%@&format=xml",Image
 longURL, APILogin, APIKey];

I’ve included warnings in the example code so that you remember to include your own credentials here. Also, if you look closely at the first part of the request string you’ll see that there is a format parameter with the value of xml being returned, (format=xml). This is how you tell bitly that you want the response to come back as XML.

Next, you need an NSURL object, which you can make based on the request string.

NSURL *requestURL = [NSURL URLWithString:requestString];

To download the data, use NSData as you did in Recipe 7.1.

recorderString = nil;
NSData *responseData = [NSData dataWithContentsOfURL:requestURL];

Here is a good place to set recorderString to nil just in case this function has been used before in this object’s lifetime. Now that you’ve downloaded the data, you may use NSXMLParser to go through the XML and pick out the content included in the shortUrl element.

To do this, instantiate a new NSXMLParser with the downloaded NSData object, set this object’s delegate to self, and then send the parse message to the NSXMLParser object.

NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:responseData];
xmlParser.delegate = self;
[xmlParser parse];

At this point, the XML parser looks through the data and uses the delegate methods previously coded to pick out the meaningful content. Everything that the XML parser finds is recorded in recorderString. Once the XML parser is finished, you can return the results back to the caller.

if(recorderString)
     return [recorderString copy];
else
     return nil;

You can use an if statement here to send a copy of recorderString if any data was found.

Finally, to use the function from another part of your program, you need to import the LinkShortener header file, instantiate a LinkShortener object, and then use the function with a long URL. Here is how to do this from main.m:

#import <Foundation/Foundation.h>
#import "LinkShortener.h"

int main(int argc, const char * argv[]){
    @autoreleasepool {

        NSString *longURL = @"http://howtomakeiphoneapps.com/how-to-Image
asynchronously-add-web-content-to-uitableview-in-ios/1732/";

        LinkShortener *linkShortener = [[LinkShortener alloc] init];

        NSString *shortURL = [linkShortener getTheShortURLVersionOfThisLongURL:longURL];

        NSLog(@"shortURL = %@", shortURL);

    }
    return 0;
}

See Listings 7-2 through 7-4 for the code.

The Code

Listing 7-2. LinkShortener.h

#import <Foundation/Foundation.h>

@interface LinkShortener : NSObject<NSXMLParserDelegate>{
    @private
    NSMutableString *recorderString;
    NSString *currentElement;
}

-(NSString *)getTheShortURLVersionOfThisLongURL:(NSString *)longURL;

@end

Listing 7-3. LinkShortener.m

#import "LinkShortener.h"

@implementation LinkShortener

-(NSString *)getTheShortURLVersionOfThisLongURL:(NSString *)longURL{

    #warning Get your API Login from https://bitly.com/ and put it here before running
    NSString *APILogin = @"[YOUR API LOGIN]";
    #warning Get your API key from https://bitly.com/ and put it here before running
    NSString *APIKey = @"[YOUR API KEY]";
  
    NSString *requestString = [[NSString alloc] initWithFormat:Image
@"http://api.bit.ly/shorten?version=2.0.1Image
&longUrl=%@&login=%@&apiKey=%@&format=xml",Image
longURL, APILogin, APIKey];

    NSURL *requestURL = [NSURL URLWithString:requestString];
    recorderString = nil;
    NSData *responseData = [NSData dataWithContentsOfURL:requestURL];
    NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:responseData];
    xmlParser.delegate = self;
    [xmlParser parse];

    if(recorderString)
        return [recorderString copy];
    else
        return nil;;
}

- (void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
   namespaceURI:(NSString *)namespaceURI
  qualifiedName:(NSString *)qName
     attributes:(NSDictionary *)attributeDict{

        currentElement = [elementName copy];
        if ([elementName isEqualToString:@"shortUrl"]) {
                recorderString = [[NSMutableString alloc] init];
        }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
        if ([currentElement isEqualToString:@"shortUrl"])
                [recorderString appendString:string];

}

@end

Listing 7-4. main.m

#import <Foundation/Foundation.h>
#import "LinkShortener.h"

int main(int argc, const char * argv[]){
    @autoreleasepool {

        NSString *longURL = @"http://howtomakeiphoneapps.com/Image
how-to-asynchronously-add-web-content-to-uitableview-in-ios/1732/";

        LinkShortener *linkShortener = [[LinkShortener alloc] init];

        NSString *shortURL = [linkShortener getTheShortURLVersionOfThisLongURL:longURL];

        NSLog(@"shortURL = %@", shortURL);

    }
    return 0;
}

Usage

To use the URL shortener function, import the header file into the class where you want to use the functionality. Then instantiate a LinkShortener object from the LinkShortener class. Finally, to use the function, send the message getTheShortURLVersionOfThisLongURL: with the long URL as a parameter to the LinkShortener object. The function will return the shortened URL if the web request was successful and nil if the request was not successful. Here is what you should see in your own console log:

shortURL = http://bit.ly/yFmJFh

NOTE: The shortened URL that you receive back from bitly may not look exactly like the one I received when I tested this code.

7.3 Consuming a Web Service Using JSON

Problem

You would like to add web services that use JSON data to your application.

NOTE: JSON is an alternative to XML that many Internet companies use when implementing web services. JSON stands for JavaScript Object Notation and is used for data storage and transportation. Web services that are implemented as REST (REpresentational State Transfer) web services provide both XML and JSON response data. Other types of web services may only provide one or the other.

Solution

As in Recipe 7.2, formulate a request string based on the documentation that the publisher of the web service provides. Create an NSURL object based on the request string and NSData to download the response from the web service. Use NSJSONSerialization to parse the JSON data that you get back.

NOTE: NSJSONSerialization is available starting with Mac OSX 10.7 and iOS 5.0.

How It Works

This recipe uses the same bitly web service and requires the same process to request and download the results as was shown in Recipe 7.2. However, NSJSONSerialization doesn’t use delegation, so you don’t need to add a new file or class to accommodate JSON parsing with NSJSONSerialization.

Since you don’t need a separate class for this, you can locate the code needed to construct the request string wherever in your application you need to use the web service. If you continue to use a command-line Mac app, this code could go right into the main.m file. Here is how to construct the request string:

NSString *longURL = @"http://howtomakeiphoneapps.com/Image
how-to-asynchronously-add-web-content-to-uitableview-in-ios/1732/";

#warning Get your API Login from https://bitly.com/ and put it here before running
NSString *APILogin = @"[YOUR API LOGIN]";
#warning Get your API key from https://bitly.com/ and put it here before running
NSString *APIKey = @"[YOUR API KEY]";

NSString *requestString = [[NSString alloc] initWithFormat:Image
@"http://api.bit.ly/shorten?version=2.0.1Image
&longUrl=%@&login=%@&apiKey=%@&format=json", longURL, APILogin, APIKey];

Then you can use NSURL and NSData to get the response based on this request string.

NSURL *requestURL = [NSURL URLWithString:requestString];

NSData *responseData = [NSData dataWithContentsOfURL:requestURL];
JSON Parsing

Next you’ll see how to parse JSON. Instead of the tagged data scheme that XML uses, JSON organizes content based on two structures: a collection of name-value pairs and an ordered list of values. A JSON collection of name-value pairs follows the same pattern as an Objective-C NSDictionary while a JSON order list of values corresponds to an Objective-C NSArray.

If you look at a JSON file, you will see these types of structures organized by curly braces instead of the tagged data in an XML file. For example, the JSON version of the Person element described in Recipe 7.2 looks like this:

{"Person":"Matthew J. Campbell","Gender":"Male"}

Since JSON data is keyed in this way, and the dictionary and array structures map so well to programming languages, JSON is generally much easier to parse. As you’ll see in a moment, you have a Foundation function at your disposal that simply turns your JSON response into an NSDictionary with the JSON content ready for you to use.

The first thing you need is an NSError object, which you pass with the JSONObjectWithData:options:error: message that must be sent to NSJSONSerialization.

NSError *error = nil;
NSDictionary *bitlyJSON = [NSJSONSerialization JSONObjectWithData:responseData
                                                          options:0
                                                            error:&error];

The result of this function is assigned to an NSDictionary that holds the contents of the JSON data. To get to the content that you need, simply access the various objects that are in the dictionary. This often requires you to reference nested dictionaries, arrays, and objects. You need to examine the response data to figure out precisely what you need. Here is what you do to get the bitly short URL from your response data:

if(!error){
    NSDictionary *results = [bitlyJSON objectForKey:@"results"];
    NSDictionary *resultsForLongURL = [results objectForKey:longURL];
    NSString *shortURL = [resultsForLongURL objectForKey:@"shortUrl"];
    NSLog(@"shortURL = %@", shortURL);
}
else{
    NSLog(@"There was an error parsing the JSON");
}

See Listing 7-5 for the code.

The Code

Listing 7-5. main.m

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]){
    @autoreleasepool {

        NSString *longURL = @"http://howtomakeiphoneapps.com/Image
how-to-asynchronously-add-web-content-to-uitableview-in-ios/1732/";

         #warning Get your API Login from https://bitly.com/ and put it here before Image
running
         NSString *APILogin = @"[YOUR API LOGIN]";
         #warning Get your API key from https://bitly.com/ and put it here before Image
 running
         NSString *APIKey = @"[YOUR API KEY]";

        NSString *requestString = [[NSString alloc] initWithFormat:
@"http://api.bit.ly/shorten?version=2.0.1Image
&longUrl=%@&login=%@&apiKey=%@&format=json", Image
longURL, APILogin, APIKey];

        NSURL *requestURL = [NSURL URLWithString:requestString];
        NSData *responseData = [NSData dataWithContentsOfURL:requestURL];

        NSError *error = nil;
        NSDictionary *bitlyJSON = [NSJSONSerialization JSONObjectWithData:responseData
                                                                  options:0
                                                                    error:&error];
        if(!error){
            NSDictionary *results = [bitlyJSON objectForKey:@"results"];
            NSDictionary *resultsForLongURL = [results objectForKey:longURL];
            NSString *shortURL = [resultsForLongURL objectForKey:@"shortUrl"];
            NSLog(@"shortURL = %@", shortURL);
        }
        else{
            NSLog(@"There was an error parsing the JSON");
        }

    }
    return 0;
}

Usage

You can use this code from any area in your application. If you are testing this with a Mac command-line application, you can simply include this code in your main.m file, but you need to obtain an API login and API login from bitly. Examine the console log window to see the results of the web service request. You should see something like this:

shortURL = http://bit.ly/yFmJFh

NOTE: JSON requires far fewer steps than XML and will probably be your first choice when working with web services (when available). However, be aware that JSON may not always be available from the web service and that you must be using Mac OSX 10.7 or iOS 5 or greater to use JSON.

7.4 Asynchronously Consuming Web Content

Problem

You want to be able to consume web content as a background process so that the network activity doesn’t affect your user interface.

Solution

Use NSURLConnection and NSURLRequest when you want to work with the network asynchronously or if you need more control over the process of using network connections and web requests.

How It Works

The first thing you need is a request string to send to a web server. You could use a request string for a web service (as you did in Recipes 7.2 and 7.3) or you could even put in a web page. For this example, I’ll use the RSS feed for my blog since I know it will provide some XML data to download.

NOTE: RSS stands for Really Simple Syndication. RSS is used for publishing content like blog posts and podcasts. RSS files are usually large files that are based on XML but also follow additional specifications that help when publishing content. Adding an RSS feed to your application is an easy way to publish information to your users since most blogging software comes with built-in RSS features.

For this recipe, I’m going to use a Mac Cocoa application. You could also use an iOS application here, but you will have problems if you attempt to use a command-line Mac application. This is because the asynchronous methods used with NSURLConnection may take longer to execute than the Mac command-line application’s lifecycle, so you may never see the results in the console log.

The code is located in the Mac Cocoa application’s AppDelegate.m file right in the applicationDidFinishLaunching: delegate method. The first thing you need is the request string (which, in this example, is my blog’s RSS feed).

NSString *requestString = @"http://www.howtomakeiphoneapps.com/feed/";

Then you can construct an NSURL object based on the request string.

NSURL *requestURL = [NSURL URLWithString:requestString];

Use the requestURL object to instantiate a new NSURLRequest.

NSURLRequest *request = [[NSURLRequest alloc] initWithURL:requestURL
                                              cachePolicy:NSURLRequestReloadImage
IgnoringLocalCacheData
                                          timeoutInterval:10];

You also get a chance to specify how you want the request to handle caching and a timeout interval. Next, you need to set up an NSOperationQueue, which will be used with NSURLConnection to execute the web request.

NSOperationQueue *backgroundQueue =[[NSOperationQueue alloc] init];

Finally, use a class method to execute your web request asynchronously. You need three parameters: the NSURLRequest object, the NSOperationQueue object, and a code block. The code block gives you chance to let NSURLConnection know what code to execute after the data is retrieved.

[NSURLConnection sendAsynchronousRequest:request
                                   queue:backgroundQueue
                       completionHandler:^(NSURLResponse *response, NSData *data, Image
NSError *error) {

    if(!error){
         NSString *requestResults = [[NSString alloc] initWithData:data
                                         encoding:NSStringEncodingConversionAllowLossy];

         NSLog(@"requestResults=%@", requestResults);
    }
    else
         NSLog(@"error=%@", error);

                }];

NOTE: This feature is only available starting with Mac OSX 10.7 and iOS 5.0.

Take a close look at the completionHandler block to see how to handle the NSData object that is returned. Usually you test the NSError object to see if everything went well before processing the data. Here all you are doing is writing out the entire RSS feed to the console log, but if you want to process the RSS feed, you can set up an XML parser like the one detailed in Recipe 7.2. See Listings 7-6 through 7-8 for the code.

The Code

Listing 7-6. main.m

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[]){
    return NSApplicationMain(argc, (const char **)argv);
}

Listing 7-7. AppDelegate.h

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>

@property (assign) IBOutlet NSWindow *window;

@end

Listing 7-8. AppDelegate.m

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{

    NSString *requestString = @"http://www.howtomakeiphoneapps.com/feed/";

    NSURL *requestURL = [NSURL URLWithString:requestString];

    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:Image
requestURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];

    NSOperationQueue *backgroundQueue =[[NSOperationQueue alloc] init];

    [NSURLConnection sendAsynchronousRequest:request
                                       queue:backgroundQueue
                           completionHandler:^(NSURLResponse *response, NSData *data, Image
NSError *error) {

                   if(!error){
                        NSString *requestResults = [[NSString alloc] initWithData:dataImage
encoding:NSStringEncodingConversionAllowLossy];
                        NSLog(@"requestResults=%@", requestResults);
                              }
                              else{
                                   NSLog(@"error=%@", error);
                              }

                }];
}

@end

Usage

To try this recipe out, include the code in the app delegate of a Mac or iOS application. Inspect the log to see the data that was downloaded from the Web. To test the error handling, disconnect your Mac from the network and inspect the log to see what the error object reports. If your web request was successful, you should see something like this appear in your console log:

requestResults=<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
        xmlns:content="http://purl.org/rss/1.0/modules/content/"
        xmlns:wfw="http://wellformedweb.org/CommentAPI/"
        xmlns:dc="http://purl.org/dc/elements/1.1/"
        xmlns:atom="http://www.w3.org/2005/Atom"
        xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
        xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
        >

<channel>
        <title>How to Make iPhone Apps</title>
        <atom:link href="http://howtomakeiphoneapps.com/feed/" rel="self"Image
 type="application/rss+xml" />

. . .

This code can be included anywhere in your application where you want to consume web content, but don’t interfere with or block other processes like your user interface.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.27.72