Your first line of Swift is probably,

print("Hello, world!")
Hello, world!

And voilà, we learnt how to send text to standard output. Add a dash of string interpolation and we’re caveman debugging like it’s 1973.

let step1 = "Lights"
let step2 = "Camera"
let step3 = "Action"
print("\(step1), \(step2), \(step3)!")
Lights, Camera, Action!

Great! Certainly easier than NSLog()’s printf-style formatting.

But string interpolation can be used anywhere, not just print(). You can pass a Swift interpolated string to NSLog() and it’ll print without a hitch.

So what’s so special about print()?

Swift’s print() can convert any items to text, and stream it to any output.

Now hold that thought for a second… . That’s as general as a function called print() can get. In essence, print() can print anything, almost anywhere.

func print(_ items: Any...,
         separator: String = " ",
        terminator: String = "\n")

You can pass in zero (!) or more Any items. So the last example can be rewritten as,

print(step1, step2, step3, separator: ", ", terminator: "!")
Lights, Camera, Action!

Yes, Any-thing will be converted to text.

enum Eight { case eight }
struct Nine { }
class Ten { }

print(true,         // Bool
      1,            // Int
      2.0,          // Double
      "three",      // String
      [4, 5],       // Array
      [6: "7"],     // Dictionary
      Eight.eight,  // Enum
      Nine(),       // Struct
      Ten(),        // Class
      { 11 })       // Function
true 1 2.0 three [4, 5] [6: "7"] eight Nine() Ten (Function)

All Swift’s built-in types convert to pretty sane, but general defaults. What if you don’t like what print() does?


To customise the text printed when passed to print(), make your type conform to CustomStringConvertible, which only requires adding a description getter.

protocol CustomStringConvertible {
  var description: String { get }

struct Spy: CustomStringConvertible {
  var firstName: String
  var lastName: String

