WWDC Quick Look 💓 By SwiftGGTeam
What's new in Swift

What's new in Swift

元の動画を見る

ハイライト

Swift 5.7 は、パッケージ マネージャー プラグイン、分散アクター、ネイティブ正規表現、簡素化された汎用構文、C 言語の相互運用性の向上など、多くの新機能をもたらし、docC および Swift.org Web サイトは正式にオープン ソースです。

主要内容

パッケージ マネージャー プラグイン: ビルド プロセスを自動化する

プロジェクトには、コードの生成、ドキュメント ツールの実行、ソース コードのフォーマットなど、繰り返しのタスクが常に存在します。以前は、これらはスクリプトまたは Xcode のビルド フェーズのいずれかに依存していたため、メンテナンスが非常に分散していました。 Swift 5.7 では、SPM に 2 つのプラグインが追加されています。コマンド プラグインは Makefile ターゲットのようなもので、コマンド ラインから呼び出すことができます。ビルド ツール プラグインはより強力で、コンパイルの前後に自動的に実行できます。同時に、同じ名前によるモジュールの競合の問題を解決するために、モジュール エイリアスも導入されました。

Swift の同時実行性: Swift 6 に向けて

Swift 5.7 は、同時実行の安全性において大きな一歩を踏み出しました。コンパイラは、クロージャ内の可変状態のキャプチャやタスク間のデータ競合など、より多くのデータ競合シナリオを検出できるようになりました。分散アクター (分散アクター) は、最も重要な新しい同時実行機能です。アクター モデルを単一のプロセスから複数のプロセスに拡張します。リモート アクターを呼び出すための構文は、ローカル アクターを呼び出す場合とまったく同じです。

詳細

パッケージ マネージャー プラグイン: ビルド プロセスを自動化する

(07:19) Swift 5.7 では、SPM に 2 つのプラグインが追加されます。コマンド プラグインは、コマンド ラインから呼び出すことができる Makefile ターゲットのようなものです。

import PackagePlugin

@main struct MyPlugin: CommandPlugin {
    func performCommand(context: PluginContext, arguments: [String]) throws {
        let doccExec = try context.tool(named: "docc").path
        let doccArgs = ["convert", "--output-path", "docs"]
        let process = try Process.run(doccExec, arguments: doccArgs)
        process.waitUntilExit()
    }
}

キーポイント:

  • CommandPluginコマンドラインからトリガーされ、ドキュメントの生成やコードのフォーマットなどのタスクに適しています
  • context.tool(named:)ハードコーディングなしでツールパスを取得

ビルド ツール プラグインはより強力で、コンパイルの前後に自動的に実行できます。

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
            )
        ]
    }
}

キーポイント:

  • BuildToolPluginコンパイルプロセス中に自動的に実行されます
  • outputFilesDirectory生成されたファイルを宣言すると、Xcode がそれらを自動的にコンパイルに追加します。

(09:23) 長期的な問題点もあります。それは、2 つの依存パッケージが同じ名前のモジュールをエクスポートしていることです。 Swift 5.7 のモジュール エイリアスは、この問題を解決します。

let package = Package(
    name: "MyStunningApp",
    dependencies: [
        .package(url: "https://.../swift-metrics.git"),
        .package(url: "https://.../swift-log.git")
    ],
    targets: [
        .executableTarget(
            name: "MyStunningApp",
            dependencies: [
                .product(name: "Logging", package: "swift-log"),
                .product(name: "Metrics", package: "swift-metrics",
                         moduleAliases: ["Logging": "MetricsLogging"]),
            ])
    ]
)

2 つのモジュール名を使用しても競合しなくなりました。

import Logging           // from swift-log
import MetricsLogging    // from swift-metrics

let swiftLogger = Logging.Logger()
let metricsLogger = MetricsLogging.Logger()

Swift の同時実行性: Swift 6 に向けて

(13:14) Swift 5.7 は同時実行の安全性において大きな一歩を踏み出しました。コンパイラは、より多くのデータ競合シナリオを検出できるようになりました。

var numbers = [3, 2, 1]

numbers.removeAll(where: { number in
    number == numbers.count  // 编译器警告:在闭包中捕获了可变状态
})

(14:10) クロスタスクのデータ競合もキャプチャされます。

var numbers = [3, 2, 1]

Task { numbers.append(0) }  // 在另一个任务中修改
numbers.removeLast()         // 编译器警告:数据竞争

(15:54) 分散アクターは、Swift 5.7 の最も重要な新しい同時実行機能の 1 つです。アクター モデルを単一プロセスからマルチプロセスに拡張します。

import Distributed

distributed actor Player {
    var ai: PlayerBotAI?
    var gameState: GameState

    distributed func makeMove() -> GameMove {
        return ai.decideNextMove(given: &gameState)
    }
}

キーポイント:

  • distributed actorリモートに存在する可能性のあるアクターを宣言する
  • distributed funcネットワーク経由で呼び出すことができるメソッドにマークを付ける
  • パラメータと戻り値はシリアル化要件を満たす必要があります (デフォルトでは Codable)

リモート アクターを呼び出すための構文は、ローカル アクターを呼び出す場合とまったく同じです。

func endOfRound(players: [Player]) async throws {
    for player in players {
        let move = try await player.makeMove()
    }
}

言語の簡略化: よりクリーンな構文

