Fun with print() in Swift
You use print()
in Swift to print a string of text to the Console, or standard output. It’s super useful for debugging and finding out what’s going on in your code. In this tutorial, we’ll discuss how you can customize print()
to code more productively.
Here’s what we’ll get into:
- How do you use
print()
in Swift? - Working with
#file
,#function
and#line
literals - Customizing
print()
‘s separator and terminator - Printing a description of custom Swift objects
Ready? Let’s go.
- Writing Output with print()
- Print Line Number, Function and File
- Customizing print()’s Output
- How To Print Custom Objects
- Further Reading
Writing Output with print()
You use the print()
function in Swift to write a string to standard output, which is typically the Console in Xcode. Here’s an example:
print("So long and thanks for all the fish!")
The above example can’t get any easier. We’re just printing a string of text to the Debug Area in an Xcode Playground.
Printing with print()
works just about anywhere you code Swift:
- When you’re debugging an app on your iPhone via Xcode, output from
print()
shows up in Xcode’s Console - It works in Xcode playgrounds, as we’ve seen in the above example
- If you run code through the
swift
command line tool, output shows up in Terminal
What else can we do with print()
?
Print Line Number, Function and File
You can use the special literals to print the file, function and line numbers of the print()
statement. Here’s how:
print("\(#file):\(#function):\(#line) -- Hey!")
// Output: Print.playground:doSomething():5 -- Hey!
In the above code, we’re calling the print()
function, which is part of a doSomething()
function. The print()
statement is on line no. 5 of the Print.playground
file. Neat!
You can use the following literals with print()
:
-
#file
for the filename -
#function
for the function name -
#line
for the line of theprint()
call
You can use these standalone, with something like print(#file)
, or as part of a string with \(···)
string interpolation, like the above example. Making an informative string, like print("\(#function):\(#line)")
, is super helpful in quickly printing where a certain print()
has happened.
Which makes you wonder: When do you use all this stuff? Well, it’s no secret that many software developers use print()
for poor man’s debugging. That is, sprinkling your code with a few print()
statements to see what’s going on in there.
Like this:
let result = snowHeight > 5 && slopeIncline < 10 && reindeer == .active
if result {
print("heyho ok this happend")
}
With the above hypothetical code, you can see with print()
when the result
expression is true
. In other words, you can get a feel for the scenarios in which your app runs and gain a better understanding of the context of your code.
It would be better, of course, to use breakpoints and inspect values at runtime with po
. But using print()
statements has a certain quality and convenience to it that’s hard to describe. It’s imperfect, sure, but so are we software developers.
OK, back to #line
and #function
. You can improve your print()
statements with those literals, especially if you’ve got a few of them. Check this out:
if snowHeight > 5
{
print("\(#function):\(#line) -- snow height ok")
if slopeIncline < 10
{
print("here ok ok")
if reindeer == .active {
print("\(#function):\(#line) -- reeindeer are good!")
}
}
}
Ugly? Hell yes! Useful? You bet.
You can also use #column
for the column number (i.e., character position from the left). It seems to indicate the starting position of the #column
literal itself, which makes it less useful than #line
for example.
Customizing print()’s Output
Even though we typically use the print statement as print(···)
, the function’s actual definition is this:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
Whoah! Here’s what that means:
- The
print()
function’s first unnamed parameteritems
has type Any, so you can pass any kind of value or reference to it. - See those 3 periods after
Any
? This means you can pass zero, one or more arguments into theprint()
function for that parameter. - The
print()
function has aseparator
parameter, a space symbol by default, which lets you separate multiple values with a custom character. - The same goes for the terminator, i.e. what comes at the end of the printed string. It’s a newline by default, but you can pick a custom character as well.
It’s important to keep in mind that print()
will ultimately print a string. You can pass in any value, thanks to the Any
type, but the print()
function will (attempt to) convert that to a string.
When you pass a value into print()
, that value is converted to a String
value with the String(···)
initializer. For most types this will print the value as a string. The String()
initializer also looks for a description
property, which is useful if you want to print custom objects.
Printing Multiple Values
OK, let’s check out a few examples. First, the basics:
let player1 = "Arthur"
let player2 = "Trillian"
print(player1, player2)
// Arthur Trillian
See how we’re providing the print()
function with 2 parameters (or arguments, really)? This prints out the value of player1
, a space symbol, the value of player2
, and a newline \n
symbol.
What’s the newline symbol for? This is added so that next time we use print()
, it’s printed text is neatly added to a new line in the Console. You don’t see the newline character, but it’s there, much like the carriage return of a typewriter.
Custom Separator and Terminator
What about the separator
parameter of print()
? Check this out:
let data = [
["Jane", 99, "a", 2.0],
["John", 32, "z", 2.99],
["Jack", 13, "x", 1.34]
]
for item in data {
print(item[0], item[1], item[2], item[3], separator: ";", terminator: ";\n")
}
The code produces the following output:
Jane;99;a;2.0;
John;32;z;2.99;
Jack;13;x;1.34;
Looks a bit like tabular comma-separated (CSV) data, right? In the code, we’ve printed out data from the data
array. We’ve used multiple values for print()
, and used custom strings for the separator
and terminator
parameters. Each item printed is separated with a semicolon, and the line ends with a semicolon and a newline. Neat!
Looking for a good example of print()
to explain code, as it’s running? Check out the final implementation of binary search, in this tutorial: Play With Code: Binary Search In Swift
How To Print Custom Objects
You can also print your own custom structs, classes, etc. with print()
. Take a look at the following struct:
struct Book {
var title:String
var author:String
var pages:Int
}
let pigs = Book(title: "Animal Farm", author: "George Orwell", pages: 112)
When you do a print(pigs)
, you get the following output:
Book(title: "Animal Farm", author: "George Orwell", pages: 112)
This is a literal representation of the Book
object. It contains too much information for it to be useful; we’d rather print something like the title and the author, for example. How?
First, make sure your custom type adopts the CustomStringConvertible
protocol. Like this:
struct Book: CustomStringConvertible { ···
Then, add a computed property description
to the struct, like this:
var description:String {
return "\(title), \(author)"
}
The type of description
is String
, and the property returns a string. You can even omit the return
keyword, if you want. In the above code, we’re returning a string that includes the title and author of the Book
object.
Finally, we can print the object:
print(pigs)
// Output: Animal Farm, George Orwell
This way you can customize how a Swift object is printed out with print()
. Awesome!
Further Reading
Working with print()
was already useful, but thanks to things like #function
and CustomStringConvertible
now you can see what’s going on even better.
In this tutorial, we’ve discussed how you use print()
, how to print multiple values, custom separators and terminators, how to print out custom objects, and more. Neat!
Want to learn more? Check out these resources: