• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Seeking advice for how to implement "notification" upon completion of asynchronous upload
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Seeking advice for how to implement "notification" upon completion of asynchronous upload


  • Subject: Re: Seeking advice for how to implement "notification" upon completion of asynchronous upload
  • From: WT <email@hidden>
  • Date: Fri, 01 Apr 2011 02:35:50 -0300

On Mar 31, 2011, at 9:08 PM, Chris Markle wrote:

> Still fairly new here to iOS and Objective-C programming, so looking
> for some advice to help keep me from going down the wrong road(s)... I
> would like to build an Objective-C class or classes to perform a file
> upload using a non-standard protocol. It would run the upload
> asynchronously and would need to notify the caller upon completion. I
> see a variety of techniques used or discussed for this and I was
> hoping to get some advice on which pattern or patterns I should use
> and which I should stay away from. I have no issue with using stuff
> that only runs on the latest iOS or OS X systems, so supporting older
> OS's in not a concern for me.
>
> I see in particular:
>
> 1. delegates - e.g., NSURLConnection connection:didFailWithError: and
> connectionDidFinishLoading:, or similar ones in ASIHTTPRequest
> requestFinished or requestFailed
>
> 2. blocks - e.g., ASIURLRequest completionBlock or failedBlock, or
> ALAssetsLibrary assetForURL:resultBlock:failureBlock: (ASIHTTPRequest
> actually implements delegates and blocks and allows you to mix and
> match them.)
>
> 3. KVO (key-value observation) - e.g., some service where you observe
> an isFinished property
>
> 4. Notifications (NSNotificationCenter, etc.) - haven't really seen
> this used much but a lot of people seem to talk about it as a good
> approach
>
> There are probably other techniques as well...
>
> For a new, modern API which approach or approaches should I use or not
> use? I suppose my main interest here is to use something that's
> flexible to the programmer that uses my API, and something that is
> conventional ion its approach so that the programmer is using
> something normal and not unusual.
>
> Thanks in advance for any counsel on this...
>
> Chris

Hi Chris,

I've written a fairly general Downloader class that takes in a url and some identifying token (so you can recognize the Downloader instance later on) and asynchronously downloads the url's content. It invokes methods on its delegate when it's done and on fail, passing it the data if the download succeeded. You can create multiple instances and they'll work independently. It also automatically keeps track of maintaining state for the network activity indicator (and does so in a thread-safe manner, even with many Downloader instances doing their thing).

It works great, for instance, when you want to populate the rows of a table view with some downloaded data (eg, images from a web service), in which case you'd use the row's index path as the identifying token. You can then fire as many Downloader instances as there are visible rows and update each row as its downloader is done.

Hope this helps.
WT

// Downloader.h
// Copyright 2010 Wagner Truppel. All rights reserved.

// Use/change as you see fit, but at your own risk.

// Usage:

// Downloader* downloader = [[Downloader alloc] init];
// downloader.idToken = ...
// downloader.urlStr = ...
// downloader.delegate = ...
// [downloader startDownload];

// If there's a need to abort the download, invoke -stopDownload on
// the instance in question.

// Note: the public methods are NOT thread safe.

#import <Foundation/Foundation.h>

@protocol DownloaderDelegate;

// ========================================================================= //

@interface Downloader: NSObject
{
    @private

        id <DownloaderDelegate>     delegate_;

        id                          idToken_;
        NSString*                   urlStr_;

        NSURLConnection*            connection_;
        NSMutableData*              activeData_;

        BOOL                        isDownloading_;
}

// ======================================== //

@property (readwrite, nonatomic, assign) id <DownloaderDelegate> delegate;

@property (readwrite, nonatomic, retain) id idToken;
@property (readwrite, nonatomic, retain) NSString* urlStr;

// ======================================== //

- (void) startDownload;
- (void)  stopDownload;

@end

// ========================================================================= //

@protocol DownloaderDelegate <NSObject>

// NOTE: these are invoked and executed in the main thread.

- (void)      downloader: (Downloader*) downloader
didFinishDownloadingData: (NSData*) data;

- (void) downloader: (Downloader*) downloader
    failedWithError: (NSError*) error;

@end

// ========================================================================= //

// Downloader.m
// Copyright 2010 Wagner Truppel. All rights reserved.

// Use/change as you see fit, but at your own risk.

#import "Downloader.h"

// ========================================================================= //

// Keeps track of the number of active connections so we can keep the
// network activity indicator on when there are active connections.
static NSInteger stConnectionCount = 0;

