For the complete documentation index, see llms.txt. This page is also available as Markdown.

Kotlin Multiplatform (KMP)

導入背景と現状

KMPは2021年からWantedly アプリに本格導入され、現在はビジネスロジックの共有において中核的な役割を果たしています。 導入の経緯については、React Nativeをやめる話とKotlin Multiplatformで詳しく解説されています。

現在の導入状況

KMPはWantedly アプリに導入され、iOS / Android間でビジネスロジック(Reactor実装、API通信、データベース操作など)を共有しています。 2025年12月に、それまで別々だったリポジトリを単一のモノレポ wantedly/visit-app に統合しました。

リポジトリ構成

visit-app/
├── shared/      # iOS / Androidで共有するKMPコード(旧 visit-app-shared)
├── iosApp/      # iOSアプリ
└── androidApp/  # Androidアプリ

モノレポ化による改善

  • KMPの変更がアプリに反映されるまで20分以上かかっていたのが、即座に反映されるようになった

  • 破壊的変更を同一PR内で検知できるようになった(従来は別リポジトリのCI実行まで気づけなかった)

  • iOS / Androidの変更を1つのPRでアトミックに行えるようになった

モノレポ化の詳細は Wantedly のモバイルアプリのモノレポ化について書かれた記事 を参照してください。

KMPの方針

Wantedly アプリ: 積極的な導入を推進

  • 新機能開発時はKMP Reactorの採用を優先

  • 既存機能の段階的な移行を実施

全社展開: 慎重な検討段階

  • 認証処理などの共通性が高い機能から実験的に導入

  • 各アプリの特性を考慮した段階的な適用

KMPのアーキテクチャ

設計思想

KMPでは、既存のReactorKitパターンを踏襲した独自のReactorアーキテクチャを採用しています。 これにより、iOS / Android開発者の学習コストを最小限に抑えながら、プラットフォーム間でのビジネスロジック共有を実現しています。

KMPアーキテクチャ

モジュール構成

shared の主要モジュール

  • :api: API通信関連(REST/GraphQL)

    • :entity: APIエンティティ定義

    • :graphql: GraphQL クライアント(Apollo)

  • :core: 共通ユーティリティ

  • :visit-app-shared: メインモジュール(Reactor実装)

  • :testing: テスト用ユーティリティ

技術スタック

  • データベース: SQLDelight(型安全なSQL)

  • API通信: Ktor Client + Apollo GraphQL

  • 非同期処理: Kotlin Coroutines

  • 依存性注入: 手動DI(DIフレームワークには依存しない)

  • テスト: Kotest

  • iOS連携: SKIE(Kotlin の共有コードを Swift から自然に扱えるようにする相互運用ツール)

iOS 連携(SKIE)

Wantedly アプリでは SKIE を導入し、KMP の共有コードを Swift にとって自然な形で公開しています。主に次の用途で活用しています。

  • sealed class の網羅的な switch: Swift 側で全ケースを網羅的に分岐でき、分岐漏れをコンパイル時に検出できる

  • suspend 関数の async/await 化: Kotlin の suspend 関数を Swift の async/await として自然に呼び出せる

  • デフォルト引数・enum のブリッジ: Kotlin のデフォルト引数や enum を Swift から自然に扱える

Reactor

概要

ReactorはReactorKitを参考に開発された、KMP環境での単方向データフローを実現するフレームワークです。 iOS / Android両プラットフォームで一貫したアーキテクチャパターンを提供し、予測可能な状態管理を実現します。

データフロー

Reactor

基本的なデータフロー

  1. Action: UIからのユーザーインタラクション(タップ、入力など)

  2. Mutate: Actionを受けてAPI通信などの副作用を実行し、Mutationを生成

  3. Reduce: Mutationを受けて現在のStateを新しいStateに変換

  4. State: UIが監視する状態オブジェクト、変更時にUI更新をトリガー

State の設計

