Skip to content

Introduction to Swift

Swift is a general-purpose, compiled programming language developed by Apple for building applications across all Apple platforms — iOS, iPadOS, macOS, watchOS, tvOS, and visionOS. It was designed to be safe, fast, and expressive, replacing Objective-C as the primary language for Apple ecosystem development.

Swift is open source (Apache 2.0 licence) and has an active community contributing to its development on platforms beyond Apple, including Linux and Windows.

  • Announced at WWDC 2014 by Chris Lattner
  • Introduced as a modern replacement for Objective-C
  • Key design goals: safety (memory management via ARC), speed (LLVM-based compiler), and expressiveness (clean syntax)
  • Interoperable with Objective-C — existing Cocoa frameworks were accessible
  • guard statement for early exits
  • error handling with throw, try, catch
  • Protocol extensions with default implementations
  • defer statement for cleanup
  • Availability checking with @available
  • Major syntax changes to improve consistency and readability
  • Renamed parameter labels became first-class citizens
  • #keyPath for type-safe key paths
  • Swift on Linux (open source release)
  • API Design Guidelines established
  • Codable protocol for JSON and plist serialisation
  • String became a Collection of Character
  • Multi-line string literals
  • Improved keypaths with \. syntax
  • ABI stability — Swift standard library is embedded in the OS, binary compatibility across Swift versions
  • Result type in the standard library
  • @dynamicCallable and @dynamicMemberLookup
  • Raw strings with #"..."#
  • isMultiple(of:) and other standard library additions
  • Property wrappers (@Published, @State, @Binding, @ObservedObject, @EnvironmentObject)
  • Opaque return types (some View)
  • @main attribute for app entry point
  • any keyword for existential types (Swift 5.6+)
  • if let shorthand and switch on if expressions
  • Typed throws (Swift 5.9+)
  • Macro system (Swift 5.9+)
  • Strict concurrency checking enabled by default
  • Complete Sendable enforcement
  • Region-based isolation for more granular concurrency control
  • Improved type system for safer async code
  • Bitwise copyable protocol
FeatureSwiftPythonTypeScriptRust
Type systemStrong, safeDynamicGradualStrong
Memory managementARCGCGCOwnership
CompilationCompiledInterpretedCompiled (JS)Compiled
Concurrencyasync/awaitasync/awaitasync/awaitasync/await
Primary domainAppleGeneralWebSystems
Null safetyOptionalsNoneUnion/nullOption

Xcode is the primary IDE for Swift development on Apple platforms. It includes the Swift compiler, Interface Builder, Instruments for profiling, and the iOS/macOS simulators.

  1. Download Xcode from the Mac App Store or developer.apple.com
  2. Open Xcode and accept the licence agreement
  3. Install the command-line tools: xcode-select --install
  4. Verify installation: swift --version
Terminal window
$ swift --version
Apple Swift version 5.10
Target: arm64-apple-macosx14.0

Swift Playgrounds provide an interactive environment for experimenting with Swift code without creating a full project.

In Xcode:

  1. File > New > Playground
  2. Choose “Blank” for a general playground
  3. Write code and see results instantly in the sidebar

Swift Playgrounds app (iPad):

  • Download from the App Store
  • Interactive tutorials and coding challenges built in
  • Supports SwiftUI for real-time UI preview
// Swift Playground example
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, Swift!")
.font(.largeTitle)
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
}
}
}
#Preview {
ContentView()
}
  1. Open Xcode > File > New > Project
  2. Select the platform (iOS, macOS, etc.)
  3. Choose the template:
    • App — Standard SwiftUI or UIKit application
    • Framework — Reusable library
    • Command Line Tool — Terminal-based Swift program
  4. Configure project options:
    • Product Name
    • Team (for signing)
    • Organisation Identifier
    • Interface: SwiftUI or Storyboard
    • Language: Swift
  5. Choose a location and click Create

For development on Linux or for server-side Swift, install the Swift toolchain directly:

Terminal window
## Install Swift on Ubuntu
sudo apt install swift
## Verify
swift --version
# Run a Swift file directly
swift main.swift
# Enter the REPL
swift

SPM is Apple”s built-in dependency management and build tool. It integrates with Xcode and works from the command line.

MyPackage/
Package.swift // Package manifest
Sources/
MyPackage/
MyFile.swift
Tests/
MyPackageTests/
MyFileTests.swift
5.10
import PackageDescription
let package = Package(
name: "MyPackage",
platforms: [
.iOS(.v17),
.macOS(.v14)
],
products: [
.library(
name: "MyPackage",
targets: ["MyPackage"]
),
.executable(
name: "MyCLI",
targets: ["MyCLI"]
)
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"),
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.8.0"),
],
targets: [
.target(
name: "MyPackage",
dependencies: ["Alamofire"]
),
.executableTarget(
name: "MyCLI",
dependencies: [
"MyPackage",
.product(name: "ArgumentParser", package: "swift-argument-parser")
]
),
.testTarget(
name: "MyPackageTests",
dependencies: ["MyPackage"]
)
]
)
Terminal window
# Create a new package
swift package init --type library
swift package init --type executable
# Build the package
swift build
# Run tests
swift test
# Generate Xcode project
swift package generate-xcodeproj
# Update dependencies
swift package update
# Resolve dependencies
swift package resolve
# Clean build artifacts
swift package clean
  1. File > Add Package Dependencies
  2. Enter the package repository URL
  3. Select the version rule (Up to Next Major, Up to Next Minor, or Exact)
  4. Choose the products to add to the target

For modular code within a workspace:

  1. File > New > Package
  2. Name the package and choose its location
  3. Add it as a dependency to your main app target
main.swift
print("Hello, World!")
// Variables and constants
let language = "Swift"
var version = 6.0
print("\(language) version \(version)")
// Swift version 6.0
// String interpolation with expressions
let count = 42
print("There are \(count) items. That's \(count % 2 == 0 ? "even" : "odd").")
// There are 42 items. That's even.
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State private var name = ""
var body: some View {
VStack(spacing: 20) {
Text("Hello, \(name.isEmpty ? "World" : name)!")
.font(.title)
TextField("Enter your name", text: $name)
.textFieldStyle(.roundedBorder)
.padding()
}
.padding()
}
}

Swift eliminates entire categories of bugs at compile time:

  • No null pointer dereference — optionals force you to handle the absence of values
  • No buffer overflow — array bounds are always checked
  • No uninitialized variables — all variables must be initialised before use
  • No integer overflow by default — arithmetic operations trap on overflow
  • Memory safety — ARC manages memory automatically; strong reference cycles are caught at compile time with @escaping and lifetime annotations

Swift matches or exceeds C++ performance in many benchmarks:

  • LLVM backend generates highly optimised native code
  • Value semantics for structs enable optimisations impossible with reference types
  • Copy-on-write for collections avoids unnecessary data copying
  • Generic specialisation eliminates the overhead of abstraction
  • Whole-module optimisation enables cross-function inlining

Swift combines the performance of a systems language with the ergonomics of a scripting language:

  • Protocol-oriented programming enables flexible abstractions without inheritance
  • Property wrappers encapsulate storage logic cleanly
  • Result builders create declarative DSLs (used by SwiftUI)
  • Pattern matching handles complex data decomposition elegantly

Swift is type-safe: every value has a known type at compile time. The compiler infers types when possible, but you can always be explicit.

let inferredInt = 42 // Int (inferred)
let explicitDouble: Double = 42 // Double (explicit)
let pi = 3.14159 // Double (inferred)
// Type mismatch is a compile error
let message: String = 42 // error: cannot convert value of type 'Int' to specified type 'String'

Optionals represent the absence of a value, eliminating null pointer errors.

var name: String? = "Alice"
name = nil // Valid: optionals can be nil
let length = name?.count // Int?, not Int

Structs are value types (copied on assignment); classes are reference types (shared).

struct Point {
var x: Int
var y: Int
}
var a = Point(x: 1, y: 2)
var b = a // Copy -- independent
b.x = 10
print(a.x) // 1 (unchanged)

Swift favours protocols over inheritance for defining shared behaviour.

protocol Identifiable {
var id: String { get }
var displayName: String { get }
}
struct User: Identifiable {
let id: String
let displayName: String
}
func greet(_ subject: Identifiable) {
print("Hello, \(subject.displayName)")
}
  • SwiftUI — Declarative UI framework for all Apple platforms
  • UIKit / AppKit — Imperative UI frameworks (pre-SwiftUI, still widely used)
  • Foundation — Core utilities (dates, data, networking, JSON)
  • Combine — Reactive programming framework (publishers and subscribers)
  • Core Data — Object graph and persistence framework
  • ARKit — Augmented reality framework
  • Metal — Low-level GPU programming
LibraryPurpose
AlamofireHTTP networking
KingfisherImage downloading and caching
SnapKitAuto Layout DSL
SwiftLintCode style and conventions
SwiftFormatCode formatting
RxSwiftReactive extensions
The Composable ArchitectureState management architecture
AlamofireHTTP networking

Swift is a capable server-side language with frameworks like:

  • Vapor — The most popular web framework for Swift
  • Hummingbird — Lightweight, high-performance HTTP server
  • SwiftNIO — Apple’s async event-driven networking framework (foundation for Vapor)

Use #if compiler(>=5.9) and @available to handle version differences:

// Availability checking for OS versions
@available(iOS 17, macOS 14, *)
func useNewAPI() {
// Code requiring iOS 17+ / macOS 14+
}
// Compiler version checking
#if compiler(>=5.9)
// Swift 5.9+ specific code
func typedThrow() throws(some Error) {
// ...
}
#else
// Fallback for older versions
#endif

Swift is a modern, safe, and fast programming language designed for the Apple ecosystem but extending well beyond it. Its combination of protocol-oriented design, value semantics, strong type safety, and modern concurrency support makes it well-suited for applications ranging from mobile UI to server-side services.

Swift is Apple’s answer to the question: can we have C++ performance with Python-like safety? The language uses Automatic Reference Counting for memory management, catching reference cycles at compile time. Value types (structs) versus reference types (classes) is a core design decision that affects performance and thread safety. Protocol-oriented programming favours composition over inheritance, letting you build flexible abstractions without the fragile base class problem.

  • [[swift/01-basics/1_variables-and-types]] - Type inference and optionals
  • [[swift/03-oop/1_classes-and-structs]] - Value versus reference type semantics
  • [[swift/04-advanced/1_error-handling]] - Error handling with do-catch
  • [[swift/04-advanced/2_concurrency]] - Structured concurrency with async/await
  • Confusing value types and reference types: Structs are value types (copied on assignment) and classes are reference types (shared reference). Beginners often use classes when structs suffice, leading to unexpected shared state. Prefer structs unless you need inheritance or reference semantics.
  • Force-unwrapping optionals carelessly: Using ! on an optional crashes at runtime if the value is nil. Always use if let, guard let, or the nil-coalescing operator ?? to safely unwrap optionals.
  • Ignoring ARC and creating retain cycles: Strong reference cycles between classes (e.g., parent-child relationships) cause memory leaks. Use weak or unowned references to break cycles, especially in closures and delegate patterns.
  • Using var where let should be used: Swift encourages immutability. Declaring variables with var when they are never mutated makes code harder to reason about and prevents compiler optimisations. Default to let and switch to var only when reassignment is needed.