-
What's new in Swift
Join us for an update on Swift. We'll take you through performance improvements, explore more secure and extensible Swift packages, and share advancements in Swift concurrency. We'll also introduce you to Swift Regex, better generics, and other tools built into the language to help you write more flexible & expressive code.
Recursos
- Celebrating learning experiences from the 2021 Swift Mentorship Program
- Contribute to Swift
- Swift Mentorship Program
- Diversity in Swift
Vídeos relacionados
WWDC22
- Create Swift Package plugins
- Demystify parallelization in Xcode builds
- Design protocol interfaces in Swift
- Eliminate data races using Swift Concurrency
- Embrace Swift generics
- Explore more content with MusicKit
- Improve app size and runtime performance
- Meet distributed actors in Swift
- Meet Swift Async Algorithms
- Meet Swift Package plugins
- Meet Swift Regex
- Swift Regex: Beyond the basics
- Visualize and optimize Swift concurrency
-
Buscar neste vídeo...
-
-
7:19 - Command plugins
@main struct MyPlugin: CommandPlugin { func performCommand(context: PluginContext, arguments: [String]) throws { let process = try Process.run(doccExec, arguments: doccArgs) process.waitUntilExit() } } -
8:34 - Build tool plugins
import PackagePlugin @main struct MyCoolPlugin: BuildToolPlugin { func createBuildCommands(context: TargetBuildContext) throws -> [Command] { // Run some command } } -
8:39 - Implementing a build tool plugin
import PackagePlugin @main struct MyCoolPlugin: BuildToolPlugin { func createBuildCommands(context: TargetBuildContext) throws -> [Command] { let generatedSources = context.pluginWorkDirectory.appending("GeneratedSources") return [ .buildCommand( displayName: "Running MyTool", executable: try context.tool(named: "mycooltool").path, arguments: ["create"], outputFilesDirectory: generatedSources) ] } } -
9:23 - Module disambiguation with module aliases
let package = Package( name: "MyStunningApp", dependencies: [ .package(url: "https://.../swift-metrics.git"), .package(url: "https://.../swift-log.git") ], products: [ .executable(name: "MyStunningApp", targets: ["MyStunningApp"]) ], targets: [ .executableTarget( name: "MyStunningApp", dependencies: [ .product(name: "Logging", package: "swift-log"), .product(name: "Metrics", package: "swift-metrics", moduleAliases: ["Logging": "MetricsLogging"]), ])]) -
9:42 - Distinguishing between modules with the same name
// MyStunningApp import Logging // from swift-log import MetricsLogging // from swift-metrics let swiftLogger = Logging.Logger() let metricsLogger = MetricsLogging.Logger() -
11:09 - Example set of protocols
public protocol NonEmptyProtocol: Collection where Element == C.Element, Index == C.Index { associatedtype C: Collection } public protocol MultiPoint { associatedtype C: CoordinateSystem typealias P = Self.C.P associatedtype X: NonEmptyProtocol where X.C: NonEmptyProtocol, X.Element == Self.P } public protocol CoordinateSystem { associatedtype P: Point where Self.P.C == Self associatedtype S: Size where Self.S.C == Self associatedtype L: Line where Self.L.C == Self associatedtype B: BoundingBox where Self.B.C == Self } public protocol Line: MultiPoint {} public protocol Size { associatedtype C: CoordinateSystem where Self.C.S == Self } public protocol BoundingBox { associatedtype C: CoordinateSystem typealias P = Self.C.P typealias S = Self.C.S } public protocol Point { associatedtype C: CoordinateSystem where Self.C.P == Self } -
13:14 - Memory safety in Swift
var numbers = [3, 2, 1] numbers.removeAll(where: { number in number == numbers.count }) -
14:10 - Thread safety in Swift
var numbers = [3, 2, 1] Task { numbers.append(0) } numbers.removeLast() -
15:54 - A distributed actor player and a distributed function
distributed actor Player { var ai: PlayerBotAI? var gameState: GameState distributed func makeMove() -> GameMove { return ai.decideNextMove(given: &gameState) } } -
16:20 - A distributed actor call
func endOfRound(players: [Player]) async throws { // Have each of the players make their move for player in players { let move = try await player.makeMove() } } -
20:12 - Optional unwrapping
if let mailmapURL = mailmapURL { mailmapLines = try String(contentsOf: mailmapURL).split(separator: "\n") } -
20:29 - Optional unwrapping with long variable names
if let workingDirectoryMailmapURL = workingDirectoryMailmapURL { mailmapLines = try String(contentsOf: workingDirectoryMailmapURL).split(separator: "\n") } -
20:35 - Cryptic abbreviated variable names
if let wdmu = workingDirectoryMailmapURL { mailmapLines = try String(contentsOf: wdmu).split(separator: "\n") } -
20:46 - Unwrapping optionals in Swift 5.7
if let workingDirectoryMailmapURL { mailmapLines = try String(contentsOf: workingDirectoryMailmapURL).split(separator: "\n") } guard let workingDirectoryMailmapURL else { return } mailmapLines = try String(contentsOf: workingDirectoryMailmapURL).split(separator: "\n") -
21:07 - Closure type inference
let entries = mailmapLines.compactMap { line in try? parseLine(line) } func parseLine(_ line: Substring) throws -> MailmapEntry { … } -
21:33 - Type inference for complicated closures
let entries = mailmapLines.compactMap { line in do { return try parseLine(line) } catch { logger.warn("Mailmap error: \(error)") return nil } } func parseLine(_ line: Substring) throws -> MailmapEntry { … } -
22:15 - Mismatches that are harmless in C...
// Mismatches that are harmless in C… int mailmap_get_size(mailmap_t *map); void mailmap_truncate(mailmap_t *map, unsigned *sizeInOut); void remove_duplicates(mailmap_t *map) { int size = mailmap_get_size(map); size -= move_duplicates_to_end(map); mailmap_truncate(map, &size); } // …cause problems in Swift. func removeDuplicates(from map: UnsafeMutablePointer<mailmap_t>) { var size = mailmap_get_size(map) size -= moveDuplicatesToEnd(map) mailmap_truncate(map, &size) } -
22:33 - Better interoperability with C-family code
func removeDuplicates(from map: UnsafeMutablePointer<mailmap_t>) { var size = mailmap_get_size(map) size -= moveDuplicatesToEnd(map) withUnsafeMutablePointer(to: &size) { signedSizePtr in signedSizePtr.withMemoryRebound(to: UInt32.self, capacity: 1) { unsignedSizePtr in mailmap_truncate(map, unsignedSizePtr) } } } -
23:41 - String parsing is hard
func parseLine(_ line: Substring) throws -> MailmapEntry { func trim(_ str: Substring) -> Substring { String(str).trimmingCharacters(in: .whitespacesAndNewlines)[...] } let activeLine = trim(line[..<(line.firstIndex(of: "#") ?? line.endIndex)]) guard let nameEnd = activeLine.firstIndex(of: "<"), let emailEnd = activeLine[nameEnd...].firstIndex(of: ">"), trim(activeLine[activeLine.index(after: emailEnd)...]).isEmpty else { throw MailmapError.badLine } let name = nameEnd == activeLine.startIndex ? nil : trim(activeLine[..<nameEnd]) let email = activeLine[activeLine.index(after: nameEnd)..<emailEnd] return MailmapEntry(name: name, email: email) } -
24:05 - String parsing is still hard with better indexing
func parseLine(_ line: Substring) throws -> MailmapEntry { func trim(_ str: Substring) -> Substring { String(str).trimmingCharacters(in: .whitespacesAndNewlines)[...] } let activeLine = trim(line[..<(line.firstIndex(of: "#") ?? line.endIndex)]) guard let nameEnd = activeLine.firstIndex(of: "<"), let emailEnd = activeLine[nameEnd...].firstIndex(of: ">"), trim(activeLine[(emailEnd + 1)...]).isEmpty else { throw MailmapError.badLine } let name = nameEnd == activeLine.startIndex ? nil : trim(activeLine[..<nameEnd]) let email = activeLine[(nameEnd + 1)..<emailEnd] return MailmapEntry(name: name, email: email) } -
24:20 - What's the problem?
let line = "Becca Royal-Gordon <beccarg@apple.com> # Comment" func parseLine(_ line: Substring) throws -> MailmapEntry { func trim(_ str: Substring) -> Substring { String(str).trimmingCharacters(in: .whitespacesAndNewlines)[...] } let activeLine = trim(line[..<(line.firstIndex(of: "#") ?? line.endIndex)]) guard let nameEnd = activeLine.firstIndex(of: "<"), let emailEnd = activeLine[nameEnd...].firstIndex(of: ">"), trim(activeLine[activeLine.index(after: emailEnd)...]).isEmpty else { throw MailmapError.badLine } let name = nameEnd == activeLine.startIndex ? nil : trim(activeLine[..<nameEnd]) let email = activeLine[activeLine.index(after: nameEnd)..<emailEnd] return MailmapEntry(name: name, email: email) } -
24:55 - Drawing a picture
"Becca Royal-Gordon <beccarg@apple.com> # Comment" / space name space < email > space # or EOL / / \h* ( [^<#]+? )?? \h* < ( [^>#]+ ) > \h* (?: #|\Z) / -
25:10 - Swift Regex using a literal
func parseLine(_ line: Substring) throws -> MailmapEntry { let regex = /\h*([^<#]+?)??\h*<([^>#]+)>\h*(?:#|\Z)/ guard let match = line.prefixMatch(of: regex) else { throw MailmapError.badLine } return MailmapEntry(name: match.1, email: match.2) } -
25:46 - Did a cat walk across your keyboard?
/\h*([^<#]+?)??\h*<([^>#]+)>\h*(?:#|\Z)/ -
26:34 - Regex builder
import RegexBuilder let regex = Regex { ZeroOrMore(.horizontalWhitespace) Optionally { Capture(OneOrMore(.noneOf("<#"))) } .repetitionBehavior(.reluctant) ZeroOrMore(.horizontalWhitespace) "<" Capture(OneOrMore(.noneOf(">#"))) ">" ZeroOrMore(.horizontalWhitespace) ChoiceOf { "#" Anchor.endOfSubjectBeforeNewline } } -
27:05 - Turn a regex into a reusable component
struct MailmapLine: RegexComponent { @RegexComponentBuilder var regex: Regex<(Substring, Substring?, Substring)> { ZeroOrMore(.horizontalWhitespace) Optionally { Capture(OneOrMore(.noneOf("<#"))) } .repetitionBehavior(.reluctant) ZeroOrMore(.horizontalWhitespace) "<" Capture(OneOrMore(.noneOf(">#"))) ">" ZeroOrMore(.horizontalWhitespace) ChoiceOf { "#" Anchor.endOfSubjectBeforeNewline } } } -
27:30 - Use regex literals within a builder
struct MailmapLine: RegexComponent { @RegexComponentBuilder var regex: Regex<(Substring, Substring?, Substring)> { ZeroOrMore(.horizontalWhitespace) Optionally { Capture(OneOrMore(.noneOf("<#"))) } .repetitionBehavior(.reluctant) ZeroOrMore(.horizontalWhitespace) "<" Capture(OneOrMore(.noneOf(">#"))) ">" ZeroOrMore(.horizontalWhitespace) /#|\Z/ } } -
27:39 - Use Date parsers within Regex builders
struct DatedMailmapLine: RegexComponent { @RegexComponentBuilder var regex: Regex<(Substring, Substring?, Substring, Date)> { ZeroOrMore(.horizontalWhitespace) Optionally { Capture(OneOrMore(.noneOf("<#"))) } .repetitionBehavior(.reluctant) ZeroOrMore(.horizontalWhitespace) "<" Capture(OneOrMore(.noneOf(">#"))) ">" ZeroOrMore(.horizontalWhitespace) Capture(.iso8601.year().month().day()) ZeroOrMore(.horizontalWhitespace) /#|\Z/ } } -
27:49 - Matching methods and strongly type captures in Regex
func parseLine(_ line: Substring) throws -> MailmapEntry { let regex = /\h*([^<#]+?)??\h*<([^>#]+)>\h*(?:#|\Z)/ // or let regex = MailmapLine() guard let match = line.prefixMatch(of: regex) else { throw MailmapError.badLine } return MailmapEntry(name: match.1, email: match.2) } -
29:02 - A use case for protocols
/// Used in the commit list UI struct HashedMailmap { var replacementNames: [String: String] = [:] } /// Used in the mailmap editor UI struct OrderedMailmap { var entries: [MailmapEntry] = [] } protocol Mailmap { mutating func addEntry(_ entry: MailmapEntry) } extension HashedMailmap: Mailmap { … } extension OrderedMailmap: Mailmap { … } -
29:26 - Using the Mailmap protocol
func addEntries1<Map: Mailmap>(_ entries: Array<MailmapEntry>, to mailmap: inout Map) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: Array<MailmapEntry>, to mailmap: inout Mailmap) { for entry in entries { mailmap.addEntry(entry) } } -
31:05 - `Mailmap` and `any Mailmap`
func addEntries1<Map: Mailmap>(_ entries: Array<MailmapEntry>, to mailmap: inout Map) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: Array<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } } -
31:17 - Improvements to `any` types
extension Mailmap { mutating func mergeEntries<Other: Mailmap>(from other: Other) { … } } func mergeMailmaps(_ a: any Mailmap, _ b: any Mailmap) -> any Mailmap { var copy = a copy.mergeEntries(from: b) return a } -
32:21 - More improvements to `any` types
protocol Mailmap: Equatable { mutating func addEntry(_ entry: MailmapEntry) } func addEntries2(_ entries: Array<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } } -
32:54 - Using Collection as an `any` type
protocol Mailmap: Equatable { mutating func addEntry(_ entry: MailmapEntry) } func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } } -
33:04 - Primary associated types
protocol Collection<Element>: Sequence { associatedtype Index: Comparable associatedtype Iterator: IteratorProtocol<Element> associatedtype SubSequence: Collection<Element> where SubSequence.Index == Index, SubSequence.SubSequence == SubSequence associatedtype Element } -
33:42 - Using primary associated types in Collection
func addEntries1<Entries: Collection<MailmapEntry>, Map: Mailmap>(_ entries: Entries, to mailmap: inout Map) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } } extension Collection<MailmapEntry> { … } -
34:35 - Example of type erasing wrappers
struct AnySprocket: Sprocket { private class Base { … } private class Box<S: Sprocket>: Base { … } private var box: Base // …dozens of lines of code you hate // having to maintain… } -
34:38 - Replace boxes with built-in `any` types
struct AnySprocket: Sprocket { private var box: any Sprocket // …fewer lines of code you hate // having to maintain… } -
34:44 - Or try type aliases
typealias AnySprocket = any Sprocket -
35:09 - `any` types have important limitations
protocol Mailmap: Equatable { mutating func addEntry(_ entry: MailmapEntry) } func areMailmapsIdentical(_ a: any Mailmap, _ b: any Mailmap) -> Bool { return a == b } -
35:44 - Using generic types vs. `any` types
func addEntries1<Entries: Collection<MailmapEntry>, Map: Mailmap>(_ entries: Entries, to mailmap: inout Map) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } } -
36:40 - `some Mailmap` and `any Mailmap`
func addEntries1<Entries: Collection<MailmapEntry>>(_ entries: Entries, to mailmap: inout some Mailmap) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } } -
36:50 - `some Mailmap` and `any Mailmap` with Collection and primary associated types
func addEntries1(_ entries: some Collection<MailmapEntry>, to mailmap: inout some Mailmap) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } }
-