WWDC 2024 New in Swift

«  WWDC 2024 Keynote Memo
WWDC 2024 Live Activity to Apple Watch  »

Supported platforms

  • Apple
  • Linux
  • Windows
  • WebAssembly
  • Ubuntu
  • Amazon Linux
  • CentOS
  • Red Hat UBI
  • Fedora
  • Debian

Static Linux SDK for Swift

--swift-sdk aarch64-swift-linux-musl

Identify which SDK to build against.

  • Built the service for macOS on macOS
  • Installed fully static Linux SDK for Swift
  • Built the service for Linux on macOS

Swift everywhere

Swift Testing

// Swift Testing - Customize test's name / organize test function with tags / parameterize test with arguments

import Testing

@Test("Recognized rating",
       .tags(.critical),
       arguments: [
           (1, "A Beach",       "⭐️⭐️⭐️⭐️⭐️"),
           (2, "Mystery Creek", "⭐️⭐️⭐️⭐️"),
       ])
func rating(videoId: Int, videoName: String, expectedRating: String) {
    let video = Video(id: videoId, name: videoName)
    #expect(video.rating == expectedRating)
}

Implicitly built modules

Explicitly built modules

  • More parallelism in builds
  • Better visibility into build steps
  • Improved reliability of builds
  • Faster debugger start-up

Language updates

  • github.com/swiftlang

Noncopyable types

  • Supported in all generic contexts
  • Standard library support for Optional, Result, Unsafe Pointers
// Before
struct File: ~Copyable {
  private let fd: CInt

  init(descriptor: CInt) {
    self.fd = descriptor
  }

  func write(buffer: [UInt8]) {
    // ...
  }

  deinit {
    close(fd)
  }
}

guard let fd = open(name) else {
  return
}
let file = File(descriptor: fd)
file.write(buffer: data)

// After
struct File: ~Copyable {
  private let fd: CInt

  init?(name: String) {
    guard let fd = open(name) else {
      return nil
    }
    self.fd = fd
  }

  func write(buffer: [UInt8]) {
    // ...
  }

  deinit {
    close(fd)
  }
}

Embedded Swift

  • Small and standalone binaries
    • Disables feature that need a runtime: reflection, “any” types
    • Special compiler techniques: full generics specialization, static linking
    • Embedded Swift subset close to “full” Swift
  • Systems with limited memory
    • ARM and RISC-V micro-controllers

C++ interoperability

  • Virtual methods
  • Default arguments
  • C++ move-only types, std::map, std::set, std::optional, std::chrono::duration
// C++
struct Person {
  Person(const Person&) = delete;
  Person(Person &&) = default;
  // ...
};

// Swift
struct Developer: ~Copyable {
    let person: Person
    init(person: consuming Person) {
      self.person = person
    }
}

let person = Person()
let developer = Developer(person: person)
person.printInfo()

Typed throws

Use typed throws when:

  • Same module error handling
  • Propagating error type in generic contexts
  • Constrained environments
// Untyped throws
enum IntegerParseError: Error {
  case nonDigitCharacter(String, index: String.Index)
}

func parse(string: String) throws -> Int {
  for index in string.indices {
    // ...
    throw IntegerParseError.nonDigitCharacter(string, index: index)
  }
}

do {
  let value = try parse(string: "1+234")
}
catch let error as IntegerParseError {
  // ...
}
catch {
   // error is 'any Error'
}

func parse(string: String) throws -> Int {
  //...
}

func parse(string: String) -> Int {
  //...
}

// typed throws
enum IntegerParseError: Error {
  case nonDigitCharacter(String, index: String.Index)
}

func parse(string: String) throws(IntegerParseError) -> Int {
  for index in string.indices {
    // ...
    throw IntegerParseError.nonDigitCharacter(string, index: index)
  }
}

do {
  let value = try parse(string: "1+234")
}
catch {
   // error is 'IntegerParseError'
}

func parse(string: String) throws(any Error) -> Int {
  //...
}

func parse(string: String) throws(Never) -> Int {
  //...
}

Data-race safety

// Pass NonSendable reference across actor isolation boundaries
class Client {
  init(name: String, balance: Double) {}
}

actor ClientStore {
  static let shared = ClientStore()
  private var clients: [Client] = []
  func addClient(_ client: Client) {
    clients.append(client)
  }
}

@MainActor
func openAccount(name: String, balance: Double) async {
  let client = Client(name: name, balance: balance)
  await ClientStore.shared.addClient(client) // the client here won't show warnings
}

// Atomic
import Dispatch
import Synchronization

let counter = Atomic<Int>(0)

DispatchQueue.concurrentPerform(iterations: 10) { _ in
  for _ in 0 ..< 1_000_000 {
    counter.wrappingAdd(1, ordering: .relaxed)
  }
}

print(counter.load(ordering: .relaxed))

// Mutex
import Synchronization

final class LockingResourceManager: Sendable {
  let cache = Mutex<[String: Resource]>([:])

  func save(_ resource: Resource, as key: String) {
    cache.withLock {
      $0[key] = resource
    }
  }
}

References

Published on 12 Jun 2024 Find me on Facebook, Twitter!

«  WWDC 2024 Keynote Memo
WWDC 2024 Live Activity to Apple Watch  »

Comments

    Join the discussion for this article at here . Our comments is using Github Issues. All of posted comments will display at this page instantly.