I/O Operations in Swift
Learn console input and output, file reading and writing, and I/O error handling in Swift with Docker-ready examples
Input and output are where a program meets the outside world, and Swift approaches them with the same philosophy it applies everywhere else: safety first. Reading from the console returns an optional, because input might not exist. File operations throw errors, because disks fail and paths go missing. Instead of sentinel values or silent crashes, Swift’s type system forces you to acknowledge these possibilities at compile time.
Swift’s I/O story spans two layers. The language itself provides print() and readLine() for console work — no imports required. Everything beyond that lives in Foundation, the core library that supplies FileManager, FileHandle, and the String and Data conveniences for reading and writing files. Foundation ships with Swift on every platform, including Linux, so the same code runs on macOS, in Docker, and on a server.
In this tutorial you’ll format console output with separators, terminators, and String(format:); read and parse input from standard input; and write, read, and append to files with proper do/try/catch error handling. Along the way you’ll see how optionals and thrown errors — two of Swift’s signature features — shape its I/O APIs.
Console Output Beyond the Basics
print() accepts multiple values plus separator and terminator parameters, and String(format:) brings printf-style formatting when you need precise control.
Create a file named io_console.swift:
| |
The separator and terminator parameters have default values (" " and "\n"), so you only specify them when you want different behavior — a small example of Swift’s default-parameter design keeping the common case clean.
Reading from Standard Input
readLine() reads one line from standard input and returns String? — an optional, because the stream may be exhausted. Unwrapping that optional with if let is the idiomatic way to handle input safely.
Create a file named io_input.swift:
| |
Notice the functional style in the parsing step: split(separator:) breaks the line into pieces, and compactMap converts each piece to Int while discarding any that fail — Int("abc") returns nil, and compactMap drops the nils. No manual loop, no exception handling, just a pipeline of transformations.
File I/O with Error Handling
File operations in Swift throw errors rather than returning status codes. You mark each risky call with try and wrap the sequence in do/catch — the compiler refuses to let you ignore a throwing call, so forgotten error checks simply can’t happen.
Create a file named io_files.swift:
| |
The atomically: true argument tells Swift to write to a temporary file first and rename it into place, so a crash mid-write can never leave a half-written file behind. FileHandle is the lower-level tool — it works with raw Data and byte positions, which is why appending uses it instead of the string conveniences.
Running with Docker
| |
Because the current directory is mounted at /app, the journal.txt created by the file example appears in your working directory on the host — inspect it after the run.
Expected Output
Running io_console.swift:
Standard output with a newline
No newline here -- continued on the same line
Swift | is | expressive
1, 2, 3
Swift version 6.0
Pi to two decimals: 3.14
Padded integer: 42
Running io_input.swift with the piped input shown above:
What is your name?
Hello, Ada!
Enter two numbers separated by a space:
Sum: 42
Running io_files.swift:
Wrote journal.txt
File contents:
Day 1: Started learning Swift I/O.
Day 2: Wrote my first file from Swift.
Appended a third entry
journal.txt exists
Final contents:
Day 1: Started learning Swift I/O.
Day 2: Wrote my first file from Swift.
Day 3: Appended a line with FileHandle.
Key Concepts
readLine()returns an optional —String?forces you to handle end-of-input explicitly, applying Swift’s nil safety to the unpredictable world of user input- File operations throw —
trymarks every call that can fail, and the compiler requires ado/catch(or explicit propagation), so I/O errors can’t be silently ignored print()is configurable —separatorandterminatorparameters with sensible defaults handle multi-value and same-line output without a formatting mini-language- Foundation is the I/O toolkit —
String(format:),FileManager,FileHandle, and the file-readingStringinitializers all requireimport Foundation, on Linux as well as macOS compactMapshines at input parsing — converting strings to numbers while discarding failures is a one-line functional pipeline instead of a validation loop- Atomic writes prevent corruption —
atomically: truewrites to a temporary file and renames it, so readers never observe a partially written file - Two levels of file API —
String/Dataconveniences read or write whole files in one call;FileHandleoffers positional access (seek, append, partial reads) when you need it - Standard error is available too —
FileHandle.standardError.write(_:)sends diagnostics to stderr, keeping them separate from program output on stdout
Comments
Loading comments...
Leave a Comment