A capstone program


After finishing the book I’ve been mulling things over in my head and reading a lot of the Apple Documentation. I decided to define a simple task and then write a program from scratch with only the developer docs and Google to help me along.

I decided to make a simple web downloader. The usage is simple, just give it a URL and, optionally, a path. This project let me combine delegates, properties, globals, run loops, and command line arguments all in one place. It’s simple enough to understand and only took me a few hours to get to a point to share it. It’s al a bit ugly, but it fulfills the requirements I set for it. :smiley:

There is a Downloader class. Essentially just a delegate class.


[code]#import <Foundation/Foundation.h>

// A nasty global, but this is how Apple says to do it with a runloop that finishes
extern BOOL shouldKeepRunning;

@interface Downloader : NSObject

@property (assign) NSString *thePath;



[code]#import “Downloader.h”

// Globals
BOOL shouldKeepRunning = YES;

@implementation Downloader

@synthesize thePath;

// Delegate Methods

// Direct download the file and use the existing name

  • (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename
    NSString *destinationFilename;

    destinationFilename = [thePath stringByAppendingPathComponent];
    [download setDestination:destinationFilename allowOverwrite:YES];

// If the download fails show the error

  • (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error

    // Inform the user.
    NSLog(@“Download failed! Error - %@ %@”,
    [error localizedDescription],
    [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
    shouldKeepRunning = NO;

// We finished so let the user know and clean up

  • (void)downloadDidFinish:(NSURLDownload *)download

    shouldKeepRunning = NO;


-(void)download:(NSURLDownload *)download didCreateDestination:(NSString *)path
const char *cString = [path UTF8String];
printf(“File downloaded to “);

Since this is a command-line app everything is in main. I went with the NSArgumentsDomain method of getting the CLI args since it’s easy to validate and they can be entered in any order.


#import <Foundation/Foundation.h>
#import "Downloader.h"

int main (int argc, const char * argv[])

    @autoreleasepool {
        //Create an instance of the downloaded class
         Downloader *downloader = [[Downloader alloc] init];
        // Gather our CLI arguments
        NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
        NSString *theURL = [standardDefaults stringForKey:@"url"];
        NSString *thePath = [standardDefaults stringForKey:@"o"];

        // Validate the path. If no path given then assume the users pwd
        if (!theURL) {
            printf("Usage: -url <someURL> [-o <path>]\n");
            shouldKeepRunning = NO;
        else if (!thePath){
            printf("No path given. Assuming user's home directory for destination\n");
            //trying to figure this out to find the cwd. It's returning a dictionary. I think it's a null ending error.
            // NSString *tmpPath = [NSString stringWithCString:(getcwd(NULL, 0)) encoding:NSUTF8StringEncoding];
            // For now take the cheap way out and find the current user's home directory and set it as the destination
            NSString *homeDirectory = NSHomeDirectory();
            [downloader setThePath:homeDirectory];
            [downloader setThePath:thePath];

        //Send the NSURLDownload request to the downloader class
            // Create the request.
            NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:theURL]
            // Create the download with the request and start loading the data.
           __unused NSURLDownload  *theDownload = [[NSURLDownload alloc] initWithRequest:theRequest 

        // Spawn the run loop and watch for us to set it to done.
        NSRunLoop *theRL = [NSRunLoop currentRunLoop];
        while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
    return 0;

Again this book has been great! Several of my friends have picked it up as a result of my raving on about it.