viernes, 18 de junio de 2010

WebServices solution for iPhone - First Stage

The FileDownload Class

After reviewing some examples of how to download a file from an URL and after implementing it by myself, I came to the idea to do it as a separate class. The objective of the new class was to handle the connection and processing needed to do the job.

The published methods are:

- (FileDownload *) initWithURL:(NSString *)url callBack:(id<FileDownloadProtocol>)delegate;


This initializer is used to immediately start the download process given the desired URL to download and the delegate, which is the object which will receive the downloaded data. Its implementation is as follows:

/**

Initialize with file to download

**/

- (FileDownload *)initWithURL:(NSString *)url callBack:(id<FileDownloadProtocol>)delegate

{

[self init];

[self startDownloadFile:url callBack:delegate];

return self;

}


I’m not checking if the object was created successfully before starting the download, that’s a to do activity for one day.

- (void)startDownloadFile:(NSString *)url callBack:(id<FileDownloadProtocol>)delegate;


This method is used to start the download process given the desired URL to download and the delegate, which is the object which will receive the downloaded data. Internally, this method is called from the initializer, when the initializer is used. Its implementation is as follows:

/**

Call to download file

**/

- (void)startDownloadFile:(NSString *)url callBack:(id<FileDownloadProtocol>)delegate

{

processing = YES;

callBackObject = delegate;

NSURLRequest *xmlURLRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];

self.dataFeedConnection = [[[NSURLConnection alloc] initWithRequest:xmlURLRequest delegate:self] autorelease];

if (self.dataFeedConnection == nil)

{

NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"No Connection Error", @"Error message displayed when not connected to the Internet.") forKey:NSLocalizedDescriptionKey];

NSError *noConnectionError = [NSError errorWithDomain:NSCocoaErrorDomain code:kCFURLErrorNotConnectedToInternet userInfo:userInfo];

[self handleError:noConnectionError];

}

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

}


Basically it is the well know recipe to download a file.

-(void)stopDownload;


This method is used to cancel any active download. If there is no active download, nothing is done. Its implementation is as follows:

/**

Stop actual download process

**/

-(void)stopDownload

{

processing = NO;

if (dataFeedConnection != nil)

{

[dataFeedConnection cancel];

}

}


I’m using the processing variable to know if something is happening and avoid calling downloadError method once the download was cancelled.

One important property of the class is:

@property (nonatomic, assign) int jobID;


This property is a logical way to identify the current download, used for the case when more than 1 instance of the class is used a same time.

The class implements the next protocol

@protocol FileDownloadProtocol

/**

Notify an error

**/

- (void) downloadError:(NSError *)error downloadID:(NSInteger)jobID;


/**

Notify download complete

**/

- (void) downloadSucceed:(NSMutableData *)data downloadID:(NSInteger)jobID;

@end


Both methods defined in the protocol are required. The protocol is used to define the delegate object that will handle the received data.

The protocol’s methods are called once the process is finished or an error happens. There are 2 options:

- (void) downloadError:(NSError *)error downloadID:(NSInteger)jobID;


This method is called back to the delegate when an error occurs. Currently, there is no way to propagate the kind of error raised, but for the class implementation I only need to know if the download was successfully or not. This is only called from the error handler method:

- (void)handleError:(NSError *)error

{

if (processing && callBackObject != nil)

{

// Call the error Callback

[callBackObject downloadError:error downloadID:jobID];

}

}


Thanks to processing, it is only called when a processing is active.

- (void) downloadSucceed:(NSMutableData *)data downloadID:(NSInteger)jobID;


This method is called when the download succeed; with this call, the class is passing the downloaded information to the delegate. This is called from the NSURLConnection delegate method connectionDidFinishLoading:

/**

Connection finishes

**/

- (void)connectionDidFinishLoading:(NSURLConnection *)connection

{

self.dataFeedConnection = nil;

[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

if (processing && callBackObject != nil)

{

// Call the Succeed Callback

[callBackObject downloadSucceed:dataData downloadID:jobID];

}

self.dataData = nil;

}


Internally, the class handles memory and the NSURLConnection delegate methods. As usual, the implemented methods are:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

- (void)connectionDidFinishLoading:(NSURLConnection *)connection;


Any object willing to use FileDownload has 2 options:
1. Create the FileDownload object with initWithURL
2. Create the FileDownload object, and then start the download process using startDownloadFile

Sample implementation might be:

self.downloadObject = [[FileDownload alloc] init];

[self.downloadObject setJobID:theID];

[self.downloadObject startDownloadFile:theURL callBack:self];

Then just wait for the FileDownloadProtocol’s methods to be called.

This class is a Soot-and-Forget download, at least I call it that way, because once the download method is called, the application is free to do any other processing until the downloaded information is ready or a download error is raised.

I now use this class whenever I need to download data from a given URL.

In the next post of this series I'll talk about the FileDownloadController class.

martes, 15 de junio de 2010

My WebServices solution for iPhone

I got into the necessity to call WebServices from within an iPhone application I was designing. After a small search with Google I found different implementations, even a “C” (or C++ I can’t recall) library, but nothing like the “easy way” used in Dot Net.

My specific application needs to show address book, kind of, information in a table. Each row shows the contact’s picture and name; when selected, in a second screen, detailed information will be show. This functionality is similar to the contacts' section of
Facebook or LinkedIn application. The source information has to be gathered from a WebService.

Note: I will focus these articles in the WebService consumption, instead of the Graphical development and application logic.

For simplification, the WebService contract will be:
1.List screen
An XLM with up to 100 results with the next structure:
<contact>
<contactid>id for this item</contactid>
<photo>Url of contact’s photo</photo>
<name>contact’s name</name>
</contact>

2.Detail screen
An XML with contact’s detail:
<contactdetail>
<detail1>bunch of information</detail1>
<detail2>bunch of information</detail2>
<otherdetail1>bunch of information</otherdetail1>
...
</contactdetail>
Details might vary according with the available information, even tag names might change

In the first screen, information from first WebService Method is used to populate the table, the photo URL is use to download the image and draw it in the same table for each contact.

Once a row is selected, the second method is called with the ID of the contact, the retrieved information will be shown in a way similar to built in Contacts’ application.

First Stage was to develop a File Downloader class. This class helps me to download information of a given URL. With this class I test downloading a hardcoded XML simulating a call to first method.

Despite the fact I also build a generic all purpose XML Parser, which I might talk in a different post, the Second Stage was to think in downloading several files at a same time, to show the photos in the main contacts’ list. So, I build a Download Controller class to keep track of downloads and the objects requesting downloads. This controller uses the file Downloader build in First Stage.

In a Third Stage I create a WebService Controller class to support the call of registered WebServices. This class publishes the WebServices which will be called to retrieve information; it uses the Download Controller from Second Stage and, by consequence, the File Downloader from First Stage.

The final WebService Controller gave me an “easy way” to call WebServices from my iPhone applications as now, I only need to type down the Methods’ Firms in ordet to be available to use.

In the Next post I'll talk about the impementation stages.