0

I am using a Master Detail Controller. In the Master list there are 5 items. On selecting each item, there are Asynchronous calls made.

There is one SingleTon class, which handles all network calls.

    [[MyNetwokCommunication connectionInstance] 
makeRequest:"url1" delegate:self];

[[MyNetwokCommunication connectionInstance] makeRequest:"url2" delegate:self];

Actual implementation in makeRequest:delegate: is [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediatley:YES].

So, is this a correct way to handle network connection with a singleton class, as it might over-ride data part of one request with another.

NNikN
  • 3,720
  • 6
  • 44
  • 86

2 Answers2

1

There are a lot of ways to handle it. Unfortunately, the fact that NSURLConnection can't be used as a key in a dictionary makes life particularly miserable.

The best thing to do, assuming you don't still need to support iOS 6 and earlier (or OS X v10.8 or earlier) is to ditch NSURLConnection for NSURLSession. Then use block-based calls to make the request and handle the response. Unlike NSURLConnection objects, you can use NSURLSessionTask objects as dictionary keys, which makes it easy to keep track of what data came from which request, and to store additional objects or other data associated with that request (e.g. storing the UI cell where the data should go).

If you have to use NSURLConnection to support older operating systems, you might consider subclassing NSURLRequest and adding extra data. Note that this alternative approach does not work with NSURLSession, and you'll need to provide your own redirect handler, because otherwise you'll end up getting back generic NSURLRequest objects whenever a redirect happens, IIRC. Or you can add Objective-C associated objects on the connection object. (At least I'm 99% sure that this works.)

dgatwood
  • 10,129
  • 1
  • 28
  • 49
  • "NSURLConnection can't be used as key" , "Redirect Handler" , Can you please elaborate so, I can understand those aspects. – NNikN Aug 25 '15 at 02:44
  • NSURLConnection doesn't conform to the NSCopying protocol, which means you can't use it as a key in an NSDictionary. NSURLSessionTask does (in an unusual way, by returning the object when you call the copy method), so you can. – dgatwood Aug 25 '15 at 18:38
  • For more on redirect handlers, look here: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/RequestChanges.html – dgatwood Aug 25 '15 at 18:39
  • Thanks. I will be going ahead with NSURLSession. But, just for my info, why would I have to think about adding it (NSURLConnection) to a dictionary. No where in NSOperation or NSOperation I have to take care of adding it to dictionary, any scenario which you can share? – NNikN Aug 26 '15 at 03:18
  • From the example I see you have mentioned about adding NSURLConnection to dictionary to track downloads. Whereas NSURLSessionTask can be added. – NNikN Aug 26 '15 at 03:26
  • You can't add NSURLConnection to a dictionary. The best you can do is add an arbitrary identifier to the connection using objective-c associated objects, and add that to a dictionary. – dgatwood Aug 26 '15 at 20:24
  • Just to clarify, the reason you would need to add it to a dictionary is if you want to cancel it later (correctly) or store information that you'll need to know later when figuring out what to do with the response. Of course, with NSURLSession tasks, you can also associate data with the request by passing in a completion handler block. You can use a completion block with NSURLConnection, too, but not if you need to provide delegate methods (e.g. for custom caching/authentication/TLS handling). – dgatwood Sep 15 '15 at 06:50
0

That's not how I would do it.

The best paradigm on iOS to serialize things is an NSOperationQueue. You can create a queue with concurrency of 1, then queue your NSURLConnection or NSURLSession children as NSOperations.

This allows you to do neat things like create operations that are dependent on the success of other operations.

Here's the creation of the queue:

@property (strong, nonatomic) NSOperationQueue *queue;

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _queue = [[NSOperationQueue alloc] init];
        [_queue setMaxConcurrentOperationCount:1];
    });

Then to create a network operation:

//  HTTPOperation.h
#import <Foundation/Foundation.h>
@interface HTTPOperation : NSOperation
@end

//
//  HTTPOperation.m
//
#import "HTTPOperation.h"

@implementation HTTPOperation


- (void) main
{
    NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://google.com"]];
    NSURLResponse * response = nil;
    NSError * error = nil;
    NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest
                                          returningResponse:&response
                                                      error:&error];

    if (error == nil)
    {
        // Parse data here
        NSLog(@"Completed successfully");
    }
}

@end

To execute the operations:

- (IBAction)queueHTTP {
    HTTPOperation *op = [HTTPOperation new];
    [self.queue addOperation:op];
}

You can queue as many as you want from anywhere and they will execute serially. I recommend watching the WWDC video on Advanced NSOperations:

https://developer.apple.com/videos/wwdc/2015/?id=226

It was very informative.

David S.
  • 6,567
  • 1
  • 25
  • 45
  • Using NSOperation with synchronous network operations is bad practice at many levels. They aren't truly cancelable (the operation will finish retrieving data no matter what you do), and synchronous use of NSURLConnection leaks memory. Don't do it this way. – dgatwood Aug 24 '15 at 01:15
  • @dgatwood Apple disagrees with you. Watch the WWDC video. – David S. Aug 24 '15 at 01:24
  • I suspect that whoever came up with that section didn't run it past the Foundation networking folks. Synchronous NSURLConnection requests run to completion unless they fail. Canceling the NSOperation does not cancel the underlying network request because it is running on an entirely different background thread until it finishes and returns data. At that point, canceling the operation just prevents you from getting the data; it doesn't stop retrieving data, because the data has already been received. That's simply an inefficient way to retrieve data. – dgatwood Aug 24 '15 at 01:30
  • Also, NSURLConnection was deprecated beginning at WWDC 2015. So that means that the session in question, if it really recommended doing that, was recommending use of a deprecated API. Ick. – dgatwood Aug 24 '15 at 01:30
  • I was not suggesting that NSURLConnection was the way to go. He was using it in his example, so I continued with it. For all intents and purposes NSURLSession supplants NSURLConnection. – David S. Aug 24 '15 at 01:34
  • Ah. Fair enough. There's certainly nothing wrong with using NSOperation to manage your network operations (though NSURLSession makes much of that largely unnecessary by having per-host concurrency limits built-in and by providing continuation blocks to handle the data). Just make the requests asynchronous. :-) – dgatwood Aug 24 '15 at 01:38
  • I still recommend the WWDC video. You can make dependency graphs for Operations, something I don't know that people realize is possible. The sample code is very interesting too (though it's in Swift, so not applicable to the question above). – David S. Aug 24 '15 at 01:54
  • NSOperation would be good to work with, along with dependency, as I have the same scenario to implement, fetching user details , show list of items are dependent on user login, so this would be smart and better way to proceed , with NSURLSession – NNikN Aug 25 '15 at 02:40
  • Though idea is more clear, but one doubt which I have is, if I have button, where a specific network call is made, If I tap 2-3 times in short time, it will add those operation in queue. Also if that opens a modal controller, which also fetches the content , then would it not crash the app.? – NNikN Sep 19 '15 at 01:38