Skip to content

Instantly share code, notes, and snippets.

@brennanMKE
Created August 9, 2025 03:41
Show Gist options
  • Save brennanMKE/537c3fd9671c293c96c2f3017953a64d to your computer and use it in GitHub Desktop.
Save brennanMKE/537c3fd9671c293c96c2f3017953a64d to your computer and use it in GitHub Desktop.
Swift 6.0, 6.1 and 6.2

Here’s a practical list of Swift Evolution proposals implemented in Swift 6.0 (the initial 6.0 release), each with a tiny demo you can paste into a Swift 6 project. I pulled the proposal statuses directly from the Swift Evolution repo to keep this tight and accurate.

Function + macro system / language usability

SE‑0415: Function body macros — generate function bodies at compile time. (GitHub)

@freestanding(expression)
macro time<T>(_ body: () -> T) -> T = #externalMacro(module: "MyMacros", type: "TimeMacro")

func work() -> Int {
  #time { (1...1_000_000).reduce(0, +) }
}

SE‑0422: Expression macro as caller‑side default argument — let a macro provide a default at the call site. (GitHub)

@freestanding(expression) macro contextID() -> String = #externalMacro(module:"Macros", type:"ContextID")

func log(_ message: String, id: String = #contextID()) { print("[\(id)] \(message)") }
log("Started") // id is generated by the macro at call site

SE‑0416: Subtyping for key‑path literals as functions — key‑path literals coerce to compatible function types more flexibly. (GitHub)

struct User { var name: String; var age: Int }
let users = [User(name:"A", age:30), User(name:"B", age:20)]
let names: [String] = users.map(\.name)     // key-path as function
let ages:  [Int]    = users.map(\.age)      // now subtypes correctly

SE‑0405: String validating initializers — failable initializers that validate code points, etc. (GitHub)

if let s = String(validating: [0x61, 0x62, 0x63]) {  // “abc”
  print(s)
}

Concurrency & actors

SE‑0421: Generalize AsyncSequenceAsyncSequence can properly throw from makeAsyncIterator() and next(). (GitHub)

struct Lines: AsyncSequence {
  struct Iterator: AsyncIteratorProtocol {
    mutating func next() async throws -> String? { /* may throw */ nil }
  }
  func makeAsyncIterator() -> Iterator { .init() } // throwing next()
}

SE‑0418: Inferring Sendable for methods and key‑path literals — fewer manual annotations; safer defaults. (GitHub)

struct Worker {
  func doIt(_ f: @escaping () -> Void) { Task { f() } } // closure can infer Sendable where needed
}

SE‑0423: Dynamic actor isolation — opt APIs into runtime‑checked isolation (useful for UI frameworks / plugins). (GitHub)

actor Counter { var value = 0 }

@dynamicActorIsolation
func bump(_ c: Counter) async { await c.value += 1 }   // isolation verified dynamically

SE‑0434: Usability of global‑actor‑isolated types — smoother use of types isolated to a global actor like @MainActor. (GitHub)

@MainActor
struct UIState { var text = "" }

let state = UIState()
await MainActor.run { print(state.text) }   // fewer friction points with global-actor types

Ownership / noncopyable types

SE‑0437: Noncopyable standard library primitives — foundational building blocks for move‑only patterns. (GitHub)

SE‑0432: Borrowing/consuming pattern matching for noncopyable types — switch/let‑binding respect ownership. (GitHub)

noncopyable struct Token { let id: Int }

func use(_ t: borrowing Token) {}
func consume(_ t: consuming Token) {}

func handle(_ t: consuming Token) {
  switch t {        // can consume/borrow in patterns
    // …
  }
}

SE‑0426: BitwiseCopyable — marker protocol to allow trivial copies by bit‑blit where safe. (GitHub)

struct POD: BitwiseCopyable { var x: Int, y: Int }   // compiler can memcpy this safely

SE‑0429: Partial consumption for noncopyable types — consume parts of a value without consuming the whole. (GitHub)

noncopyable struct Pair { var a: Int; var b: Int }
func foo(_ p: consuming Pair) { let a = consume p.a /* … keep using p.b … */ }

SE‑0408: Pack iteration (variadic generics) — loop over parameter packs directly. (GitHub)

func zipPack<each S: Sequence>(_ s: repeat each S) {
  for (repeat each element) in repeat (each s) { /* … */ }
}

Distributed actors

SE‑0428: Resolve distributed‐actor protocols — better protocol interactions for distributed actors. (GitHub)

distributed actor Node: Sendable {
  distributed func ping() async -> String { "pong" }
}

Atomics

SE‑0410: Atomics — standard library atomics module. (GitHub)

import Atomics

let counter = ManagedAtomic(0)
counter.wrappingIncrement(ordering: .relaxed)

Collections & algorithms

SE‑0270: RangeSet & collection operations — merged in Swift 6 toolchains. (GitHub)

var set = RangeSet(0..<3)
set.insert(contentsOf: [10..<12, 20..<21])
print(set.contains(10)) // true

Here are the Swift Evolution proposals that shipped in Swift 6.1, with tiny, copy‑pasteable examples.

SE‑0449 — nonisolated for global‑actor cutoff

Lets you mark a type or extension as nonisolated so members there don’t inherit a global actor (e.g. @MainActor). This is great for protocol conformances and pure utility APIs. (Swift.org)

@MainActor
struct User {
    var id: Int
    var name: String
}

// Members here are *not* main-actor isolated:
nonisolated extension User: CustomStringConvertible, Equatable {
    var description: String { "User(id: \(id), name: \(name))" }
    static func == (l: User, r: User) -> Bool { l.id == r.id }
}

Refs: proposal text, overview in the 6.1 release notes. (GitHub, Swift.org)

SE‑0442 — Infer TaskGroup Child Result Type

You no longer need to spell the of: T.self generic when it’s obvious. (Swift.org, GitHub)

struct Message { let text: String }

func downloadMessage(for id: Int) async -> Message {
    .init(text: "msg \(id)")
}

let ids = [1, 2, 3]
let messages = await withTaskGroup { group in
    for id in ids {
        group.addTask { await downloadMessage(for: id) }
    }
    var out: [Message] = []
    for await m in group { out.append(m) }
    return out
}

SE‑0436 — Objective‑C implementations in Swift

Implement imported Obj‑C APIs directly in Swift using @objc @implementation on an extension. (Swift.org, Swift Forums)

// MyView.h (Obj‑C, imported into Swift)
@interface MyView : UIView
@property (nonatomic) CGFloat cornerRadius;
- (void)animate;
@end
// MyView+Impl.swift (Swift)
@objc @implementation extension MyView {
    var cornerRadius: CGFloat {
        get { layer.cornerRadius }
        set { layer.cornerRadius = newValue }
    }

    func animate() {
        UIView.animate(withDuration: 0.25) { self.alpha = 1 }
    }
}

SE‑0439 — Trailing commas in (almost) all comma‑separated lists

Trailing commas are now allowed in tuples, parameter lists, generic parameter lists, capture lists, and string interpolation segments (beyond just array/dictionary literals). (Swift.org, Swift Forums)

func makePoint(
    x: Int,
    y: Int,
) -> (x: Int, y: Int) {
    (x: x, y: y,)
}

struct Box<T, U,>(value: (T, U,),)

let sum = "\(1 + 2,)"

SE‑0450 — Package traits (SwiftPM)

Packages can define traits to conditionally expose APIs or deps (e.g. for Embedded Swift or WASM). Clients can opt into a package’s traits from Package.swift. (Swift.org, Swift Forums, InfoQ)

// In the package that *defines* traits (simplified):
// Package.swift
// .packageTraits([.embedded, .wasm])  // see proposal for full syntax

// In a *client* package:
dependencies: [
    .package(
        url: "https://github.com/Org/SomePackage.git",
        from: "1.0.0",
        traits: [
            .default,  // enable the package's defaults
            "Embedded" // opt-in to an additional trait by name
        ]
    ),
]

Here’s the stuff that actually shipped with Swift 6.2 (and how to use it), sticking to Swift 6 language mode, Swift Concurrency, and the modern Testing framework.

SE‑0469 — Task naming

Swift 6.2 lets you attach human‑readable names to tasks and task‑group children. These names show up in tools (debugger/Instruments) and are also queryable at runtime. You can use them with Task { name: … } and with TaskGroup.addTask(name: …) (and the “unless cancelled” variant). (Swift.org, Apple Developer, GitHub)

// Name an unstructured task
let handle = Task(name: "Load profile") {
  try await api.loadProfile()
}
print(await handle.value)

// Name children in a task group
await withTaskGroup(of: Void.self) { group in
  group.addTask(name: "Thumbs prefetch") { await prefetchThumbnails() }
  group.addTask(name: "Hero image")      { await fetchHeroImage() }
}

SE‑0461 — Run nonisolated async functions on the caller’s actor by default

Swift now runs nonisolated async functions on the caller’s actor unless you opt out with @concurrent. Use @concurrent when you want the function to hop to the cooperative thread‑pool (freeing the caller’s actor). (Swift Forums, Apple Developer)

actor Cache {
  // Runs on the caller’s actor (often MainActor in UI code)
  nonisolated func item(for id: ID) async -> Item? { await db.lookup(id) }

  // Explicitly concurrent — will run off the caller’s actor
  @concurrent nonisolated func parse(_ data: Data) async throws -> Model {
    try await parser.parse(data)
  }
}

SE‑0466 — Control default actor‑isolation inference

Swift 6.2 adds an opt‑in build setting to infer @MainActor by default for a target (the “approachable concurrency” mode). In packages: (Use Your Loaf)

// swift-tools-version: 6.2
import PackageDescription

let package = Package(
  name: "MyFeature",
  targets: [
    .target(
      name: "MyFeature",
      swiftSettings: [
        .defaultIsolation(MainActor.self)   // SE‑0466
      ]
    )
  ]
)

SE‑0470 — Global‑actor‑isolated conformances

You can now isolate a protocol conformance to a global actor (e.g. @MainActor) so requirements always run on that actor. This eliminates a raft of conformance/isolation errors. (Swift Forums, Swift.org)

@MainActor
protocol Reloading {
  func reload()
}

final class Controller { /* nonisolated by default */ }

// The conformance itself is main‑actor‑isolated:
@MainActor
extension Controller: Reloading {
  func reload() {
    // guaranteed on the main actor
  }
}

SE‑0463 — Import Objective‑C completion handlers as @Sendable

Objective‑C APIs that take completion‑handler blocks are now imported with @Sendable closures, which aligns them with Swift’s data‑race safety. Your Swift code doesn’t change—your closures just type‑check as @Sendable now. (GitHub)

// Objective‑C
typedef void (^MyCompletion)(NSData * _Nullable, NSError * _Nullable);
- (void)fetchWithCompletion:(MyCompletion)completion;
// Swift 6.2 — imported as @Sendable (Data?, Error?) -> Void
api.fetchWithCompletion { (data: Data?, error: Error?) in
  // Closure is @Sendable — safe to cross concurrency domains
}

SE‑0371 — isolated deinit

deinit can now be explicitly isolated so it runs on the correct actor (e.g. the main actor for UIKit/AppKit types). This makes clean‑up code safe without workarounds. (Swift Forums, Apple Developer)

@MainActor
final class CameraController: UIViewController {
  // Guaranteed to run on the main actor:
  isolated deinit {
    previewLayer?.removeFromSuperlayer()
    session.stopRunning()
  }
}

Bonus: tiny tests with the modern Testing framework

import Testing

@Test
func taskNamesRoundTrip() async {
  let t = Task(name: "UnitTest Task") { 42 }
  let v = await t.value
  #expect(v == 42)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment