Highlight
Swift 5.5 introduces async/await, structured concurrency, and actors, and complements the development experience of package ecology, documentation, build performance, and daily syntax through tools such as Swift Collections, Swift Algorithms, Swift System, and DocC.
Core Content
The pain point of many Swift projects is not in the business code, but in the peripheral code.
To find a third-party package, first search the web and then copy the Git URL to Xcode. The standard library does not have double-ended queues, ordered sets, or ordered dictionaries. The team can only write them themselves or introduce implementations from different sources. When making system calls, the low-level interface is close to C, and the type safety and cross-platform experience are not natural enough.
Asynchronous code is more typical. A network request will be split intodataTask, completion handler, error parameters andresume(). The real business process is hidden in the closure. do you want to writetry/catch, but you have to stuff every error into a callback.
Swift 5.5 does two things.
First, Apple has expanded the Swift ecosystem. Xcode 13 supports Swift Package Collections, and Swift’s official open source package adds new capabilities such as Collections, Algorithms, System, and Numerics. DocC enters Xcode and uses Markdown annotations to generate framework documents. Swift Driver becomes the default compilation driver, and incremental import reduces the scope of recompilation after changing modules.
Second, the Swift language adds a new concurrency model.asyncIndicates that the function can be suspended,awaitMark the hanging point,async letLet related tasks run in parallel,actorUse the compiler and runtime to coordinate access to shared mutable state.
The results are straightforward. When writing network requests, the code is executed from top to bottom. When writing parallel rendering, subtasks will not escape the parent function. When writing statistical counters, the actor queues concurrent access to prevent multiple threads from modifying the same value at the same time.
Detailed Content
Swift Collections: Common data structures outside the standard library
(06:16) Swift Collections is a new open source package that supplements the missing data structures of the standard library. First edition containsDeque、OrderedSet、OrderedDictionary。
DequeLike an array, but supports efficient insertion and deletion at both ends.
import Collections
var colors: Deque = ["red", "yellow", "blue"]
colors.prepend("green")
colors.append("orange")
// `colors` is now ["green", "red", "yellow", "blue", “orange"]
colors.popFirst() // "green"
colors.popLast() // "orange"
// `colors` is back to ["red", "yellow", "blue"]
Key points:
import CollectionsIntroducing the Swift Collections package. -var colors: Deque = [...]Create a deque. -colors.prepend("green")Insert element at the beginning of the queue. -colors.append("orange")Insert element at the end of the queue. -colors.popFirst()Removes and returns the element from the head of the queue. -colors.popLast()Removes and returns the element from the end of the queue.
(06:25)OrderedSetWhile preserving order and uniqueness. Like an array, it supports subscript access, and like a set, it guarantees that elements appear only once.
import Collections
var buildingMaterials: OrderedSet = ["straw", "sticks", "bricks"]
for i in 0 ..< buildingMaterials.count {
print("Little piggie #\(i) built a house of \(buildingMaterials[i])")
}
// Little piggie #0 built a house of straw
// Little piggie #1 built a house of sticks
// Little piggie #2 built a house of bricks
buildingMaterials.append("straw") // (inserted: false, index: 0)
Key points:
OrderedSetSave insertion order. -buildingMaterials.countReturns the number of elements. -buildingMaterials[i]Read elements sequentially. -append("straw")Try inserting an existing element.- in the return value
inserted: falseIndicates that no new elements are inserted,index: 0Indicates the existing element position.
(06:42)OrderedDictionarySuitable for scenarios where a stable order of key-value pairs is required, such as displaying a list of HTTP status codes.
import Collections
var responses: OrderedDictionary = [200: "OK", 403: "Forbidden", 404: "Not Found"]
for (code, phrase) in responses {
print("\(code) (\(phrase))")
}
// 200 (OK)
// 403 (Forbidden)
// 404 (Not Found)
Key points:
OrderedDictionarySave the order of key-value pairs.- literal
[200: "OK", ...]Create initial content. -for (code, phrase) in responsesIterate over the keys and values in the order they were saved. -printOutput status code and reason phrase.
Swift Algorithms and Swift System: Make common algorithms and system calls into Swift API
(07:39) Swift Algorithms is a new open source package that provides Sequence and Collection algorithms. The speech mentioned that more than 40 algorithms have been added, including combination, permutation, grouping, minimum value, maximum value, and random sampling.
import Algorithms
let testAccounts = [ ... ]
for testGroup in testAccounts.uniquePermutations(ofCount: 0...) {
try validate(testGroup)
}
let randomGroup = testAccounts.randomSample(count: 5)
Key points:
import AlgorithmsIntroducing the Swift Algorithms package. -testAccounts.uniquePermutations(ofCount: 0...)Generates unique permutations of a specified range of numbers. -for testGroup in ...Verify the generated test combinations one by one. -try validate(testGroup)Indicates that the verification process may have thrown an error. -randomSample(count: 5)Pick 5 elements at random from the set.
(07:52) Swift System provides a low-level system call interface that is more in line with Swift habits. It supports Apple platforms, Linux and Windows.
import System
let fd: FileDescriptor = try .open(
"/tmp/a.txt", .writeOnly,
options: [.create, .truncate], permissions: .ownerReadWrite)
try fd.closeAfter {
try fd.writeAll("Hello, WWDC!\n".utf8)
}
Key points:
import SystemIntroducing the Swift System package. -FileDescriptorUse types to represent file descriptors. -.open("/tmp/a.txt", .writeOnly, ...)Open the file for writing only. -options: [.create, .truncate]If it does not exist, create it; if it exists, it will truncate it. -permissions: .ownerReadWriteSet owner read and write permissions. -fd.closeAfter { ... }Close the file descriptor after the closure has been executed. -fd.writeAll("Hello, WWDC!\n".utf8)Write UTF-8 bytes.
(08:06)FilePathA new path operation API is added, which can query and set extensions, add components, delete components, and perform path normalization.
import System
var path: FilePath = "/tmp/WWDC2021.txt"
print(path.lastComponent) // "WWDC2021.txt"
print(path.extension) // "txt"
path.extension = "pdf" // path == "/tmp/WWDC2021.pdf"
path.extension = nil // path == "/tmp/WWDC2021"
print(path.extension) // nil
path.push("../foo/bar/./") // path == "/tmp/wwdc2021/../foo/bar/."
path.lexicallyNormalize() // path == "/tmp/foo/bar"
print(path.ends(with: "foo/bar")) // true!
Key points:
FilePathRepresent file paths using Swift types. -path.lastComponentRead the last path component. -path.extensionRead extension. -path.extension = "pdf"Modify the extension. -path.extension = nilRemove the extension. -path.push("../foo/bar/./")Append path components. -path.lexicallyNormalize()Standardize..and.。path.ends(with: "foo/bar")Check the path end.
Swift 5.5 syntax improvements: Codable, static member lookup, parameter property wrappers
(17:04) In the past, enumerations with associated values were implementedCodable, need to be written by handinit(from:)、encode(to:)and multiple groupsCodingKey. Swift 5.5 can be synthesized by the compiler.
enum Command: Codable {
case load(key: String)
case store(key: String, value: Int)
}
Key points:
enum CommandDefine the command type. -: CodableDeclare that it supports encoding and decoding. -case load(key: String)With an associated value. -case store(key: String, value: Int)With two associated values.- The compiler generates encoding and decoding logic for these cases.
(17:26) Type inference allows the type name to be omitted when calling enumeration values. Swift 5.5 expands the static member lookup capabilities so that static properties on the protocol can also form an enumeration-like API.
enum Coffee {
case regular
case decaf
}
func brew(_ coffee: Coffee) { ... }
brew(.regular)
Key points:
enum CoffeeDefine types of coffee. -case regularandcase decafIs an enumeration member. -func brew(_ coffee: Coffee)The parameter type is explicitlyCoffee。brew(.regular)Omit with type inferenceCoffee.regular。
(18:25) Property wrapper (Property Wrapper) was originally commonly used for properties. Swift 5.5 allows this to be used with function parameters and closure parameters via SE-0293.
@propertyWrapper
struct NonEmpty<Value: Collection> {
init(wrappedValue: Value) {
precondition(!wrappedValue.isEmpty)
self.wrappedValue = wrappedValue
}
var wrappedValue: Value {
willSet { precondition(!newValue.isEmpty) }
}
}
func logIn(@NonEmpty _ username: String) {
print("Logging in: \(username)")
}
Key points:
@propertyWrapperstatementNonEmptyis a property wrapper. -Value: CollectionRestrictions that the wrapped value must be a collection type. -init(wrappedValue:)Check that the collection is not empty during initialization. -precondition(!wrappedValue.isEmpty)Triggers a runtime check failure when a null value is passed in. -wrappedValueSave the true value. -willSetCheck that the new value is not empty before modifying it. -func logIn(@NonEmpty _ username: String)Apply the same set of constraints to function parameters.
SwiftUI code shortening: bound array, dot syntax, CGFloat conversion
(19:37) The talk uses a settings list to show the impact of Swift 5.5 on daily SwiftUI code. In the past, you had to use subscripts to access the binding in the array, and you also had to explicitly writeCheckboxToggleStyle()andCGFloat(padding). Binding arrays can now be passed in directly.
// You can now write this.
import SwiftUI
struct SettingsView: View {
@State var settings: [Setting]
private let padding = 10.0
var body: some View {
List($settings) { $setting in
Toggle(setting.displayName, isOn: $setting.isOn)
#if os(macOS)
.toggleStyle(.checkbox)
#else
.toggleStyle(.switch)
#endif
}
.padding(padding)
}
}
Key points:
@State var settings: [Setting]Saves an array of mutable settings. -List($settings)Pass the projection binding of the settings array to the list. -{ $setting in ... }The closure obtains the binding of each element directly. -Toggle(setting.displayName, isOn: $setting.isOn)At the same time, read the display name and bind the switch status. -#if os(macOS)Let the same piece of code select styles by platform. -.toggleStyle(.checkbox)Use simplified static member dot syntax. -.padding(padding)Depends on the compilerDoubleandCGFloatConvert between.
async/await: Change callback network requests into sequential code
(22:20) Used beforeURLSession.dataTaskWhen making a request, the function will return first, and the real data processing occurs in the closure. Errors are also passed via additional parameters.
// Instead of writing this...
func fetchImage(id: String, completion: (UIImage?, Error?) -> Void) {
let request = self.imageURLRequest(for: id)
let task = URLSession.shared.dataTask(with: request) {
data, urlResponse, error in
if let error = error {
completion(nil, error)
} else if let httpResponse = urlResponse as? HTTPURLResponse,
httpResponse.statusCode != 200 {
completion(nil, MyTransferError())
} else if let data = data, let image = UIImage(data: data) {
completion(image, nil)
} else {
completion(nil, MyOtherError())
}
}
task.resume()
}
Key points:
completion: (UIImage?, Error?) -> VoidUse callbacks to return success values or errors. -URLSession.shared.dataTask(with: request)Create an asynchronous task.- The closure is received after the request is completed
data、urlResponse、error. - Called for every error branch
completion(nil, ...). - Successful branch call
completion(image, nil)。 task.resume()Start the task.
(24:40) Swift 5.5URLSession.shared.data(for:)can be withtry awaitused together. function signature declarationasync throws, the error enters Swift’s original throwing mechanism.
// You can now write this.
func fetchImage(id: String) async throws -> UIImage {
let request = self.imageURLRequest(for: id)
let (data, response) = try await URLSession.shared.data(for: request)
if let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode != 200 {
throw TransferFailure()
}
guard let image = UIImage(data: data) else {
throw ImageDecodingFailure()
}
return image
}
Key points:
async throws -> UIImageIndicates that the function will hang or throw an error, and return the picture when successful. -let request = self.imageURLRequest(for: id)Construct the request. -try await URLSession.shared.data(for: request)Make a request and wait for the result. -let (data, response)Deconstruct the returned data and response metadata. -HTTPURLResponseBranch check status code. -throw TransferFailure()Handle transmission errors to the caller. -guard let image = UIImage(data: data)Decode the picture. -return imageReturn the final result.
Structured concurrency: let parallel tasks be constrained by parent functions
(27:06) Some tasks can be executed in parallel. In the example, the background image and foreground image can be rendered at the same time, the title image can be rendered separately, and finally the three results can be merged.
func titleImage() async throws -> Image {
async let background = renderBackground()
async let foreground = renderForeground()
let title = try renderTitle()
return try await merge(background,
foreground,
title)
}
Key points:
func titleImage() async throws -> ImageDeclare functions to support suspending and throwing errors. -async let background = renderBackground()Start the background image rendering task. -async let foreground = renderForeground()Start the foreground rendering task. -let title = try renderTitle()Generate a title map in the current process. -try await merge(...)Wait for parallel results to be ready before merging.- two
async letSubtasks cannot exceedtitleImage()life cycle. - When an error is thrown within a function, the runtime will wait for unfinished tasks and signal early completion to them.
Actor: Put shared mutable state into quarantine
(29:26) Multiple threads modify the same counter at the same time, and the result may be corrupted. Swift 5.5 actors let this kind of state be managed by a concurrency-safe reference type.
actor Statistics {
private var counter: Int = 0
func increment() {
counter += 1
}
func publish() async {
await sendResults(counter)
}
}
var statistics = Statistics()
await statistics.increment()
Key points:
actor StatisticsDefine an actor. -private var counter: Int = 0Saves protected mutable state. -func increment()Modify the counter inside the actor. -func publish() asyncThe statement may hang while publishing results. -await sendResults(counter)Wait for network and other asynchronous operations to complete. -var statistics = Statistics()Create an actor instance. -await statistics.increment()Used when calling methods from outside the actorawait.- Actor suspends calls before operations that may cause data corruption and waits until it is safe to execute them.
Core Takeaways
-
What to do: Create an async/await network loading layer for the image list. Why it’s worth doing:
URLSession.shared.data(for:)Let the request, status code check, and picture decoding be written in order, and errors will go awaythrows. How to start: Change the old completion handler tofunc fetchImage(id:) async throws -> UIImage, used in the view modeltry awaitcall. -
What to do: Maintain an ordered, deduplicated list for chat, notification or log pages. Why it’s worth doing:
OrderedSetPreserve the order and ensure that the elements are unique, suitable for data that is “displayed in order of arrival, but not repeated”. How to start: IntroductionCollections,useOrderedSetStore the message ID or notification ID, and then render the list in subscript order. -
What to do: Generate a more systematic account combination for the test system. Why it’s worth doing: Swift Algorithms provides set algorithms such as permutation, combination, and random sampling to reduce handwriting cycles. How to start: Introduction
Algorithms,useuniquePermutations(ofCount:)To enumerate test account combinations, userandomSample(count:)Do sampling verification. -
What to do: Encapsulate the point count, download progress, and synchronization status into actors. Why it’s worth doing: actor is a variable state suitable for simultaneous access by multiple tasks, in the speech
StatisticsThis is the counter example. How to start: Put the originalclass StatisticsChange toactor Statistics, set the variable attribute toprivate, add when calling from outsideawait。 -
What: Generate publishable documentation for internal tools or Swift packages. Why it’s worth doing: DocC is integrated in Xcode 13 and uses Markdown comments in Swift source code to generate documentation. How to start: First add Markdown comments to the public type, and then use Xcode’s DocC workflow to generate a document archive.
Related Sessions
- Meet async/await in Swift — explained from the perspective of syntax and control flow
async、await、throwscombination. - Explore structured concurrency in Swift — Expand
async let, task groups, cancellation and parent-child task relationships. - Protect mutable state with Swift actors — Explain how actors isolate mutable state and avoid data races.
- Swift concurrency: Update a sample app — Use a sample app to demonstrate the concurrency model migration path.
- Meet the Swift Algorithms and Collections packages — An in-depth introduction to the data structure of Collections and the sequence algorithm of Algorithms.
Comments
GitHub Issues · utterances