Slide 1

Slide 1 text

SwiftPM のプラグイン機能を iOS アプリ開発に活用する 宇佐見公輔 / 株式会社ゆめみ

Slide 2

Slide 2 text

自己紹介 宇佐見公輔(うさみこうすけ) 株式会社ゆめみ / iOS テックリード このトーク以外にも、パンフレット記事を2 つ書きました。

Slide 3

Slide 3 text

このトークの内容 SwiftPM (Swift Package Manager )とは iOS アプリ開発でSwiftPM を活用する SwiftPM のプラグイン機能とは iOS アプリ開発でSwiftPM プラグインを活用する ※ Swift Package Manager を略してSwiftPM と呼ぶことにする。

Slide 4

Slide 4 text

Swift Package Manager とは

Slide 5

Slide 5 text

Swift Package Manager とは Swift コードをパッケージとして管理する パッケージをビルドしてライブラリや実行プログラムを生成する ライブラリ:他のSwift コードでインポートできるモジュール 実行プログラム:シェル上で実行できるCLI ツールなど 他のパッケージを依存物として利用できる

Slide 6

Slide 6 text

パッケージ パッケージはSwift ソースファイルと Package.swift で構成される ` `

Slide 7

Slide 7 text

Package.swift import PackageDescription let package = Package( name: "MyLibrary", products: [ .library(name: "MyLibrary", targets: ["MyLibrary"]), ], dependencies: [], targets: [ .target(name: "MyLibrary", dependencies: []), ] )

Slide 8

Slide 8 text

配布されているパッケージの利用 dependencies: [ .package(url: "https://example.com/AwesomePackage", from: "1.0.0"), ], import PackageDescription let package = Package( name: "MyLibrary", products: [ .library(name: "MyLibrary", targets: ["MyLibrary"]), ], targets: [ .target(name: "MyLibrary", dependencies: []), ] )

Slide 9

Slide 9 text

iOS アプリ開発で SwiftPM を活用する

Slide 10

Slide 10 text

Xcode プロジェクト iOS アプリはXcode プロジェクトを使って開発する

Slide 11

Slide 11 text

余談: Swift Playgrounds App Swift Playgrounds でも開発可能、プロジェクト形式が異なる 「ゆめみ大技林 '22 」に書いた(技術書典で配布) ※ これも面白いが、このトークではこれ以上述べない。

Slide 12

Slide 12 text

Xcode プロジェクトと SwiftPM 配布されているパッケージを利用する CocoaPods やCarthage で配布ライブラリを利用する代わりに、 SwiftPM で配布ライブラリを利用する アプリのコード(の一部)をパッケージ化する コードをXcode プロジェクトの管理外に置ける

Slide 13

Slide 13 text

Xcode で配布パッケージを利用する Xcode の「File →Add Packages… 」で依存パッケージを追加できる ※ ライブラリ管理の手法として有益だが、このトークではこれ以上述べない。

Slide 14

Slide 14 text

アプリのコードをパッケージ化する ローカルのSwift パッケージをアプリでインポートする ※ このトークでは、こちらの手法を扱う。

Slide 15

Slide 15 text

アプリのコードをパッケージ化する 一部だけでなく、ほとんどのコードをパッケージに入れても良い

Slide 16

Slide 16 text

Xcode プロジェクト内のソース App.swift import UIKit import AppFeature @main final class AppDelegate: AppFeature.AppDelegate {} final class SceneDelegate: AppFeature.SceneDelegate {}

Slide 17

Slide 17 text

Swift パッケージ内のソース AppDelegate.swift import UIKit open class AppDelegate: UIResponder, UIApplicationDelegate { public final func application(_ application: UIApplication, ...) -> Bool { return true } }

Slide 18

Slide 18 text

パッケージ化のメリット Xcode プロジェクト(xcodeproj )でのソースコード管理が減る xcodeproj は、ファイルの追加や削除などでGit のコンフリクトを招く Swift パッケージ管理だと、Git のコンフリクトを起こしにくい アプリ内のモジュール分割が容易になる Swift パッケージのほうが簡単に扱える

Slide 19

Slide 19 text

パッケージ化で未解決の問題 ビルドスクリプトはXcode プロジェクトで管理する必要がある SwiftGen でコード生成 SwiftLint でコードチェック 実はこの問題は、SwiftPM のプラグイン機能で解決できる

Slide 20

Slide 20 text

SwiftPM のプラグイン 機能とは

Slide 21

Slide 21 text

SwiftPM のプラグイン機能 2022 年3 月のSwift 5.6 で追加された機能 コマンドプラグイン ビルド以外のタスクを定義できる ビルドツールプラグイン ビルド時に行う処理を追加できる

