I am using ASIHTTPRequest in an Objective-C Mac application. I am creating a class that accesses a web service's public API (via ASIHTTPRequest), let's call it PublicAPI. What I am wanting to do is write methods that directly return data from the web service, something like this...
publicAPI = [[PublicAPI alloc] initWithAccountInfo:email:[self email] password:[self password]];
NSArray *theData = [publicAPI getData];
So, getData needs to initiate the ASIHTTPRequest AND return the response from the server. The problem is that I want to make the ASIHTTPRequest calls with ASIHTTPRequest's startAsynchronous option, which delegates the server's response to a requestFinished method.
How can I use the result of requestFinished as the return value of the getData method?
Thanks for your advice!
Your approach would require that getData wait until the response came back before returning which would make the connection synchronous. This does not really make sense.
The delegate pattern that is already implemented by ASIHTTPRequest (and NSURLConnection for that matter) is the best approach here.
If you just need to make a synchronous connection for testing, NSURLConnection has a class method for that:
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
Make your
[publicAPI getData]
actually be a subclass of ASIHTTPRequest. That will receive the ASI delegate's callbacks (success,fail) and that will process (JSON, XML, CoreData, whatever) your incoming data.
You could use any type of notification, after the data is processed, to get the NSArray data back to where you want it.
If you subclass for each of your backend's API calls, with a common system of processing each for your API, you can make communicating with your backend nice and easy.
Related
I am developing my first iPhone app and I am making a call to an API to return some JSON which populates different elements of my UI. Currently I have implemented a synchronous method in a helper class which I call in the viewDidLoad method.
-(NSDictionary*) dataRequest: (NSString*)url withMethod:(NSString*)method
{
NSError *e;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:url]];
[request setHTTPMethod:method];
NSURLResponse *requestResponse;
NSData *requestHandler = [NSURLConnection sendSynchronousRequest:request returningResponse:&requestResponse error:nil];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData: requestHandler options: NSJSONReadingMutableContainers error: &e];
return json;
}
The problem here is I lock up my UI, I tried to implement this asynchronously but the data gets returned long after the code attempts to populate the UI elements what is the best and correct way to implement asynchronous calls and populate my UI elements with the correct data?
It's expected that the data gets returned long after the request is made (long, relative to nearly instantaneous execution of the next line of code after the send request). The trick is to defer the update of the UI until the request completes.
// optionally update the UI to say 'busy', e.g. placeholders or activity
// indicators in parts that are incomplete until the response arrives
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// optionally update the UI to say 'done'
if (!error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData: requestHandler options: NSJSONReadingMutableContainers error: &e];
// update the UI here (and only here to the extent it depends on the json)
} else {
// update the UI to indicate error
}
}];
Even more abstractly -- and more correctly -- consider that the data being fetched is likely part of the app's model. A fetch from the server is just one cause of change to the model. When the model is changed for any reason, either through user action or this fetch or some other event, it is the view controller's job to observe that it changed, and tell the views to update.
There are some cases when making a synchronous request makes sense, but not if the view controller is waiting (blocking the main thread) while the synchronous request completes. I have made a blog post to help answer this question in depth: http://jasoncross-ios-development.blogspot.com/2015/04/asynchronous-json-requests-in-objective.html. The post also discusses other issues surrounding consuming JSON from a web server for an iOS application: which design patterns to consider under different circumstances.
One of the most common patterns in iOS development is the display of data which originates from a Web Service and is transmitted via JSON. This post discusses synchronous and asynchronous fetching of data, and other related design patterns.
What is the difference between synchronous and asynchronous http requests? In a synchronous http request, the currently executing code "blocks" (stops and waits) until the request finishes. If this code is on the main thread, then the app will appear frozen and broken while the network call is made. This is a bad idea, is against best practices, and should always be avoided.
In contrast, an asynchronous http request allows the currently executing code to continue while the request is initiated and while the request is running. Once the request completes, the code "reports back" to a listener. In the case of NSURLConnection sendAsynchronousRequest:queue:completionHandler: , the listener is the caller of the method who passes in a block as a method argument. In the case of network requests being made from the main thread, the User Interface does not lock, the application continues to respond to user gestures, and is the accepted best practice.
In a nutshell, that's all you need to know: use the asynchronous method. But that begs the question: why is the synchronous method even available if it should never be used? The answer is that there are many scenarios when making a synchronous request makes sense. Detailing some of these scenarios comprises the remainder of the blog post.
Consider using a "Service Layer" to make the requests, as follows:
model classes. These often mirror the JSON objects
service layer. These classes know about the API and make network calls to fetch JSON. When the network calls complete, JSON is converted to native model classes (Objective-C objects).
View controller. These classes handle user interaction and ask for data from the service layer as needed.
If your iOS application only needs to fetch data once and is not concerned about updates, the service layer can perform an asynchronous request, and update the caller when the request complete, using a block. The caller may ask the service layer for data as per below:
[serviceLayer fetchDataFromAPIWithResponseBlock:^(NSArray * arrayOfObjects, NSError *error) {
if (nil != error) {
// deal with error appropriately
}
else if (nil != arrayOfObjects) {
// update the user interface
}
}];
But if your application is concerned with data that changes, either on the server or on the client, then you will want a way to coordinate model changes with updates to the User Interface. In this case, you should consider using either the delegate design pattern or the observer design pattern. If using a delegate pattern, the View Controller would be a delegate of the Service Layer. When the model data changes, the service layer informs the delegate. In this case, the service layer could use a synchronous request because the View Controller is not blocking while it waits to hear back from the service layer. An alternative to implementing the delegate pattern is to use the NSNotificationCenter to inform the View Controller when the model data changes. The biggest deciding factor between using the delegate pattern and Notifications is how many view controller need to be informed when the model data changes. If there is only one view controller, it can be made a delegate. If there are more than one view controller, they can all be informed via Notifications.
If individual properties of model objects are being changed (possibly from elsewhere in the iOS application), then you should consider using Key Value Observing (KVO) to update the views when the model changes.
More in depth discussion may be found at the blog post mentioned above.
Why should one use AFNetworking's async methods, when an async call can be done simply with GCD?
dispatch_async(bgQ, ^{
//NSURLConnection code
dispatch_async(dispatch_get_main_queue(), ^{
//UI code
});
});
This is answered in detail at the top of the AFNetworking FAQ:
While NSURLConnection provides +sendAsynchronousRequest:queue:completionHandler: and +sendSynchronousRequest:returningResponse:error:, there are many benefits to using AFNetworking:
AFURLConnectionOperation and its subclasses inherit from NSOperation, which allows requests to be cancelled, > suspended / resumed, and managed by an NSOperationQueue.
AFURLConnectionOperation also allows you to easily stream uploads and downloads, handle authentication challenges, > monitor upload and download progress, and control the caching behavior or requests.
AFHTTPRequestOperation and its subclasses distinguish between successful and unsuccessful requests based on HTTP > status codes and content type.
AFNetworking includes media-specific request operations that transform NSData into more useable formats, like JSON, > XML, images, and property lists.
AFHTTPClient provides a convenient interface to interact with web services, including default headers, authentication, > network reachability monitoring, batched operations, query string parameter serialization, and multipart form requests.
UIImageView+AFNetworking adds a convenient way to asynchronously loads images.
I'm currently building an iPhone app that needs to connect to an API. I built an API class that makes an async web request and uses a delegate to get the response (on the main UI thread). It looks something like this:
Api* api = [[Api alloc] init]
api.delegate = self;
[api request:#"foo/bar"]; // makes async API call via NSURLConnection
-(void) apiRespondedWith(id) response
{
// do stuff with API response.
}
This works well, but I'd like to make several API requests in a ViewController and would like an easier way to distinguish between the calls. Is there a way to have specific callbacks for each API call made? Selectors or blocks seem like a way to do this, but I'm unsure of the best way to implement this.
There are several ways to accomplish this. For example, you could perform the web request in an NSOperation/NSOperationQueue. Note that if you use this approach the NSURLConnection will need to be performed synchronously inside the NSOperation - because the NSOperation is already executing asynchronously. Each web download request is encapsulated in an NSOperation which, in turn, is submitted to an NSOperationQueue.
You can check out an example of using NSOperation.
You could extend the example above by providing the NSOperation a completion block.
Alternatively, you could consider using a third-party library such as AFNetworking which uses this sort of blocks-based callback mechanism.
OK, I am building an application that will be using ASIHttpRequest in several places to either get JSON data from a web service or will be posting to a web service. I have it working, but really don't like having several instantiations of the request object sprinkled around the different view controllers (Login view controller, a tableview controller, and a detail view controller. I would like to move all my ASIHttpRequest methods to one class and just get back a dictionary of values that came back from the web service. The problem with this is that the delegate methods don't return that I need to have returned.
What would be some suggestions on how I can consolidate all the methods that will create an HTTPRequest and return values when the request is finished.
I can post code if need be, but this is really more of a general question and not a specific issue. If there are any tutorials on how this should be done, I would appreciate it. I want my code to be efficient and pretty :)
Thanks!
Your problem is going to be asynchronousity. I'm not sure what you mean by consolidate, but you can have a singleton (you can just use your app delegate) to call the requests. So you would do something like this:
[(MyAppDelegateClass *)[UIApplication sharedApplication].delegate doLoginStuff:(NSDictionary *)params delegate:self];
If you're doing all this asynchronously, you can't just call this method and have it return something. You'll be stuck with having some sort of callback to your view controller, which is the way ASI works out of the box essentially. At best, you can minimize the code to generate the request and set any repetitive properties.
I'm not sure what you mean by having the instantiations floating throughout. If it's memory you're worried about, it would be no different reusing the same object. ASI typically uses an autoreleased request, so it would be the same as creating it in a "consolidated" method. If it's just clean code, I would do a singleton way and maybe make a shortcut c-based method call in some type of Utilities class that you create and import in with your prefix file. But again, you need the callback methods in your view controller so it doesn't get too much cleaner than it already is meant to work. I share your pain though.
When any request comes back, I almost always have a method that parses the results. Typically I'm only working with one type of web service, so have a method that parses the results and (also logs it to NSLog for debugging purposes) also gives me back an NSDictionary or NSArray.
My app currently has what I'm pretty sure is a bad design, but I'm not sure how I can organize it any better. For example: LoginViewController sends the input text for the username and password to an instance of UserController. UserController sends a request to my server to validate the username and password, via NetworkRequestController which is responsible for dealing with all client-server communications. NetworkRequestController parses the server JSON responses and sends it back to a designated selector on the UserController, which then does all of it's business before informing the view of the result.
Currently, the UserController is instantiated as a local property to the LoginViewController during the LoginViewController's viewDidLoad method - though I feel I would prefer it to be a singleton class. The problem is that I need to set another object (such as LoginViewController) as the delegate on UserController in order to get the login result all the way back to the view with the appropriate selectors.
//LoginViewController sends request to local UserController instance
[userController validateLoginWithDelegate:self
withResult:#selector(loginResult:)
forEmail:email.text
forPassword:password.text];
//UserController asks NetworkRequestController to send login to server
- (void)validateLoginWithDelegate:(id)delegateObj
withResult:(SEL)onResult
forEmail:(NSString *)email
forPassword:(NSString *)password {
if(delegateObj != nil && onResult != nil){
self.delegate = delegateObj;
self.resultSelector = onResult;
}
NSString *url = [NSString stringWithFormat:#"/UserService.cfc?method=Authenticate&email=%#&password=%#&returnFormat=JSON", email, password];
[[NetworkRequestController sharedNetworkRequestController]
requestJSONWithURLString:url
andDelegate:self
onFinished:#selector(onLoginResult:)
onFailed:nil
onOffline:nil
progressView:nil
timeout:30];
}
//NetworkRequestController sends the request to the server and sends the parsed JSON back to UserController
[delegate performSelector:onFinished withObject:parsedJSON];
//UserController does it's business and sends the result to the view
if(delegate != nil && resultSelector != nil){
[delegate performSelector:resultSelector withObject:result];
}
I've only been working with Obj-C and Cocoa a few months and I'm pretty sure this is almost considered good, but I'm more certain there's something I'm missing. Can this be achieved with protocols? Advice?
Delegates are intended for two way communication between objects that serve different roles. Usually one object knows how to do something and the other object, the delegate, knows what to do.
When you have one way communication, notifications are often a better choice. You do not need the formality of a delegate with one way communication.
The two can be used together. For example, UIApplication has a delegate but also sends notifications for many of the delegate calls.
If the UserController is a singleton, then any instance can make requests and it can broadcast a notification of the results to all interested objects.
[NSNotificationCenter defaultCenter] sends notifications within your application.