State は 2 種類の情報で構成します。

  • モードに関わらず常に持つ情報(例: 一時的に表示するエラートースト)→ data class のフィールドにする

  • 画面のモード(Loading / Succeeded / Error のいずれか一つ)→ sealed class にし、各モードに固有のデータはそのサブタイプに持たせる

shared(KMP)で定義するのは State だけです。State を UI に反映する処理は、後述のとおり iOS / Android の各プラットフォーム側が担います。

下記は、ある一覧画面(会社一覧など)を例にした最小の State 定義です。

hasNextPage / isNextPageLoading / nextPageCursor は成功後にしか意味を持たない情報なので、LoadingError からは触れないよう Succeeded の中に閉じ込めています。追加読み込みは「新しい画面モードに遷移する」のではなく、Succeeded のまま copy でフラグを更新することで表現します。

なぜ sealed class で分けるのか

  • 網羅性の強制: Kotlin の when / Swift の switch が全サブタイプの分岐を要求するため、状態を追加したときの「対応漏れ」がコンパイルエラーで検出できる。

  • 不正な状態アクセスの排除: Succeeded だけが一覧データを、Error だけが原因を持つため、「ローディング中なのに一覧を参照する」といったアクセスを型レベルで防げる。

  • Declarative UI との相性: 「状態 → UI」を宣言的な一対一マッピングとして書ける。

なお「追加読み込み中」のような成功状態の中の細かな進捗は、新たなサブタイプを増やすのではなく Succeeded のフィールド(フラグや cursor)として持たせることで、状態の数を最小限に保っています。

実装詳細

詳細な実装については以下を参照:

Database as Single Source of Truth

設計原則

KMPアーキテクチャでは、SQLDelightを使用したデータベースをSingle Source of Truthとして採用しています。 この設計により、データの一貫性を保ちながら、複数画面間でのリアルタイムな状態同期を実現しています。

技術実装

SQLDelight の特徴

  • 型安全性: SQLクエリのコンパイル時検証

  • マルチプラットフォーム: iOS / Android共通のDB操作

  • Reactive: Kotlin Coroutines Flowによるリアクティブクエリ

  • パフォーマンス: ネイティブSQLiteの性能を活用

データフロー例:ブックマーク機能

依存関係

依存の流れ

各画面のReactorは、SQLDelightのFlowを通じてデータベースの変更を監視します。 破線は、データの購読(Observe)関係を表しています。

データストリーム

データの流れ
  1. 初期データ取得: 各画面が必要なデータをDBから取得

  2. リアルタイム監視: DBの変更をFlowで監視

  3. 自動UI更新: データ変更時に自動的にUI更新

状態変更フロー

ブックマークのフロー
  1. ユーザーアクション: 詳細画面でブックマークボタンをタップ

  2. API通信: サーバーにブックマーク状態を送信

  3. DB更新: レスポンス受信後、ローカルDBを更新

  4. 自動伝播: DB変更が全ての購読者(画面)に自動通知

  5. UI反映: 一覧画面と詳細画面の両方で即座にUI更新

実装上の利点

  • データ整合性: 単一のデータソースによる一貫した状態管理

  • パフォーマンス: ローカルDBによる高速なデータアクセス

  • オフライン対応: ネットワーク状況に依存しない基本機能

  • 開発効率: プラットフォーム間でのロジック共有

GraphQL統合

導入背景 Wantedlyでは開発効率とパフォーマンス向上のためにGraphQLを採用しており、新しい画面は基本的にGraphQLで実装しています。 詳細はGraphQL Gateway - アプリ向けにAPIを公開するを参照してください。

KMPでの実装

  • クライアント: Apollo GraphQL Kotlin版

  • コード生成: GraphQLスキーマからKotlinコードを自動生成

  • 型安全性: クエリとレスポンスの型安全な操作

  • キャッシュ: Apollo Clientの自動キャッシュ機能

開発方針

  • 新機能: GraphQLを優先的に採用

  • 既存機能: REST APIから段階的に移行

  • パフォーマンス: 必要なデータのみを取得(Over-fetching回避)

話を聞きに行きたい

もっと知りたい

最終更新