# Displaying console output in a text field

**URL:** <https://forums.kodeco.com/t/displaying-console-output-in-a-text-field/85983>\
**Category:** Swift\
**Created:** [October 23, 2019, 6:14pm UTC](https://forums.kodeco.com/t/displaying-console-output-in-a-text-field/85983 "2019-10-23T18:14:54Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![ncrusher](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/ncrusher/32/81494_2.png) [@ncrusher](https://forums.kodeco.com/u/ncrusher)\
**Post date:** [October 23, 2019, 6:14pm UTC](https://forums.kodeco.com/t/displaying-console-output-in-a-text-field/85983/1 "2019-10-23T18:14:54Z")

</div>

I finally had a breakthrough and got my ffmpeg GUI app working. My next challenge is getting the console output to print to a text field in the app instead of to the console so that the user can monitor ffmpeg’s progress and possible errors.

In my research, I see lots of how-tos about redirecting console output to a log file, but not about redirecting it to a text field.

EDIT: I actually managed to get a version of this working using [this approach](https://stackoverflow.com/a/29549342/12199136). Unfortunately each new line overwrites the last so it’s not a scrolling text, but it’s a step in that direction.

Here’s my code if anyone is interested:

```
		convertTask.arguments = arguments
		convertTask.standardInput = FileHandle.nullDevice
		
		let pipe = Pipe()
		convertTask.standardError = pipe
		let outHandle = pipe.fileHandleForReading
		outHandle.waitForDataInBackgroundAndNotify()
		
		var obs1 : NSObjectProtocol!
		obs1 = NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable,
																									object: outHandle, queue: nil) { notification -> Void in
																										let data = outHandle.availableData
																										if data.count > 0 {
																											if let str = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
																												self.ffmpegLogOutput.string = ("\(str)")
																											}
																											outHandle.waitForDataInBackgroundAndNotify()
																										} else {
																											print("EOF on stderr from process")
																											NotificationCenter.default.removeObserver(obs1!)
																										}
		}
		
		var obs2 : NSObjectProtocol!
		obs2 = NotificationCenter.default.addObserver(forName: Process.didTerminateNotification,
																									object: convertTask, queue: nil) { notification -> Void in
																										print("terminated")
																										NotificationCenter.default.removeObserver(obs2!)
		}
		convertTask.launch()
		convertTask.waitUntilExit()
	}

```

---

<div class="post-metadata">

**Author:** ![shogunkaramazov](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/shogunkaramazov/32/108826_2.png) [@shogunkaramazov](https://forums.kodeco.com/u/shogunkaramazov)\
**Post date:** [November 14, 2019, 3:19pm UTC](https://forums.kodeco.com/t/displaying-console-output-in-a-text-field/85983/2 "2019-11-14T15:19:38Z")

</div>

@ncrusher Do you still have issues with this?

---

<div class="post-metadata">

**Author:** ![ncrusher](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/ncrusher/32/81494_2.png) [@ncrusher](https://forums.kodeco.com/u/ncrusher)\
**Post date:** [November 14, 2019, 9:48pm UTC](https://forums.kodeco.com/t/displaying-console-output-in-a-text-field/85983/3 "2019-11-14T21:48:29Z")

</div>

Someone from the Swift forums helped me build a better what. Here is what I ended up with:

```
    /// The pipe that will receive standard out and standard error.
    let pipe = Pipe()
    // Hook up the pipe.
    process.standardOutput = pipe
    process.standardError = pipe

    /// Starts the external process.
    // (This does the same as ”launch()“, but it is a more modern API.)
    try process.run()

    /// A variable to store the output as we catch it.
    var output = String()

    /// A buffer to store partial lines of data before we can turn them into strings.
    ///
    /// This is important, because we might get only the first or the second half of a multi‐byte character. Converting it before getting the rest of the character would result in corrupt replacements (�).
    var stream = Data()

    /// The POSIX newline character. (Windows line endings are more complicated, but they still contain it.)
    let newline = "\n"
    /// The newline as a single byte of data instead of a string (since we will be looking for it in data, not strings.
    let newlineData = newline.data(using: String.Encoding.utf8)!

    /// Reads whatever data the pipe has ready for us so far, returning `nil` when the pipe finished.
    func read() -> Data? {
        /// The data in the pipe.
        ///
        /// This will only be empty if the pipe is finished. Otherwise the pipe will stall until it has more. (See the documentation for `availableData`.)
        let newData = pipe.fileHandleForReading.availableData
        // If the new data is empty, the pipe was indicating that it is finished. `nil` is a better indicator of that, so we return `nil`.
        // If there actually is data, we return it.
        return newData.isEmpty ? nil : newData
    }

    // The purpose in the following loop‐based design is two‐fold:
    // 1. Each line of output can be handled in real time as it arrives.
    // 2. The entire thing is encapsulated in a single synchronous function. (It is much easier to send a synchronous task to another queue than it is to stall a queue to wait for the result of an asynchronous task.)

    /// Whether the loop should end. We will set it to `true` when we want it to stop.
    var shouldEnd = false
    // Loop as long as “shouldEnd” is “false”.
    while !shouldEnd {
        // “autoreleasepool” cleans up after some Objective C stuff Foundation will be doing.
        autoreleasepool {
            guard let newData = read() else {
                // “read()” returned `nil`, so we reached the end.
                shouldEnd = true
                // This returns from the autorelease pool.
                return
            }
            // Put the new data into the buffer.
            stream.append(newData)

            // Loop as long as we can find a line break in what’s left.
            while let lineEnd = stream.range(of: newlineData) {
                /// The data up to the newline.
                let lineData = stream.subdata(in: stream.startIndex ..< lineEnd.lowerBound)
                // Clear the part of the buffer we’ve already dealt with.
                stream.removeSubrange(..<lineEnd.upperBound)

                /// The line converted back to a string.
                let line = String(decoding: lineData, as: UTF8.self)

                output += line+newline
            }
        }
    }

    /// Stall until the external process is done, even if it closed its pipes early.
    while process.isRunning {}

    /// Check whether it ended with success or an error.
    if process.terminationStatus == 0 {
        // It succeeded.
        // Return the output.
        return output
    } else {
        // It failed.
        throw ExternalProcessError.processErrored(
            exitCode: Int(process.terminationStatus),
            output: output)
    }
}

```

---

<div class="post-metadata">

**Author:** ![jayantvarma](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/jayantvarma/32/111768_2.png) [@jayantvarma](https://forums.kodeco.com/u/jayantvarma)\
**Post date:** [February 1, 2020, 10:08am UTC](https://forums.kodeco.com/t/displaying-console-output-in-a-text-field/85983/4 "2020-02-01T10:08:43Z")

</div>

Hi @ncrusher,  
Did it work for you?

cheers,

---

<div class="post-metadata">

**Author:** ![shogunkaramazov](https://assets.chunter.kodeco.com/user_avatar/forums.kodeco.com/shogunkaramazov/32/108826_2.png) [@shogunkaramazov](https://forums.kodeco.com/u/shogunkaramazov)\
**Post date:** [April 7, 2020, 10:22am UTC](https://forums.kodeco.com/t/displaying-console-output-in-a-text-field/85983/6 "2020-04-07T10:22:00Z")

</div>


