1.1 — A short history of Swift, and which version you should care about

Opening scenario

It’s your first day on a new iOS team. You clone the repo, open Xcode, and the project complains: “This file requires Swift 5.5 or later.” You look at the build settings and see SWIFT_VERSION = 5.0. The CI logs mention “Swift 6 language mode is opt-in.” A colleague drops a Slack message: “FYI we’re not on strict concurrency yet, still on the 5 mode but Xcode 16.” You nod knowingly. You have no idea what they mean.

By the end of this chapter, you will.

The story so far

Swift was announced at WWDC 2014. Apple had been building Objective-C apps for 30 years (NeXT, then macOS, then iOS), and Objective-C — for all its dynamism — was showing its age: manual memory management before ARC, nil messaging hiding bugs, square-bracket syntax that scared off newcomers, no value types, no generics worth the name.

Chris Lattner (the creator of LLVM) had been working on Swift in secret since 2010. It was designed to interoperate with Objective-C (so Apple’s gigantic existing codebase didn’t have to be thrown away) while being safer, faster, and more modern.

Here’s the version timeline that actually matters in 2026:

VersionYearWhat changed (the version that defined the era)
Swift 1.02014Initial release. Nobody used it in production yet.
Swift 2.02015guard, defer, try/catch, protocol extensions. Suddenly usable.
Swift 3.02016The Great Renaming. Half the standard library changed. Every project broke.
Swift 4.02017Codable. JSON parsing stopped being painful.
Swift 5.02019ABI stability. Apps stopped shipping the Swift runtime inside them.
Swift 5.52021async/await, actors, structured concurrency. The biggest leap since 1.0.
Swift 5.92023Macros, parameter packs, if/switch expressions.
Swift 6.02024Strict concurrency by default (opt-in language mode). Data-race safety enforced at compile time.
Swift 6.1+2025–2026Refinements, better C++ interop, embedded Swift maturing.

If you’re starting today, you are writing Swift 6 in Swift 5 language mode on Xcode 16. That sentence sounds insane, so let me unpack it.

Concept → Why → How → Code

Concept: Swift version vs. language mode

A modern Swift toolchain ships with one compiler binary that understands multiple language modes. The toolchain version (e.g. Swift 6.0) tells you what features the compiler can handle. The language mode (e.g. -swift-version 5) tells the compiler which set of defaults and warnings to apply.

Why this split exists

Apple has hundreds of millions of lines of Swift code in the wild. If Swift 6 had simply forced every project into strict concurrency checking on day one, every existing app would break. Instead, Apple chose: ship the new defaults under a flag, let teams adopt incrementally.

How you read it in practice

  • SWIFT_VERSION = 5.0 in your Xcode build settings = “use Swift 5 defaults” — your code is permissive about sendability, isolation, etc.
  • SWIFT_VERSION = 6.0 = “Swift 6 language mode” — strict concurrency errors become errors, not warnings. You opt in when you’re ready.
  • The actual compiler may be Swift 6.1 — the toolchain bundled with Xcode 16.

Code

Check what your machine actually has:

$ swift --version
swift-driver version: 1.115 Apple Swift version 6.1.2 (swiftlang-6.1.2.0.0 clang-1700.0.13.5)
Target: arm64-apple-macosx15.0

In a Swift package, you declare both:

// swift-tools-version:6.0
// ^ minimum tool version that can READ this manifest

import PackageDescription

let package = Package(
    name: "MyLib",
    swiftLanguageVersions: [.v6]  // ^ language MODE to compile under
)

In Xcode, look at Build Settings → Swift Compiler – Language → Swift Language Version.

In the wild

  • Apple’s own apps (Music, TV, Wallet) reportedly adopted Swift 6 language mode gradually through 2025. Even Apple doesn’t flip the switch overnight on a million-LOC codebase.
  • Airbnb wrote a public retrospective in 2024 about migrating their iOS app to Swift Concurrency — they spent ~6 engineer-months just untangling DispatchQueue and @MainActor annotations.
  • Open-source libraries like Alamofire, SwiftUI Introspect, and TCA (The Composable Architecture) advertise their minimum Swift version prominently in their README — because consumers need to know whether they can use the library without bumping their own toolchain.

