Objective C print stdout to UIAlertView
-
29-06-2021 - |
Question
I have a C function that prints to stdout using fprintf
, and I'm attempting to display the contents of stdout in a UIAlertView
. My code is as follows:
NSFileHandle *stdoutFileHandle = [NSFileHandle fileHandleWithStandardOutput];
NSData *stdoutData = [stdoutFileHandle availableData];
NSString *stdoutString = [[NSString alloc] initWithData:stdoutData encoding:NSASCIIStringEncoding];
UIAlertView *stdoutAlert = [[UIAlertView alloc] initWithTitle:@"STDOUT OUTPUT" message:stdoutString delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[stdoutAlert show];
I'm getting the following error when I run my code.
Terminating app due to uncaught exception 'NSFileHandleOperationException', reason: '[NSConcreteFileHandle availableData]: Bad file descriptor'
I get an equivalent error when I replace [stdoutFileHandle availableData]
with [stdoutFileHandle readDataToEndOfFile]
.
Solution
The problem is you are reading from a output stream. To make this work you need to trick stdout to write it's contents to an input stream instead of to the console.
I know the old C way to do this, but you're not gonna like it. This uses pipe() and dup2().
int pipefd[2];
pipe(pipefd);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
At this point anything written to stdout can be read by pipefd[0]
. At that point you can use -initWithFileDescriptor:
to read from pipefd[0]
.
NSFileHandle *stdoutReader = [[NSFileHandle alloc] initWithFileDescriptor:pipefd[0]];
Note, you will want to do lots of error checking. Hope that helps.
OTHER TIPS
I followed the selected answer post in this question: What is the best way to redirect stdout to NSTextView in Cocoa?
It felt a bit more familiar to follow for me. I created an NSPipe, and NSFileHandler object for the pipe and read handler and I used notifications. I put the open method below in the viewDidAppear and viewDidDisappear method because of my needs but you can put it wherever it's appropriate
// Piping stdout info from here WILL NOT PIPE NSLOG:
// https://stackoverflow.com/questions/2406204/what-is-the-best-way-to-redirect-stdout-to-nstextview-in-cocoa
- (void) openConsolePipe {
_pipe = [NSPipe pipe];
_pipeReadHandle = [_pipe fileHandleForReading] ;
dup2([[_pipe fileHandleForWriting] fileDescriptor], fileno(stdout)) ;
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleNotification:) name: NSFileHandleReadCompletionNotification object: _pipeReadHandle] ;
[_pipeReadHandle readInBackgroundAndNotify] ;
}
- (void) closeConsolePipe {
if (_pipe != nil) {
[[_pipe fileHandleForWriting] closeFile];
// documentation suggests don't need to close reading file handle b/c auto but this suggests otherwise:
// https://stackoverflow.com/questions/13747232/using-nstask-and-nspipe-causes-100-cpu-usage
// [[_pipe fileHandleForReading] closeFile];
}
}
- (void) handleNotification:(NSNotification *)notification {
[_pipeReadHandle readInBackgroundAndNotify] ;
NSString *str = [[NSString alloc] initWithData: [[notification userInfo] objectForKey: NSFileHandleNotificationDataItem] encoding: NSUTF8StringEncoding];
// do what you want with the str here.
[_consoleView setText:[_consoleView.text stringByAppendingString:str]];
[_consoleView scrollRangeToVisible:NSMakeRange([_consoleView.text length], 0)];
}
I hope this ends up helping someone else googling this...