  var description: String {
    return "My name is \(lastName), \(firstName) \(lastName)."

let james = Spy(firstName: "James", lastName: "Bond")
My name is Bond, James Bond.

What’s happening behind the scenes, is that print() converts your item into a String using init(describing:).

Interestingly, the initialiser doesn’t just check for CustomStringConvertible conformance. It tries a cascading stack of protocols, in order to get the “best” String representation possible.

The protocols are TextOutputStreamable, CustomStringConvertible, then CustomDebugStringConvertible — attempted in that order. If none are implemented, the initialiser fallbacks to an “unspecified result”, which is whatever your Swift version deems fit.

Hold up … what is TextOutputStreamable, and why is it ranked first? To answer that, we must know its sibling TextOutputStream first.


There are actually two print() functions in Swift’s standard library. The second one has an additional to: parameter that takes a TextOutputStream.

func print<Target : TextOutputStream>(_ items: Any...,
                                    separator: String = " ",
                                   terminator: String = "\n",
                                    to output: inout Target)

print() can output to anything that implements this protocol.

protocol TextOutputStream {
  mutating func write(_ string: String)

Conveniently, a String is a TextOutputStream.

var cue = "And..."
print(step1, step2, step3, separator: ", ", terminator: "!", to: &cue)
And...Lights, Camera, Action!

As you can tell, the original string was appended with print()’s output. If you ever wondered how to display print() output in a UITextView, this is how you can extract it into a string first.

To output to standard error, we can write a TextOutputStream wrapper around putc().

struct StandardError: TextOutputStream {
  func write(_ text: String) {
    for character in text.utf8 {
      putc(CInt(character), stderr)

var standardError = StandardError()
let error = "I'm sorry Dave, I'm afraid I cannot do that."
print(error, to: &standardError)
I'm sorry Dave, I'm afraid I cannot do that.

Hang on, is that putc() from Version 1 Unix?

Under the hood, print(_:separator:terminator:) uses an internal TextOutputStream struct called _Stdout. If you dig into Swift’s source, this struct essentially writes to standard output in the same way I’ve shown above — using putc() too! Yes, Swift’s standard I/O are simply wrappers to C’s stdio library.

Before we create FILE pointers with abandon, it’s best practice to stick to the highest abstractions possible. Rather than C’s stderr file descriptor, we should use FileHandle APIs and its sensibly named standardError factory method.

Here’s the last example reworked to use FileHandle.

struct StandardError: TextOutputStream {
  func write(_ text: String) {
    guard let data = String.Encoding.utf8) else {

var standardError = StandardError()
print(Date(), "Stasis interrupted", to: &standardError)
2016-09-13 00:23:11 +0000 Stasis interrupted


Now that we’ve explained TextOutputStream, let’s jump back to the mysterious TextOutputStreamable.

If TextOutputStream represents an output, TextOutputStreamable represents a streamable thing.

protocol TextOutputStream {
  mutating func write(_ string: String)

protocol TextOutputStreamable {
  func write<Target : TextOutputStream>(to target: inout Target)

In contrast with a String getter, TextOutputStreamable is implemented with a write(to:) function, which takes a TextOutputStream. That means the conforming type is expected to know how to stream itself directly to text output.

The difference is subtle. Semantically, we’re no longer just providing a textual description of your instance (possibly with Swift-related metadata), but its actual representation as a text stream. Just picture this in your head — the difference between a JSON item that may describe itself as JSON(["foo": "bar"]) versus its actual textual representation, a literal {"foo": "bar"} JSON string.

That’s why print() gives the highest precedence to TextOutputStreamable, because it gives the most appropriate representation possible for text output.

Is there a point in implementing both TextOutputStreamable and CustomStringConvertible?

Yes, if they differ, because of debugPrint().


Stumbling upon debugPrint() usually triggers instant FOMO, only to discover that it practically prints the same stuff.

One common misconception is, debugPrint() prints in debug builds only.

debugPrint("Release builds won't see me.")
// NOT shorthand for...
  print("Release builds won't see me.")

Nope, not even close. Documentation says debugPrint()

writes the textual representations of the given items most suitable for debugging.

But what exactly is “most suitable for debugging”? If you try debugPrint() on all the built-in types, only these few print differently from vanilla print().

enum Team { case red, blue }

let aSet: Set = []
let string = "\tvs.\t"
let enumeration: Team = .blue

print(aSet, string, enumeration)
debugPrint(aSet, string, enumeration)
[] 	vs.	 blue
Set([]) "\tvs.\t"

The implementation explains why.

debugPrint() uses a different String initialiser, init(reflecting:). Like init(describing:), it also checks a cascading stack of protocols. The same ones in fact, but in reverse orderCustomDebugStringConvertible, CustomStringConvertible, then TextOutputStreamable. Failing which, it also fallbacks to “unspecified results”.

Logically if output from debugPrint() and print() differ, it means that at least two protocols were implemented.

protocol CustomDebugStringConvertible {
    var debugDescription: String { get }

CustomDebugStringConvertible takes the highest precedence this time, and is implemented with just a debugDescription getter. This should be your go-to protocol for customising debugPrint() output.

In fact, most built-in types only implement CustomDebugStringConvertible, or stub the same private method in both description and debugDescription getters.

That’s why both print functions frequently output the same thing.

If they’re so similar, why should I use debugPrint() over print()?

Well if you study what Apple does, for enumerations for example, debugPrint() shows the fully qualified name. This information really only matters to developers, and is likely to be useless to your users — if not dangerous.

You should use debugPrint() every time you care about introspecting a variable by logging it to standard output. If that sounds strangely familiar, that’s the very definition of caveman debugging.


Let’s talk about standard input, or what little we can.

Unlike output’s family of functions and protocols, there’s only one function to learn.

func readLine(strippingNewline: Bool = true) -> String?

readLine() reads from standard input until the current end of line or EOF is encountered. Any invalid bytes are replaced by Unicode replacement characters “�”. The only parameter it has is strippingNewline:, which does exactly what it says. If EOF is read immediately after calling it, nil is returned.

There is one caveat. In Playgrounds, readLine() always returns nil immediately. So, only use this function in an Xcode command line project or Swift script.

Here’s a script that Yoda-fies your input.

extension String {
  func yodafied() -> String {
    var words = self.components(separatedBy: " ")
    if words.count > 2 {
      words = words.dropFirst(2) + Array(words.prefix(upTo: 2))
    words = [words.first!.capitalized] + words.dropFirst()
    return words.joined(separator: " ").appending(". Hmmm...")

while true {
  print("> ", terminator: "")
  guard let input = readLine() else {
> I hope you learnt something today.
You learnt something today. I hope. Hmmm...

Press Ctrl + D (EOF character) to exit.

That’s all, now you can port Zork to Swift!