Brief Intro to URL Handling of Foundation Framework (Examples: Downloading from a URL)

URL Handling

The URL handling classes are used for interacting with URLs and communicating with resources using standard Internet protocols (ftp, http, https, local files). The classes provide the following functionality:

  • URL loading
  • Cache management
  • Authentication and credentials management
  • Cookie management
  • Protocol support

These classes also support proxy servers and SOCKET Secure (SOCKS) gateways per the user’s system preferences.

URL Loading

NSURL objects are used to manage URLs and the resources they refer to. The class includes methods for creating and initializing NSURL objects, querying a URL, and accessing the components of a URL (e.g., host, port, etc.). NSURL also provides methods for working with bookmark data. Abookmark is a persistent reference to the location of a file. It is a valuable tool for locating files because it can be used to re-create a URL to a file, even if the file is moved or renamed. The following code fragment demonstrates the use of the NSURL APIs to create an NSURLobject and access its components.

NSURL *url = [NSURL URLWithString:@"http://www.apress.com:80
"];
NSLog(@"URL: scheme %@, host %@, port %@", url.scheme, url.host, url.port);

The classes NSURLRequest and NSMutableURLRequest are used to represent a URL load request. NSURLRequest is independent of the network protocol and URL scheme. The class includes methods to create and initialize a URL request, and to retrieve request properties.NSMutableURLRequest is a subclass of NSURLRequest that provides methods that enable you to change a URL and its component parts. The following statement creates a mutable URL request, and then uses thesetHTTPMethod: method to set the HTTP request method value.

NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:
                            [NSURL URLWithString:@"http://www.apress.com:80
"]];
[req setHTTPMethod:@"GET"];

NSURLResponse and NSHTTPURLResponse classes are used to represent the response returned by a URL request. An NSURLConnection object is created by either an object that performs a synchronous load of a URL request (sendSynchronousRequest:returningResponse:error:) or objects that conform to the NSURLConnectionDataDelegate protocol. The NSHTTPURLResponse class is a subclass of NSURLResponse that provides methods for accessing HTTP-specific information returned by a URL request.

The NSURLConnection and NSURLDownload classes are used to download the contents of a resource identified by a URL. NSURLConnectionsupports synchronous and asynchronous resource loading, starting and stopping a connection, and managing a delegate object used to control various aspects of a URL connection (i.e., cache management, authentication and credentials, protocol support, and cookie management). For asynchronous loading, a delegate object must be set. This object must conform to the NSURLConnectionDelegate protocol and thus implement, at a minimum, the following methods:

  • connection:didReceiveData: (to retrieve data loaded)
  • connection:didFailWithError: (to handle connection errors
  • connection:didFinishLoading: (to perform processing after a connection has finished loading data)

Listing 11-13 demonstrates use of various URL loading APIs to synchronously load the data from a URL.

Listing 11-13.  Using NSURLConnection to Synchronously Load the Contents of a URL

NSURL *url = [NSURL URLWithString:@"http://www.apress.com/index.html
"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSError *aerror;
NSData *data = [NSURLConnection sendSynchronousRequest:request
                                     returningResponse:&response
                                                 error:&aerror];
NSLog(@"Expected content length = %lld; loaded %lu bytes",
      [response expectedContentLength], [data length]);

As shown in Listing 11-13, the code creates an NSURL instance and then a corresponding NSURLRequest. It then creates a connection and loads the data using the NSURLConnectionsendSynchronousRequest:returningResponse:error: method. The result is returned in an NSData object.

The NSURLDownload class is used to asynchronously download the contents of a URL to a file. It includes methods to initialize a download (set request and delegate), set the destination path, resume a partial download, and cancel loading the request. The class also provides support for decoding files stored in the MacBinary, BinHex, and gzip formats. The delegate object must conform to theNSURLDownloadDelegate protocol and thus implement, at a minimum, the following methods:

  • download:didFailWithError: (to handle connection errors)
  • downloadDidFinish: (to perform processing after a connection has finished loading data)

The NSURLDownload class is available for use on the OS X platform only (i.e., this class is not available for the iOS platform).

Cache Management

The cache management classes (NSURLCache and NSCachedURLResponse) provide a cache memory for responses to URL requests. The NSURLCacheclass provides a general-purpose cache for URLs, whereas theNSCachedURLResponse encapsulates a URL response (NSURLResponseobject) and the data loaded from the URL (an NSData object). AnNSURLConnection delegate object implements theconnection:willCacheResponse: method, providing aNSCachedURLResponse object initialized with an NSURLCache, to manage caching. How this works is best illustrated by an example; so now you’ll create a program that uses the URL loading classes to download a resource. Let’s begin!

Downloading a Resource with the URL Loading APIs

In Xcode, create a new project by selecting New image Project . . . from the Xcode File menu. In the New Project Assistant pane create a command-line application. In the Project Options window, specifyNetConnector for the Product Name, choose Foundation for the Project Type, and select ARC memory management by selecting the Use Automatic Reference Counting check box. Specify the location in your file system where you want the project to be created (if necessary selectNew Folder and enter the name and location for the folder), uncheck the Source Control check box, and then click the Create button.

Now you’re going to create a class that downloads a URL resource using the URL loading APIs. Select New image File . . . from the Xcode File menu, select the Objective-C class template, and name the classNetConnector. Select the NetConnector folder for the files location and the NetConnector project as the target, and then click the Createbutton. Next, in the Xcode project navigator pane, select the resulting header file named NetConnector.h and update the interface, as shown in Listing 11-14.

Listing 11-14.  NetConnector Interface

#import <Foundation/Foundation.h>
#define HTTP_SCHEME       @"http"
#define CACHE_MEMORY_SIZE (4 * 1024 * 1024)

@interface NetConnector : NSObject <NSURLConnectionDelegate>

@property (readonly) BOOL finishedLoading;

- (id) initWithRequest:(NSURLRequest *)request;
- (void) reloadRequest;

@end

The NetConnector interface adopts the NSURLConnectionDelegateprotocol, thereby enabling it to asynchronously load data from anNSURLConnection instance. The property finishedLoading is used to indicate when the NSURLConnection instance is finished loading data. The initWithRequest: method initializes a NetConnector object and loads a URL using the input NSURLRequest. The reloadRequest method is used to reload the input NSURLRequest. Now select theNetConnector.m file and update the implementation, as shown inListing 11-15.

Listing 11-15.  NetConnector Implementation

#import "NetConnector.h"

// Extension to declare provide properties
@interface NetConnector()

@property NSURLRequest *request;
@property BOOL finishedLoading;
@property NSURLConnection *connector;
@property NSMutableData *receivedData;

@end

@implementation NetConnector

- (id) initWithRequest:(NSURLRequest *)request
{
  if ((self = [super init]))
  {
    _request = request;
    // Create URL cache with appropriate in-memory storage
    NSURLCache *URLCache = [[NSURLCache alloc] init];
    [URLCache setMemoryCapacity:CACHE_MEMORY_SIZE];
    [NSURLCache setSharedURLCache:URLCache];
    // Create connection and begin downloading data from resource
    _connector = [NSURLConnection connectionWithRequest:request delegate:self];
  }
  return self;
}

- (void) reloadRequest
{
  self.finishedLoading = NO;
  self.connector = [NSURLConnection connectionWithRequest:self.request
                                                 delegate:self];
}

#pragma mark -
#pragma mark Delegate methods

- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
  if (self.receivedData != nil)
  {
    [self.receivedData setLength:0];
  }
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
  NSURLResponse *response = [cachedResponse response];
  NSURL *url = [response URL];
  if ([[url scheme] isEqualTo:HTTP_SCHEME])
  {
    NSLog(@"Downloaded data, caching response");
    return cachedResponse;
  }
  else
  {
    NSLog(@"Downloaded data, not caching response");
    return nil;
  }
}

- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
   if (self.receivedData != nil)
   {
     [self.receivedData appendData:data];
   }
   else
   {
     self.receivedData = [[NSMutableData alloc] initWithData:data];
   }
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  NSUInteger length = [self.receivedData length];
  NSLog(@"Downloaded %lu bytes from request %@", length, self.request);
  // Loaded data, set flag to exit run loop
  self.finishedLoading = YES;
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
  NSLog(@"Error loading request %@", [error localizedDescription]);
  self.finishedLoading = YES;
}

@end

OK, so I know that this is a lot of code, but don’t worry, you’ll take it one step at a time!. The file begins with a category extension that declares private properties used within the NetConnector implementation. The implementation defines both the methods declared by the interface and methods declared by the NSURLConnectionDelegate protocol. TheinitWithRequest: method assigns the input NSURLRequest to the private NetConnector request property, creates and initializes anNSURLCache instance for caching responses, and ends by creating anNSURLConnection object and starting the download of the URL. The following statements from Listing 11-15 create the cache, configure it with in-memory storage, and then set it to be the shared cache used with the connection.

NSURLCache *URLCache = [[NSURLCache alloc] init];
[URLCache setMemoryCapacity:CACHE_MEMORY_SIZE];
[NSURLCache setSharedURLCache:URLCache];

The reloadRequest method reloads the URL. It first resets thefinishedLoading flag, and then creates a new NSURLConnection object with the saved request and downloads the URL.

The remaining methods implemented here areNSURLConnectionDelegate protocol methods. Theconnection:didReceiveResponse: message is sent when a connection is able to send a response to the request. As shown in Listing 11-15, this method sets the receivedData object to a length of zero, thereby discarding data received from previous requests.

The connection:willCacheResponse: message is sent before a connection stores the response in the cache. This enables the method implementation to modify the response or prevent it from being cached. As shown in Listing 11-15, the method performs conditional logic that enables/disables caching of the response based on whether or not the scheme for the URL is http.

The connection:didReceiveData: message is sent as the data is received from the resource. This method may be called multiple times during a single request if the data is received incrementally. As shown inListing 11-15, the method appends the received data to thereceivedData object.

The connectionDidFinishLoading: message is sent when a connection has finished loading the data successfully. The method implementation here just logs a message to the output pane, indicating that the connection has finished loading the resource, and then sets thefinishedLoading flag used to exit the run loop.

Finally, the connection:didFailWithError: message is sent when a connection fails to load a request successfully. Here you just log a message to the output pane, indicating the error that occurred, and set the finishedLoading flag.

OK, now that you have implemented the NetConnector class, let’s use this to load a URL. In the Xcode project navigator, select the main.m file and update the main() function, as shown in Listing 11-16.

Listing 11-16.  NetConnector main( ) Function

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

#define INDEX_URL       @"http://www.wikipedia.com/index.html
"

int main(int argc, const char * argv[])
{
  @autoreleasepool
  {
    // Retrieve the current run loop
    NSRunLoop *loop = [NSRunLoop currentRunLoop];

    // Create the request with specified cache policy, then begin downloading!
    NSURLRequest *request = [NSURLRequest
                             requestWithURL:[NSURL URLWithString:INDEX_URL]
                                cachePolicy:NSURLRequestReturnCacheDataElseLoad
                            timeoutInterval:5];
    NetConnector *netConnect = [[NetConnector alloc] initWithRequest:request];
    // Loop until finished loading the resource (Note the empty statement!)
    while (!netConnect.finishedLoading &&
           [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

    // Log the amount of memory being used by the cache
    NSLog(@"Cache memory usage = %lu bytes", [[NSURLCache sharedURLCache]
                                              currentMemoryUsage]);

    // Reload data from request, this time it will be retrieved from the cache!
    [netConnect reloadRequest];
    while (!netConnect.finishedLoading &&
           [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
    // Zero out cache
    [[NSURLCache sharedURLCache] removeAllCachedResponses];
  }
  return 0;
}

The main() function begins by retrieving the current run loop, required for asynchronous URL loading with NSURLConnection objects. Next, anNSURLRequest instance is created using the convenience constructorrequestWithURL:cachePolicy:timeoutInterval:. This constructor enables you to select the cache policy for the request. The policyNSURLRequestReturnCacheDataElseLoad specifies that a cache value should be used (even if out of date) if available; otherwise, the data should be loaded from the resource. Note that if a URL does not support caching, the data will not be loaded from the cache, even if the policy specifies otherwise. A NetConnector instance is then created using the request and its NSURLConnection object begins downloading the resource. The next statement is a loop used to keep the application running until the connection has finished loading the resource.

while (!netConnect.finishedLoading &&
       [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

Looks familiar, doesn’t it? This loop is identical to the one used for the Bonjour service browser program that you implemented earlier in this chapter. It runs the loop, receiving events from its input sources and executing any corresponding delegate methods, until the connection has finished loading the resource. The next statement logs the cache memory usage to the output pane, enabling us to view cache utilization. Then you reload the request. As shown in Listing 11-16the next set of statements reloads the URL. As the data is already stored in the cache and the cache policy specifies that a cache value should be used, it is immediately retrieved without being loaded from the URL. Finally, the in-memory cache is zeroed out, thereby freeing this memory. Now when you compile and run the NetConnector program, you should observe the messages in the output pane, as shown in Figure 11-2

9781430250500_Fig11-02.jpg

Figure 11-2NetConnector program output

.

As shown in the output pane, the response is cached after the data is downloaded. The total amount of data is then logged to the console, along with the URL of the request. Next, the cache memory utilization is output. This is identical to the data downloaded. The request is then reloaded. Because the response is already in the cache, it is retrieved from there (notice that the delegate methodconnection:willCacheResponse: is not invoked). OK, that was pretty involved, wasn’t it? Well, you now have a pretty good handle on the use of the URL loading APIs to asynchronously download a URL. Once you’re ready, let’s move on and look into handling authentication challenges when trying to load a resource.

Authentication and Credentials Management

The authentication and credentials classes ( NSURLProtectionSpace,NSURLCredentialStorageNSURLCredential,NSURLAuthenticationChallenge, andNSURLAuthenticationChallengeSender ) provide support for authenticating users requesting access to protected URLs. A resource that requires authentication requests credentials from a client attempting to load the resource. The Foundation FrameworkNSURLAuthenticationChallenge class encapsulates a challenge from a server that requires authentication from the client. NSURLCredentialrepresents an authentication credential returned by a user in response to an authentication challenge. The delegate protocols for NSURLConnectionand NSURLDownload instances are sent messages when a connection request issues an authentication challenge. The corresponding methods should be implemented to return the appropriate credential.

For the NSURLConnectionDelegate protocol, the messageconnection:willSendRequestForAuthenticationChallenge: should be implemented to return the appropriate credential. An example implementation of this method for the NetConnector program is shown inListing 11-17.

Listing 11-17.  Handling Authentication Challenges

- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
  NSURLCredential *credential =
    [NSURLCredential credentialWithUser:@"TestUser"
                               password:@"TestPassword"
                            persistence:NSURLCredentialPersistenceForSession];
  [[challenge sender] useCredential:credential
         forAuthenticationChallenge:challenge];
}

As shown in Listing 11-17, the method creates a credential with a user name of TestUser and a password of TestPassword;. TheuseCredential:forAuthenticationChallenge: message uses this credential to respond to an authentication challenge for the connection.

Cookie Management

The Foundation Framework classes NSHTTPCookie andNSHTTPCookieStorage facilitate the creation and management of HTTP cookies, which are used to provide persistent storage of data across URL requests. An NSHTTPCookie instance represents a cookie, andNSHTTPCookieStorage is a singleton object used to manage cookies. For OS X applications, cookies are shared and kept in sync across processes.Session cookies are local to a single process and not shared between programs. The NSHTTPCookie class provides methods to create cookies, convert cookies to request headers, and retrieve cookie properties. TheNSHTTPCookieStorage class provides methods for retrieving the shared cookie storage instance, managing the cookie accept policy, and managing (i.e., adding, removing, retrieving) cookies.

Protocol Support

The Foundation Framework classes NSURLProtocol andNSURLProtocolClient enable the creation of custom protocols for loading data from a URL. NSURLProtocol is an abstract class that provides the basic structure to perform protocol-specific URL loading. It includes methods for creating NSURLProtocol objects, registering/unregistering protocol classes, request management, retrieving protocol attributes, and starting/stopping downloads.NSURLProtocolClient is a protocol used by NSURLProtocol subclasses to communicate with the URL loading system.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值