Question

I am using a NSOperation queue to fetch images and shown them on my tableview cells. Until the image comes back I show the loading overlay and once the operation completes it intimates the delegate and then I remove the loading overlay.

Now, I wanted to time out the fetch operation after say 5 secs and remove the loading overlay but the timer based approach isn't working out. Please suggest.

Below is my code:

#import "MyImageFetchOperation.h"
#import "MyImageFetchController.h"
#import "MyHTTPRequest.h"

@interface MyImageFetchOperation ()

@property (nonatomic, strong) NSString *imageURL;
@property (nonatomic, weak) id <MyOperationCompletedDelegate> delegate;
@property (nonatomic, assign) BOOL isCompleted;
@property (nonatomic, weak) NSTimer *timeoutTimer;

@end

@implementation MyImageFetchOperation
@synthesize imageURL;
@synthesize delegate;
@synthesize isCompleted;

#define kMyImageFetchTimeout 5

#pragma mark -
#pragma mark Destruction

- (void)dealloc {
    self.delegate = nil;
}


- (id)initWithImageURL:(NSString *)iImageURL delegate:(id)iDelegate {
    if (self = [super init]) {
        self.imageURL = iImageURL;
        self.delegate = iDelegate;
    }
    return self;
}


- (void)main {
    self.isCompleted = NO;
    NSMutableURLRequest *aRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.imageURL]];
    [aRequest setTimeoutInterval:kMyImageFetchTimeout];
    [aRequest setHTTPMethod:@"GET"];

    self.timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:kMyImageFetchTimeout target:self selector:@selector(requestTimedOut) userInfo:nil repeats:NO];

    [NSURLConnection sendAsynchronousRequest:aRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *iData, NSError *iConnectionError) {
        if (!self.isCompleted) {
            if (iConnectionError) {
                [[MyImageFetchController sharedRunnerImageFetchControllerMy] urlFailed:self.imageURL];
            }

            UIImage *anImage = [UIImage imageWithData:iData];

            if (anImage) {
                [MyUtilities cacheFile:anImage withName:[self.imageURL runnerMD5HashMy] toDirectory:[self.delegate cacheDirectoryForImages]];
            } else {
                [[MyImageFetchController sharedRunnerImageFetchControllerMy] urlFailed:self.imageURL];
            }

            [self.delegate operationCompletedForURL:self.imageURL];
            self.isCompleted = YES;
        }
    }];
}


- (void)requestTimedOut {
    self.isCompleted = YES;
    [self.timeoutTimer invalidate];
    self.timeoutTimer = nil;
    [[MyImageFetchController sharedRunnerImageFetchControllerMy] urlFailed:self.imageURL];
    [self.delegate operationCompletedForURL:self.imageURL];
}

@end
Was it helpful?

Solution

You need a run loop to make your timer work, something like this:

- (void)main
{
    // your code
    yourTimer = ... // create your timer

    NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
    [runLoop addTimer:yourTimer forMode:NSRunLoopCommonModes];
    [runLoop run];
}

Oh, and, i guess it's better to set up your own autorelease pool in main.

OTHER TIPS

Why don't you call

[self.delegate operationCompletedForURL:self.imageURL];

inside this if?

if (iConnectionError) {

As a side not, you shouldn't be using self inside that block, you should use a __block variable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top