objective-cxcodensarraynsdictionarynsurl

Defining the type of a completionHandler in Objective-C


I can't manage to get the responseCompletion type correct, at the for cycle. Tried to replace it with typedef, but got no luck, only more errors.

@interface ImageManager : NSObject

//typedef void(^responseBlock)(UIImage * _Nullable image);

@property (nonatomic, strong) NSDictionary<NSString *,NSMutableArray<void(^)(UIImage * _Nullable image)> *> *loadingResponses;
@end

-(void)fetchImage:(NSString *)urlString and:(void (^_Nonnull)(UIImage * _Nullable image))completionhHandler {

    UIImage *image = [self.cache objectForKey:urlString];

    if (image != nil) {
        completionhHandler(image);
        return;
    }

    if (self.loadingResponses[urlString] != nil) {
        [self.loadingResponses[urlString] addObject:completionhHandler];
        return;
    } else {
        [[self.loadingResponses objectForKey:urlString] setArray:[[NSMutableArray alloc] initWithObjects:completionhHandler, nil]];
    }

    NSURL *url = [[NSURL alloc] initWithString:urlString];
    [[self.session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        if (error != nil) {
            UIImage *image = [[UIImage alloc] initWithData:data];
            [self.cache setObject:image forKey:urlString];
            completionhHandler(image);

            NSArray *array = [self.loadingResponses objectForKey:urlString];
            
            for ((void(^)(UIImage * _Nullable image) *responseCompletion) in array) {
                responseCompletion(image);
            }
        }

    }] resume];
}

Solution

  • Your typedef is fine as-is though I would name it ResponseBlock, not responseBlock.

    typedef void(^ResponseBlock)(UIImage * _Nullable image);
    

    When you use ResponseBlock, don't use a pointer.

    I also suggest changing loadingResponses to be an NSMutableDictionary so you can assign values to it.

    Here's an updated version of your code:

    The interface:

    typedef void(^ResponseBlock)(UIImage * _Nullable image);
    
    @interface ImageManager : NSObject
    
    @property (nonatomic, strong) NSMutableDictionary<NSString *, NSMutableArray<ResponseBlock> *> *loadingResponses;
    
    @end
    

    The method:

    - (void)fetchImage:(NSString *)urlString and:(ResponseBlock)completionHandler {
        UIImage *image = [self.cache objectForKey:urlString];
    
        if (image != nil) {
            completionHandler(image);
            return;
        }
    
        if (self.loadingResponses[urlString] != nil) {
            [self.loadingResponses[urlString] addObject:completionHandler];
            return;
        } else {
            self.loadingResponses[urlString] = [NSMutableArray arrayWithObject:completionHandler];
        }
    
        NSURL *url = [[NSURL alloc] initWithString:urlString];
        [[self.session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (error != nil) {
                UIImage *image = [[UIImage alloc] initWithData:data];
                [self.cache setObject:image forKey:urlString];
                completionHandler(image);
    
                NSArray *array = [self.loadingResponses objectForKey:urlString];
    
                for (ResponseBlock responseCompletion in array) {
                    responseCompletion(image);
                }
            }
        }] resume];
    }
    

    I'm assuming somewhere you initialize loadingResponses as:

    self.loadingResponses = [NSMutableDictionary dictionary];
    

    You also need to call the completion handler if there is an error. Perhaps you mean to do:

        [[self.session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            UIImage *image = nil;
            if (error != nil) {
                image = [[UIImage alloc] initWithData:data];
                [self.cache setObject:image forKey:urlString];
            }
    
            completionhHandler(image);
    
            NSArray<ResponseBlock> *array = self.loadingResponses[urlString];
    
            for (ResponseBlock responseCompletion in array) {
                responseCompletion(image);
            }
        }] resume];