How do you return from asynchronous NSURLConnection to the calling class?

I have the following class that makes an asynchronous HTTP request to avoid problems with the main UI thread:

@implementation DataFeeder -(void) doLookup:(NSString *)inputValue { NSString *myRequestString = [NSString stringWithFormat:@"val=%@", inputValue]; NSMutableData *myRequestData = [ NSMutableData dataWithBytes: [ myRequestString UTF8String ] length: [ myRequestString length ] ]; NSURL * myUrl = [NSURL URLWithString: @"http://mywebsite/results.php"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: myUrl]; [request setHTTPMethod: @"POST"]; [request setHTTPBody: myRequestData]; [request setTimeoutInterval:10.0]; [[NSURLConnection alloc] initWithRequest:request delegate:self]; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { responseData = [[NSMutableData alloc] init]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [responseData appendData:data]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { // Show error message } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { // Use responseData // Got all my response data here, so build up an object ready to send back } @end 

I call this from my ViewController using the following line of code:

MyObject * myObj = [feeder doLookup:@"SomeStaticStringForNow"];

So, as I understand it:

  • doLookup will execute an asynchronous connection request.
  • When the data is fully loaded, it will call connectionDidFinishLoading
  • Once the data has finished loading, I will create an object from the response data, which I will send back to the calling controller.

How can I get the calling controller to listen to this? Do I need to implement my own callback methods in the ViewController that will listen for the call and then stop the counter and update the user interface based on the contents of myObj ?

I hope theres a really easy way that I overlooked ...

thanks

+4
source share
3 answers

Yes, you must implement your callback using the delegate template. That is, in my opinion, the easiest and most standard way to do this. There are other ways, as you can see in the other answers.

In your DataFeeder.h file:

 @protocol DataFeederDelegate - (void)dataReady:(NSData*)data; @end @interface DataFeeder : NSObject { id delegate_; } - (id)initWithDelegate:(id<DataFeederDelegate>)delegate; @end 

In DataFeeder.m :

 @implementation DataFeeder - (id)initWithDelegate:(id<DataFeederDelegate>)delegate { self = [super init]; if(self) { delegate_ = delegate; } return self; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { [delegate_ dataReady:responseData]; } @end 

You must create an instance of the DataFeeder object as follows:

 DataFeeder *dataFeeder = [[DataFeeder alloc] initWithDelegate:self]; 

Of course, the calling view controller must implement the DataFeederDelegate methods.

+6
source

You have several ways to get a ViewController notification when data is available:

  • define the delegate protocol between the ViewController and the DataFeeder , so that the latter sends a message to the first line in connectionDidFinishLoading: ViewController

  • use NSNotificationCenter to decouple DataFeeder and ViewController : ViewController adds itself as an observer to the default notification center:

      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataIsThere:) name:kMyNetworkNotificationDataIsThere object:nil]; 

while DataFeeder send a notification at the right time:

  [[NSNotificationCenter defaultCenter] postNotificationName:kMyNetworkNotificationDataIsThere object:self]; 
  • make ViewController implement the delegate methods for NSURLConnection and process the response itself (to do this, pass it as a parameter to the DataFeeder constructor).
+4
source

A good way to do this would be to use blocks. Your doLookup: method can accept a block object, and you can call it when the connection ends.

Since you probably want to be able to execute multiple requests with different completion blocks, you need to associate the passed in block with the corresponding NSURLConnection.

To do this, you can either use the NSURLConnection subclass with the completionBlock property, or use the objective-C related objects (using the objc_setAssociatedObject and objc_getAssociatedObject ) to bind the block to the connection object.

When everything is ready in the connectionDidFinishLoading: method, and you have prepared the final response object, you grab the block from the NSURLConnection object and call it, passing it the final data.

So, you end up wanting your client code to look like this:

 [feeder doLookup:@"Something" completionBlock:(FetchedData *data, NSError *error) { if (error) { // ... return; } // access the returned data }]; 

I hope this was detailed enough for you.

+2
source

Source: https://habr.com/ru/post/1415393/


All Articles