Skip to content

Instantly share code, notes, and snippets.

@gngrwzrd
Created June 1, 2021 04:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gngrwzrd/938f32d84b6e337d76c8b0145ce3ab99 to your computer and use it in GitHub Desktop.
Save gngrwzrd/938f32d84b6e337d76c8b0145ce3ab99 to your computer and use it in GitHub Desktop.
#import <Foundation/Foundation.h>
@class PipedTaskRunner;
@protocol PipedTaskRunnerDelegate
@optional
- (void) taskRunner:(PipedTaskRunner *) taskRunner didReadData:(NSData *) data;
- (void) taskRunner:(PipedTaskRunner *) taskRunner didReadDataFromStdOut:(NSData *) data;
- (void) taskRunner:(PipedTaskRunner *) taskRunner didReadDataFromStdErr:(NSData *) data;
- (void) taskRunnerTerminated:(PipedTaskRunner *) taskRunner;
@end
@interface PipedTaskRunner : NSObject {
BOOL _terminated;
}
@property (assign) NSObject * delegate;
@property BOOL collectDataUntilTerminated;
@property NSTask * task;
@property NSPipe * standardOutput;
@property NSPipe * standardError;
@property NSMutableData * data;
@end
#import "PipedTaskRunner.h"
@implementation PipedTaskRunner
- (id) init {
self = [super init];
self.task = [[NSTask alloc] init];
self.data = [NSMutableData data];
[self setupPipesAndNotifications];
return self;
}
- (void) setupPipesAndNotifications {
self.standardOutput = [[NSPipe alloc] init];
self.standardError = [[NSPipe alloc] init];
self.task.standardOutput = self.standardOutput;
self.task.standardError = self.standardError;
NSNotificationCenter * nfc = [NSNotificationCenter defaultCenter];
//add notification to task for termination
[nfc addObserver:self selector:@selector(taskTerminated:) name:NSTaskDidTerminateNotification object:self.task];
//add notifications for read data on standard out and standard error
[nfc addObserver:self selector:@selector(dataAvailable:) name:NSFileHandleDataAvailableNotification object:self.standardOutput.fileHandleForReading];
[nfc addObserver:self selector:@selector(dataAvailable:) name:NSFileHandleDataAvailableNotification object:self.standardError.fileHandleForReading];
[self.standardOutput.fileHandleForReading waitForDataInBackgroundAndNotify];
[self.standardError.fileHandleForReading waitForDataInBackgroundAndNotify];
}
- (void) dataAvailableForFileHandle:(NSFileHandle *) fileHandle {
NSData * data = [fileHandle availableData];
if(data.length > 0) {
if(self.collectDataUntilTerminated) {
[self.data appendData:data];
} else {
if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunner:didReadData:)]) {
[self.delegate taskRunner:self didReadData:data];
}
if(fileHandle == self.standardOutput.fileHandleForReading) {
if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunner:didReadDataFromStdOut:)]) {
[self.delegate taskRunner:self didReadDataFromStdOut:data];
}
}
if(fileHandle == self.standardError.fileHandleForReading) {
if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunner:didReadDataFromStdErr:)]) {
[self.delegate taskRunner:self didReadDataFromStdErr:data];
}
}
}
}
if(!_terminated) {
[fileHandle waitForDataInBackgroundAndNotify];
}
}
- (void) dataAvailable:(NSNotification *) notification {
NSFileHandle * fileHandle = notification.object;
[self dataAvailableForFileHandle:fileHandle];
}
- (void) taskTerminated:(NSNotification *) notification {
_terminated = TRUE;
[self dataAvailableForFileHandle:self.standardOutput.fileHandleForReading];
[self dataAvailableForFileHandle:self.standardError.fileHandleForReading];
if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunnerTerminated:)]) {
[self.delegate taskRunnerTerminated:self];
}
}
- (void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
if(self.task.isRunning) {
[self.task terminate];
}
self.task = nil;
self.delegate = nil;
self.standardOutput = nil;
self.standardError = nil;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment