A capstone program


#1

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.

Downloader.h

[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

//ivars
@property (assign) NSString *thePath;

@end[/code]

Downloader.m

[code]#import “Downloader.h”

// Globals
BOOL shouldKeepRunning = YES;

@implementation Downloader

//ivars
@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 “);
printf(cString);
printf(”\n”);
}
@end[/code]

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.

main.m

#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];
        }
        else
        {
            [downloader setThePath:thePath];

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

        
      
        // 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.