// ========================================================================= //

@interface Downloader ()

@property (readwrite, nonatomic, retain) NSURLConnection* connection;
@property (readwrite, nonatomic, retain) NSMutableData* activeData;

@end

// ========================================================================= //

@interface Downloader (Private)

- (void) incrementActivityCount;
- (void) decrementActivityCount;

- (void) didFinishDownloadingData;
- (void) failedWithError: (NSError*) error;

- (void) cleanup;

@end

// ========================================================================= //

@implementation Downloader

@synthesize delegate = delegate_;

@synthesize idToken = idToken_;
@synthesize urlStr = urlStr_;

@synthesize connection = connection_;
@synthesize activeData = activeData_;

// ======================================== //

- (void) dealloc;
{
    [self cleanup];

    self.delegate = nil;

    self.idToken = nil;
    self.urlStr = nil;

    [super dealloc];
}

// ======================================== //

- (void) startDownload;
{
    if (isDownloading_)
    { return; }

    isDownloading_ = YES;

    NSLog(@"starting download from:");
    NSLog(@"%@", self.urlStr);
    NSLog(@"");

    [self performSelectorOnMainThread: @selector(incrementActivityCount)
                           withObject: nil waitUntilDone: NO];

    self.activeData = [NSMutableData data];

    NSURLRequest* urlRequest = [NSURLRequest
        requestWithURL: [NSURL URLWithString: self.urlStr]
           cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
       timeoutInterval: 20];

    // Allocated here but released on completion or failure
    // (through the -cleanup method).
    NSURLConnection* connection = [[NSURLConnection alloc]
        initWithRequest: urlRequest delegate: self];
    self.connection = connection;
    [connection release];
}

// ======================================== //

- (void) stopDownload;
{
    if (! isDownloading_)
    { return; }

    [self cleanup];
}

// ========================================================================= //
                  #pragma mark NSURLConnection delegate methods
// ========================================================================= //

- (void) connection: (NSURLConnection*) connection
     didReceiveData: (NSData*) data;
{
    [self.activeData appendData: data];
}

// ======================================== //

- (void) connectionDidFinishLoading: (NSURLConnection*) connection;
{
    [self performSelectorOnMainThread: @selector(didFinishDownloadingData)
                           withObject: nil waitUntilDone: NO];
}

// ======================================== //

- (void) connection: (NSURLConnection*) connection
   didFailWithError: (NSError*) error;
{
    [self performSelectorOnMainThread: @selector(failedWithError:)
                           withObject: error waitUntilDone: NO];
}

// ========================================================================= //
                        #pragma mark private methods
// ========================================================================= //

- (void) incrementActivityCount;
{
    stConnectionCount += 1;
    NSLog(@"stConnectionCount: %i", stConnectionCount);

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}


// ======================================== //

- (void) decrementActivityCount;
{
    stConnectionCount -= 1;
    NSLog(@"stConnectionCount: %i", stConnectionCount);

    if (stConnectionCount == 0)
    {
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    }
}

// ======================================== //

- (void) didFinishDownloadingData;
{
    if ([self.delegate respondsToSelector:
        @selector(downloader: didFinishDownloadingData:)])
    {
        [self.delegate downloader: self
         didFinishDownloadingData: self.activeData];
    }

    [self cleanup];
}

// ======================================== //

- (void) failedWithError: (NSError*) error;
{
    if ([self.delegate respondsToSelector:
        @selector(downloader: failedWithError:)])
    {
        [self.delegate downloader: self
                  failedWithError: error];
    }

    [self cleanup];
}

// ======================================== //

- (void) cleanup;
{
    if (self.connection != nil)
    {
        [self.connection cancel];
        [self performSelectorOnMainThread: @selector(decrementActivityCount)
                               withObject: nil waitUntilDone: NO];
    }

    self.connection = nil;
    self.activeData = nil;

    isDownloading_ = NO;
}

// ========================================================================= //

@end

_______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden

References: 
 >Seeking advice for how to implement "notification" upon completion of asynchronous upload (From: Chris Markle <email@hidden>)

  • Prev by Date: Simulate touch event with cordinate?
  • Next by Date: Re: (no subject)
  • Previous by thread: Seeking advice for how to implement "notification" upon completion of asynchronous upload
  • Next by thread: <AppDelegate> does not implement the NSTextViewDelegate protocol
  • Index(es):
    • Date
    • Thread