2025 Developer ToolsSwift
WWDC25 · 38 min · Developer Tools / Swift
What’s new in Swift
Join us for an update on Swift. We’ll talk about workflow improvements that make you more productive, and new and modernized library APIs for fundamental programming tasks. We’ll show examples of Swift adoption throughout more layers of the software stack. Finally, we’ll explore new language features for both improving approachability of concurrency, and achieving peak performance when you need it.
Watch at developer.apple.com ↗Chapters
- 0:00 — Introduction & Agenda
- 0:48 — swiftlang updates
- 3:06 — Development workflow: Writing code
- 4:40 — Development workflow: Building
- 7:36 — Development workflow: Debugging
- 9:14 — Libraries: Subprocess
- 10:45 — Libraries: Foundation
- 12:31 — Libraries: Observation
- 14:13 — Libraries: Testing
- 16:08 — Swift throughout the stack: Embedded Swift
- 18:00 — Swift throughout the stack: Security
- 19:37 — Swift throughout the stack: Server
- 23:23 — Swift throughout the stack: Platforms
- 26:11 — Language evolution: Performance
- 30:28 — Language evolution: Concurrency
- 37:15 — Wrap up
Code shown on screen · 26 snippets
Subprocess: Call `run` with string
import Subprocess
let result = try await run(
.name("pwd")
) Subprocess: Call `run` with file path
import Subprocess
let swiftPath = FilePath("/usr/bin/swift")
let result = try await run(
.path(swiftPath),
arguments: ["--version"]
) Subprocess: Accessing standard output
import Subprocess
let swiftPath = FilePath("/usr/bin/swift")
let result = try await run(
.path(swiftPath),
arguments: ["--version"]
)
let swiftVersion = result.standardOutput NotificationCenter: Dynamic types
import UIKit
class KeyboardObserver {
func registerObserver(screen: UIScreen) {
let center = NotificationCenter.default
let token = center.addObserver(
forName: UIResponder.keyboardWillShowNotification,
object: screen,
queue: .main
) { notification in
guard let userInfo = notification.userInfo else { return }
let startFrame = userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect
let endFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
guard let startFrame, let endFrame else { return }
self.keyboardWillShow(startFrame: startFrame, endFrame: endFrame)
}
}
func keyboardWillShow(startFrame: CGRect, endFrame: CGRect) {}
} NotificationCenter: Concrete types
import UIKit
class KeyboardObserver {
func registerObserver(screen: UIScreen) {
let center = NotificationCenter.default
let token = center.addObserver(
of: screen,
for: .keyboardWillShow
) { keyboardState in
let startFrame = keyboardState.startFrame
let endFrame = keyboardState.endFrame
self.keyboardWillShow(startFrame: startFrame, endFrame: endFrame)
}
}
func keyboardWillShow(startFrame: CGRect, endFrame: CGRect) {}
} NotificationCenter: Conformances
extension UIResponder {
public struct KeyboardWillShowMessage: NotificationCenter.MainActorMessage
}
extension HTTPCookieStorage {
public struct CookiesChangedMessage: NotificationCenter.AsyncMessage
} Observation: The @Observable macro
import Observation
enum Item {
case none
case banana
case star
}
class Player {
let name: String
var score: Int = 0
var item: Item = .none
init(name: String) {
self.name = name
}
} Observation: The Observations type
import Observation
enum Item {
case none
case banana
case star
}
class Player {
let name: String
var score: Int = 0
var item: Item = .none
init(name: String) {
self.name = name
}
}
let player = Player(name: "Holly")
let values = Observations {
let score = "\(player.score) points"
let item =
switch player.item {
case .none: "no item"
case .banana: "a banana"
case .star: "a star"
}
return "\(score) and \(item)"
} Observation: Transactional updates
import Observation
enum Item {
case none
case banana
case star
}
class Player {
let name: String
var score: Int = 0
var item: Item = .none
init(name: String) {
self.name = name
}
}
let player = Player(name: "Holly")
let values = Observations {
let score = "\(player.score) points"
let item =
switch player.item {
case .none: "no item"
case .banana: "a banana"
case .star: "a star"
}
return "\(score) and \(item)"
}
player.score += 2
player.item = .banana Observation: AsyncSequence
import Observation
enum Item {
case none
case banana
case star
}
class Player {
let name: String
var score: Int = 0
var item: Item = .none
init(name: String) {
self.name = name
}
}
let player = Player(name: "Holly")
let values = Observations {
let score = "\(player.score) points"
let item =
switch player.item {
case .none: "no item"
case .banana: "a banana"
case .star: "a star"
}
return "\(score) and \(item)"
}
player.score += 2
player.item = .banana
for await value in values { print(value) } Swift Testing
import Testing
import Foundation
import EvolutionMetadataModel
func validateProposalID() async throws {
let (data, _) = try await URLSession.shared.data(from: evolutionJSONMetadataURL)
let jsonDecoder = JSONDecoder()
let metadata = try jsonDecoder.decode(EvolutionMetadata.self, from: data)
for proposal in metadata.proposals {
#expect(proposal.id.starts(with: "SE"))
}
} Swift Testing: Attachments
import Testing
import Foundation
import EvolutionMetadataModel
func validateProposalID() async throws {
let (data, _) = try await URLSession.shared.data(from: evolutionJSONMetadataURL)
Attachment.record(data, named: "evolution-metadata.json")
let jsonDecoder = JSONDecoder()
let metadata = try jsonDecoder.decode(EvolutionMetadata.self, from: data)
for proposal in metadata.proposals {
#expect(proposal.id.starts(with: "SE"))
}
} Exit Tests: Preconditions
extension Proposal {
public var number: Int {
let components = id.split(separator: "-")
precondition(
components.count == 2 && components[1].allSatisfy(\.isNumber),
"Invalid proposal ID format \(id); expected SE-<Number>"
)
return Int(components[1])!
}
} Exit Tests: processExitsWith argument
import Testing
import EvolutionMetadataModel
func invalidProposalPrefix() async throws {
await #expect(processExitsWith: .failure) {
let proposal = Proposal(id: "SE-NNNN")
_ = proposal.number
}
} Concurrency: Async function error message
class PhotoProcessor {
func extractSticker(data: Data, with id: String?) async -> Sticker? { }
}
final class StickerModel {
let photoProcessor = PhotoProcessor()
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
guard let data = try await item.loadTransferable(type: Data.self) else {
return nil
}
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
}
} Concurrency: Run async functions on the caller's actor
// Run async functions on the caller's actor
class PhotoProcessor {
func extractSticker(data: Data, with id: String?) async -> Sticker? {}
}
final class StickerModel {
let photoProcessor = PhotoProcessor()
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
guard let data = try await item.loadTransferable(type: Data.self) else {
return nil
}
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
}
} Concurrency: Conformance error
protocol Exportable {
func export()
}
extension StickerModel: Exportable { // error: Conformance of 'StickerModel' to protocol 'Exportable' crosses into main actor-isolated code and can cause data races
func export() {
photoProcessor.exportAsPNG()
}
} Concurrency: Isolated conformances
// Isolated conformances
protocol Exportable {
func export()
}
extension StickerModel: @MainActor Exportable {
func export() {
photoProcessor.exportAsPNG()
}
} Concurrency: Isolated conformance use
// Isolated conformances
struct ImageExporter {
var items: [any Exportable]
mutating func add(_ item: StickerModel) {
items.append(item)
}
func exportAll() {
for item in items {
item.export()
}
}
} Concurrency: Isolated conformance error
// Isolated conformances
nonisolated
struct ImageExporter {
var items: [any Exportable]
mutating func add(_ item: StickerModel) {
items.append(item) // error: Main actor-isolated conformance of 'StickerModel' to 'Exportable' cannot be used in nonisolated context
}
func exportAll() {
for item in items {
item.export()
}
}
} Concurrency: Unsafe static variable
final class StickerLibrary {
static let shared: StickerLibrary = .init() // error: Static property 'shared' is not concurrency-safe because non-'Sendable' type 'StickerLibrary' may have shared mutable state
} Concurrency: Protecting static variables
final class StickerLibrary {
static let shared: StickerLibrary = .init()
} Concurrency: Protecting classes
final class StickerLibrary {
static let shared: StickerLibrary = .init()
} Concurrency: A single-threaded program
final class StickerLibrary {
static let shared: StickerLibrary = .init()
}
final class StickerModel {
let photoProcessor: PhotoProcessor
var selection: [PhotosPickerItem]
}
extension StickerModel: @MainActor Exportable {
func export() {
photoProcessor.exportAsPNG()
}
} Concurrency: Mode to infer main actor by default
// Mode to infer main actor by default
final class StickerLibrary {
static let shared: StickerLibrary = .init()
}
final class StickerModel {
let photoProcessor: PhotoProcessor
var selection: [PhotosPickerItem]
}
extension StickerModel: Exportable {
func export() {
photoProcessor.exportAsPNG()
}
} Concurrency: Explicitly offloading async work
// Explicitly offloading async work
class PhotoProcessor {
var cachedStickers: [String: Sticker]
func extractSticker(data: Data, with id: String) async -> Sticker {
if let sticker = cachedStickers[id] {
return sticker
}
let sticker = await Self.extractSubject(from: data)
cachedStickers[id] = sticker
return sticker
}
static func extractSubject(from data: Data) async -> Sticker {}
} Resources
Related sessions
-
28 min -
33 min -
32 min -
32 min -
21 min -
13 min -
15 min -
22 min -
35 min