Created
June 1, 2021 04:52
-
-
Save gngrwzrd/938f32d84b6e337d76c8b0145ce3ab99 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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