A Swift Tour
Tradition says the first program in a new language should print "Hello, world!" — and in Swift that's one line. This chapter is a whirlwind tour of the whole language: enough of every major feature to read real code and feel oriented. Each topic gets a full chapter later; here we just want the shape of the thing.
Tip: The best way to follow along is in an Xcode
Playground or a .swift file. Type the examples yourself — Swift
is far easier to learn by running code than by reading about it.
Hello, world and printing
No imports, no main function — top-level code just runs.
print("Hello, world!") // → Hello, world!
print writes a line to the console and is your go-to tool for
quick experiments.
Variables, constants, and type inference
Use let for a value that never changes and var for
one that does. Prefer let — Swift will warn you if a
var is never mutated.
let maxAttempts = 3 // constant; inferred as Int
var currentAttempt = 0 // variable; inferred as Int
currentAttempt += 1
let pi: Double = 3.14159 // explicit type annotation
Constants, variables, and an explicit annotation.
Notice Swift inferred the types of maxAttempts and
currentAttempt from their initial values. You only write a
type when you want to be explicit or when there's no initial value to infer
from.
Strings and interpolation
Strings are full Unicode and support interpolation — embedding values with
\(...) — as well as multiline literals.
let name = "Ada"
let count = 3
let greeting = "Hi \(name), you have \(count) messages."
print(greeting) // → Hi Ada, you have 3 messages.
let poem = """
Roses are red,
Swift is too.
"""
Interpolation and a multiline string literal.
Arrays and dictionaries
Swift's main collection types are arrays (ordered) and dictionaries (key-value). Both use square brackets.
var fruits = ["apple", "pear", "plum"]
fruits.append("fig")
print(fruits.count) // → 4
var ages = ["Ada": 36, "Alan": 41]
ages["Grace"] = 85
print(ages["Ada"]) // → Optional(36)
let empty: [Int] = [] // an empty array needs a type
let noPairs: [String: Int] = [:] // an empty dictionary
Building and reading collections.
Looking up a key returns an optional, because the key might not be present — which is the perfect bridge to control flow and optionals.
Control flow
Swift has if, for-in,
while, and a powerful switch. Conditions don't
need parentheses, but bodies always need braces.
let scores = [85, 42, 90, 70]
var passes = 0
for score in scores {
if score >= 60 {
passes += 1
}
}
print("\(passes) passed") // → 3 passed
switch in Swift is exhaustive, has no implicit fall-through,
and can match ranges, tuples, and bind values:
let temperature = 18
switch temperature {
case ..<0:
print("Freezing")
case 0..<15:
print("Cold")
case 15..<25:
print("Comfortable") // → Comfortable
default:
print("Warm")
}
An exhaustive switch matching ranges.
Optionals
An optional either holds a value or is nil. You can't use the
value until you safely unwrap it — that's how Swift prevents null crashes.
let input = "42"
let number = Int(input) // number is Int? — could fail to parse
if let number {
print("Parsed \(number)") // → Parsed 42
} else {
print("Not a number")
}
let safe = number ?? 0 // nil-coalescing: default if nil
Optional binding with if let and the ?? operator.
You can also use guard let to unwrap early and exit if the value
is missing, which keeps the happy path un-nested.
Functions and closures
Functions use func, with labeled parameters and an arrow for
the return type. Argument labels make call sites read like sentences.
func greet(_ name: String, from town: String) -> String {
return "Hello \(name) from \(town)!"
}
print(greet("Ada", from: "London"))
// → Hello Ada from London!
A closure is a function without a name that you can pass
around. Many APIs take closures, and trailing-closure syntax keeps them
readable. $0 is shorthand for the first argument.
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled) // → [2, 4, 6, 8, 10]
let evens = numbers.filter { $0 % 2 == 0 }
print(evens) // → [2, 4]
Closures passed to map and filter.
Structs, classes, and enums
Swift gives you several ways to model data. Structs are value types (copied on assignment) and are the default choice. Classes are reference types and support inheritance. Enums model a fixed set of cases and can carry associated values.
struct Point {
var x: Int
var y: Int
func moved(by dx: Int) -> Point {
Point(x: x + dx, y: y)
}
}
let p = Point(x: 1, y: 2) // free memberwise initializer
print(p.moved(by: 4)) // → Point(x: 5, y: 2)
A class adds reference semantics and inheritance:
class Animal {
let name: String
init(name: String) { self.name = name }
func sound() -> String { "..." }
}
class Dog: Animal {
override func sound() -> String { "Woof" }
}
print(Dog(name: "Rex").sound()) // → Woof
Enums shine for modeling distinct states, with optional associated values:
enum Result {
case success(String)
case failure(code: Int)
}
let outcome = Result.failure(code: 404)
switch outcome {
case .success(let message):
print(message)
case .failure(let code):
print("Failed with \(code)") // → Failed with 404
}
An enum with associated values, matched in a switch.
Protocols and extensions
A protocol is a contract: a set of requirements a type can adopt. Extensions add functionality to existing types, including conforming them to protocols.
protocol Describable {
var summary: String { get }
}
struct Book {
let title: String
}
extension Book: Describable {
var summary: String { "Book: \(title)" }
}
print(Book(title: "Swift").summary) // → Book: Swift
Conforming to a protocol through an extension.
This protocol-oriented style — small contracts plus extensions for default behavior — is central to idiomatic Swift.
Error handling
Functions that can fail are marked throws, and callers use
do/catch with try. Swift 6 also
supports typed throws, where you declare the exact error
type.
enum ParseError: Error { case empty }
func firstWord(of text: String) throws(ParseError) -> String {
guard let word = text.split(separator: " ").first
else { throw .empty }
return String(word)
}
do {
let word = try firstWord(of: "hello there")
print(word) // → hello
} catch {
print("No words")
}
A typed-throwing function handled with do/catch.
Generics
Generics let you write flexible code that works with any type while staying type-safe. The angle brackets introduce a placeholder type.
func firstAndLast<T>(_ items: [T]) -> (T, T)? {
guard let first = items.first,
let last = items.last else { return nil }
return (first, last)
}
print(firstAndLast([10, 20, 30])!) // → (10, 30)
print(firstAndLast(["a", "z"])!) // → ("a", "z")
One generic function, many element types.
The standard library is built on generics — Array,
Dictionary, and Optional are all generic types you
already used in this tour.
The big picture
You just touched every major pillar of Swift: values and types, collections, control flow, optionals, functions and closures, the three modeling tools (struct, class, enum), protocols and extensions, error handling, and generics. None of it is memorized yet — and it doesn't need to be. Each of these gets a full, careful chapter ahead. Next we slow down and start at the foundation: constants, variables, and basic types in The Basics.