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

AmebaブログはKMPから卒業します

 AmebaブログはKMPから卒業します

Amebaブログでは、2023 年から約 3 年半、データ層を中心とした実装を Kotlin Multiplatform(KMP)で iOS と Android に共通化してきました。ひとつの実装を両 OS で共有できる恩恵を受けてきた一方で、私たちは今、KMP から卒業し、それぞれの OS のネイティブ実装へ置き換える道を選んでいます。導入した当時は合理的だったはずの判断が、なぜ変わったのか。本セッションでは、3 年半の運用で見えてきた KMP の良し悪しと、私たちが卒業を決めた理由をお話しします。KMP を使っているチームにも、これから検討するチームにも、コード共通化とどう付き合うかを考える材料をお渡しします。

Avatar for 伊藤光次郎

伊藤光次郎

October 16, 2026

Other Decks in Programming

Transcript

  1. 今日話すこと 01 02 03 04 05 Amebaブログ について KMPとは KMP

    を入れて 良かったこと それでも 卒業する理由 まとめ 3
  2. Amebaブログ(iOS) の技術スタック 画面は Swift、共通ロジックだけ KMP 言語・UI Swift / SwiftUI /

    UIKit アーキテクチャ TCA/ MVVM + RxSwift / Flux プロジェクト管理 XcodeGen / SPM 対応 OS 共通ロジック iOS 18.0+ Kotlin Multiplatform 6
  3. KMP(Kotlin Multiplatform)とは Kotlin で書いたコードを、iOS と Android で共有する仕組み Presentation層 Kotlin Swift

    KMP(Kotlin)で 1 回だけ書く Domain層 UseCase・Entity Data層 Repository・API 通信・レスポンスの変換 iOS からは、KMP の部分が Objective-C 互換のフレームワークとして見える 8
  4. 良かったこと① 1 回書けば、両 OS で動く 同じものを Swift と Kotlin で

    2 回書かなくていい KMP なら KMP(Kotlin)で 1 回書く iOS Android そのまま両方で使える 両方が同じコードを使うので、iOS と Android で動きがそろう 10
  5. 良かったこと② OS の垣根を越えて開発できた KMP の部分は、iOS・Android どちらのエンジニアも書ける Android エンジニア • 担当の

    OS に関係なく、手が空いている人が共通部分を進められた ◦ • KMP iOS の機能の API 連携を Android エンジニアが書くことも、その逆もあった 中身は API 呼び出しと詰め替えが中心 ◦ 既存の実装を真似れば、普段 Kotlin を書かない人でも書けた 11 iOS エンジニア
  6. iOS のつらさ① Objective-C 越しの API Swift にあって Objective-C にない機能が、使えない形で届く Kotlin

    (KMP) Objective-C ヘッダー Swift にしかない機能は、ここで落ちる 13 Swift
  7. 1. 名前がぶつかると、引数ラベルに _ が付く interface どうしで、同じ名前のメソッドがあると _ が足される Kotlin(AmebaKMM の実際のコード)

    Swift から見ると // ハッシュタグ取得 interface IosGetOfficialHashtags { fun execute(request: GetOfficialHashtagsInput) } // ハッシュタグ取得 execute(request: ...) // Remote Config 取得 execute(request_: ...) // Remote Config 取得 interface IosGetBlogRemoteConfig { fun execute(request: GetBlogRemoteConfigInput) } // Apollo(ライブラリ)の中のメソッド execute(request__: ...) 14
  8. 2. Kotlin の数値型は、Swift の Int にならない Int は Int32、Long は

    Int64。Swift の Int を渡すたびに変換を書く Kotlin(KMP) Swift から見ると class UserRepository { fun fetch( entryId: Long, limit: Int, ) { … } } repo.fetch( entryId: Int64(entryID), limit: Int32(limit) ) // Swift の Int をそのまま渡せない 15
  9. 3. デフォルト引数が使えない 毎回すべての引数を書き、引数が増えると呼び出し側が全部壊れる Kotlin(KMP) Swift から見ると class SearchRepository { fun

    search( query: String, page: Int = 1, limit: Int = 20, ) { … } } // デフォルト値があっても省略できない repo.search( query: "swift", page: 1, limit: 20 ) 16
  10. 4. enum の switch に default が必須 網羅チェックが効かず、ケースが増えてもコンパイラが教えてくれない Kotlin(KMP) Swift

    から見ると enum class BlogType { GENERAL, OFFICIAL, } switch type { case .general: ... case .official: ... default: break } // ケースが増えても気づけない ※ SKIE を入れれば enum などは改善できる 17
  11. iOS のつらさ② Swift 6 の並行性チェック KMP の型は Sendable にならず、チェックを弱めるしかない •

    • • Swift 6 では、スレッドをまたぐ値は Sendable である必要がある KMP の型は、Swift からはすべて Sendable ではない型として見える 結果、@preconcurrency import と @unchecked Sendable が広がる ◦ @unchecked の型は、Swift 側でどう使ってもコンパイラがチェックしない Swift // 並行性の警告を抑えて import する @preconcurrency import AmebaKMM // 安全かどうかは、コンパイラではなく人が保証する extension KotlinThrowable: @retroactive Error, @unchecked Sendable {} 18
  12. iOS のつらさ③ リポジトリ間の直列依存 iOS の作業が、KMP のリリースを待たないと始められない ここで待つ 1 KMP で実装

    レビュー・マージ 2 KMP を リリース 3 iOS で バージョンを上げる KMP 側に不具合があれば、1 からやり直し 19 4 やっと iOS の 実装・動作確認
  13. 両 OS が 1 本のバージョンに縛られる 欲しい変更だけを選んで取り込むことはできない まとめて入ってくる v1.0 v1.1 v1.2

    いま使っている 今は取り込みたくない変更 欲しい変更 欲しい変更 1 つのために、取り込みたくない変更への対応もセットでついてくる アプリごとに API クライアントを持っていれば、必要な変更だけを自分のタイミングで入れられる 20
  14. KMP の分だけアプリが重い 中身は API クライアントなのに、framework が大きい Amebaブログ iOS アプリ 画面・機能(Swift)

    大きい理由 • • • Kotlin/Native のランタイム(GC・標準ライブラリ) Ktor・Apollo・Koin・kotlinx 系のライブラリ Objective-C 向けのヘッダー・シンボル そのほかのライブラリ KMP(API クライアント) 完全に外せば、その分アプリが軽くなる見込み 21
  15. 得ていたのは API クライアントの共通化だけ では、そのコストを払って、何を得ていたのか サーバー(BFF) 表示の形・判定結果まで計算して返す JSON • • KMP:API

    クライアント+詰め替え • Entity KMP が一番活きるのは、両 OS で同じビジネスロ ジックを共有するとき サーバーが BFF 寄りになり、クライアントにロジッ クがあまり残らない KMP の中身は「API を呼んで Entity に詰め替える」 がほとんど iOS / Android:画面 これだけのコストを払って、共通化していたのは API クライアントくらい 22
  16. AI 時代① 「2 回書くコスト」が下がった 比べるべきは、書く以外のコスト 2023 年(KMP 導入時) いま 同じロジックを

    Swift と Kotlin で 2 回書 仕様さえあれば、AI がそれぞれの言語で くのは手間がかかる 短時間に書ける → 1 回で済む共通化に価値 → 2 回書く手間は、ほぼ問題にならない 得ていたもの 2 回書かなくて済む < 23 払っているコスト 直列依存・ブリッジ・ラッパー・サイズ
  17. AI 時代② KMP が AI の開発ループを長くする 仕様から実装・テストまでを、1 つの流れで回せない KMP を残して

    AI で自動化 仕様 KMP 実装 Framework 生成&リリース iOS / Android 実装 間に KMP のマージと framework の生成が挟まる(CI のビルドだけで 10 分以上) KMP をやめて AI で自動化 仕様 iOS / Android 実装 テスト 仕様から実装・テストまでを 1 つの流れで回せる • • AI が間違えたときの直しも、KMP のリリースからやり直し AI が読むコードが、2 言語・2 リポジトリに分かれる 24 テスト
  18. ツールで解決するつらさ、しないつらさ KMP 側も進化している。それでも卒業の判断は変わらない ツールで小さくなっていく • ツールでは解決しない Swift の言語機能が使えないつらさ ◦ ◦

    • • • Swift Export(JetBrains 公式):まだ Alpha SKIE(Touchlab):本番で使える • 25 リポジトリ間の直列依存 両 OS が 1 本のバージョンに縛られる 共通化していたのは API クライアントく らい AI 時代のコスト構造
  19. まとめ 共通化は目的ではなく、手段 • KMP は導入当時は合理的だった。1 回書けば両 OS で動き、OS の垣根も越えられた 得ていたもの

    API クライアントの共通化 払っていたコスト Objective-C 越しの API・Swift 6・直列依存 一本のバージョン・アプリのサイズ < AI で「2 回書く」手間が下がり、天秤は大きく傾いた 私たちにとっては、各 OS のネイティブで書く方が 速くて開発のループを回せる 27