Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

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 ↗

Transcript all transcripts

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 swift · at 9:44 ↗
import Subprocess

let result = try await run(
  .name("pwd")
)
Subprocess: Call `run` with file path swift · at 10:04 ↗
import Subprocess

let swiftPath = FilePath("/usr/bin/swift")
let result = try await run(
  .path(swiftPath),
  arguments: ["--version"]
)
Subprocess: Accessing standard output swift · at 10:05 ↗
import Subprocess

let swiftPath = FilePath("/usr/bin/swift")
let result = try await run(
  .path(swiftPath),
  arguments: ["--version"]
)

let swiftVersion = result.standardOutput
NotificationCenter: Dynamic types swift · at 10:51 ↗
import UIKit

@MainActor 
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 swift · at 11:34 ↗
import UIKit

@MainActor
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 swift · at 12:01 ↗
extension UIResponder { 
  public struct KeyboardWillShowMessage: NotificationCenter.MainActorMessage
}

extension HTTPCookieStorage {
  public struct CookiesChangedMessage: NotificationCenter.AsyncMessage
}
Observation: The @Observable macro swift · at 12:48 ↗
import Observation

enum Item {
  case none
  case banana
  case star
}

@Observable
class Player {
  let name: String
  var score: Int = 0
  var item: Item = .none

  init(name: String) {
    self.name = name
  }
}
Observation: The Observations type swift · at 12:58 ↗
import Observation

enum Item {
  case none
  case banana
  case star
}

@Observable
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 swift · at 13:56 ↗
import Observation

enum Item {
  case none
  case banana
  case star
}

@Observable
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 swift · at 14:05 ↗
import Observation

enum Item {
  case none
  case banana
  case star
}

@Observable
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 swift · at 14:17 ↗
import Testing
import Foundation
import EvolutionMetadataModel

@Test
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 swift · at 14:54 ↗
import Testing
import Foundation
import EvolutionMetadataModel

@Test
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 swift · at 15:23 ↗
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 swift · at 15:34 ↗
import Testing
import EvolutionMetadataModel

@Test
func invalidProposalPrefix() async throws {
  await #expect(processExitsWith: .failure) {
    let proposal = Proposal(id: "SE-NNNN")
    _ = proposal.number 
  }
}
Concurrency: Async function error message swift · at 31:06 ↗
class PhotoProcessor {
  func extractSticker(data: Data, with id: String?) async -> Sticker? {     }
}

@MainActor
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 swift · at 32:06 ↗
// Run async functions on the caller's actor

class PhotoProcessor {
  func extractSticker(data: Data, with id: String?) async -> Sticker? {}
}

@MainActor
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 swift · at 32:36 ↗
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 swift · at 33:04 ↗
// Isolated conformances

protocol Exportable {
  func export()
}


extension StickerModel: @MainActor Exportable {
  func export() {
    photoProcessor.exportAsPNG()
  }
}
Concurrency: Isolated conformance use swift · at 33:20 ↗
// Isolated conformances

@MainActor
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 swift · at 33:31 ↗
// 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 swift · at 33:51 ↗
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 swift · at 34:01 ↗
final class StickerLibrary {
  @MainActor
  static let shared: StickerLibrary = .init()
}
Concurrency: Protecting classes swift · at 34:05 ↗
@MainActor
final class StickerLibrary {
  static let shared: StickerLibrary = .init()
}
Concurrency: A single-threaded program swift · at 34:15 ↗
@MainActor
final class StickerLibrary {
  static let shared: StickerLibrary = .init()
}

@MainActor
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 swift · at 34:22 ↗
// 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 swift · at 35:06 ↗
// 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
  }

  @concurrent
  static func extractSubject(from data: Data) async -> Sticker {}
}

Resources