Slide 22

Slide 22 text

プラグイン機能の活用方法 配布されているプラグインを使う 配布されているものは、現時点では多くはない プラグインを自分で実装する 独自の処理を行いたい場合はこの方法になる ※ プラグインの実装方法はパンフレット記事を参照。

Slide 23

Slide 23 text

ビルドツールプラグイン let package = Package( targets: [ .target( name: "MyTarget", plugins: [ .plugin(name: "MyPlugin"), ] ), .plugin( name: "MyPlugin", capability: .buildTool() ), ] )

Slide 24

Slide 24 text

ビルドツールプラグインの処理内容 以下の2 つのタイミングで処理が実行される ビルド前(pre-build ) ビルド中(in-build ) プラグインであらかじめ定義された処理が実行される 処理内容を自分で決めたい場合は、プラグインを自分で実装する

Slide 25

Slide 25 text

外部ツールを使う プラグイン外のツールを実行できる Mac 内のコマンドを実行できる 公開されているコマンドラインツールをダウンロードできる artifact bundle 形式で公開されているバイナリが使える

Slide 26

Slide 26 text

iOS アプリ開発で SwiftPM プラグインを 活用する

Slide 27

Slide 27 text

Xcode と SwiftPM プラグイン Xcode でもSwiftPM プラグインは動作する Xcode 13.3 以降で動作する Xcode 14 でSwiftPM 対応が改善されている(ビルドログなど) ただし、一部の動作に問題がある(後述)

Slide 28

Slide 28 text

事例: SwiftGen プラグイン SwiftGen 公式から、プラグインとartifact bundle が提供されている ビルド前(pre-build )にソースコード生成処理が行われる 生成先は ${DERIVED_SOURCES_DIR} 以下となる swiftgen.yml で定義する なお、ビルドツールだけでなくコマンドプラグインも提供されている ` ` ` `

Slide 29

Slide 29 text

SwiftGen プラグインの利用 (1) 注意:この方法が正式だが、現時点では問題がある let package = Package( dependencies: [ .package(url: "https://github.com/SwiftGen/SwiftGenPlugin", from: "6.6.2") ], targets: [ .target( name: "MyTarget", plugins: [ .plugin(name: "SwiftGenPlugin", package: "SwiftGenPlugin") ] ), ] )

Slide 30

Slide 30 text

Xcode で発生する問題 外部プラグイン利用時、Xcode が重くなる Xcode のCPU 使用率が100% 以上になる Xcode のエディタの動きがもたつく 外部プラグインの中でartifact bundle を使っていると発生する 外部ツールをダウンロードする機能 SwiftGen プラグインは swiftgen コマンドをartifact bundle で使用 ` `

Slide 31

Slide 31 text

Xcode で発生する問題の回避方法 Xcode の問題を回避するには、プラグインを自分で実装する artifact bundle の利用自体は問題ない 外部プラグインの中でartifact bundle が使われているとダメ ローカルプラグインの中でartifact bundle を使うのは大丈夫

Slide 32

Slide 32 text

SwiftGen プラグインの利用 (2) let package = Package( targets: [ .plugin( name: "SwiftGenPlugin", capability: .buildTool(), dependencies: ["swiftgen"]), .binaryTarget( name: "swiftgen", url: "https://github.com/SwiftGen/SwiftGen/releases/...", checksum: "..." ), ] )

Slide 33

Slide 33 text

事例: SwiftLint プラグイン SwiftLint 公式から、artifact bundle が提供されている これを利用して、自分でプラグインを実装すればよい

Slide 34

Slide 34 text

SwiftLint プラグインの実装 struct SwiftLintPlugins: BuildToolPlugin { func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { return [buildCommand( displayName: "Linting \(target.name)", executable: try context.tool(named: "swiftlint").path, arguments: [ "lint", "--in-process-sourcekit", target.directory.string ], environment: [:])] } }

Slide 35

Slide 35 text

SwiftLint プラグインの利用 let package = Package( targets: [ .plugin( name: "SwiftLintXcode", capability: .buildTool(), dependencies: ["SwiftLintBinary"] ), .binaryTarget( name: "SwiftLintBinary", url: "https://github.com/realm/SwiftLint/releases/...", checksum: "..." ), ] )

Slide 36

Slide 36 text

まとめ Swift Package Manager (SwiftPM )とは iOS アプリ開発でSwiftPM を活用する SwiftPM のプラグイン機能とは iOS アプリ開発でSwiftPM プラグインを活用する サンプル: https://github.com/usami-k/XcodeSwiftPMSample