- July 29, 2025
- Mins Read
func foo(a: Int = 1, b: String = "hi") { … }
func connect(to host: String, port: Int = 80, useSSL: Bool = false) { … }
connect(to: "example.com")
// is treated as:
connect(to: "example.com", port: 80, useSSL: false)
Defaults aren’t limited to literals; any expression that’s valid in that scope is allowed:
let globalTimeout = 30
func fetchData(endpoint: String,
timeout: TimeInterval = TimeInterval(globalTimeout),
cache: Bool = true)
{
// …
}
or even calling other functions:
func now() -> Date { Date() }
func log(message: String, at timestamp: Date = now()) {
print("[\(timestamp)]", message)
}
Swift distinguishes between the external label (used by callers) and the internal name (used in the function body):
func move(from start: Point, to end: Point = .zero) { … }
// Callers write:
move(from: p1) // second uses default .zero
move(from: p1, to: p2)
You can even omit the external label (_
) while still providing a default:
func increment(_ value: Int = 1) -> Int {
return base + value
}
increment() // adds 1
increment(5) // adds 5
You can combine variadics and defaults—though variadics always subsume any values provided:
func sum(_ values: Int..., initial: Int = 0) -> Int {
return values.reduce(initial, +)
}
sum() // returns 0
sum(1, 2, 3) // returns 6
sum(4, 5, 6, initial: 10) // returns 25
Swift’s rich error‐handling model gives you fine-grained control over failure paths, cleanup, and propagation.
enum
s conforming to Error
are the standard way:
enum DataError: Error {
case notFound(path: String)
case invalidFormat(reason: String)
case permissionDenied
}
throws
and use throw
anywhere inside:
func loadJSON(from path: String) throws -> [String: Any] {
guard FileManager.default.fileExists(atPath: path) else {
throw DataError.notFound(path: path)
}
// … attempt to read & parse …
guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else {
throw DataError.invalidFormat(reason: "Unreadable data")
}
// parse JSON…
return jsonObject
}
rethrows
:func perform<T>(_ work: () throws -> T) rethrows -> T {
// If `work` throws, this function will too.
return try work()
}
try
. Omitting try
is a compile-error.try?
or try!
to convert or force-unwrap if needed, but most of the time you’ll use plain try
to let errors bubble up.do-catch
do {
let json = try loadJSON(from: "config.json")
print("Loaded config:", json)
} catch DataError.notFound(let path) {
print("File not found at \(path)")
} catch DataError.invalidFormat(let reason) {
print("Bad format:", reason)
} catch {
print("Unexpected error:", error)
}
catch
lets you extract associated values.catch
without a pattern catches any error.defer
If you need guaranteed cleanup—regardless of success or failure—use defer
:
func processFile(_ path: String) throws {
let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path))
defer {
handle.closeFile()
print("File closed")
}
// If any throw happens below, the defer block still runs
let data = try handle.readToEnd() ?? Data()
// …
}
defer
blocks; they execute in reverse order of appearance.try?
and try!
try?
nil
on error:
let maybeJSON = try? loadJSON(from: "config.json")
// maybeJSON is [String: Any]?; error is swallowed
try!
let guaranteedJSON = try! loadJSON(from: "default.json")
// Only use if you're 100% sure it can't fail
When calling Swift throwing functions from Obj-C (or Cocoa APIs expecting NSError**
):
@objc func objcLoadJSON(from path: String, error: NSErrorPointer) -> [String: Any]? {
do {
return try loadJSON(from: path)
} catch let err as NSError {
error?.pointee = err
return nil
}
}
You can catch an error, wrap or annotate it, then rethrow:
func enhancedLoad(path: String) throws -> [String: Any] {
do {
return try loadJSON(from: path)
} catch {
throw DataError.invalidFormat(reason: "Failed at path \(path): \(error)")
}
}
This lets you add context while preserving the error-handling flow.
1. Taking Action When a Property Changes: Property Observers Swift lets you observe and respond to changes in a property’s ...
1. Creating Your Own Structs In Swift, a struct is a value type that you define with the struct keyword. ...
1. Trailing Closure Syntax When the last parameter to a function is a closure, you can write that closure after ...
1. What Is a Closure (and Why Swift Loves Them) A closure in Swift is a self-contained block of functionality ...