Another Delegate for ASIHTTPRequest Asynchronous? - iphone

How can I create a new file containing just it's own delegate so that I can make an ASIHTTPRequest with its own asynchronous ending, and something easy enough where I just need to set [request setDelegate:self]; to something like [request setDelegate:AlternateDelegate]; and just add an include at the begining of the document to reference the AlternateDelegate delegate

I know this question is old, but in case anyone comes across it:
#Hankweb seemes to be talking about using a request as its own delegate. There are certainly situations where this works. For example, I'm working on a project that uses ASIHTTPRequest to fetch JSON from a remote source and import it into a Core Data store.
This operation (literally, as ASIHTTPRequest is a subclass of NSOperation) is almost entirely self-contained; I have a custom request on a background thread using a streaming JSON parser to import objects into a NSManagedObjectContext, which, when saved, triggers a notification that I catch internally and pass to the main thread's context using performSelectorOnMainThread:waitUntilDone:.
I'm using ASIHTTPRequest's block support to accomplish this; in my custom initWithURL: method, I set up the relevant callbacks (dataReceivedBlock, completionBlock, failureBlock, etc.). The traditional delegation pattern (using the ASIHTTPRequestDelegate protocol) should also work, though.
One gotcha: you should make sure the request doesn't retain itself too many times, or else you'll end up with a memory leak. This can be easy to miss when using multiple threads, and especially when using blocks. Instead of:
- (id)initWithURL:(NSURL *aURL) {
//...
[self setCompletionBlock:^{
[self doSomething];
}];
//...
return self;
}
Use the __weak attribute (or __block if you're not using ARC) when referencing self within the blocks:
- (id)initWithURL:(NSURL *aURL) {
//...
__weak id blockSelf = self;
[self setCompletionBlock:^{
[blockSelf doSomething];
}];
//...
return self;
}
If you don't know why this is important, make sure to read Apple's guide to blocks in Objective-C, and the ASIHTTPRequest block API documentation.

A delegate for ASIHTTPRequest is just a standard objective C object. Just create a new class, include it's header, create/get the object and set the delegate to be that object.
Have you tried this and run into a problem? If so what is the problem?

Related

Is there any issue if I used sync connections in iPhone?

I know that the pattern in iPhone is to use ASync connection calls (using the informal protocols that is implemented by the current class).
In my case, I've created a utility class to do the networking staff and then return the data to the ViewController.
I find is inadequate to implement the connection model as Async in a utility class because r I will write a block of code in the ViewControlle such following: (which IMHO is bad)
MyUtilityConnection* utilConn = ....
while (true)
{
if ([utilConn checkUnderlyingAsyncConnectionFinishedLoading]) break;
}
NSData* dataFromUrl = [utilConn dataFromUnderlayingConn];
So, the question is, Does using Sync connection model in iPhone could causes problem? and solutions?
(What about the drawing will stril hanging until the data come???)
AVOID by all means to do synchronous connections! This will obviously freeze your UI (and it gets worse if you don't have a good bandwidth of course).
What you could do is to use the blocks syntax to write more readable code when you need to download data. Create a class that implements the NSURLConnection delegate methods, and then call the block when the data is done.
See my OHURLLoader class on github for example that does exactly that (and that's only one solution).
Usage example:
NSURL* url = ...
NSURLRequest* req = [NSURLRequest requestWithURL:url];
OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
[loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
NSLog(#"Download of %# done (statusCode:%d)",url,statusCode);
outputTextView.text = loader.receivedString;
} errorHandler:^(NSError *error) {
NSLog(#"Error while downloading %#: %#",url,error);
outputTextView.text = [error localizedDescription];
}];
During sync methods (sendSynchronousRequest:returningResponse:error:) the UI is non-responsive (assuming that the sync method is called on the main thread).
But they are fine on background threads, the easiest way to accomplish sync calls on a background thread is with GCD.

deleting delegate on dealloc without an instance variable

so i start a ASIFormDataRequest on my [viewDidLoad] in a UIViewController.
ASIFormDataRequest *detailRequest = [ASIFormDataRequest requestWithURL:url];
detailRequest.delegate = self;
[detailRequest startAsynchronous];
If my UIViewController gets released before my Request finishes, my app crashes.
If i add my ASIFormDataRequest as an instance variable for example
#property(nonatomic, retain) ASIFormDataRequest *detailRequest;
and nil the delegate on dealloc
-(void)dealloc {
if(self.detailRequest != nil) { self.detailRequest.delegate = nil; }
self.detailRequest = nil;
[super dealloc];
}
the app no longer crashes.
but i don't think it's necessary to create a instance variable just for this, especially if i have multiple requests.
is there a better way to do this?
I usually create an array and store all active requests in the array. When the request is completed I remove the request, and when the controller calls dealloc I cancel all of the requests and nil the delegate.
In order to release it you must have a pointer to it so yes, use an ivar. iars are not expensive.
By doing self.detailRequest = [ASIFormDataRequest requestWithURL:url]; I am guessing it is creating an autorelease object whose lifespan isn't bound to your controller class. If the creation and deletion of your object is bound to your controller, it's logical to use a instance variable.
More details about autorelease
You could do this:
detailRequest.delegate = [self retain];
and then call
[self autorelease];
In the ASIFormDataRequest callback method. That's what I generally tend to do, anyway.
That way, the request object retains its delegate for the duration of the request.
As this is the Asynchronous request so if you set delegate it means as soon as response comes your delegate methods will be called. Till that time your object should be alive to handle the response. So making it retain and releasing in the dealloc is fine and before than that you have to set delegate to nil. So that if response comes after releasing the method, framework should not be misguided to search for method of dead object.
To handle multiple request the best way is to create the array and number of objects you want to use. When you are done with the objects, in dealloc method iterate through each object and set delegate nil and release the object.

iOS Design: Using the delegate pattern in a library

I have a library project that uses ASIHTTPRequest to make URL requests and parse the responses. The library will be used by a separate iPhone app project.
If my iPhone controller code responds to a touch event, then calls into the library to make URL requests, how do I best perform the requests asynchronously?
In the library, if I use the delegate pattern for asynchronous requests as shown in the ASIHTTPRequest sample code, how do I return data from the library back to the calling code in the iPhone controller?
If I instead make synchronous URL requests with ASIHTTPRequest inside the library, what's the easiest way to put the calls to the library from the iPhone controller on a separate thread to avoid tying up the UI thread?
I'm no ASIHTTPRequest expert (NSURLRequest has always done me fine), but from a quick poke at the code, it looks like you'd use its delegate and didFinishSelector properties to give it someone to tell when the URL request is finished. So, for example:
- (void)startURLRequest
{
ASIHTTPRequest *myRequest;
/* code to set the request up with your target URL, etc here */
myRequest.delegate = self;
myRequest.didFinishSelector = #selector(HTTPRequestDidFinish:);
/* ... */
[myRequest startAsynchronous];
}
- (void)HTTPRequestDidFinish:(ASIHTTPRequest *)request
{
NSLog(#"Request %# did finish, got data: %#", request, request.data);
[myTargetForData didReceiveData:request.data fromURL:request.originalURL];
}
Apple explicitly recommend that you use the built-in runloop style mechanisms for asynchronous HTTP fetching, not separate threads. Using separate threads is likely to result in worse performance — at least in terms of battery life and/or device heat, even if it's still fast enough.
That said, as a learning point, by far the quickest way to switch something onto a separate thread and have it report back to the main thread (remember: UIKit objects may be messaged only from the main thread) is by changing this:
- (void)postResult:(NSString *)result
{
instanceOfUILabel.text = result;
}
- (void)doExpensiveOperationOn:(NSString *)source
{
/* lots of expensive processing here, and then... */
[self postResult:result];
}
- (IBAction)userWantsOperationDone:(id)sender
{
[self doExpensiveOperationOn:#"some value or another"];
}
Into this:
- (void)postResult:(NSString *)result
{
instanceOfUILabel.text = result;
}
- (void)doExpensiveOperationOn:(NSString *)source
{
/* we're on a thread without an autorelease pool now, probably we'll want one */
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
/* lots of expensive processing here, and then... */
/* in this simplified example, we assume that ownership of 'result' is here on this thread, possibly on the autorelease pool, so wait until postResult has definitely finished before doing anything that might release result */
[self performSelectorOnMainThread:#selector(postResult:) withObject:result waitUntilDone:YES];
[pool release];
}
- (IBAction)userWantsOperationDone:(id)sender
{
[self performSelectorOnBackgroundThread:#selector(doExpensiveOperationOn:) withObject:#"some value or another"];
}
There's about a million possible concurrency errors you can make by just going threaded without thinking about it though, and in that example an obvious problem is that whatever triggered the IBAction can [probably] trigger it several more times before doExpensiveOperationOn has finished. Multithreading is not something to be dashed into lightly.
For anyone's future reference, the easiest approach I found is to use the async request functionality built into ASIHTTPRequest, setting my library object as the delegate and setting the didFinishSelector: and didFailSelector: values to different methods inside my library for each request.
At the end of processing each response, I assign the parsed response (an NSString* or NSArray*) to a property of my library object instead of returning a value.
When my iOS view controller delegate is loaded, I add a change observer to each of the properties in the library using Key-Value Observing. When the response is parsed and assigned to the property in the library, the observeValueForKeyPath:ofObject:change:context: method is called in the code of my view controller delegate, and from there I can figure out which property was changed and therefore what UI needs to be updated.

Sharing NSOperationQueue across View Controllers?

I'm using an NSOperationQueue to manage HTTP connections (using ASI-HTTPRequest). Since I have multiple views and the need to have these different views requesting HTTP connections, should I try to create a global NSOperationQueue in the app delegate, or should I have one in each of the views? I'm not familiar with NSOperationQueue.
I'd like to know a) what the best practice is and b) if there is no best practice, what the tradeoffs are if any.
I did try to put the operation queue in the class (as a property) where I handle the server connections but the task never fired. Couldnt figure it out but [queue operations] = 0. If someone knows a solution to this, I presume this would be the best place to put it.
I have solved this by adding a class method on NSOperationQueue that I think Apple has missed; a shared operation queue. I add this as a category on NSOperationQueue as this:
// NSOperationQueue+SharedQueue.h
#interface NSOperationQueue (SharedQueue)
+(NSOperationQueue*)sharedOperationQueue;
#end
// NSOperationQueue+SharedQueue.m
#implementation NSOperationQueue (SharedQueue)
+(NSOperationQueue*)sharedOperationQueue;
{
static NSOperationQueue* sharedQueue = nil;
if (sharedQueue == nil) {
sharedQueue = [[NSOperationQueue alloc] init];
}
return sharedQueue;
}
#end
This way I do not need to manage a whole bunch of queues unless I really need to. I have easy access to a shared queue from all my view controllers.
I have even added a category to NSObject to make it even easier to add new operations on this shared queue:
// NSObject+SharedQueue.h
#interface NSObject (SharedQueue)
-(void)performSelectorOnBackgroundQueue:(SEL)aSelector withObject:(id)anObject;
#end
// NSObject+SharedQueue.m
#implementation NSObject (SharedQueue)
-(void)performSelectorOnBackgroundQueue:(SEL)aSelector withObject:(id)anObject;
{
NSOperation* operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:aSelector
object:anObject];
[[NSOperationQueue sharedOperationQueue] addOperation:operation];
[operation release];
}
#end
My personal preference for this is to have a singleton that manages all http requests. Each view would then ask the singleton to make the http call, passing itself as a delegate for that call then the singleton hands that delegate and call off to an NSOperation and then NSOperation calls back once the call is done.
If you already have a pointer to a class that handles connections in each view/view controller, there's no reason you would also need to have a pointer to the operation queue.
I suppose what you want to do is something like: a) view(Controller) hands url(+data) to server handling object, b) server handling objects creates operation and puts it in a queue that it and only it has a pointer to.
It's hard to figure out why that didn't work if you don't provide more detail.
I highly recommend taking a look at ASIHTTPRequest which provides a NetworkQueue class to handle this kind of task. It has several convenient delegate fields that lets you register to keep track of progress, know when a download or upload finished etc.

Creating multiple NSURLConnections. How to identify which Async call

I am intending to create 2 requests using NSURLConnection. When the server responds and calls connectionDidFinishLoading it passes in the connection as the parameter, but how do I identify which connection is passed in?
Save both NSURLConnection objects as member variables of whatever delegate object you passed to connectionWithRequest:delegate:. Then you can just compare each of those to the NSURLConnection passed to connectionDidFinishLoading:, and respond appropriately:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == firstConnection) {
// do something
}
else if (connection == secondConnection) {
// do something else
}
}
Another slightly more object-oriented option would be to create two different delegate objects, each of which knows how to deal with each each type of connection. Then just pass the appropriate delegate when you create each connection. That way you don't need to check to see which connection you have, because each delegate will only receive connectionDidFinishLoading: for its own connection.
I prefer different delegates for each connection, too. Although it's a bit of overhead. Fortunately, you can simplify things by using blocks. It's a new feature that doesn't exist in standard SDK yet, but there is 3rd-party framework called PLBlocks that you can use already. Here is an article on how to use them, it also contains example for NSURLConnection.
This is the client code making HTTP request with block callback:
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]];
[NSURLConnection sendAsynchronousRequest:req onCompletionDo: ^(NSData *data, NSURLResponse *res, NSError *err) {
NSLog(#"data: %ld bytes. res: %#, error: %#", (long)[data length], res, err);
[cell.activity stopAnimating];
}];
I used to create a custom wrapper around NSURLConnection, too, but I've now switched over to ASIHTTPRequest. This is a fantastic library providing much more flexibility and features than NSURLConnection. Have a look and give it a try, it's really worth it.
What I do in my projects is create a wrapper class for the connection. This way, you can keep a new instance for each connection you need, and maintain these classes in another manager class.
Something like [AsynchronousConnection initWithURL:delegate:selector:]
Then you can be ensure the right thing is called when the NSURLConnection is done/failed.
Please do not refer https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html
GO to the NSURLConnection.h file and you will find the following.
When created, an NSURLConnection performs a deep-copy of the
NSURLRequest. This copy is available through the -originalRequest
method. As the connection performs the load, this request may change
as a result of protocol canonicalization or due to following
redirects. -currentRequest can be used to retrieve this value.
Ultimately [connection currentRequest].URL absoluteURL might help.
Regards,
PRASANNA.