Pergunta

Say I want to create an easy way to download an image from Imgur. I do it frequently enough within my app that it'd be nice to not have to go through all of the setup for a NSURLSessionDownloadTask and configuring it for this use-case, and instead I could use a subclass that would handle most of that configuration.

Is it possible to subclass NSURLSession in a way that would allow easy interfacing with a specific service?

Say I frequently take the image ID from an Imgur link, for example mFJlvPf. Instead of setting up the NSURLSession, setting its configuration, creating the NSURLRequest, grabbing the result from the NSURL location it was downloaded from, I could instead just supply the image ID and go to town:

ImgurDownloadTask *downloadTask = [ImgurDownloadTask taskWithImageID:@"mFJlvPf"
            progressBlock:^(CGFloat bytesWritten, CGFLoat totalBytesExpectedToBeWritten) {
            } 
            completionBlock:^(UIImage *downloadedImage) { 
            }];

[downloadTask resume];

Is such a simplification possible? If so, how would I go about doing it?

Foi útil?

Solução

This is indeed possible. I would approach the problem like this:

@interface ImgurClient : NSObject
+(instancetype) sharedClient;
-(NSURLSessionDownloadTask*)taskWithImageID:(NSString*)imageId
                              progressBlock:(void (^)(/*TBD*/))progressBlock
                          completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler
@end

@implementation ImgurClient {
    NSURLSession* session;
}

+(instancetype) sharedClient {
    static ImgurClient* _sharedClient = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        _sharedClient = [[ImgurClient alloc] init];
    });

    return _sharedClient;
}

-(id) init {
    self = [super init];
    if (self) {
        // Additional initialisation, setting up NSURLSession, etc
    }
}

-(NSURLSessionDownloadTask*)taskWithImageID:(NSString*)imageId
                              progressBlock:(void (^)(/*TBD*/))progressBlock
                          completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler {
 // Build URL, store progress handler, create download task
}

That is, there's a ImgurClient object that is a singleton and whenever you need to do a request you use it like [[ImgurClient sharedClient] taskWithImageId:@"..." ....];.

Outras dicas

Your intuition of wrapping the complexity of this download task is excellent, but you probably would not subclass NSURLSessionDownloadTask, but rather start your custom classes with the NSURLSession object, because to implement progress block, you need to implement delegate methods, and these task-related delegate methods are implemented called in the session's delegate.

So, wrap your NSURLSession in an object, and put your custom factory method with its progress/completion blocks there. If you're going to supply task-specific progress/completion blocks, then you will also have to maintain some mapping (e.g. via NSMutableDictionary) between your task identifiers and these blocks (so your delegate knows which block to call on the basis of the task identifier).

BTW, if you are going to support background sessions, that introduces a whole different set of challenges (because background tasks persist beyond the standard lifecycle of your app and your custom objects). You might want to explicitly decide whether you're going to support background sessions or not (and your life will be easier if you exclude them).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top