(20:12) Swift 5.7 では、オプションのアンラップが簡素化されています。以前は次のように書かなければなりませんでした。

if let mailmapURL = mailmapURL {
    mailmapLines = try String(contentsOf: mailmapURL).split(separator: "\n")
}

変数名は短いと読みにくくなりますが、長いと冗長になります。 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) クロージャ型推論も改善されました。以前は複雑なクロージャには明示的な型アノテーションが必要でしたが、現在はコンパイラがコンテキストからそれを推論できるようになりました。

let entries = mailmapLines.compactMap { line in
    try? parseLine(line)
}

func parseLine(_ line: Substring) throws -> MailmapEntry { }

(22:15) C 言語の相互運用性の観点から、Swift 5.7 ではポインタ型変換が改善されています。

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)
        }
    }
}

キーポイント:

  • withMemoryRebound無事にIntそしてUInt32ポインタを次の間で変換します。
  • メモリレイアウトを手動で管理する必要はもうありません

Swift Regex: 文字列処理の新しい方法

(23:41) 文字列の解析は、Swift において常に問題点でした。以前に使用したStringインデックス作成操作は冗長であり、エラーが発生しやすくなります。

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)
}

(25:10) Swift 5.7 ではネイティブ正規表現が導入され、リテラルで厳密に型指定されたキャプチャがサポートされています。

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)
}

キーポイント:

  • /.../正規表現リテラルである場合、コンパイラは構文をチェックします
  • match.1そしてmatch.2は厳密に型指定されたキャプチャであり、型はSubstring

(26:34) より複雑なシナリオの場合、正規表現ビルダーは宣言構文を提供します。

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) Regex コンポーネントは再利用でき、Foundation のパーサーに埋め込むこともできます。

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/
    }
}

キーポイント:

  • RegexComponentプロトコルを使用すると、一般的なパターンを再利用可能なコンポーネントにカプセル化できます。
  • .iso8601.year().month().day()Foundation の日付パーサーに直接埋め込む
  • キャプチャの種類は次のとおりです。Date、文字列ではありません

簡略化された汎用構文

(29:02) Swift 5.7 では、汎用コードをより自然に記述できるようになりました。以前は、プロトコルをタイプとして使用する場合、多くの制限がありました。

// 以前:只能用 generic 函数
func addEntries1<Map: Mailmap>(_ entries: Array<MailmapEntry>, to mailmap: inout Map)

// 以前:protocol 不能直接作为类型
func addEntries2(_ entries: Array<MailmapEntry>, to mailmap: inout Mailmap)  // 编译错误

(31:05) Swift 5.7 では明示的な導入が行われていますany文法:

func addEntries2(_ entries: Array<MailmapEntry>, to mailmap: inout any Mailmap) {
    for entry in entries {
        mailmap.addEntry(entry)
    }
}

キーポイント:

  • any Mailmapこれが実存型であることを明確にする
  • タイプが関連付けられたプロトコルは、次のようにも使用できます。anyタイプの使用法

(32:54) プライマリ関連タイプにより、制約式がより簡潔になります。

// 以前
func addEntries1<Entries: Collection, Map: Mailmap>(_ entries: Entries, to mailmap: inout Map)
    where Entries.Element == MailmapEntry

// Swift 5.7
func addEntries1(_ entries: some Collection<MailmapEntry>, to mailmap: inout some Mailmap)

// 或者用 any
func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap)

キーポイント:

  • some Collection<MailmapEntry>に相当some Collection where Element == MailmapEntry
  • any異種ストレージを必要とするシナリオで使用されます。some具体的なタイプを非表示にするために使用されます

重要ポイント

1. SPM プラグインを使用してプロジェクト ワークフローを自動化

コード生成、ドキュメント構築、書式チェック、その他のタスクを SPM プラグインとして記述します。コマンド プラグインは CI/CD およびローカル開発コマンドに適しており、ビルド ツール プラグインはコード生成に適しています。プロジェクト内のどのシェル スクリプトを移行できるかを確認してください。

入り口:PackagePluginフレーム、CommandPlugin / BuildToolPluginプロトコル

2.文字列解析コードを Regex Builder で置き換えます

すべてプロジェクトで使用されていますString.index手動による解析が必要な場合は、Regex または Regex Builder で置き換えることができるかどうかを確認する価値があります。特に、ログの解析、構成ファイルの読み取り、データ形式の変換などのシナリオです。

入り口:RegexBuilderモジュール、RegexComponentプロトコル

3. Swift 6 の厳密な同時実行チェックに備える

コンパイラの厳密な同時実行チェックをオンにして、型に徐々に注釈を付けますSendable。分散アクターは、プロセス間通信にタイプセーフな抽象化を提供し、ゲームやコラボレーション ツールなど、リアルタイム通信を必要とするアプリに適しています。

入り口:-strict-concurrency=completeSendableプロトコル、distributed actor

4.既存の汎用コードを簡素化

プロジェクト内の手書きのタイプ消去ラッパーを確認してください (AnyPublisherAnyCollectionカスタマイズされたバージョンを待ちます) を直接置き換えることができるかどうかを確認してください。any文法。新しいsome+ メインの関連付けタイプの記述方法により、API 署名がより短く明確になります。

入り口:any ProtocolName<AssociatedType>some ProtocolName<AssociatedType>

関連セッション

コメント

GitHub Issues · utterances