How can I output to STDERR with Swift?
MacosSwiftMacos Problem Overview
I'm trying to make a command line tool for OS X with Xcode 6 and the new Swift language. How can I send output to stderr? Is this done with println?
Macos Solutions
Solution 1 - Macos
For Swift 4.x:
import Darwin
fputs("hello from libc\n", stderr)
Solution 2 - Macos
Here is a Swift 3 snippet modified from https://gist.github.com/algal/0a9aa5a4115d86d5cc1de7ea6d06bd91.
import Foundation
var standardError = FileHandle.standardError
extension FileHandle: TextOutputStream {
public func write(_ string: String) {
let data = Data(string.utf8)
self.write(data)
}
}
print("I am printed to stderr", to: &standardError)
Solution 3 - Macos
May be a better way to do it, but you can use NSFileHandle
:
import Foundation
// Create a file handle to work with
let stderr = NSFileHandle.fileHandleWithStandardError()
// Build up a string; whatever you want
let stuff = "something"
let something = "I'm a string with \(stuff) in it\n"
// Write it
stderr.writeData(something.dataUsingEncoding(NSUTF8StringEncoding))
Solution 4 - Macos
Not really a separate answer, but building on top of Rob Napier's answer, we can create a stderr like object so that there is not much to change when Apple comes around to providing stderr as an OutputStreamType
:
import Foundation
class StandardErrorOutputStream: OutputStreamType {
func write(string: String) {
let stderr = NSFileHandle.fileHandleWithStandardError()
stderr.writeData(string.dataUsingEncoding(NSUTF8StringEncoding))
}
}
var mx_stderr = StandardErrorOutputStream()
println("on-stdout")
println("on-stderr", &mx_stderr)
EDIT: As of 8/26/2015, Xcode 7 Beta 6, you need the toStream:
parameter name, like so:
println("on-stderr", toStream:&mx_stderr)
Solution 5 - Macos
Swift 4, similar to Ryan's solution but instead of extending the FileHandle, created a new struct which also lets you create a StdErr specific class and avoids the global.
struct StandardErrorOutputStream: TextOutputStream {
let stderr = FileHandle.standardError
func write(_ string: String) {
guard let data = string.data(using: .utf8) else {
fatalError() // encoding failure: handle as you wish
}
stderr.write(data)
}
}
Usage example:
do {
try somethingThatMightFail()
} catch let error {
var errStream = StandardErrorOutputStream()
print("\(error)", to: &errStream)
exit(EXIT_FAILURE)
}
Solution 6 - Macos
Here are three different methods of increasing complexity:
Compliments of Erica Sadun at http://ericasadun.com/2015/06/09/swift-2-0-how-to-print/:
public struct StderrOutputStream: OutputStreamType {
public mutating func write(string: String) {
fputs(string, stderr)
}
}
public var errStream = StderrOutputStream()
debugPrint("Hello", toStream: &errStream) // prints with new line
For a slightly different method using NSFileHandle.fileHandleWithStandardError, see: http://crunchybagel.com/building-command-line-tools-with-swift/ in the section titled: Writing to stdout / stderr, but this method does not use Swift's print library function.
And for a really crazy ride, check out the method offered by rosettacode.org using NSOutputStream at https://www.rosettacode.org/wiki/Hello_world/Standard_error#Swift:
let out = NSOutputStream(toFileAtPath: "/dev/stderr", append: true)
let err = "Goodbye, World!".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
out?.open()
let success = out?.write(UnsafePointer<UInt8>(err!.bytes), maxLength: err!.length)
out?.close()
if let bytes = success {
print("\nWrote \(bytes) bytes")
}
Solution 7 - Macos
Xcode 13.2+ and Swift 5.5+
Model:
class StandardError: TextOutputStream {
func write(_ string: String) {
try! FileHandle.standardError.write(contentsOf: Data(string.utf8))
}
}
Usage:
var standardError = StandardError()
print("Error!", to: &standardError)
Solution 8 - Macos
A condensed and modernized version of @RobNapiers suggested solution:
"A line of text"
.data(using: .utf8)
.map(FileHandle.standardError.write)