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

SwiftUI + Kotlin Multiplatform 開発で見えた長所と短所

swiftty
September 02, 2023

SwiftUI + Kotlin Multiplatform 開発で見えた長所と短所

swiftty

September 02, 2023
Tweet

Other Decks in Technology

Transcript

  1. © 2023 Wantedly, Inc.
    SwiftUI + Kotlin
    Multiplatform 開発で
    見えた長所と短所
    iOSDC Japan 2023 スポンサーセッション
    Sep. 2 2023 - ウォンテッドリー株式会社 / 林達也

    View Slide

  2. 自己紹介
    id/tatsuya_hayashi_ar
    swiftty
    © 2023 Wantedly, Inc.
    ウォンテッドリー株式会社
    2023 年 3 月〜
    Wantedly Visit の iOS アプリの開発

    View Slide

  3. 今日話すこと
    前提
    ● iOS エンジニアで Kotlin Multiplatform (KMP) には触れたことがない
    ● 過去のプロダクトは基本 UIKit で SwiftUI は部分的な導入程度
    話すこと
    ● KMP や SwiftUI での開発の際にどのような取り組みがあったかを紹介
    ○ プロフィールリニューアルについて
    ○ SwiftUI 事例
    ○ KMP と Swift/SwiftUI の組み合わせ
    © 2023 Wantedly, Inc.

    View Slide

  4. はじめに
    © 2023 Wantedly, Inc.

    View Slide

  5. プロフィールリニューアル プロジェクト
    ● 各プラットフォームでのプロフィール体験を
    揃える
    ● ウォンテッドリーではもともと KMP を採用し
    てマルチプラットフォームでの開発を行って
    いた
    ○ Android が先行していたため KMP
    側の実装は完了していた 🎉
    ● SwiftUI の採用 🎉
    © 2023 Wantedly, Inc.
    引用: https://www.wantedly.com/companies/wantedly/post_articles/489565

    View Slide

  6. SwiftUI の採用
    ● 今までも設定画面など部分的な導入はあっ

    ● 今回のリニューアルでは SwiftUI で
    実現できるのかの検証も含めて始まった
    © 2023 Wantedly, Inc.

    View Slide

  7. KMP とは
    ● Kotlin Multiplatform
    ● ビジネスロジックを共通化
    ● UI は各プラットフォームで実装
    © 2023 Wantedly, Inc.
    引用: https://kotlinlang.org/lp/multiplatform/

    View Slide

  8. KMP とは
    ● Kotlin Multiplatform
    ● ビジネスロジックを共通化
    ● UI は各プラットフォームで実装
    © 2023 Wantedly, Inc.

    View Slide

  9. ビジネスロジックの共通化
    Reactor
    ● 単方向データフローアーキテクチャを採用
    ○ View → Action → (Mutation) → State → View → …
    ○ アプリケーション全体でひとつの状態管理ではなく集約されたビュー単位で状態管理
    ● Kotlin で定義されたクラスなので ObservableObject に適合していない
    ○ 後付けで ObservableObject が要求する objectWillChange を
    実装するのは大変
    © 2023 Wantedly, Inc.

    View Slide

  10. ビジネスロジックの共通化
    Reactor
    ● 単方向データフローアーキテクチャを採用
    ○ View → Action → (Mutation) → State → View → …
    ○ アプリケーション全体でひとつの状態管理ではなく集約されたビュー単位で状態管理
    ● Kotlin で定義されたクラスなので ObservableObject に適合していない
    ○ 後付けで ObservableObject が要求する objectWillChange を
    実装するのは大変
    © 2023 Wantedly, Inc.
    → Swift 側で Reactor をラップする  

    View Slide

  11. SwiftUI とReactor
    © 2023 Wantedly, Inc.

    View Slide

  12. SwiftUI とReactor
    © 2023 Wantedly, Inc.

    View Slide

  13. SwiftUI とReactor
    © 2023 Wantedly, Inc.
    → 以降は通常の SwiftUI と同じ  

    View Slide

  14. SwiftUI とReactor
    © 2023 Wantedly, Inc.
    → 以降は通常の SwiftUI と同じ  

    View Slide

  15. SwiftUI とReactor
    © 2023 Wantedly, Inc.
    → 以降は通常の SwiftUI と同じ  

    View Slide

  16. SwiftUI 事例
    プロフィールヘッダー
    © 2023 Wantedly, Inc.

    View Slide

  17. プロフィールヘッダー
    © 2023 Wantedly, Inc.
    UIKit でもよくある UI
    ● コンテンツをスクロールするとヘッダーが縮む
    ● タブを切り替えたらヘッダーはそれに追従する

    View Slide

  18. プロフィールヘッダー
    SwiftUI ではどうする?
    ● ScrollView のスクロールを起点にレイアウトの制御を行いたい
    ● SwiftUI の ScrollView の問題
    ○ contentInset が調整できない
    ○ オフセットを監視できない
    ○ オフセットを変更できない
    © 2023 Wantedly, Inc.

    View Slide

  19. プロフィールヘッダー
    SwiftUI ではどうする?
    ● contentInset が調整できない
    → 空のビューでヘッダー分コンテンツの開始位置をずらす?
    ● オフセットを監視できない
    → 子要素を GeometryReader で監視?
    ● オフセットを変更できない
    → ScrollViewReader を使って scroll ならできる?
    ● 最終的にオフセットをもとにヘッダービューの高さを変更すれば
    当初のレイアウトを実現できそう 💭
    © 2023 Wantedly, Inc.

    View Slide

  20. プロフィールヘッダー
    © 2023 Wantedly, Inc.

    View Slide

  21. プロフィールヘッダー
    © 2023 Wantedly, Inc.
    TabView を page スタイルで表示

    View Slide

  22. プロフィールヘッダー
    © 2023 Wantedly, Inc.
    各 content 内の ScrollView に高さ 0 で設置した
    ビューの位置をコンテンツの開始位置として
    anchorPreference で監視

    View Slide

  23. プロフィールヘッダー
    © 2023 Wantedly, Inc.
    各 content 内の ScrollView に高さ 0 で設置した
    ビューの位置をコンテンツの開始位置として
    anchorPreference で監視
    overlayPreferenceValue でその位置をもとに
    ヘッダーの高さを算出しオーバーレイで重ねる

    View Slide

  24. プロフィールヘッダー
    © 2023 Wantedly, Inc.
    オフセットが変化するたびに選択されたタブ以外の
    スクロール位置を scrollTo で同期させる

    View Slide

  25. プロフィールヘッダー
    © 2023 Wantedly, Inc.

    View Slide

  26. プロフィールヘッダー
    © 2023 Wantedly, Inc.
    → 思ったより素直なコードで実現できた 🎉  

    View Slide

  27. プロフィールヘッダー
    良かった点
    ● 複雑なレイアウトを SwiftUI でシンプルに実現
    ○ UIKit なら数百行↑
    悪かった点
    ● ScrollView と List で挙動が変わる
    ○ scrollTo の対象(ビュー単位、セル単位)
    ● OS バージョンでの挙動の違いやパフォーマンスの観点でリリース間際まで格闘
    ○ 検証時は GeometryReader/onPreferenceChange を利用
    ○ safeAreaInset なども試したがその領域の奥へのインタラクションが反応しないなど問
    題あり
    © 2023 Wantedly, Inc.

    View Slide

  28. Xcode Previews
    © 2023 Wantedly, Inc.

    View Slide

  29. Xcode Previews
    Wantedly Visit アプリ
    ● Preview を積極的に活用して開発
    ○ 動作確認しやすいのでチームで自然とそうなった
    ● Feature Modules + KMP 構成(に移行中)
    ○ ビジネスロジック(KMP)がビルド済みの XCFramework
    ○ 依存の少ない新規 Profile モジュール
    ● Preview 時は AnySwiftUIReactor に任意の State を設定してパターンを用意
    © 2023 Wantedly, Inc.

    View Slide

  30. Xcode Previews
    © 2023 Wantedly, Inc.

    View Slide

  31. Xcode Previews
    © 2023 Wantedly, Inc.

    View Slide

  32. Xcode Previews
    © 2023 Wantedly, Inc.

    View Slide

  33. Xcode Previews
    良かった点
    ● ビルド時間がほぼなく快適な体験
    ● 任意の State の注入によるパターンの確認しやすさ
    悪かった点
    ● 任意の State を注入できるため、コンポーネントの適切な設計は
    要検討
    ○ State のバケツリレー
    ■ どこまでまとまったデータで扱うか、どこでプリミティブな値で扱うか
    © 2023 Wantedly, Inc.

    View Slide

  34. KMP を Swift で扱う際の短所
    © 2023 Wantedly, Inc.

    View Slide

  35. KMP を Swift で扱う際の短所
    ● ネストされた型の階層の制限
    ● sealed class の網羅性チェック
    © 2023 Wantedly, Inc.

    View Slide

  36. KMP を Swift で扱う際の短所
    ● ネストされた型の階層の制限
    ● sealed class の網羅性チェック
    © 2023 Wantedly, Inc.

    View Slide

  37. ネストされた型の階層の制限
    © 2023 Wantedly, Inc.
    KMP は Objective-C のモジュールを生成する

    View Slide

  38. ネストされた型の階層の制限
    © 2023 Wantedly, Inc.
    KMP は Objective-C のモジュールを生成する
    → swift_name が複数階層をサポートしていない  

    View Slide

  39. ネストされた型の階層の制限
    © 2023 Wantedly, Inc.
    Swift で見ると3階層以降はまとめられてしまう
    Swift だと型推論を活かして `.` の左側は省略する
    ことが多い
    階層が定義通りなら KMP の場合でも省略できるが …

    View Slide

  40. KMP を Swift で扱う際の短所
    ● ネストされた型の階層の制限
    ● sealed class の網羅性チェック
    © 2023 Wantedly, Inc.

    View Slide

  41. sealed class の網羅性チェック
    © 2023 Wantedly, Inc.
    Swift では default が必要になる

    View Slide

  42. sealed class の網羅性チェック
    © 2023 Wantedly, Inc.
    Enum を再定義しコンパイルタイムで検知できるよう
    にインターフェースを用意

    View Slide

  43. sealed class の網羅性チェック
    © 2023 Wantedly, Inc.
    Enum を再定義しコンパイルタイムで検知できるよう
    にインターフェースを用意
    ※ ライブラリ管理の移行が落ち着いたら自動生成などを検討
    https://github.com/icerockdev/moko-kswift
    → 利用するときは網羅性チェックが効くように 🎉  

    View Slide

  44. まとめ KMP と SwiftUI
    ● 100% SwiftUI でリニューアルを実現 🎉
    ● KMP も SwiftUI の組み合わせも(癖はありつつ)体験は 👍
    ● Android も含めたモバイル開発チームの連携で大きくつまづくこともなかった 🤝
    © 2023 Wantedly, Inc.

    View Slide

  45. ご清聴ありがとうございました
    © 2023 Wantedly, Inc.

    View Slide

  46. © 2023 Wantedly, Inc.

    View Slide