Slide 1

Slide 1 text

App Intentsのビルドプロセス を支える技術 @kntk iOSDC2026 #iosdc #b 2026/09/11

Slide 2

Slide 2 text

はじめに •App Intentsフレームワーク • アプリをSiriやShortcutアプリに統合 • Apple Intelligence連携機能によって再注目

Slide 3

Slide 3 text

はじめに •App Intentsフレームワーク import AppIntents struct SomeIntent: AppIntent { static let title: LocalizedStringResource = "Some Intent" static let description = IntentDescription("A plain demo intent.") } func perform() async throws -> some IntentResult { // do something return .result() }

Slide 4

Slide 4 text

はじめに •ビルドするだけでシステムが認識 import AppIntents struct SomeIntent: AppIntent { static let title: LocalizedStringResource = "Some Intent" static let description = IntentDescription("A plain demo intent.") } func perform() async throws -> some IntentResult { // do something return .result() }

Slide 5

Slide 5 text

はじめに •ビルドするだけでシステムが認識 import AppIntents struct SomeIntent: AppIntent { static let title: LocalizedStringResource = "Some Intent" static let description = IntentDescription("A plain demo intent.") } func perform() async throws -> some IntentResult { // do something return .result() } 静的解析でメタデータを生成しアプリに同封

Slide 6

Slide 6 text

はじめに: この発表で話すこと •App Intentsのビルドプロセス •静的解析でメタデータを生成しアプリに同封 •支える技術

Slide 7

Slide 7 text

はじめに: この発表で話すこと •App Intentsのビルドプロセス •静的解析でメタデータを生成しアプリに同封 •支える技術 動作原理を理解し、開発中の問題解決を効率化

Slide 8

Slide 8 text

ビルドプロセス概要

Slide 9

Slide 9 text

ビルドプロセス概要 .xcodeproj .app Xcode exec *.swift Info.plist Swift Build llbuild その他ツール swiftc

Slide 10

Slide 10 text

ビルドプロセス概要 .xcodeproj Xcode Project管理 Swift Build ビルド計画 llbuild 計画の実行 swiftc コンパイル等 *.swift その他ツール

Slide 11

Slide 11 text

ビルドプロセス概要: App Intents .xcodeproj .app Xcode exec *.swift Info.plist Swift Build llbuild その他ツール swiftc

Slide 12

Slide 12 text

ビルドプロセス概要: App Intents .xcodeproj .app Xcode exec *.swift Info.plist Swift Build llbuild appintentsmetadataprocessor swiftc

Slide 13

Slide 13 text

ビルドプロセス概要: App Intents .xcodeproj .app Xcode exec *.swift Info.plist Swift Build Metadata.appintents appintentsmetadataprocessor llbuild swiftc

Slide 14

Slide 14 text

ビルドプロセス概要: App Intents .xcodeproj .app Xcode exec *.swift Info.plist Swift Build Metadata.appintents appintentsmetadataprocessor .swiftconstvalues llbuild swiftc

Slide 15

Slide 15 text

ビルドプロセス概要: App Intents .xcodeproj .app Xcode exec *.swift Info.plist Swift Build llbuild appintentsmetadataprocessor swiftc Metadata.appintents

Slide 16

Slide 16 text

ビルドプロセス概要: App Intents デバイス .app exec Info.plist Metadata.appintents

Slide 17

Slide 17 text

ビルドプロセス概要: App Intents デバイス .app exec Siri Info.plist Metadata.appintents ショートカット ランタイムでアプリが持つApp Intentを認識

Slide 18

Slide 18 text

ビルドプロセス概要: App Intents .xcodeproj .app Xcode exec *.swift Info.plist Swift Build Metadata.appintents appintentsmetadataprocessor .swiftconstvalues llbuild swiftc Metadata.appintents

Slide 19

Slide 19 text

swiftc Const Extract .xcodeproj .app Xcode exec *.swift Info.plist Swift Build Metadata.appintents appintentsmetadataprocessor .swiftconstvalues llbuild swiftc Metadata.appintents

Slide 20

Slide 20 text

swiftc Const Extract •コードから型情報を抽出する仕組み

Slide 21

Slide 21 text

swiftc Const Extract •コードから型情報を抽出する仕組み swiftc -c Source.swift \ -Xfrontend -emit-const-values-path -Xfrontend X.swiftconstvalues \ -Xfrontend -const-gather-protocols-file -Xfrontend protocols.json

Slide 22

Slide 22 text

swiftc Const Extract •コードから型情報を抽出する仕組み swiftc -c Source.swift \ -Xfrontend -emit-const-values-path -Xfrontend X.swiftconstvalues \ -Xfrontend -const-gather-protocols-file -Xfrontend protocols.json Const Extractを実行するオプション

Slide 23

Slide 23 text

swiftc Const Extract •コードから型情報を抽出する仕組み swiftc -c Source.swift \ -Xfrontend -emit-const-values-path -Xfrontend X.swiftconstvalues \ -Xfrontend -const-gather-protocols-file -Xfrontend protocols.json Const Extractの対象となるprotocol

Slide 24

Slide 24 text

swiftc Const Extract swiftc -c Source.swift \ -Xfrontend -emit-const-values-path -Xfrontend X.swiftconstvalues \ -Xfrontend -const-gather-protocols-file -Xfrontend protocols.json Source.swift protocol SomeProtocol {} struct SomeType: SomeProtocol { let title = "Hello World" let isEnabled = true } protocols.json ["SomeProtocol"]

Slide 25

Slide 25 text

"properties": [ { "label": "title", "type": "Swift.String", "mangledTypeName": "n/a - deprecated" "isStatic": "false", [ "isComputed": "false", { "file": "Source.swift", "typeName": "Source.SomeType", "line": 4, "mangledTypeName": "6Source8SomeTypeV", "valueKind": "RawLiteral", "kind": "struct", "value": "Hello World" "file": "Source.swift", }, "line": 3, { "conformances": [ "label": "isEnabled", "Source.SomeProtocol", "type": "Swift.Bool", "Swift.Sendable" "mangledTypeName": "n/a - deprecated" ], "isStatic": "false", "allConformances": [ "isComputed": "false", { "file": "Source.swift", "protocolName": "Source.SomeProtocol", "line": 5, "conformanceDefiningModule": "Source" "valueKind": "RawLiteral", }, "value": "true" { } "protocolName": "Swift.Sendable", "conformanceDefiningModule": "Source" … } swiftc Const Extract

Slide 26

Slide 26 text

swiftc Const Extract Source.swift protocol SomeProtocol {} struct SomeType: SomeProtocol { let title = "Hello World" let isEnabled = true } Source.swiftconstvalues [ { "typeName": "Source.SomeType", "mangledTypeName": "6Source8SomeTypeV", "kind": "struct", "file": "Source.swift", "line": 3, "conformances": [ "Source.SomeProtocol", "Swift.Sendable" ], "properties": [ { "label": "title", "type": "Swift.String", "mangledTypeName": "n/a - deprecated" "isStatic": "false", "isComputed": "false", "file": "Source.swift", "line": 4, "valueKind": "RawLiteral", "value": "Hello World" }, { "label": "isEnabled", "type": "Swift.Bool", "mangledTypeName": "n/a - deprecated" "isStatic": "false", "isComputed": "false", "file": "Source.swift", "line": 5, "valueKind": "RawLiteral", "value": "true" } …

Slide 27

Slide 27 text

swiftc Const Extract Source.swift protocol SomeProtocol {} struct SomeType: SomeProtocol { let title = "Hello World" let isEnabled = true } Source.swiftconstvalues [ { "typeName": "Source.SomeType", "mangledTypeName": "6Source8SomeTypeV", "kind": "struct", "file": "Source.swift", "line": 3, "conformances": [ "Source.SomeProtocol", "Swift.Sendable" ], "properties": [ { "label": "title", "type": "Swift.String", "mangledTypeName": "n/a - deprecated" "isStatic": "false", "isComputed": "false", "file": "Source.swift", "line": 4, "valueKind": "RawLiteral", "value": "Hello World" }, { "label": "isEnabled", "type": "Swift.Bool", "mangledTypeName": "n/a - deprecated" "isStatic": "false", "isComputed": "false", "file": "Source.swift", "line": 5, "valueKind": "RawLiteral", "value": "true" } …

Slide 28

Slide 28 text

swiftc Const Extract Source.swift protocol SomeProtocol {} struct SomeType: SomeProtocol { let title = "Hello World" let isEnabled = true } Source.swiftconstvalues [ { "typeName": "Source.SomeType", "mangledTypeName": "6Source8SomeTypeV", "kind": "struct", "file": "Source.swift", "line": 3, "conformances": [ "Source.SomeProtocol", "Swift.Sendable" ], "properties": [ { "label": "title", "type": "Swift.String", "mangledTypeName": "n/a - deprecated" "isStatic": "false", "isComputed": "false", "file": "Source.swift", "line": 4, "valueKind": "RawLiteral", "value": "Hello World" }, { "label": "isEnabled", "type": "Swift.Bool", "mangledTypeName": "n/a - deprecated" "isStatic": "false", "isComputed": "false", "file": "Source.swift", "line": 5, "valueKind": "RawLiteral", "value": "true" } …

Slide 29

Slide 29 text

swiftc Const Extract •コードから型情報を抽出する仕組み •入力: .swift, protocols.json •指定したprocolに準拠した型のみを抽出する •出力: .swiftconstvalues •型名・プロパティ(名前・型・初期値)等

Slide 30

Slide 30 text

swiftc Const Extract: App Intents •App Intentsのビルドプロセスでも利用 •AppIntent等を記述したprotocols.jsonを利用 https://github.com/swiftlang/swift-build/blob/25db117806a542f464b587dec321675610bdd042/Sources/SWBApplePlatform/Plugin.swift#L357

Slide 31

Slide 31 text

swiftc Const Extract: App Intents •ビルドするだけでシステムが認識 import AppIntents struct SomeIntent: AppIntent { static let title: LocalizedStringResource = "Some Intent" static let description = IntentDescription("A plain demo intent.") } func perform() async throws -> some IntentResult { // do something return .result() }

Slide 32

Slide 32 text

swiftc Const Extract: 初期値抽出の制約 •初期値は即値のみ抽出可能 struct SomeIntent: AppIntent { // 🔴 Unsupported value for interpolated string segment: memberReference(baseType: "L10n.AppIntent", memberLabel: "title") static let title: LocalizedStringResource = “\(L10n.AppIntent.title)" // 🔴 The property 'description' must be static, have a compile-time constant value and cannot be computed or dynamic static var description: IntentDescription { let result = IntentDescription("A plain demo intent.") // do something } } return result

Slide 33

Slide 33 text

swiftc Const Extract: 初期値抽出の制約 Source.swiftconstvalues •初期値は即値のみ抽出可能 Source.swift let anotherTitle = "title" struct SomeType: SomeProtocol { let title1: String = anotherTitle var title2: String { let title = "title" } } return title "properties": [ { "label": "title1", "type": "Swift.String", "mangledTypeName": "n/a "isStatic": "false", "isComputed": "false", "file": "Source.swift", "line": 5, "valueKind": "Runtime" }, { "label": "title2", "type": "Swift.String", "mangledTypeName": "n/a "isStatic": "false", "isComputed": "true", "file": "Source.swift", "line": 7, "valueKind": "Runtime" }

Slide 34

Slide 34 text

swiftc Const Extract: 初期値抽出の制約 •初期値は即値のみ抽出可能 struct SomeIntent: AppIntent { // 🔴 Unsupported value for interpolated string segment: memberReference(baseType: "L10n.AppIntent", memberLabel: "title") static let title: LocalizedStringResource = “\(L10n.AppIntent.title)" // 🔴 The property 'description' must be static, have a compile-time constant value and cannot be computed or dynamic static var description: IntentDescription { let result = IntentDescription("A plain demo intent.") // do something } } return result

Slide 35

Slide 35 text

swiftc Const Extract: 技術面 •コードから型情報を抽出する仕組み •型システムを利用した精度の高い静的解析 •コンパイル本流のASTを流用するのでコストも安い

Slide 36

Slide 36 text

swiftc Const Extract: まとめ •コードから型情報を抽出する仕組み •プロパティ情報(名前・型・初期値)も抽出 •appintents metadata processorが利用 •初期値は即値のみ抽出可能

Slide 37

Slide 37 text

ビルドプロセス概要: 主要なツール .xcodeproj .app Xcode exec *.swift Info.plist Swift Build Metadata.appintents appintentsmetadataprocessor .swiftconstvalues llbuild swiftc Metadata.appintents

Slide 38

Slide 38 text

専用ツールによるメタデータの出力 •appintents metadata processor •主な入力: .swiftconstvalues, .swift •出力: Metadata.appintents

Slide 39

Slide 39 text

専用ツールによるメタデータの出力 •appintents metadata processor •主な入力: .swiftconstvalues, .swift •出力: Metadata.appintents •追加のバリデーションも行う

Slide 40

Slide 40 text

専用ツールによるメタデータの出力 xcrun appintentsmetadataprocessor \ --toolchain-dir ... \ --module-name ... \ --sdk-root ... \ --xcode-version ... \ --platform-family ... \ --deployment-target ... \ --bundle-identifier ... \ --output ... \ --target-triple ... \ --binary-file ... \ --dependency-file ... \ --stringsdata-file ... \ --source-file-list SwiftFileList \ --metadata-file-list DependencyMetadataFileList \ --static-metadata-file-list DependencyStaticMetadataFileList \ --swift-const-vals-list SwiftConstValuesFileList \ --compile-time-extraction \ --deployment-aware-processing \ --no-app-shortcuts-localization

Slide 41

Slide 41 text

専用ツールによるメタデータの出力 xcrun appintentsmetadataprocessor \ --toolchain-dir ... \ --module-name ... \ --sdk-root ... \ --xcode-version ... \ --platform-family ... \ --deployment-target ... \ --bundle-identifier ... \ --output ... \ --target-triple ... \ --binary-file ... \ --dependency-file ... \ --stringsdata-file ... \ --source-file-list SwiftFileList \ --metadata-file-list DependencyMetadataFileList \ --static-metadata-file-list DependencyStaticMetadataFileList \ --swift-const-vals-list SwiftConstValuesFileList \ --compile-time-extraction \ --deployment-aware-processing \ --no-app-shortcuts-localization swiftconstvalues(のパスを羅列したファイル)

Slide 42

Slide 42 text

メタデータの形式

Slide 43

Slide 43 text

メタデータの形式 •Metadata.appintents/: ディレクトリ

Slide 44

Slide 44 text

メタデータの形式 •Metadata.appintents/: ディレクトリ •version.json: バージョン •extract.actionsdata: メタデータ本体

Slide 45

Slide 45 text

メタデータの形式 •Metadata.appintents/: ディレクトリ •version.json: バージョン •extract.actionsdata: メタデータ本体

Slide 46

Slide 46 text

メタデータの形式: extract.actionsdata { } "actions": {}, "assistantEntities": [], "assistantIntentNegativePhrases": [], "assistantIntents": [], "autoShortcuts": [], "entities": {}, "enums": [], "negativePhrases": [], "queries": {}, "shortcutTileColor": 14, "version": 1

Slide 47

Slide 47 text

メタデータの形式: extract.actionsdata import AppIntents struct SomeIntent: AppIntent { static let title: LocalizedStringResource = "Some Intent" static let description = IntentDescription("A plain demo intent.") } func perform() async throws -> some IntentResult { // do something return .result() }

Slide 48

Slide 48 text

メタデータの形式: extract.actionsdata { "actions": { "SomeIntent": { "assistantDefinedSchemaTraits": [], "assistantDefinedSchemas": [], "authenticationPolicy": 0, "availabilityAnnotations": { "LNPlatformNameWildcard": { "introducedVersion": "*" } }, "descriptionMetadata": { "descriptionText": { "alternatives": [], "key": "A plain demo intent." }, "searchKeywords": [] }, "effectiveBundleIdentifiers": [], "fullyQualifiedTypeName": "MyApp.SomeIntent", "identifier": "SomeIntent", "isAuthPolExplicit": false, "isDiscoverable": true, "mangledTypeName": "5MyApp10SomeIntentV", "mangledTypeNameByBundleIdentifier": {}, }, "mangledTypeNameByBundleIdentifierV2": {}, "mangledTypeNameV2": "5MyApp10SomeIntentV", "openAppWhenRun": false, "outputFlags": 0, "parameters": [], "presentationStyle": 0, "requiredCapabilities": [], "supportedModes": 1, "systemProtocolMetadata": [], "systemProtocolMetadataV2": [], "systemProtocols": [], "title": { "alternatives": [], "key": "Some Intent" }, "typeSpecificMetadata": [], "visibilityMetadata": { "assistantOnly": false, "isDiscoverable": true } }

Slide 49

Slide 49 text

メタデータの形式: extract.actionsdata { "actions": { "SomeIntent": { "assistantDefinedSchemaTraits": [], "assistantDefinedSchemas": [], "authenticationPolicy": 0, "availabilityAnnotations": { "LNPlatformNameWildcard": { "introducedVersion": "*" } }, "descriptionMetadata": { "descriptionText": { "alternatives": [], "key": "A plain demo intent." }, "searchKeywords": [] }, "effectiveBundleIdentifiers": [], "fullyQualifiedTypeName": "MyApp.SomeIntent", "identifier": "SomeIntent", "isAuthPolExplicit": false, "isDiscoverable": true, "mangledTypeName": "5MyApp10SomeIntentV", "mangledTypeNameByBundleIdentifier": {}, }, "mangledTypeNameByBundleIdentifierV2": {}, "mangledTypeNameV2": "5MyApp10SomeIntentV", "openAppWhenRun": false, "outputFlags": 0, "parameters": [], "presentationStyle": 0, "requiredCapabilities": [], "supportedModes": 1, "systemProtocolMetadata": [], "systemProtocolMetadataV2": [], "systemProtocols": [], "title": { "alternatives": [], "key": "Some Intent" }, "typeSpecificMetadata": [], "visibilityMetadata": { "assistantOnly": false, "isDiscoverable": true } }

Slide 50

Slide 50 text

メタデータの形式: extract.actionsdata { "actions": { "SomeIntent": { "assistantDefinedSchemaTraits": [], "assistantDefinedSchemas": [], "authenticationPolicy": 0, "availabilityAnnotations": { "LNPlatformNameWildcard": { "introducedVersion": "*" } }, "descriptionMetadata": { "descriptionText": { "alternatives": [], "key": "A plain demo intent." }, "searchKeywords": [] }, "effectiveBundleIdentifiers": [], "fullyQualifiedTypeName": "MyApp.SomeIntent", "identifier": "SomeIntent", "isAuthPolExplicit": false, "isDiscoverable": true, "mangledTypeName": "5MyApp10SomeIntentV", "mangledTypeNameByBundleIdentifier": {}, }, "mangledTypeNameByBundleIdentifierV2": {}, "mangledTypeNameV2": "5MyApp10SomeIntentV", "openAppWhenRun": false, "outputFlags": 0, "parameters": [], "presentationStyle": 0, "requiredCapabilities": [], "supportedModes": 1, "systemProtocolMetadata": [], "systemProtocolMetadataV2": [], "systemProtocols": [], "title": { "alternatives": [], "key": "Some Intent" }, "typeSpecificMetadata": [], "visibilityMetadata": { "assistantOnly": false, "isDiscoverable": true } }

Slide 51

Slide 51 text

メタデータの形式: AppSchema @AppIntent(schema: .system.search) struct SearchIntent: AppIntent { var criteria: StringSearchCriteria } func perform() async throws -> some IntentResult { // do search something .result() }

Slide 52

Slide 52 text

メタデータの形式: AppSchema @AppIntent(schema: .system.search) struct SearchIntent: AppIntent { var criteria: StringSearchCriteria } func perform() async throws -> some IntentResult { // do search something .result() } "SearchIntent": { "assistantDefinedSchemaTraits": [], "assistantDefinedSchemas": [ { "domain": "system", "name": "ShowInAppSearchResultsIntent", "version": "1.0.0" } ], … "systemProtocolMetadata": [ "com.apple.link.systemProtocol.AssistantIntent",

Slide 53

Slide 53 text

メタデータの形式: まとめ •App Intentsの設定はすべてメタデータに現れる •メタデータを直接見るとデバッグが容易

Slide 54

Slide 54 text

App Intentsメタデータの形式 “extract app intents metadata”でビルドログを検索

Slide 55

Slide 55 text

Swift Build: メタデータのハンドリング .xcodeproj .app Xcode exec *.swift Info.plist Swift Build Metadata.appintents appintentsmetadataprocessor .swiftconstvalues llbuild swiftc Metadata.appintents

Slide 56

Slide 56 text

Swift Build: メタデータのハンドリング •Const Extarctの結果と専用ツールを繋ぎこむ •メタデータをアプリに同封する

Slide 57

Slide 57 text

Swift Build: マルチモジュール

Slide 58

Slide 58 text

Swift Build: マルチモジュール •Xcode 26以降で可能

Slide 59

Slide 59 text

Swift Build: マルチモジュール •Xcode 26以降で可能 •モジュール単位でメタデータを生成する

Slide 60

Slide 60 text

Swift Build: マルチモジュール •Xcode 26以降で可能 •モジュール単位でメタデータを生成する •複数のメタデータを処理してアプリに同封 •リンク方式によって処理方式は異なる

Slide 61

Slide 61 text

Swift Build: マルチモジュール static link •static link •メタデータがマージされる

Slide 62

Slide 62 text

Swift Build: マルチモジュール static link

Slide 63

Slide 63 text

Swift Build: マルチモジュール static link App.app Module A Module B struct AIntent struct BIntent Module C struct CIntent SPMを利用したマルチモジュール構成で検証

Slide 64

Slide 64 text

Swift Build: マルチモジュール static link App.app static link Module A Module B struct AIntent struct BIntent Module C struct CIntent library(type: .static)

Slide 65

Slide 65 text

Swift Build: マルチモジュール static link App.app Module A Module B struct AIntent struct BIntent Module C struct CIntent

Slide 66

Slide 66 text

Swift Build: マルチモジュール static link App.app Module A Module B struct AIntent struct BIntent Module C struct CIntent C/Metadata.appintents "CIntent"

Slide 67

Slide 67 text

Swift Build: マルチモジュール static link App.app Module A Module B struct AIntent struct BIntent A/Metadata.appintents B/Metadata.appintents "AIntent" "BIntent" "CIntent" "CIntent" Module C struct CIntent C/Metadata.appintents "CIntent"

Slide 68

Slide 68 text

Swift Build: マルチモジュール static link Metadata.appintents "AIntent" App.app "BIntent" "CIntent" Module A Module B struct AIntent struct BIntent A/Metadata.appintents B/Metadata.appintents "AIntent" "BIntent" "CIntent" "CIntent" Module C struct CIntent C/Metadata.appintents "CIntent"

Slide 69

Slide 69 text

Swift Build: マルチモジュール static link .app Metadata.appintents extract.actionsdata "AIntent" "BIntent" "CIntent"

Slide 70

Slide 70 text

Swift Build: マルチモジュール static link •static link •メタデータがマージされる

Slide 71

Slide 71 text

Swift Build: マルチモジュール dynamic link •static link •メタデータがマージされる •dynamic link •メタデータがbundleごとに同封される

Slide 72

Slide 72 text

Swift Build: マルチモジュール dynamic link App.app .framework struct SomeIntent Xcodeのframework target (Mach-O Type=Dynamic Library)を利用して検証

Slide 73

Slide 73 text

Swift Build: マルチモジュール dynamic link App.app .framework/Metadata.appintents .framework struct SomeIntent “SomeIntent"

Slide 74

Slide 74 text

Swift Build: マルチモジュール dynamic link App.app Metadata.appintents .framework/Metadata.appintents .framework struct SomeIntent “SomeIntent"

Slide 75

Slide 75 text

Swift Build: マルチモジュール dynamic link .app Metadata.appintents extract.actionsdata Frameworks .framework Metadata.appintents extract.actionsdata “SomeIntent"

Slide 76

Slide 76 text

Swift Build: マルチモジュール dynamic link .app Metadata.appintents extract.actionsdata Frameworks .framework Metadata.appintents extract.actionsdata “SomeIntent" システムは自動でFrameworksまで参照しない

Slide 77

Slide 77 text

Swift Build: マルチモジュール dynamic link •AppIntentsPackage https://developer.apple.com/documentation/appintents/appintentspackage

Slide 78

Slide 78 text

Swift Build: マルチモジュール dynamic link LibraryKit/Source.swift public struct LibraryKitAppIntentsPackage: AppIntentsPackage {} App Target/Source.swift import ModuleC struct AppTargetAppIntentsPackage: AppIntentsPackage {}

Slide 79

Slide 79 text

Swift Build: マルチモジュール dynamic link LibraryKit/Source.swift public struct LibraryKitAppIntentsPackage: AppIntentsPackage {} App Target/Source.swift import ModuleC struct AppTargetAppIntentsPackage: AppIntentsPackage { static var includedPackages: [any AppIntentsPackage.Type] { [ LibraryKitAppIntentsPackage.self ] } }

Slide 80

Slide 80 text

Swift Build: マルチモジュール dynamic link •AppIntentsPackage •extract.packagedataを生成する

Slide 81

Slide 81 text

Swift Build: マルチモジュール dynamic link •AppIntentsPackage •extract.packagedataを生成する .app/Metadata.appintents/extract.packagedata { "includes": [ "10LibraryKit0aB16AppIntentsPackageV" ], "version": 1 }

Slide 82

Slide 82 text

Swift Build: マルチモジュール dynamic link .app Metadata.appintents extract.actionsdata Frameworks LibraryKit.framework Metadata.appintents extract.actionsdata “SomeIntent"

Slide 83

Slide 83 text

Swift Build: マルチモジュール dynamic link .app Metadata.appintents extract.actionsdata extract.packagedata “10LibraryKit0aB16AppIntentPackageV" Frameworks LibraryKit.framework Metadata.appintents extract.actionsdata “SomeIntent"

Slide 84

Slide 84 text

Swift Build: マルチモジュール dynamic link .app Metadata.appintents extract.actionsdata extract.packagedata “10LibraryKit0aB16AppIntentPackageV" Frameworks LibraryKit.framework Metadata.appintents extract.actionsdata “SomeIntent"

Slide 85

Slide 85 text

Swift Build: マルチモジュール dynamic link .app Metadata.appintents extract.actionsdata extract.packagedata “10LibraryKit0aB16AppIntentPackageV" Frameworks LibraryKit.framework Metadata.appintents extract.actionsdata “SomeIntent" ※Siriの挙動なので、推測を含みます

Slide 86

Slide 86 text

Swift Build: マルチモジュール dynamic link .app Metadata.appintents extract.actionsdata extract.packagedata “10LibraryKit0aB16AppIntentPackageV" Frameworks LibraryKit.framework Metadata.appintents extract.actionsdata “SomeIntent" ※Siriの挙動なので、推測を含みます

Slide 87

Slide 87 text

Swift Build: マルチモジュール dynamic link .app Metadata.appintents extract.actionsdata extract.packagedata “10LibraryKit0aB16AppIntentPackageV" Frameworks LibraryKit.framework Metadata.appintents extract.actionsdata “SomeIntent" ※Siriの挙動なので、推測を含みます

Slide 88

Slide 88 text

Swift Build: マルチモジュール •static link •メタデータが一つにマージされる •dynamic link •メタデータがbundleごとに同封される •AppIntentsPackageでappから参照する必要あり

Slide 89

Slide 89 text

Swift Build: マルチモジュール xcrun appintentsmetadataprocessor \ --static-metadata-file-list DependencyStaticMetadataFileList \ —-metadata-file-list DependencyMetadataFileList \ •static-metadata- le-list: static link用 •依存メタデータをマージする •metadata- le-list: dynamic link用 fi fi •依存メタデータのマージはしない、解決のみ

Slide 90

Slide 90 text

Swift Build: マルチモジュールの注意

Slide 91

Slide 91 text

Swift Build: マルチモジュールの注意 •AppShortcutsProviderはマージされない •.app/.appex (bundle root) に記述する必要あり struct MultiAppShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: AppTargetModuleIntent(), phrases: [ "Run parent intent in \(.applicationName)", ], shortTitle: “Run Intent", systemImageName: "1.circle" ) } }

Slide 92

Slide 92 text

App Intentsのビルドプロセスまとめ •静的解析でメタデータを生成しアプリに同封 •メタデータが信用できる最終出力 •Xcode 26以降はマルチモジュール構成に対応 •dynamic linkとShortcutは注意が必要

Slide 93

Slide 93 text

参考 • Swiftビルド弾丸ツアー - Swift Buildが作る新しいエコシステム • swiftlang/swift lib/ConstExtract • swiftlang/swift-build AppIntentsMetadataTaskProducer • サンプルコード • https://github.com/kntkymt/ios-appintents-metadata-build-sample