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.
Related
My ASP.NET core application uses Entity Framework Core. As you would expect must controller methods are async and call async methods of EF Core.
I also have controller methods thats need to read from and write to excel files. I'm using OpenXml. Since these are IO operation, ideally I they would be an async operation but OpenXml doesn't offer any async methods. Here is a simplified example
private async Task<model> ReadFromExcel()
{
using var document = SpreadsheetDocument.Open("filePathAndName", false);
// read data into model
document.Close();
context.Models.Add(newModel);
await Context.SaveAsync();
return newModel;
}
Also, I need to find the file in a folder first which I would also like to make async.
Directory.EnumerateFiles("excelFolderName", ".xlsx");
According to this document ASP.NET Core Performance Best Practices I shouldn't use Task.Run to make an synchronous API asynchronous. I understand why but does that rule apply to IO operations which will block the thread potential for a few seconds? Should I make these IO operations async and if so what is the base way to make reading and writing excel file and getting file list asynchronous?
Since these are IO operation, ideally I they would be an async operation but OpenXml doesn't offer any async methods.
Also, I need to find the file in a folder first which I would also like to make async.
Ideally, those would be asynchronous APIs. But they're not. The way to make them asynchronous is to fix the API, not wrap it in Task.Run. You can open a request with the maintainers of OpenXml for asynchronous APIs. The file system operation is more awkward; it's a Win32 limitation, not a BCL limitation, and it's unlikely to be fixed, but you can ask.
does that rule apply to IO operations which will block the thread potential for a few seconds?
Yes.
The request is blocked for the same amount of time whether it's synchronous or asynchronous. So the thing to consider is how threads are blocked. In the ideal asynchronous case, no threads are blocked. Since you only have synchronous APIs, you do have to block a thread; calling the API directly will block a thread pool thread, and shoving it off to Task.Run will block a different thread pool thread - which is pointless.
Should I make these IO operations async and if so what is the base way to make reading and writing excel file and getting file list asynchronous?
You can't "make them async". You can request async APIs and then use the synchronous ones for now.
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.
I want to implement a webservice client in iOS which uses SOAP and XML for requests/responses.
My view starts the initial businnes logic (a user presses a button or something and initiates some businnes method called method_A).
So I have a class with method_A and this method checks if the user is logged in etc and then starts the request asynchronous via the SOAPConnector-class. So the UI is not blocked (asynchronous).
The SOAPConnector-class takes the XML and handles the requests. I use therefore NSURLRequest and NSURLConnection with sendSynchronousRequest.
The response is sended back to a Response-class which takes the response. This class then wants to parse the response XML. Therefore I use an extra class called XMLManager which uses NSXMLParser to parse the xml. But again here we need a delegate which gets the parsed xml. And again after parsing I have to implement an extra method to give back the parsed xml to the first class which initiated the request.
I am really wondering if this is the right way. The first problem is asnychronous request to not block the UI (the first callback). The second problem is the parsing where I am forced to use the delegate (the second callback). This results in a lot of classes and methods and I doubt this is the right way. The classes' only purpose is to manage the delegate and async problems. So I am asking for any suggestions and help how to solve this. Do you know some good design patterns to solve this problem?
Apart from some inconsistencies in the way you describe the design patterns you've selected:
and then starts the request asynchronous
vs.
I use therefore NSURLRequest and NSURLConnection with
sendSynchronousRequest.
That said, your approach seems sound. Addressing the issues you've identified:
I use therefore NSURLRequest and NSURLConnection with
sendSynchronousRequest.
Isn't that the purpose of using an asynchronous API? If your NSURLConnection is really operating asynchronously, that issue should be covered.
The second problem is the parsing where I am forced to use the
delegate
This approach does result in more classes, delegation, etc. but it conforms to best practices when it comes to testing. If you are performing unit testing or other testing strategies (you are aren't you?) then testing in isolation is all the harder unless you breakdown this process functionally.
If you have access to the book Test-Drive iOS Development there is a great section on the best practices for consuming web services with a view toward testability.
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.
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.