2026 Developer ToolsSwift
WWDC26 · 33 min · Developer Tools / Swift
What’s new in Swift
Join us for an update on Swift. Discover the latest language advancements, including updates for everyday ergonomics, improved concurrency, and safer high-performance code. Explore workflow and language interoperability improvements and updates in embedded Swift.
Watch at developer.apple.com ↗Chapters
- 0:07 — Introduction
- 0:44 — Everyday Language Improvements
- 1:55 — anyAppleOS Availability
- 3:02 — @diagnose Attribute
- 3:52 — Module Selectors (::)
- 5:59 — Library Updates
- 6:16 — Standard Library
- 7:31 — Swift Testing Updates
- 9:29 — Subprocess 1.0
- 10:14 — Foundation
- 11:59 — Beyond Apple Platforms
- 12:35 — Swift–C Interoperability (@C attribute)
- 15:09 — Swift-Java
- 16:03 — Editor support
- 16:44 — WebAssembly (Wasm) & JavascriptKit
- 18:08 — Embedded Swift
- 19:59 — Performance Tuning
- 21:29 — Optimizer Control: @inline(always) & @specialized
- 24:29 — Ownership System & Noncopyable Types
- 26:18 — Iterable Protocol & Borrow/Mutate Accessors
- 28:57 — New Standard Library Types: UniqueBox, UniqueArray, Ref
- 31:11 — The Future of Swift
Code shown on screen · 35 snippets
Better Swift Concurrency diagnostics (catching in the task)
Task {
do {
try lander.fly(to: moon)
}
catch {
lander.abort()
}
} Better Swift Concurrency diagnostics (saving the task for later)
let landingTask = Task {
try lander.fly(to: moon)
}
defer {
await orbiter.rendezvous(with: lander)
}
try await orbiter.justHangOut(waitingFor: landingTask) Better 'Sendable' conformances
final class Spacecraft: Sendable {
...
weak let dockedAt: SpaceStation?
...
}
class Mission: ~Sendable { ... }
class CrewedMission: Mission, @unchecked Sendable { ... } More accessible memberwise initializers
struct Briefing {
internal var topic: String
internal var scheduledAt: Date
private var attendees: [Person] = []
}
// Generated memberwise initializers:
// extension Briefing {
// private init(topic: String, scheduledAt: Date, attendees: [Person] = []) {
// self.topic = topic
// self.scheduledAt = scheduledAt
// self.attendees = attendees
// }
//
// internal init(topic: String, scheduledAt: Date) {
// self.topic = topic
// self.scheduledAt = scheduledAt
// self.attendees = []
// }
// } 'anyAppleOS' availability (before)
extension Mission {
@available(macOS 27, iOS 27, watchOS 27, tvOS 27, visionOS 27, *)
func showStatus() { ... }
@available(macOS 27, iOS 27, watchOS 27, visionOS 27, *)
@available(tvOS, unavailable)
func launch() { ... }
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS)
func makeLiveActivityWidget() -> some Widget { ... }
#endif
} 'anyAppleOS' availability (after)
extension Mission {
@available(anyAppleOS 27, *)
func showStatus() { ... }
@available(anyAppleOS 27, *)
@available(tvOS, unavailable)
func launch() { ... }
#if os(anyAppleOS)
func makeLiveActivityWidget() -> some Widget { ... }
#endif
} Controlling warnings with '@diagnose'
(DeprecatedDeclaration, as: ignored, reason: "Flying with surplus hardware")
func makeApolloSoyuzMission() -> Mission {
CrewedMission(
rocket: makeSaturnIRocket(),
payload: makeApolloCSM(),
crew: [.daniellePoole, .nathanMorrison]
)
}
(StrictMemorySafety, as: warning)
func uplinkCommand(from receiver: inout Receiver, to computer: inout Computer) {
let commandSize = receiver.receiveInt()
receiver.withReceivedData(byteCount: commandSize) {
computer.receiveUplinkedCommand($0)
}
}
(ErrorInFutureSwiftVersion, as: error)
func fetchPosition() -> (x: Double, y: Double, z: Double) {
return self.rotation
} Clarifying code with module selectors
import Rocket
import GiftShopToys
let rocket1 = SaturnV() // could mean `Rocket::SaturnV` or `GiftShopToys::SaturnV`
let rocket2 = Rocket.SaturnV() // prefers `Rocket::Rocket.SaturnV`
let rocket3 = Rocket::SaturnV() // correctly finds `Rocket::SaturnV` Clarifying code with module selectors (module selectors work on members, too)
//
// Module Chemistry
//
public protocol Flammable { ... }
extension Flammable {
/// Set `self` on fire.
public func fire() { ... }
}
//
// Module HumanResources
//
import Chemistry
public protocol Employee { ... }
extension Employee {
/// Remove `self` from job.
public func fire() { ... }
}
public class LaunchPadTechnician: Employee, Flammable { ... }
//
// Module main
//
import HumanResources
import Chemistry
let launchPadTechnician = LaunchPadTechnician(...)
launchPadTechnician.HumanResources::fire() Task cancellation
// Radio for help
extension Radio {
func send(_ data: [UInt8] {
if Task.isCancelled { return }
// ...
}
}
extension EmergencyTransponder {
func sendSOS() {
radio.send(makeSOSPacket())
}
} Task cancellation shield
// Radio for help
extension Radio {
func send(_ data: [UInt8] {
if Task.isCancelled { return }
// ...
}
}
extension EmergencyTransponder {
func sendSOS() {
withTaskCancellationShield {
radio.send(makeSOSPacket())
}
}
} Constructing a new dictionary
// Map values with keys
func makeCalendarDisplayNames(for missions: [Mission: LaunchWindow]) -> [Mission: String] {
let new: [Mission: String] = .init(
uniqueKeysWithValues: missions.lazy.map { mission, launchWindow in
(mission, makeDisplayName(for: mission, in: launchWindow))
}
)
return new
} Dictionary.mapKeyedValues
// Map values with keys
func makeCalendarDisplayNames(for missions: [Mission: LaunchWindow]) -> [Mission: String] {
missions.mapKeyedValues { mission, launchWindow in
makeDisplayName(for: mission, in: launchWindow)
}
} The new FilePath type
// FilePath handling macOS-named resources
var path: FilePath = "/var/www/static"
path.components.append("WWDC")
print(path.components)
// [ "var", "www", "static", "WWDC" ]
var path: FilePath = "/var/www/static/..namedresource/rsrc"
print(path.components)
// [ "var", "www", "static" ] Issue Severity
// Issue severity
(arguments: allRockets)
func testBurn(rocket: Rocket) throws {
rocket.burn(for: .seconds(150))
let remaining = rocket.propellantKg / rocket.totalPropellantKg
if remaining < 0.10 {
Issue.record(
"\(rocket.name) remaining fuel is below 10% reserve target",
severity: .warning
)
}
#expect(remaining > 0.02, "\(rocket.name) propellant critically low - abort")
} Test Cancellation
// Test Cancellation
(arguments: allRockets)
func testBurn(rocket: Rocket) throws {
// solid-fuel rocket engines can't be stopped
if rocket.engineType == .solid {
try Test.cancel("\(rocket.name) has solid fuel")
}
rocket.burn(for: .seconds(150))
let remaining = rocket.propellantKg / rocket.totalPropellantKg
if remaining < 0.10 {
Issue.record(
"\(rocket.name) remaining fuel is below 10% reserve target",
severity: .warning
)
}
#expect(remaining > 0.02, "\(rocket.name) propellant critically low - abort")
} XCTest interoperability: Using XCTest from Swift Testing
// XCTest interoperability: Using XCTest from Swift Testing
func checkedTransmitAndReceive(on radio: Radio,
packet: Packet,
expectedByteCount: Int) throws -> [UInt8] {
try radio.transmit(bytes: packet.data)
let bytes = try radio.receive()
XCTAssertEqual(bytes.count, expectedByteCount)
return bytes
}
func pingTest() throws {
let radio = Radio()
let bytes = try checkedTransmitAndReceive(on: radio, packet: .ping, expectedByteCount: 8)
#expect(bytes == [0x00, 0x00, 0xf0, 0x37, 0x0f, 0xc7, 0x00, 0x01])
} XCTest interoperability: Using Swift Testing from XCTest
// XCTest interoperability: Using Swift Testing from XCTest
class RadioTests: XCTestCase {
func testPingPacketTransmission() {
let radio = Radio()
let bytes = try checkedTransmitAndReceive(on: radio,
packet: .ping,
expectedByteCount: 8)
#expect(bytes == [0x00, 0x00, 0xf0, 0x36, 0x0f, 0xc7, 0x00, 0x02])
}
} Subprocess Output Stream
// Subprocess output streaming
let result = try await Subprocess.run(.name("ls"),
input: .none,
output: .sequence,
error: .string(limit:4096)) { execution in
execution.standardOtput.strings().filter { $0.hasSuffix(".obj") }
}
for try await objectFiles in result.closureOutput {
print("Object file: \(objectFile)")
} Progress Manager - Concurrency
// Progress reporting - Concurrency
let manager = ProgressManager(totalCount: 100)
try await rocket.launch(mission.subprogress(assigningCount: 100))
extension Rocket {
func launch(_ progress: consuming Subprogress? = nil) async throws {
let stage = progress?.start(totalCount: 3)
try await ignite(); stage?.complete(count: 1)
try await liftoff(); stage?.complete(count: 1)
try await stageSeparation(); stage?.complete(count: 1)
}
} Progress Manager - progress reporting
// Progress reporting - progress reporting
let manager = ProgressManager(totalCount: 100)
try await rocket.launch(mission.subprogress(assigningCount: 100))
Task {
for await update in Observations({ mission.fractionCompleted }) {
print("🚀 Mission \(Int(update * 100))%")
}
} Progress reporting - metadata
// Progress reporting - metadata
extension Rocket {
func ascend(_ progress: consuming Subprogress) async throws {
let stage = progress.start(totalCount: 3)
stage.detlaV = 3_400; try await burn(); stage.complete(count: 1)
stage.detlaV = 2_100; try await stageSeparation(); stage.complete(count: 1)
stage.detlaV = 1_800; try await coast(); stage.complete(count: 1)
}
}
print("Δv to orbit: \(mission.summary(of: \.deltaV)) m/s") Directly control inlining (source code)
func histogram<Values>(of values: Values) -> [256 of Int] where Values: Sequence<UInt8> {
var result = makeInts(randomized: false)
for value in values {
result[Int(value)] += 1
}
return result
}
func makeInts(randomized: Bool) -> [256 of Int] {
if randomized {
InlineArray { _ in Int.random(in: (.min)...(.max)) }
} else {
InlineArray(repeating: 0)
}
} Directly control inlining (inlined, but not optimized)
func histogram<Values>(of values: Values) -> [256 of Int] where Values: Sequence<UInt8> {
var result = if false { //
InlineArray { _ in Int.random(in: (.min)...(.max)) } //
} else { // Inlined code
InlineArray(repeating: 0) //
} //
for value in values {
result[Int(value)] += 1
}
return result
}
func makeInts(randomized: Bool) -> [256 of Int] {
if randomized {
InlineArray { _ in Int.random(in: (.min)...(.max)) }
} else {
InlineArray(repeating: 0)
}
} Directly control inlining (inlined and optimized)
func histogram<Values>(of values: Values) -> [256 of Int] where Values: Sequence<UInt8> {
var result = InlineArray(repeating: 0) // Inlined and optimized code
for value in values {
result[Int(value)] += 1
}
return result
}
func makeInts(randomized: Bool) -> [256 of Int] {
if randomized {
InlineArray { _ in Int.random(in: (.min)...(.max)) }
} else {
InlineArray(repeating: 0)
}
} Directly control inlining (preventing inlining)
(never)
func makeInts(randomized: Bool) -> [256 of Int] {
if randomized {
InlineArray { _ in Int.random(in: (.min)...(.max)) }
} else {
InlineArray(repeating: 0)
}
} Directly control inlining (forcing inlining)
(always)
func makeInts(randomized: Bool) -> [256 of Int] {
if randomized {
InlineArray { _ in Int.random(in: (.min)...(.max)) }
} else {
InlineArray(repeating: 0)
}
} Making generic functions faster with '@specialized'
func histogram<Values>(of values: Values) -> [256 of Int] where Values: Sequence<UInt8> {
var result = makeInts(randomized: false)
for value in values {
result[Int(value)] += 1
}
return result
}
// Note: Specialized function doesn't actually have a directly callable name.
func `histogram of [UInt8]`(of values: [UInt8]) -> [256 of Int] { //
var result = makeInts(randomized: false) //
//
for value in values { //
result[Int(value)] += 1 // Specialized code
} //
//
return result //
} // Making generic functions faster with '@specialized' (explicitly requesting specialization)
(where Values == [UInt8])
func histogram<Values>(of values: Values) -> [256 of Int] where Values: Sequence<UInt8> {
var result = makeInts(randomized: false)
for value in values {
result[Int(value)] += 1
}
return result
}
// Note: Specialized function doesn't actually have a directly callable name.
func `histogram of [UInt8]`(of values: [UInt8]) -> [256 of Int] { //
var result = makeInts(randomized: false) //
//
for value in values { //
result[Int(value)] += 1 // Specialized code
} //
//
return result //
} // Associated types can be '~Copyable' and '~Escapable'
protocol Iterable<Element, Failure>: ~Copyable, ~Escapable {
associatedtype Element: ~Copyable
associatedtype IterableIterator: IterableIteratorProtocol<Element, Failure>, ~Copyable, ~Escapable
associatedtype Failure: Error = Never
func makeIterableIterator() -> IterableIterator
var underestimatedCount: Int { get }
}
protocol IterableIteratorProtocol<Element, Failure>: ~Copyable, ~Escapable {
associatedtype Element: ~Copyable
associatedtype Failure: Error = Never
mutating func nextSpan(maximumCount: Int) throws(Failure) -> Span<Element>
mutating func skip(by maximumOffset: Int) throws(Failure) -> Int
} The problem with existing accessors
public struct UniqueBox<Value>: ~Copyable {
private let valuePointer: UnsafeMutablePointer<Value>
public init(_ value: consuming Value) {
valuePointer = UnsafeMutablePointer.allocate(capacity: 1)
valuePointer.initialize(to: value)
}
public var value: Value {
get { valuePointer.pointee }
set { valuePointer.pointee = newValue }
}
deinit {
valuePointer.deinitialize(count: 1)
valuePointer.deallocate()
}
} 'borrow' and 'mutate' accessors
public struct UniqueBox<Value: ~Copyable>: ~Copyable {
private let valuePointer: UnsafeMutablePointer<Value>
public init(_ value: consuming Value) {
valuePointer = UnsafeMutablePointer.allocate(capacity: 1)
valuePointer.initialize(to: value)
}
public var value: Value {
borrow { valuePointer.pointee }
mutate { &valuePointer.pointee }
}
deinit {
valuePointer.deinitialize(count: 1)
valuePointer.deallocate()
}
} Using 'MutableRef' to eliminate repeated accesses (with un-hoisted access)
func updateCount<Key: Hashable>(
for key: Key,
from sets: [Set<Key>],
in counts: inout [Key: Int]
) {
for set in sets {
if set.contains(key) {
counts[key, default: 0] += 1
}
}
} Using 'MutableRef' to eliminate repeated accesses (hoisted by 'inout' parameter)
func updateCount<Key: Hashable>(
for key: Key,
from sets: [Set<Key>],
in counts: inout [Key: Int]
) {
func updateCountImpl(count: inout Int) {
for set in sets {
if set.contains(key) {
count += 1
}
}
}
updateCountImpl(count: &counts[key, default: 0])
} Using 'MutableRef' to eliminate repeated accesses (hoisted by 'MutableRef')
func updateCount<Key: Hashable>(
for key: Key,
from sets: [Set<Key>],
in counts: inout [Key: Int]
) {
var countRef = MutableRef(&counts[key, default: 0])
for set in sets {
if set.contains(key) {
countRef.value += 1
}
}
} Resources
Related sessions
-
21 min -
24 min