Common misconceptions

  1. “Swift 6 means I have to rewrite everything.” No. Swift 6 language mode is opt-in. Until you flip the flag in your build settings, your code compiles exactly the same as it did under Swift 5.x.

  2. “Newer Swift always means faster compile times.” Often the opposite. Each new feature adds inference work. Swift 5.7+ improved compile times measurably, but the trend has been “more features, more compiler work.”

  3. “Objective-C is dead.” Most of UIKit and Foundation is still Objective-C under the hood. Every Swift iOS app you ship is calling Objective-C runtime code on every line. Knowing a little Obj-C is still useful in 2026.

  4. “I should use the bleeding-edge Swift version for my open-source library.” Then you exclude every team that hasn’t upgraded yet. Library authors typically support N-1 or N-2 Xcode versions.

Seasoned engineer’s take

The version-vs-language-mode split looks ugly but it’s the single most important decision Apple’s Swift team has made for ecosystem health. Compare to Python 2 → 3, where the abrupt break fragmented the community for nearly a decade. Swift’s incremental opt-in model means a 2026 codebase can have one module in Swift 6 strict concurrency, another in Swift 5 mode, and another linking to Objective-C — all in the same app, all building today. That’s the part you should internalize: Swift is designed to be migrated to, not jumped to.

When you join a team, the first three questions you should ask are:

  1. What Xcode version are we on?
  2. What SWIFT_VERSION is set per target?
  3. Are we adopting strict concurrency, and if so, on which modules first?

The answers tell you 80% of what to expect about the codebase’s age, technical debt, and how cautious the team is.

TIP: Bookmark swift.org/documentation/articles/ and the Swift evolution proposals dashboard. Every change in the language is documented there before it ships.

WARNING: Never copy-paste a Swift snippet from Stack Overflow without checking the answer date. A DispatchQueue.main.async { … } answer from 2019 is technically still valid, but in 2026 the idiomatic version is await MainActor.run { … } or @MainActor func. Old answers compile; they just mark you as an engineer who hasn’t kept up.

Interview corner

Question (asked at almost every iOS interview): “What’s the difference between Swift 5 and Swift 6, and how would you migrate a project?”

Junior answer: “Swift 6 is the newer version. It has strict concurrency. I’d update the SWIFT_VERSION in Xcode.” → Technically correct but shallow. You’d pass a screen, probably not an onsite.

Mid-level answer: “Swift 6 introduces strict concurrency checking — the compiler now enforces data-race safety, requiring Sendable conformance on types crossing actor boundaries. The migration is opt-in via SWIFT_VERSION = 6.0 per target. I’d enable it gradually: start with the most isolated leaf modules, fix Sendable warnings under Swift 5 mode first (set -strict-concurrency=complete), then flip the language mode once the warnings are clean.” → Strong answer. Demonstrates you’ve actually done this.

Senior answer: All of the above, plus: “The real cost of the migration isn’t fixing warnings — it’s deciding the isolation architecture. Strict concurrency forces you to make explicit what was implicit: which code runs on the main actor, which models are sendable value types versus reference types pinned to an actor, where you need nonisolated(unsafe) escape hatches because of legacy frameworks. On a large codebase I’d dedicate a small team to define the isolation strategy for shared types (network layer, persistence, app-state) before enabling strict mode anywhere. Otherwise you end up sprinkling @unchecked Sendable everywhere, which gives you the warnings-clean checkbox but none of the safety. The migration is an architecture exercise, not a compiler exercise.” → That’s the answer that gets the offer.

Red-flag answer: “Swift 6 is just Swift 5 with bug fixes.” → Instant signal you haven’t touched the language in two years.

Lab preview

Lab 1.A (Playground exploration) gets you typing actual Swift in a Playground. You’ll touch every language version’s flagship feature in one file: optionals (1.0), guard (2.0), Codable (4.0), async/await (5.5), and a macro (5.9). You’ll feel the language’s history in your hands.


Next up: how to actually run Swift — Playgrounds, REPL, SPM, and the choice that trips up every beginner. → Setup, Playgrounds & SPM