0

I want to implement file downloading with progress from my server. I my code I'm using a custom class which is delegated by

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://example.com"]];
            if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {
                DownloadCallback *dc = [[DownloadCallback alloc] initWithCallbackProgress:^(long long res){
                    NSLog(@"%lld", res);
                } withCallbackReady:^(long long res){
                    NSLog(@"READY %lld", res);
                    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

                    }];

                } withCallbackError:^(NSError * error) {
                    NSLog(@"READY %@", error.domain);
                }];

                NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:dc];
//                [connection setDelegateQueue:[[NSOperationQueue alloc] init]];
                [connection start];

header:

@interface DownloadCallback: NSObject<NSURLConnectionDataDelegate>{
     @private void (^_progressHandler)(long long someParameter);
     @private void (^_readyHandler)(long long someParameter);
     @private void (^_errorHandler)(NSError *someParameter);
}
-(id) initWithCallbackProgress:(void(^)(long long))handler withCallbackReady:(void(^)(long long))handlerReady  withCallbackError:(void(^)(NSError*))handlerError;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
@end

body:

@implementation DownloadCallback
-(id) initWithCallbackProgress:(void(^)(long long))handler withCallbackReady:(void(^)(long long))handlerReady  withCallbackError:(void(^)(NSError*))handlerError{
    self = [super init];
    if (self) {
        _progressHandler = [handler copy];
        _readyHandler = [handlerReady copy];
        _errorHandler = [handlerError copy];
    }
    return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
   // self.expectedTotalSize = response.expectedContentLength;

    // Call completion handler.
    if (_readyHandler != nil)
        _readyHandler(response.expectedContentLength);

    // Clean up.
//    [_completionHandler release];
    _readyHandler = nil;
    _progressHandler = nil;
    _errorHandler = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//    self.recievedData += data.length;
    if (_progressHandler != nil)
        _progressHandler(data.length);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    if (_errorHandler != nil)
        _errorHandler(error);
}
@end

But the callback events are not fired! At all!

The simple synch code work prefectly:

    // Send a synchronous request
    NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com"]];
    NSURLResponse * response = nil;
    NSError * error = nil;
    NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest
                                          returningResponse:&response
                                                      error:&error];
    if (error == nil) {
        // Parse data here
    }

But I need a callback! How to resolve it? I've not found in stackoverflow a solution. Futhermore, if I'm using a simple delegate to major class instead of DownloadCallback the same: the connection callbacks are not fired too.

Vyacheslav
  • 26,359
  • 19
  • 112
  • 194

1 Answers1

0

Add the dealloc method to your callback class and out a breakpoint or log statement in it. See if it is deallocated before the callbacks are called.

If this is the case, your callback class instance is destroyed too soon. Make it a property of a class that will for sure live longer then the request.

Also, you should make sure that this code:

NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:dc];
[connection start];

is called on a thread outlives the connection and has a runloop. The easiest way to achieve this is to call that code on the main-queue. Your code-example does not show on which queue that is called. If it is not working I assume it is because your calling it on a background queue. You can dispatch to a background queue from the delegate callbacks of you want/need to.

As a sidenote, if you are building something new, you should try and use NSURLSession instead of NSURLConnection. NSURLSession is more secure, easier to use and not deprecated. NSURLConnection is deprecated.

Joride
  • 3,722
  • 18
  • 22
  • Sadly, but this is not the reason:( – Vyacheslav Apr 10 '16 at 11:35
  • What about your `NSURLConnection *connection`? Can you show the enclosing scope of the first snippet of code that you show? If you do not maintain a strong reference to your connection after `[connection start]`, it will be deallocated immediately after you call `start`. – Joride Apr 10 '16 at 16:45
  • I found that the problem is about main thread execution - this is not possible to execute `NSURLConnection ` until main thread is finished. – Vyacheslav Apr 10 '16 at 16:50
  • so, I do not understand , how to launch th NSURLConnection inside a `[[NSOperationQueue alloc] init]` correctly. – Vyacheslav Apr 10 '16 at 16:51