Syntax
Swift’s syntax is clean and concise — type inference admits omitting most type annotations, semicolons are not required (they may appear only when separating multiple statements on one line), parentheses around if and while conditions are not required (and conventionally omitted), and the convention favours expression-orientation where it admits substantial conciseness. The compiler enforces type safety — the type system catches substantial classes of errors at compile time. Swift admits trailing closure syntax (a hallmark of the language: closures may follow the call’s argument list rather than appearing inside the parentheses), and the let vs var distinction (immutable vs mutable bindings). The combination — concise syntax, type inference, mandatory braces, the conventional discipline favouring let — is the substance of Swift’s syntactic identity.
This page covers the surface a working programmer encounters routinely.
A complete program
The classical hello world:
print("Hello, world!")
A more substantial example:
import Foundation
struct Person {
let name: String
let age: Int
func greeting() -> String {
"Hello, \(name)."
}
}
let url = URL(fileURLWithPath: "people.json")
let data = try Data(contentsOf: url)
let people = try JSONDecoder().decode([Person].self, from: data)
for person in people.sorted(by: { $0.age < $1.age }) {
print(person.greeting())
}
The principal features visible:
import Foundation— module import.struct Person { ... }— value-type declaration.let name: String— immutable property.func greeting() -> String— function with return type."Hello, \(name)."— string interpolation.try Data(contentsOf:)— labelled argument.[Person].self— type-as-value (metatype).{ $0.age < $1.age }— closure with shorthand parameter names.for person in ...— iteration.
Compilation and execution:
swiftc hello.swift -o hello && ./hello
# Or interpreted:
swift hello.swift
For project-style work, the Swift Package Manager:
swift package init --type executable
swift run
Source character set
Swift source is interpreted as UTF-8. Identifiers may use Unicode letters; ASCII identifiers are conventional in code, though Unicode is admitted for variable names (and even emoji — let 🐶 = "dog").
Identifiers and naming conventions
The conventional Swift naming follows the Swift API Design Guidelines:
| Form | Use | Example |
|---|---|---|
lowerCamelCase | variables, parameters, methods, properties | userName, toString() |
UpperCamelCase | types, protocols, generic parameters | Person, Comparable, T |
lowerCamelCase | enum cases (since Swift 3) | case running, case .pending |
UPPER_SNAKE_CASE | rare; use lowerCamelCase for let constants too |
The convention is enforced by community consensus (not by the language) — the Swift Standard Library and Apple’s frameworks follow it strictly. The conventional contemporary Swift is the Swift API Design Guidelines style.
Reserved keywords
The reserved keywords:
associatedtype class deinit enum extension
fileprivate func import init inout
internal let open operator private
protocol public rethrows static struct
subscript typealias var
break case continue default defer
do else fallthrough for guard
if in repeat return switch
where while
as Any catch false is
nil super self Self throw
throws true try
_ // wildcard
There are also contextual keywords that are reserved only in specific positions: actor, async, await, convenience, dynamic, final, lazy, mutating, nonisolated, optional, override, prefix, postfix, required, some, weak, unowned, etc.
For using a reserved word as an identifier, the backtick form:
let `class` = "Math 101" // class is reserved
The form is admitted but conventionally avoided.
Comments
Two comment forms:
// A single-line comment, terminated by the end of the line.
/* A block comment.
Block comments admit nesting:
/* nested comment */
*/
The block-comment form’s nesting (since the original language) is rare among C-family languages — admits substantial commenting-out without breaking on inner block comments.
For documentation comments, the /// and /** */ forms admit Markdown:
/// Greets the named person.
///
/// - Parameter name: The name to greet.
/// - Returns: A greeting string.
func greet(name: String) -> String {
"Hello, \(name)."
}
/**
A more substantial documentation comment.
Admits Markdown formatting and multiple paragraphs.
*/
The conventional Swift documentation tooling (Xcode’s Quick Help, DocC) consumes these comments to produce structured documentation.
Statement terminators
Newlines terminate statements; semicolons admit multiple statements on one line:
let x = 5
let y = 10
let z = x + y
// Equivalent:
let x = 5; let y = 10; let z = x + y
The conventional discipline:
- One statement per line — newlines terminate.
- Semicolons rarely used — only for compactness on one line.
Variable declarations
Two principal forms:
let x = 5 // immutable (preferred)
var y = 10 // mutable
let name: String = "Alice" // explicit type
let name = "Alice" // type inferred as String
var age: Int = 30
var age = 30 // type inferred as Int
The conventional discipline:
- Use
letby default — the compiler warns onvarthat is never reassigned. - Use
varwhen reassignment is required. - Type inference — let the compiler determine the type when the initialiser is unambiguous.
Type annotations
Type annotations follow the colon convention:
let name: String = "Alice"
let age: Int = 30
let pi: Double = 3.14159
let isActive: Bool = true
var values: [Int] = [1, 2, 3] // array of Int
var lookup: [String: Int] = [:] // dictionary
func add(a: Int, b: Int) -> Int {
a + b
}
The Swift type system is strict — implicit conversions are not admitted (e.g., Int is not implicitly convertible to Double):
let n: Int = 5
let f: Double = n // ERROR
let f: Double = Double(n) // OK
The strictness eliminates a substantial class of conversion bugs.
Functions
The func keyword introduces a function:
func add(_ a: Int, _ b: Int) -> Int {
a + b
}
func greet(name: String) -> String {
"Hello, \(name)"
}
func performAction() { // no return value (Void)
print("acting")
}
Swift functions admit parameter labels — distinct from the parameter names. By default, the parameter name is its label:
func send(message: String, to recipient: String) {
print("sending \(message) to \(recipient)")
}
send(message: "hi", to: "Alice") // labels visible at call site
// The _ wildcard suppresses the label:
func add(_ a: Int, _ b: Int) -> Int { a + b }
add(3, 4) // no labels needed
Treated in Functions and closures.
Block syntax
All control-flow constructs require braces — no single-statement form:
if condition {
doSomething()
}
if condition { doSomething() } // OK; one-liner
if condition
doSomething() // ERROR
The braces are required; the discipline eliminates the dangling-else ambiguity and produces consistent formatting.
The parentheses around if and while conditions are not required (and conventionally omitted):
if x > 0 { // no parens
print("positive")
}
while !done { // no parens
advance()
}
String interpolation
Double-quoted strings admit \(expression) for interpolation:
let name = "Alice"
let greeting = "Hello, \(name)!"
let total = 42
let formatted = "Total: \(total) items at $\(price * Double(total))"
The mechanism admits substantial conciseness for formatted strings; treated in Strings.
Trailing closure syntax
A trailing closure — a closure passed as the last argument may appear outside the parentheses:
[1, 2, 3].map({ n in n * 2 }) // closure inside parens
[1, 2, 3].map { n in n * 2 } // trailing closure
[1, 2, 3].map { $0 * 2 } // shorthand parameters
// With multiple trailing closures (Swift 5.3+):
UIView.animate(
withDuration: 0.5,
animations: { /* ... */ }
) {
// completion handler
}
The mechanism admits substantial DSL-style syntax (SwiftUI, Combine, etc.).
Optionals
Swift’s distinctive nullability mechanism: optionals — types that may contain nil:
var name: String? // optional; may be nil
name = "Alice"
name = nil // OK
let name: String = "Alice" // non-optional; cannot be nil
let name: String = nil // ERROR
// Unwrapping:
if let unwrapped = name { // optional binding
print(unwrapped) // unwrapped is String, not String?
}
let length = name?.count // optional chaining (returns Int?)
let length = name!.count // force unwrap (crashes if nil)
let length = name ?? "default" // nil-coalescing
Treated in Optionals.
Type-as-value
Swift admits referring to a type as a value via .self:
let stringType = String.self
let arrayType = [Int].self
let optionalType = Int?.self
print(type(of: 42)) // Int
print(type(of: "hello")) // String
The mechanism is conventional in generic functions and JSONDecoder.decode(_:from:) patterns.
Modules and imports
Each Swift project produces a module — a unit of code distribution:
import Foundation // import a system module
import MyLibrary // import a third-party module
import struct MyLibrary.Person // import a specific type
Within a module, all files share a common namespace; access between modules is controlled by access control modifiers (public, internal, etc.).
Treated in Scope and modules.
A note on what Swift admits
Several distinguishing features:
- Optionals — explicit nullability via
Optional<T>(treated separately). - Value types as default — structs are conventional; classes for reference semantics.
- Protocols and POP — protocols with default implementations.
- Generics with constraints — substantial type-system surface.
- Pattern matching — switch with exhaustive checking.
- Type inference — admits omitting most annotations.
- Structured concurrency — async/await, actors, structured task groups.
- ARC — Automatic Reference Counting for memory management.
- Property wrappers — declarative attribute extension (
@State,@Published). - Result builders — DSL syntax (SwiftUI’s view-tree DSL).
- Codable — automatic serialisation/deserialisation.
A note on what is not in Swift
- No null pointer dereferences — Optionals encode absence at the type level.
- No undefined behaviour in safe Swift — overflow checking, bounds checking.
- No implicit conversions —
InttoDoublerequires explicit conversion. - No raw pointers in safe Swift — UnsafePointer admitted but explicitly named.
- No multiple inheritance — class single inheritance plus protocol composition.
- No exceptions in the C++ sense —
throwsis a typed mechanism. - No GC — ARC is deterministic.
- No header files — single source file per type.
- No preprocessor — but
#if,#availableare admitted.
The combination — Swift’s particular blend of safety, performance, expressiveness, and Apple ecosystem integration — is the substance of the language’s identity. The discipline produces concise, readable, type-safe code with substantial compile-time guarantees.