Need help desiging an API wrapper for iOS / iPhone application - objective-c

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.

Related

ASP.NET core & C#. How to run a synchronous method asynchronously

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.

Why use AFNetworking instead of dispatch_async?

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 need help in design patterns for an iOS webservice client

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.

Using Login View Controller with ASIHttpRequest and Storyboard

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.

Objective-C & ASIHTTPRequest return values

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.