2024 Developer ToolsSwift
WWDC24 · 30 min · Developer Tools / Swift
What’s new in Swift
Join us for an update on Swift. We’ll briefly go through a history of Swift over the past decade, and show you how the community has grown through workgroups, expanded the package ecosystem, and increased platform support. We’ll introduce you to a new language mode that achieves data-race safety by default, and a language subset that lets you run Swift on highly constrained systems. We’ll also explore some language updates including noncopyable types, typed throws, and improved C++ interoperability.
Watch at developer.apple.com ↗Chapters
- 0:00 — Introduction
- 0:12 — Swift over the years
- 3:44 — Agenda
- 3:58 — Swift project update
- 4:08 — Community
- 4:59 — Packages
- 5:50 — Blogs
- 6:33 — Swift everywhere
- 7:37 — Cross compilation to Linux
- 11:27 — Foundation
- 13:06 — Swift Testing
- 14:34 — Improvements to builds
- 16:15 — Swift's new space
- 17:03 — Language updates
- 17:29 — Noncopyable types
- 19:55 — Embedded Swift
- 21:47 — C++ interoperability
- 23:34 — Typed throws
- 26:07 — Swift 6 language mode and data-race safety
- 28:43 — Low-level synchronization primitives
- 29:59 — Wrap up
Code shown on screen · 25 snippets
Swift Build
swift build Inspecting the build output
file .build/debug/CatService Run the REST API service
.build/debug/CatService Make a request to REST API service
curl localhost:8080/api/emoji Install the Fully static Linux SDK for Swift
swift sdk install ~/preview-static-swift-linux-0.0.1.tar.gz Swift build command flag to cross compile
swift build --swift-sdk aarch64-swift-linux-musl Inspecting the build output
file .build/debug/CatService Copy the service over to our Linux server and run it
scp .build/debug/CatService demo-linux-host:~/CatService
./CatService Make a request to REST API service from macOS to Linux
curl demo-linux-host:8080/api/emoji Swift Testing - Declare a test function
// Swift Testing
import Testing
func rating() {
let video = Video(id: 2, name: "Mystery Creek")
#expect(video.rating == "⭐️⭐️⭐️⭐️")
} Swift Testing - Customize a test’s name
// Swift Testing
import Testing
("Recognized rating")
func rating() {
let video = Video(id: 2, name: "Mystery Creek")
#expect(video.rating == "⭐️⭐️⭐️⭐️")
} Swift Testing - Organize test function with tags
// Swift Testing
import Testing
("Recognized rating",
.tags(.critical))
func rating() {
let video = Video(id: 2, name: "Mystery Creek")
#expect(video.rating == "⭐️⭐️⭐️⭐️")
} Swift Testing - Parameterize test with arguments
// Swift Testing
import Testing
("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)
} Noncopyable types
struct File: ~Copyable {
private let fd: CInt
init(descriptor: CInt) {
self.fd = descriptor
}
func write(buffer: [UInt8]) {
// ...
}
deinit {
close(fd)
}
} Noncopyable types
guard let fd = open(name) else {
return
}
let file = File(descriptor: fd)
file.write(buffer: data) Noncopyable types
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)
}
} C++ Interoperability
struct Person {
Person(const Person&) = delete;
Person(Person &&) = default;
// ...
}; C++ Interoperability
struct Developer: ~Copyable {
let person: Person
init(person: consuming Person) {
self.person = person
}
}
let person = Person()
let developer = Developer(person: person) C++ Interoperability
struct Developer: ~Copyable {
let person: Person
init(person: consuming Person) {
self.person = person
}
}
let person = Person()
let developer = Developer(person: person)
person.printInfo() 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'
} 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'
} Typed throws - any and Never error types
func parse(string: String) throws -> Int {
//...
}
func parse(string: String) throws(any Error) -> Int {
//...
}
func parse(string: String) -> Int {
//...
}
func parse(string: String) throws(Never) -> Int {
//...
} Passing a 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)
}
}
func openAccount(name: String, balance: Double) async {
let client = Client(name: name, balance: balance)
await ClientStore.shared.addClient(client)
} 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
}
}
} Resources
Related sessions
-
22 min -
24 min -
27 min -
15 min -
22 min -
35 min -
22 min -
18 min -
42 min