Defining the type of a completionHandler in Objective-C

29 Views Asked by At

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];
}
1

There are 1 best solutions below

0
HangarRash On BEST ANSWER

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];