I am currently designing an iOS 5 iPhone app that will use a .NET RESTful Web Service to provide data updates. When the application is initially installed, it will connect to the WS to download all the data in a JSON format. Thereafter, it will only perform updates. The WS provides a POST method for each table as GetAllTableRecords() and GetLastUpdatedTableRecords().
I am using iOS 5 and I’ve got the NSURLConnection and JSON serialization/deserialization working correctly with native libraries. Each WS POST method call is currently residing in its own Obj-C class with all of the delegate methods. Additionally, each class handles the local datastore inserts and updates.
Each NSURLConnection is asynchronous and all of the WS calls are driven off of button events from view controllers.
My questions are:
- Is this the right setup in terms of code encapsulation and reuse?
- How do I handle making multiple WS calls while keeping the user
informed via the UI?
Currently there are two tables to download. This means the app will call the WS twice to get the initial data and twice again during each refresh. I know that since each NSURLConnection is asynchronous, the connection will make the request but the UI will continue on while the delegate handles the data download. I’ve done some research into GCD and NSOperation/Queue but I don’t know enough about either one to code a solution or know if that’s even a correct solution.
Any insight would be most helpful!
Edit #1: What about providing real time updates back to the UI? The Mint app does something similar when updating transactions and accounts. They have a little status bar that pops up at the bottom while requests are made.
Edit #2: Ok, I believe I’ve made some progress. We are using Story Boards and the entry point is the Login View/Controller. When the login button is clicked, a NSURLConnection is made to the webservice. If the response status code is 200 in connectionDidFinishLoading:(NSURLConnection *)connection, a segue is performed to go the Data Sync View. The purpose of this view is to either initialize or update the database while providing feedback to the user. Either updating or initializing requires two additional web service calls.
Here’s my DataSyncView.m:
@synthesize pvStatus, lbStatus;
// pvStatus = progress indicator
// lbStatus = label
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self StartDataSync];
}
- (void)StartDataSync
{
[lbStatus setText:@"Syncing data..."];
[pvStatus setProgress:0.0f];
// TODO: Determine if database is popuplated
[self PerformInitialSync];
// Next screen
[self performSegueWithIdentifier:@"SegueFromSync" sender:self];
}
// Populates data store will data from web service
- (void)PerformInitialSync
{
// Kicks off a series of synchronous requests
[self DownloadAllEmployeeDataA];
}
- (void)DownloadAllDataA
{
// Dictonary holds POST values
NSMutableDictionary *reqDic = [NSMutableDictionary dictionary];
// Populate POST key/value pairs
[reqDic setObject:passWord forKey:@"Password"];
[reqDic setObject:userName forKey:@"UserName"];
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:reqDic options:NSJSONWritingPrettyPrinted error:&error];
// Convert dictionary to JSON
NSString *requestJSON = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// Declare Webservice URL, request, and return data
NSURL *url = [[NSURL alloc] initWithString:@"http://wsurl/getalla"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSData *postData = [NSData dataWithBytes:[requestJSON UTF8String] length:[requestJSON length]];
// Build the request
[request setHTTPMethod:@"POST"];
[request setValue:[NSString stringWithFormat:@"%d", [postData length]] forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
[request setCachePolicy:NSURLRequestUseProtocolCachePolicy];
[request setTimeoutInterval:60.0];
NSURLResponse *response;
[lbStatus setText:@"Downloading employee data..."];
[pvStatus setProgress:0.1f];
// Make the response
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// If return data received
if(returnData)
{
// Get the response and check the code
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int code = [httpResponse statusCode];
// Check to make sure successful code
if (code == 200)
{
// Convert JSON objects to Core Data Entity
// Update UIProgressView and Label
// Call next WS call
[self DownloadAllEmployeeDataA];
}
}
}
- (void)DownloadAllDataB
{
// Same code as above but with different URL and entity
}
My problem that I am having is this: The UIProgressView and Label are not updating as the calls are being made. As I stated before, I don’t even know if this is the best way to make these calls. It doesn’t appear that I’m blocking the main thread but I could be wrong. Again, I’ll pose the question: what is the best way to make multiple url calls while keeping the UI updated on the progress? Thanks!!!
In your question you said you made asynchronous load of url request. But in the above line of code you are making a synchronous request ?
Is this the right setup in terms of code encapsulation and reuse?
Controller shouldn’t manage loading URL connections. You can create a
class that does that and using delegates inform the view controller
whether data is downloaded or failed to download,etc.
How do I handle making multiple WS calls while keeping the user informed via the UI?
and NSOperationQueue. Try avoiding GCD ( Refer to WWDC 2010 Session
208 ).
My problem that I am having is this: The UIProgressView and Label are not updating as the calls are being made.
code UIProgressView shouldn’t update.
Refer URL Loading System Programming Guide
Another comment I have is your method names, start method name with small letter. Rest of it looks fine. Coding Guidelines for Cocoa