//SCTaskRunner.h #import @class SCTaskRunner; @protocol SCTaskRunnerDelegate @optional - (void) taskRunner:(SCTaskRunner *) taskRunner didReadDataFromStdOut:(NSData *) data; - (void) taskRunner:(SCTaskRunner *) taskRunner didReadDataFromStdErr:(NSData *) data; - (void) taskRunnerTerminated:(SCTaskRunner *) taskRunner; @end @interface SCTaskRunner : NSObject @property (assign) NSObject * delegate; @property NSTask * task; @property NSPipe * standardInput; @property NSPipe * standardOutput; @property NSPipe * standardError; - (id) initWithStandardInput:(NSPipe *) pipe; @end //SCTaskRunner.m #import "SCTaskRunner.h" @implementation SCTaskRunner - (id) init { self = [super init]; self.task = [[NSTask alloc] init]; [self setupPipesAndNotifications]; return self; } - (id) initWithStandardInput:(NSPipe *) standardInput { self = [self init]; self.standardInput = standardInput; return self; } - (void) setupPipesAndNotifications { self.standardOutput = [[NSPipe alloc] init]; self.standardError = [[NSPipe alloc] init]; //add notification to task for termination NSNotificationCenter * nfc = [NSNotificationCenter defaultCenter]; [nfc addObserver:self selector:@selector(taskTerminated:) name:NSTaskDidTerminateNotification object:self.task]; //add notifications for read data on standard out and standard error self.task.standardOutput = self.standardOutput; self.task.standardError = self.standardError; [nfc addObserver:self selector:@selector(dataAvailable:) name:NSFileHandleReadCompletionNotification object:self.standardOutput.fileHandleForReading]; [nfc addObserver:self selector:@selector(dataAvailable:) name:NSFileHandleReadCompletionNotification object:self.standardError.fileHandleForReading]; [self.standardOutput.fileHandleForReading readInBackgroundAndNotify]; [self.standardError.fileHandleForReading readInBackgroundAndNotify]; if(self.standardInput) { self.task.standardInput = self.standardInput; } } - (void) dataAvailable:(NSNotification *) notification { NSData * data = [notification.userInfo objectForKey:NSFileHandleNotificationDataItem]; if(notification.object == self.standardOutput.fileHandleForReading) { if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunner:didReadDataFromStdOut:)]) { [self.delegate taskRunner:self didReadDataFromStdOut:data]; } } else if(notification.object == self.standardError.fileHandleForReading) { if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunner:didReadDataFromStdErr:)]) { [self.delegate taskRunner:self didReadDataFromStdErr:data]; } } } - (void) taskTerminated:(NSNotification *) notification { if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunnerTerminated:)]) { [self.delegate taskRunnerTerminated:self]; } } - (void) dealloc { NSLog(@"DEALLOC: SCTaskRunner"); [[NSNotificationCenter defaultCenter] removeObserver:self]; self.task = nil; self.delegate = nil; self.standardOutput = nil; self.standardError = nil; self.standardInput = nil; } @end