はじめに
本田技研工業でiOSアプリ開発を担当している薄田です。
現在、弊社ではモバイルアプリを開発するにあたり、KotlinMultiplatform(以下KMP)の導入を検討しています。
KMPを使えば、Kotlinで書かれたコードを複数のプラットフォーム(iOS/Android/Web等)で共有することができます。これによりロジック実装の重複を避けることができ、開発効率の向上が見込めます。
最近はマルチモジュール構成のiOSプロジェクトをよく見かけるため、KMPプロジェクトでも同様の構成でiOSアプリを作成することができないか気になり調べてみました。
本記事ではKMPプロジェクトでiOSアプリをマルチモジュール構成にする方法を紹介したいと思います。
マルチモジュール構成のメリット
少しだけマルチモジュール構成のメリットに関して触れようと思います。
マルチモジュール構成を採用することで、以下のようなメリットを得ることができます。
- モジュール単位でビルド可能なため、Xcodeプレビューやテストを実行する際のビルド時間を短縮することができる
- いくつかのモジュールを組み合わせて機能単体で動くミニアプリを構築できるため、開発サイクルが高速に回せる
- 機能ごとにモジュールを分割することで、モジュール間の依存関係が明確になる
KMPプロジェクトでマルチモジュール構成にする
KMPプロジェクトの作成
まずはAndroidStudioからKMPプロジェクトを作成します。
このとき、iOS framweork distributionはRegular frameworkを選択してください。
iOSプロジェクトを開く
KMPプロジェクトを作成すると、.xcodeprojファイルも自動で生成されます。
/入力したApp名/入力したApp名.xcodeproj
にあるため、iosApp名をデフォルトの状態で作成した場合は/iosApp/iosApp.xcodeproj
となります。
このプロジェクトファイルを開いてください。
SwiftPackageの追加
次にSwiftPackageを追加していきます。
ツールバーのFile > New > Package...を選択してください。
Add to, Groupには対象のプロジェクトを選択し、作成してください。
共有ロジックをXCFramework形式で生成する
KMPでは、Kotlin/Nativeコンパイラが各プラットフォームに合わせてファイルを生成します。iOS向けにはFrameworkが生成されます。これをXcodeプロジェクトに組み込むことで、SwiftコードからKotlinコードを呼び出せるようになっています。
しかしながら、このFrameworkはSwiftPackageManagerで管理することができず、障壁となっていました。
そこで、共有ロジックをXCFrameworkとして生成します。XCFrameworkであればbinaryTargetで対象モジュールに対して依存追加を行うことが可能となります。
XCFrameworkの生成方法はKMP公式サイトで紹介されているので、同様のやり方で生成します。
https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#build-xcframeworks
まず、XCFrameworkを作成するためにshared
ディレクトリのbuild.gradle.ktsを編集します。
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
plugins {
alias(libs.plugins.kotlinMultiplatform)
}
kotlin {
val xcFramework = XCFramework()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
xcFramework.add(this)
}
}
}
build.gradle.ktsでXCFrameworkを宣言したことで、gradleに以下3つのタスクが追加されます。
- assembleReleaseXCFramework
-
/shared/build/XCFrameworks/release
にRelease用のXCFrameworkを生成
-
- assembleDebugXCFramework
-
/shared/build/XCFrameworks/debug
にdSYMを含んだXCFrameworkを生成
-
- assembleXCFramework
- Debug用、Release用どちらも生成
上記いずれかのタスクを実行することでXCFrameworkが生成されます。
$ ./gradlew assembleDebugXCFramework
以下のようにコードを修正することでXCFramework名を変更することができます。
kotlin {
val xcFramework = XCFramework("Hoge") // ココ
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "Hoge" // ココ
xcFramework.add(this)
}
}
}
この変更を加えるとgradleのタスク名も変更されるので注意してください。
- assembleHogeReleaseXCFramework
- assembleHogeDebugXCFramework
- assembleHogeXCFramework
Package.swiftでXCFrameworkを取り込む
作成したXCFrameworkをSwiftPackageManagerでiOSプロジェクトに取り込んでいきます。
XCFrameworkを依存追加する場合は、Package.swiftのtargetsにbinaryTargetを追加します。
先ほど生成したXCFrameworkをpathに指定しましょう。
また、共有ロジックを必要とするモジュールのdependenciesにbinaryTargetを追加しましょう。
// swift-tools-version: 5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Modules",
products: [
.library(
name: "Modules",
targets: ["ModuleA"]),
],
targets: [
.binaryTarget(name: "shared", path: "../../../shared/build/XCFrameworks/debug/shared.xcframework"),
.target(
name: "ModuleA", dependencies: ["shared"]),
.testTarget(
name: "ModuleATests",
dependencies: ["ModuleA"]),
]
)
SwiftPackage内で共有ロジックを利用することができるようになりました。
Build Phaseからスクリプトを削除する
iOSプロジェクトのBuild Phaseを見ると、もともとFramework形式で共有する際に必要だったスクリプトが設定されています。こちらは不要となったため削除しましょう。
以上の操作でマルチモジュール構成にできたかと思います。
必要なモジュールを追加していってください!
終わりに
KMPプロジェクトであっても、共有ロジックをXCFramework形式で生成することで、マルチモジュール構成のiOSプロジェクトにすることができました。
少しでも参考になれば幸いです。