iOS開発者向けKotlinマルチプラットフォーム
DRY(Don't Repeat Yourself)はプログラミングの基本原則のひとつですが、複数のプラットフォームで実行されるアプリケーションの開発では、多数のロジックを繰り返し記述することがしばしば必要になります。繰り返しを最小にする姿勢は、プログラミングでは大切なことです。また、プラットフォーム間で共有されるコードが多くなれば、繰り返しが少なくなることで、コードも向上します。
iOSの現在のプロジェクトについて考えてみてください。それはAndroidやWebなど、他のプラットフォームでも利用できますか?もしそうなら、そのiOSアプリは、他のプラットフォーム上のものと、どの程度のロジックを共有しているでしょうか?そうでないとしても、別のプラットフォームで利用可能にする計画がロードマップにあるとしたら、次のプラットフォームの開発では、どれくらい同じことを繰り替えさなければならないでしょうか?どちらにしても、答えはおそらく、"たくさん"でしょう。
Kotlin Multiplatform(KMP)の話題に入りましょう。Kotlinは、Swiftと非常によく似た、静的な型付けプログラミング言語で、Javaと100%の相互運用が可能です。多くの点で、Android用Swiftである、と言ってよいでしょう。KMPは、アプリのさまざまなプラットフォーム間でコードを共有する、Kotlinの機能です。各プラットフォームのネイティブにプログラムされたUIが、共通のコードを呼び出します。KMPは、すべてのプラットフォームで100%のコード共有を達成するための最終ステップではありませんが、 その目標に向かうための自然な一歩なのです。
KMPでは、Kotlinを使用して、アプリケーションのさまざまなプラットフォームに共通するビジネスロジックをプログラムします。次に、各プラットフォームでネイティブにプログラムされたUIが、その共通ロジックを呼び出します。UIロジックにはプラットフォーム固有の部分が多いため、ネイティブにプログラムする必要があるのです。iOSにおいては、他の外部ライブラリと同じように、.frameworkファイル -- 元はKMPで記述されたもの -- をXcodeプロジェクトにインポートする形式になります。iOS上でKMPを使用するにはSwiftが必要です。Swiftが不要になることはありません。
KMPは段階的に導入することも可能なので、現行のプロジェクトを中断することなく実装することができます。既存のSwiftコードを置き換える必要はありません。次回、さまざまなプラットフォームを対象にしたアプリケーション機能を実装する時、KMPを使用してビジネスロジックを作成し、それを各プラットフォームにデプロイして、UIをネイティブにプログラムすればよいのです。iOSの場合、これは、ビジネスロジックでKotlinを、UIロジックでSwiftを使用する、という意味になります。
Swiftの構文とKotlinの構文の密接な類似性は、KMPによるビジネスロジック開発に伴う学習曲線を、大部分で大幅に削減してくれます。学習曲線に残るのはIDE、すなわちAndroid Studioです。
Kotlin Multiplatform Projectは、まだKotlinの実験的機能です。従って、更新ごとにAPIが変更される可能性があります。
入門
このチュートリアルは、Android StudioやKotlinの使用経験がほとんど、あるいはまったくないiOS開発者を対象としています。Android Studioが手元にない場合は、 インストールガイドに従ってください。
みなさんのために、私たちが用意したスタータプロジェクトをクローンしてください。その中には、空のKMPライブラリを含む、KMPプロジェクトの定形部分が含まれています。このスタータプロジェクトには、iOSアプリケーションとAndroidアプリケーションも含まれていて、いずれも25個のハードコードされた"meh"URLのリストからGIFを表示します。
これらのアプリは提供されていますが、ライブラリは提供されていません。今回の記事はKMPライブラリを作ることにのみ注目しているのであって、ライブラリを使用するアプリ側ではないからです。ですが、安心してください。Android開発を始める上でも価値のある知識を身に付けることができます。
KMPを使用して、Giphyの公開APIで"whoa"というフレーズを検索して、25個のURLを取得するネットワークロジックを作成します。取得したURLで、各プラットフォームにハードコードされているものを置き換えます。
Android Studioを開いて、"Open an existing Android Studio project"を実行してください。開いたFinderウィンドウで、クローンしたスタータプロジェクトの最上位にある、GifGetter/ディレクトリを選択します。
このような追加ダイアログが表示されたら、Updateを押してください。
Android Studio
今までAndroid Studioを詳しく調べたことがないのであれば、覚えることがたくさんあると思います。ここでは、今回のチュートリアルで使用しているAndroid Studioの部分について紹介します。全体的なイントロダクションが必要な場合は、 こちらを参照してください。
基本部分から始めましょう。左側がプロジェクトナビゲータで、ファイル構造が表示されます。ファイル構造が表示されていない場合は、左上隅にあるProjectタブを選択すれば、プロジェクトナビゲータが表示されます。プロジェクトナビゲータの一番上に、Androidというラベルの付いたドロップダウンメニューが表示されているはずです。それを選択して、ドロップダウンメニューからProjectオプションを選択してください。
- Android Studioの上部にあるボタン列はツールバーです。その中にあるツールは、構築、実行、デバッグ実行、新たな変更の適用、Android Virtual Device(AVD) Managerの起動など、多くの機能を持っています。その下にはナビゲーションバーがあって、プロジェクトのフォルダ構造の中で、現在開いているファイルの場所を示しています。この図では、"/GifGetter/GifLibrary/commonMain/kotlin/platform/common.kt"です。
- ツールバーには次のようなボタンがあります。
- ハンマーはBuildボタン
- その隣のドロップダウンはConfigurations
- その隣はXcodeと同じRunボタン、です。
- ツールバーには次のようなボタンがあります。
- Androidの公式文書では、これをツールウィンドウのひとつと呼んでいますが、一般的にはプロジェクト構造、 ファイル構造、フォルダ構造など同の用語で呼ばれています。Xcodeのプロジェクトナビゲータから類推すれば、当然そうでしょう。
- 上部にあるProjectドロップダウンメニューに注目してください。このチュートリアルではProjectと表示されるはずです。Androidやその他のものに変更する場合、 Projectを選択し直してください。そうしないと、下の構造にフォルダやファイルの一部が表示されないことがあります。
- これはエディタウィンドウです。ここでコードを書きます。一番上にある、android.kt、common.kt、ios.kt、GifLibraryなどのファイルに注目してください。これらは現在、バックグラウンドで開かれているタブで、いずれかを右クリックすると、左右上下のどこかに、エディタウィンドウを開くためのオプションが表示されます。
- Android Studioの外側を囲むツールウィンドウバーの一部です。唯一関連があるのは、黄色の丸で囲まれている部分、特にTerminalとBuildタブです。
プロジェクト構造
KMPプロジェクトの構造の概要は、次のとおりです。
- .gradle/および.idea/folders: 大部分のAndroid Studioプロジェクトにおいて、ソース管理にコミットする必要のないローカルファイルです。IDE、プロジェクト、依存関係のローカル設定が含まれているもので、このチュートリアルには関係ありません。
- androidApp/フォルダ: より大きな意味でのKMPプロジェクトに含まれる、Androidアプリプロジェクトのルートディレクトリで、Android側のUIロジックをすべて含みます。
- build/フォルダ: KMPビルドからの出力が配置されます。これも、ソース管理にコミットする必要はありません。
- iosApp/フォルダ: より大きな意味でのKMPプロジェクトに含まれる、Xcodeプロジェクトのルートディレクトリです。
- GifLibrary/フォルダ: KMPロジックのホームです。Androidプロジェクトのビジネスロジックとなり、iOSプロジェクトではGifLibrary.frameworkに生成されます。src/フォルダには、次のものが含まれます。
- androidLibMain: Androidプラットフォーム固有のKMPロジック
- commonMain: プラットフォーム固有のコードを必要としないKMPビジネスロジック
- iosMain: AppleのAPI(UIKit, GCD, その他)を操作するための、iOSプラットフォーム固有のKMPロジック
- main: GifLibrary/をAndroidライブラリとして定義する、AndroidManifest.xmlファイルを含む。Xcode.plistと多くの共通点がある
- build.gradle: これは新しい概念かも知れません。iOS開発には直接対応するものがないからです。プロジェクトの依存関係やスクリプトなどの設定を定義するという意味では、ビルドスクリプトないしmakefileに近いものですが、.xcodeprojファイルに似た部分もあります。
- 外部ライブラリ: プロジェクトのすべての依存関係です。AndroidとKMPプロジェクトは、はるかに多くの外部依存関係を必要とするという点で、iOSプロジェクトとは異なっています。
プロジェクトの最上位レベルにある他のファイル(例: local.propertiesとすべての.imlファイル)は、Android Studioが生成するもので、KMPの動作には関係ありません。一方で、settings.gradleは、ソース管理に含む必要のある設定ファイルです。build.gradleファイルとは異なり、 settings.gradleはビルドスクリプトではなく、gradle用の設定ファイルです。
Android Studioの基本を理解できたので、次はKMPプロジェクトを見てみましょう。
KMP用にGradleを設定する
*注: 前述のように、KMPはまだ実験的なものであるため、これらのgradle構成は大きく変更される可能性があります。*
GifLibrary/build.gradleを開いて、kotlin{}
ブロックを見てください。
kotlin {
targets {
fromPreset(presets.android, 'androidLib')
def buildForDevice = project.findProperty("device")?.toBoolean() ?: false
def iosPreset = (buildForDevice) ? presets.iosArm64 : presets.iosX64
def iOSTarget = System.getenv('SDK_NAME')?.startsWith('iphoneos') \
? presets.iosArm64 : presets.iosX64
fromPreset(iosPreset, 'ios') {
binaries {
framework {
// Disable bitcode embedding for the simulator build.
if (!buildForDevice) {
embedBitcode("disable")
}
}
}
}
fromPreset(iOSTarget, 'ios') {
compilations.main.outputKinds('FRAMEWORK')
}
}
sourceSets {
commonMain {
dependencies {
implementation "io.ktor:ktor-client-json:$ktor_version"
}
}
}
}
これはプロジェクトを始める上で、極めて標準的なKMP gradleの設定です。最初に、kotlin {}
ブロック内にある、targets {}
ブロックを見てください。詳細な説明は省略しますが、fromPreset() {}
はKMP用に事前設定されたgradleを提供します。 iOSのプリセットはデバイスビルドとシミュレータビルドで異なるため、さらに定義が必要です。
次にsourceSets {}
ブロックを見てみましょう。現時点では、commonMain
が依存関係を持つ唯一のsourceSet
ですが、さらに2つの、依存関係の必要なsourceSets
があります。sourceSets {}
のcommonMain {}
の後に、 以下を追加してください。
androidLibMain {
dependencies {
implementation "io.ktor:ktor-client-json-jvm:$ktor_version"
}
}
iosMain {
dependencies {
implementation "io.ktor:ktor-client-ios:$ktor_version"
implementation "io.ktor:ktor-client-json-native:$ktor_version"
}
}
これらを追加すると、"Gradleファイルは最後のプロジェクト同期以降に変更されました。IDEが正しく動作するためには、プロジェクトの同期が必要になる場合があります"、というメッセージバーが表示されるはずです。現時点ではまだ、このメッセージを無視してください。このgradleファイルには、まだ変更が必要だからです。
ここまでで、3つの必須の依存関係すべてがsourceSets
に含まれましたが、gradleの準備が整うには、最後にもうひとつ変更が必要です。kotlin {}
ブロックの下に、copyFramework{}
というtask
があります。これがKMPプロジェクトをiOSフレームワークとして、適切なフォルダにコピーすることで、iosApp/プロジェクトから参照できるようにします。
プロジェクトのビルド時に、これらのタスクが確実に実行されるようにするためには、 copyFramework{}
の後に次の行を挿入します。
tasks.build.dependsOn copyFramework
ここでSync Nowを押して、gradleファイルを更新してください。新しい依存関係をダウンロードする必要がある場合は、多少の時間が必要です。Android Studioの下部にある タブで同期の進行を見れば、gradle同期の進行状況を確認することができます。
GiphyのAPIを調べる
GiphyのAPIに移動して、Giphy開発者アカウントを作成してください。あるいは、すでにGiphy開発者アカウントを持っていれば、ログインしてください。続いて"Create an App"を選択して、プロンプトが表示されたならば、適切な説明とともに"GifGetter"と入力します。新たなApiキーをコピーしておきましょう。
次に、Android Studioに戻ってGifLibrary/src/commonMain/kotlin/GiphyAPIを開き、以下のクラスを見つけてください。
class GiphyAPI {
val apiKey: String = ""
}
先程のGiphy APIキーを、現在は空文字列になっている、このval
ステートメントの値に貼り付けます。これがKotlinでクラスと文字列定数を宣言する方法です。これまでのコードはすべてSwiftでも可能ですが、ただひとつの例外はval
キーワードす。これは、Kotlinのlet
と同等です。
プラットフォーム固有のコード
KMPは、共通コードに対して、さまざまなプラットフォームから何を要求するかを伝えることで、iOSプラットフォームとAndroidプラットフォームの差異を把握します。それでは、GifLibrary/src/commonMain/kotlin/platform/に移動してください。"File" → "New" → "Kotlin File Class"を選択し、新しいKotlinファイル/クラスを作成して、commonと名前を付けます。Kindドロップダウンメニューで"File"を選択してください。
これらiOSおよびAndroidアプリではネットワーク呼び出しが必要なため、非同期コードを実行する必要があります。iOSではこのような並行処理にGrand Central Dispatch(GCD)を使用しますが、Androidにはないので、共通コードでは使用できません。代わりに、共通コードに対して、そのプラットフォームが何を要求(expect)
しているかを伝えます。このケースでは、次の"dispatcher
"が必要になります。
import kotlinx.coroutines.CoroutineDispatcher
internal expect val dispatcher: CoroutineDispatcher
An error will appear because the iosMan/ and androidLibMain/ modules are now expect
ing a declaration of a constant named dispatcher
of type CoroutineDispatcher
. ここでエラーが発生します。iosMan/とandroidLibMain/モジュールが、CoroutineDispatcher
型のdispatcher
という名前の定数宣言を要求
するためです。最初はiOS側について、GifLibrary/src/iosMain/platform/に移動し、 iOSという新しいファイルを作成した上で、次のコードを貼り付けます。
internal actual val dispatcher: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue())
internal class NsQueueDispatcher(private val dispatchQueue: dispatch_queue_t) : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
dispatch_async(dispatchQueue.freeze()) {
block.run()
}
}
}
このロジックは、iOSプラットフォームの場合、KMPがGCDを使用して、非同期コードを実行するためのメインキューを取得することを示しています。次に、Androidモジュール側の対応するフォルダである、GifLibrary/src/androidLibMain/platform/に移動します。もうひとつKotlinファイルを作成して、Androidという名称にします。幸いなことにAndroidプラットフォームでは、メインのディスパッチャを簡単に入手することができます。
internal actual val dispatcher: CoroutineDispatcher = Dispatchers.Main
これによって、両プラットフォームのcommonMaineが期待するディスパッチャが、それぞれの実装でサポートされたので、コンパイルエラーは消えるはずです。
データを用意する
GiphyのAPIから受け取ったJSONデータを表現するクラスが必要です。 GifLibrary/src/commonMain/kotlin/に新しいKotlinファイルを作成し、 Dataという名前にして、次のコードを貼り付けてください。
import kotlinx.serialization.Serializable
@Serializable
data class GifResult(
val `data`: List<Data>
)
@Serializable
data class Data(
val images: Images
)
@Serializable
data class Images(
val original: Original
)
@Serializable
data class Original(
val url: String
)
ファイルの先頭でSerializable
をimport
して、データクラス
の前の@Serializable
ステートメントを認識する必要があります。Swiftでは、データクラスを構造体と考えてください。これらは値型オブジェクトではありませんが、同じように振る舞います。Giphy APIからの応答JSONは、これらのデータクラスにマッピングされます。これでcommonMain
はビジネスロジックの大部分が準備できました。
KMPビジネスロジックを書く
GiphyAPIファイルに戻って、apiKey
の下に次のコードを貼り付けてください。
private val client: HttpClient = HttpClient { // 1
install(JsonFeature) { // 2
serializer = KotlinxSerializer(Json.nonstrict).apply { // 3
setMapper(GifResult::class, GifResult.serializer()) // 4
}
}
}
private fun HttpRequestBuilder.apiUrl(path: String) { // 5
url { // 6
takeFrom("https://api.giphy.com/") // 7
encodedPath = path // 8
}
}
このコードの中で何が起きているのか、段階的に詳しく説明します。
HttpClient
型の定数を宣言します。繰り返しになりますが、この行と、相当するSwiftの唯一の違いは、 キーワードがlet
ではなくval
であることです。- JSONをシリアライズおよびデシリアライズできるようにするために、
JsonFeature
をclient
オブジェクトにインストールします。 HttpClient
オブジェクトのserializer
プロパティをインスタンス化して設定します。このプロパティは、デフォルトではnull
となっています。"Json.nonstrict"は、応答のJSONが、次の行に設定されたデータクラスとは無関係のフィールドを含むことを示します。- serializerの最上位データクラスを提供し、応答JSONを
GifResult
オブジェクトにシリアライズします。JSONの "data"フィールドは、対応するGifResult.data
プロパティに入力されます。フィールドの値は、ネストしたデータクラス階層の下位へ伝搬されます。 HttpRequestBuilder
クラスに、関数apiUrl(path: String)
を追加します。- URLを構築します。
- ベースURLを設定します。
- 指定されたパスでURLを拡張します。
定型的なネットワークロジックが整ったので、次は、Giphyのエンドポイントに対して、"whoa"検索結果を取得するネットワーク呼び出しを実行します。先程貼り付けたのネットワークコードの下に、以下を追加してください。
suspend fun callGiphyAPI(): GifResult = client.get {
apiUrl(path = "v1/gifs/trending?api_key=$apiKey&limit=25&rating=G")
}
ですが、ちょっと待ってください、 suspend
キーワードとは何でしょう、なぜそれが必要なのでしょうか?そう、これはコルーチンを通じて、Kotlinで非同期コードを実行する方法のひとつなのです。現時点では、これはコルーチンや、HttpClient
のget{}
関数など他のsuspend関数からのみ呼び出し可能な関数であるという意味だ、と理解してください。
次のステップでは、iOSとAndroidのアプリで利用可能な関数を定義します。繰り返しになりますが、構文はSwiftに非常によく似ています。
fun getGifUrls(callback: (List<String>) -> Unit) { // 1
GlobalScope.apply { // 2
launch(dispatcher) { // 3
val result: GifResult = callGiphyAPI() // 4
val urls = result.data.map { // 5
it.images.original.url // 6
}
callback(urls) // 7
}
}
}
- 関数"
getGifUrls(callback: (List<String>) → Unit)
"を宣言します。この関数は、AndroidとiOSのコードが呼び出すもので、Unit
戻り値型は、Swiftのvoid
に相当します。 GlobalScope
のコンテキストで以下のコードを実行します。これには独自のディスパッチャと、ジョブキャンセルのロジックが含まれています。- デフォルトではない、独自の
dispatcher
で起動します。dispatcher
の実装は、各プラットフォームに固有のものであることに注意してください。 - "
callGiphyApi()
"関数からの戻り値に相当する値を宣言します。 GifResult
オブジェクトのデータリストを ...- ...それぞれの
url
プロパティまでたどってマップします。url
は文字列なので、List<String>
オブジェクトにマッピングしています。Kotlinのit
キーワードは、それが1つしかない場合には、ラムダのパラメータを表します。 - そのURLリストを関数のパラメータとして、コールバックに提供します。
AndroidでKMPを実装する
Androidアプリの最後のステップは、ハードコードされた既存のURLのリストを、GiphyのAPIから取得したURLに置き換えることです。これはそれほど難しいことではありません。
ファイルandroidApp/src/main/kotlin/MainActivityを開いてください。AndroidのActivity
はViewController
に密接に相関しています。 ここでMainActivity
に最初に実装する関数"onCreate(savedInstanceState: Bundle?)
"には、"viewDidLoad()
"に似た部分があります。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val gifList: RecyclerView = findViewById(R.id.gif_list)
val layoutManager = LinearLayoutManager(this)
gifList.layoutManager = layoutManager
adapter = GifAdapter(this)
gifList.adapter = adapter
adapter.setResults(urls)
}
fakeData
で結果を設定している関数の最後の行を削除して、次の行で置き換えてください。
getGifUrls {
adapter.setResults(it)
}
"setResults(results: List<String>)
"関数は、取得したgif URLを新しいデータソースとして設定し、RecyclerView
をリロードします。基本的にはUITableView
とUICollectionView
のAndroidバージョンですが、重要な違いがありますので、興味のある方は詳しく調べてみるとよいでしょう。その違いは、すべてAndroid側のロジックのためなのです!
Autoインポートが実行されず、未解決の参照エラーがある場合は、ファイルの上部にあるインポートのリストに次の行を追加する必要があります。
import org.gifLibrary.GiphyAPI
Androidエミュレータ上で実行する
Android Studioの上部にある、ツールバーの"Run"ボタンをクリックしてください。アイコンはXcodeの"Run"ボタンと非常によく似ています。シミュレータを管理するためのXcodeのウィンドウに似たようなウィンドウが、Android仮想デバイス(AVD)マネージャから表示されます。違いは、Xcodeにはダウンロード済のシミュレータが付属していることです。
新しいウィンドウで、使用可能なAndroidデバイスがリストに表示されたら、そのデバイスを選択してアプリを実行します。表示されない場合は、"Create New Virtual Device"を押してください。デバイス一覧中のPhoneカテゴリとPixel 2 XLを選択し、右下にある"Next"をクリックします。
次に、Androidの最新バージョンの横にあるDownloadを選択してください。これは、最もAPIレベルの高いものになります。選択したバージョンのAndroidをインストールするために、別のウィンドウが表示されます。Androidの最新バージョンにDownloadボタンが表示されていなければ、あなたは幸運です!そのバージョンを選択して、もう一度Nextを押してください。Android OSがダウンロードされる間にコーヒーを1杯飲んで、新しいAndroid仮想デバイスに戻ったら、Finishを選択してください。
すべてAndroid側で実行します。AndroidアプリとKMPネットワーキングロジックの恩恵を受け取りましょう。次は、おなじみのテリトリに戻ります。
Xcodeの設定
Xcodeプロジェクトを開き、 iosAppターゲットの"Build Phases"に移動して、赤丸で囲まれている+ボタンを押してください。
表示されたリストから、"New Run Script Phase"オプションを選択すると、メインウィンドウのリストの最後に、新たに"Run Script"が表示されます。リスト内を上にドラッグして、"Compile Sources"の上になるようにしてください。その横にある矢印をクリックして、次のスクリプトを貼り付けます。
cd "$SRCROOT/../"
./gradlew GifLibrary:copyFramework \
-Pconfiguration.build.dir="$CONFIGURATION_BUILD_DIR" \
-Pkotlin.build.type="$KOTLIN_BUILD_TYPE" \
-Pdevice="$KOTLIN_DEVICE"
この実行スクリプトは、 "copyFramework
" gradleタスクで使用するために、これらの定義に関するXcodeプロジェクトの"Build Settings"を読み取ります。この内容は、 GifGetter/GifLibrary/build.gradleに戻って確認することができます。ただし、Xcodeプロジェクトには、まだこれらの設定がありません。それらを作成するには、ターゲットの"Build Settings"タブに移動して、次に示すように+ボタンを押します。
表示される小さなドロップダウンメニューから、"Add User-Defined Setting"を選択してください。新しい設定には、"KOTLIN_BUILD_TYPE"という名前を付けます。横にある矢印をクリックすると、DebugとReleaseという2つの環境が表示されます。Debugに "DEBUG"という値を、 Releaseには"RELEASE"を、それぞれ設定してください。
もうひとつのユーザ定義設定を追加して、"KOTLIN_DEVICE"という名前を付けます。次に、 Debugの右側にある+ボタンをクリックしてください。これによって、 Debugの下に"Any Architecture | Any SDK"と表示されるフィールドが作成されます。それをクリックして選択可能なアーキテクチャ一覧を表示して、"Any iOS Simulator SDK"を選択してください。その上で、そのフィールドにfalseを設定します。Debugに対しても同じことを実施しますが、今回は"Any iOS SDK"を追加して、それをtrueにします。
"KOTLIN_DEVICE"の下のリリースに、これらの手順を繰り返します。すべてが終了すると、 ユーザー定義設定は次のようになっているはずです。
次に、右上の検索バーで"Framework Search Paths"を検索してください。
DebugとRelease両方の"Frame Search Paths"に、以下を追加してください。
"$(SRCROOT)/../GifLibrary/build"
このパスは、KMPが.frameworkを出力する場所です。 このファイルには、iOSアプリに必要なビジネスロジックが含まれています。FinderでGifGetter/GifLibrary/build/に移動すれば、Xcodeで使用できるGifLibrary.frameworkファイルが確認できます。
次に、 iOSAppターゲットの"General"タブに移動し、一番下までスクロールして、"Embedded Binaries"の下にある+ボタンを押してください。表示されたウィンドウで、一番下の"Add Other"を選択します。表示されるFinderウィンドウで、 GifGetter/GifLibrary/build/にあるGifLibrary.frameworkファイルを選択して、"Open"を押します。これによってフレームワークが、"Linked Binaries and Frameworks"に追加されるはずです。
GifRetriever.swiftを開くと、25個のURL文字列をハードコーディングした配列が表示されます。その下には、ハードコードされたURLを返す"requestGifs(_closure: @escaping StringsClosure)
"関数があります。ファイル先頭の"import Foundation
"ステートメントの下に、"import GifLibrary
"を追加してください。
そこまでできたら、"requestGifs(_closure: @escaping StringsClosure)
"関数の本体を次のように置き換えます。
func requestGifs(_ closure: @escaping StringsClosure) {
GiphyAPI().getGifUrls { gifs -> KotlinUnit in
closure(gifs)
return KotlinUnit()
}
}
iOSアプリをシミュレータで実行する準備が整いました!これで、KMPを介して互いにネットワーキングロジックを共有する、2つの異なるプラットフォーム上のアプリが完成したことになります。
次のステップ
ご希望であれば、GitHubで完成したプロジェクトを確認できます。KMPはまだ実験段階であり、変更される可能性が大きいことを忘れないでください。KMPは改善されて、急速に進化しているのです。それだけはありません。 KMPはすでに、App Storeで入手可能なアプリケーションの構築に成功しています 。他のプラットフォーム用のアプリケーションを開発するためのブランチアウトを望む、すべてのiOS開発者にとって、KMPは当たり前に選択される次世代テクノロジなのです。
KMPは、iOSエンジニアが二刀流(dual threat)のモバイル開発者になる最短ルートであるだけでなく、ビジネスロジックの既存リポジトリを、他のプラットフォームにも展開できるように作成可能であるという意味でもあります。そのアプリケーションをJavascript環境にデプロイしたい場合には、すでにビジネスロジックを書き終えている、ということなのです。
KMPやKotlin全般についてもっと深く知りたい場合は、Kotlin subredditとSlackチャンネルへのリンクなど、これらのリソースを調べてください。Touchlabでは、2019年6月4日に、このチュートリアルと同じテーマでウェビナを実施します。それでは最後に、一歩離れて、TouchlabのKMPに関する他の背景記事をいくつか紹介しましょう。
著者について
Ben Whitleyは、ニューヨークを拠点とするiOS開発者で、Xcode、Swift、および最初はObjective-Cで、4年間の経験を持っています。この1年半の間、氏はTouchlabの中心的なiOS開発者として、複数の著名なクライアントと密接に協力してきました。現在は、Kotlin MultiplatformをiOS開発者のネイティブプラットフォームとして推進し、iOSおよびXcodeに関する質問に答えることで、Android開発者のKMPへの移行を支援しています。