I have multiple views which make the same NSURLRequest/NSURLConnection request
. Ideally, in order to get some code reuse, I'd like to have some sort of a "proxy" which does all the underlying work of creating/executing the (asynchronous) request/connection, setting up all the delegate methods, etc., so I don't have to copy all those NSURLConnection
delegate method handlers in each view. First of all, is this design approach reasonable? Second, how would I go about doing something like that?
For a little background info, I attempted this and got it to "work", however, it doesn't appear to be executing asynchronously. I created a Proxy.h/m file which has instance methods for the different web service calls (and also contains the NSURLConnection
delegate methods):
@interface Proxy : NSObject {
NSMutableData *responseData;
id<WSResponseProtocol> delegate;
}
- (void)searchForSomethingAsync:(NSString *)searchString delegate:(id<WSResponseProtocol>)delegateObj;
@property (nonatomic, retain) NSMutableData *responseData;
@property (assign) id<WSResponseProtocol> delegate;
@end
The WSResponseProtocol is defined as such:
@protocol WSResponseProtocol <NSObject>
@optional
- (void)responseData:(NSData *)data;
- (void)didFailWithError:(NSError *)error;
@end
To use this, the view controller simply needs to conform to the WSResponseProtocol
protocol, to catch the response(s). Making the web service call is done like so:
Proxy *p = [[Proxy alloc] init];
[p searchForSomethingAsync:searchText delegate:self];
[p release];
I can provide more code but the remaining can be assumed. Before calling, I "startAnimating" a UIActivityIndicatorView
spinner. But the spinner never spins. If I simply put the NSURLConnection delegate methods directly in the view controller, then the spinner spins. So, it makes me think that my implementation isn't executing asynchronously. Any thoughts/ideas here?
Your approach is reasonable, however, I'm not sure why you are creating your own protocol. This is not necessary. Everything you need to get this implemented is in Apple's documentation on NSURLConnection. If you take the code from that page where the NSURLConnection is instantiated, and make the connection an ivar instead of just creating it as a local variable, you can then compare connection objects in each of the callback methods and respond accordingly. For example, take this code from the docs and change the connection object to an ivar:
// create the request
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData that will hold
// the received data
// receivedData is declared as a method instance elsewhere
receivedData=[[NSMutableData data] retain];
} else {
// inform the user that the download could not be made
}
The variable theConnection is our ivar. Then you can check it like this:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (connection == theConnection)
{
// do something with the data object.
[connectionSpecificDataObject appendData:data];
}
}
You can certainly implement it creating your own protocol as you're suggesting and then call back out to the delegate that conforms to your protocol, but you may be better off just instantiating your object using a success and failure selector that you can check. Something like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (connection == theConnection)
{
if (delegate && [delegate respondsToSelector:successSelector])
[delegate performSelector:successSelector
withObject:connectionSpecificDataObject];
}
[connection release];
}
Where dataDidDownloadSelector is a SEL instance variable that you set when you created your download delegate where all of this code is contained--your Proxy object. Something like this:
Proxy *p = [[Proxy alloc] init];
[p searchForSomethingAsync:searchText
delegate:self
successSelector:@selector(didFinishWithData:)
failSelector:@selector(didFailWithError:)];
Implement your selectors like this:
- (void)didFinishWithData:(NSData*)data;
{
// Do something with data
}
- (void)didFailWithError:(NSError*)error
{
// Do something with error
}
This has become a longer answer than I intended. Let me know if it doesn't make sense and I can try to clarify.
Best regards,
Your code as is, I think would do nothing - you release the Proxy just after you initialize, so it never even runs.
An approach I like to use is synchronous NSURLConnection calls, inside of an NSOperation which in turn is managed by an NSOperationQueue. I make the object the queue lives in a Singleton, so I just access the instance from anywhere and tell it when I need a new connection started.