Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SwiftPMのプラグイン機能をiOSアプリ開発に活用する / Development App With SwiftPM Plugins

USAMI Kosuke
September 10, 2022

SwiftPMのプラグイン機能をiOSアプリ開発に活用する / Development App With SwiftPM Plugins

USAMI Kosuke

September 10, 2022
Tweet

More Decks by USAMI Kosuke

Other Decks in Programming

Transcript

  1. SwiftPM
    のプラグイン機能を


    iOS
    アプリ開発に活用する
    宇佐見公輔
    /
    株式会社ゆめみ

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  4. Swift Package
    Manager
    とは

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  7. Package.swift
    import PackageDescription

    let package = Package(

    name: "MyLibrary",

    products: [

    .library(name: "MyLibrary", targets: ["MyLibrary"]),

    ],

    dependencies: [],

    targets: [

    .target(name: "MyLibrary", dependencies: []),

    ]

    )

    View full-size slide

  8. 配布されているパッケージの利用












    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: []),
    ]
    )

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  11. 余談:
    Swift Playgrounds App
    Swift Playgrounds
    でも開発可能、プロジェクト形式が異なる
    「ゆめみ大技林 '22
    」に書いた(技術書典で配布)

    これも面白いが、このトークではこれ以上述べない。

    View full-size slide

  12. Xcode
    プロジェクトと
    SwiftPM
    配布されているパッケージを利用する
    CocoaPods
    やCarthage
    で配布ライブラリを利用する代わりに、

    SwiftPM
    で配布ライブラリを利用する
    アプリのコード(の一部)をパッケージ化する
    コードをXcode
    プロジェクトの管理外に置ける

    View full-size slide

  13. Xcode
    で配布パッケージを利用する
    Xcode
    の「File
    →Add Packages…
    」で依存パッケージを追加できる

    ライブラリ管理の手法として有益だが、このトークではこれ以上述べない。

    View full-size slide

  14. アプリのコードをパッケージ化する
    ローカルのSwift
    パッケージをアプリでインポートする

    このトークでは、こちらの手法を扱う。

    View full-size slide

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

    View full-size slide

  16. Xcode
    プロジェクト内のソース
    App.swift
    import UIKit

    import AppFeature

    @main

    final class AppDelegate: AppFeature.AppDelegate {}

    final class SceneDelegate: AppFeature.SceneDelegate {}

    View full-size slide

  17. Swift
    パッケージ内のソース
    AppDelegate.swift
    import UIKit

    open class AppDelegate: UIResponder, UIApplicationDelegate {

    public final func application(_ application: UIApplication, ...) -> Bool {

    return true

    }

    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  20. SwiftPM
    のプラグイン
    機能とは

    View full-size slide

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

    View full-size slide

  22. プラグイン機能の活用方法
    配布されているプラグインを使う
    配布されているものは、現時点では多くはない
    プラグインを自分で実装する
    独自の処理を行いたい場合はこの方法になる

    プラグインの実装方法はパンフレット記事を参照。

    View full-size slide

  23. ビルドツールプラグイン
    let package = Package(

    targets: [

    .target(

    name: "MyTarget",

    plugins: [

    .plugin(name: "MyPlugin"),

    ]

    ),

    .plugin(

    name: "MyPlugin",

    capability: .buildTool()

    ),

    ]

    )

    View full-size slide

  24. ビルドツールプラグインの処理内容
    以下の2
    つのタイミングで処理が実行される
    ビルド前(pre-build

    ビルド中(in-build

    プラグインであらかじめ定義された処理が実行される
    処理内容を自分で決めたい場合は、プラグインを自分で実装する

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  27. Xcode

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

    View full-size slide

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

    View full-size slide

  29. 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")

    ]

    ),

    ]

    )

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  32. SwiftGen
    プラグインの利用
    (2)
    let package = Package(

    targets: [

    .plugin(

    name: "SwiftGenPlugin",

    capability: .buildTool(),

    dependencies: ["swiftgen"]),

    .binaryTarget(

    name: "swiftgen",

    url: "https://github.com/SwiftGen/SwiftGen/releases/...",

    checksum: "..."

    ),

    ]

    )

    View full-size slide

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

    View full-size slide

  34. 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: [:])]

    }

    }

    View full-size slide

  35. SwiftLint
    プラグインの利用
    let package = Package(

    targets: [

    .plugin(

    name: "SwiftLintXcode",

    capability: .buildTool(),

    dependencies: ["SwiftLintBinary"]

    ),

    .binaryTarget(

    name: "SwiftLintBinary",

    url: "https://github.com/realm/SwiftLint/releases/...",

    checksum: "..."

    ),

    ]

    )

    View full-size slide

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

    View full-size slide