Slide 1

Slide 1 text

© DMM © DMM Compose Multiplatform for iOS で iOSアプリを作る DMM.swift#2 - 3/28 合同会社DMM.com - 堤下 薫

Slide 2

Slide 2 text

© DMM 堤下 薫 / Tsutsumishita Kaoru 合同会社DMM.com 2020年中途入社 開発統括本部 - アプリ開発室 DMMTV iOS開発のリードエンジニア 最近個人開発アプリを React Native → Compose Multiplatform に置き換えました💪 自己紹介 2

Slide 3

Slide 3 text

© DMM ✋ Compose Multiplatform 触ったことありますか?

Slide 4

Slide 4 text

© DMM この発表の対象者イメージ ・Compose Multiplatform for iOS  の存在は知ってるけど、利用して開発したことはない ・Kotlin Multiplatformは使ってるけど、UIはOS別で開発してる ・Compose Multiplatformを利用した  Android/iOS開発がイメージできるようになる💪 4

Slide 5

Slide 5 text

© DMM この発表で話すこと ・Compose Multiplatform ( for iOS ) ってなに? ・iOSアプリを作るまでの流れ ・iOS上で動作するまでの仕組み ・iOSっぽいアプリって作れる? ・直近の更新情報 5

Slide 6

Slide 6 text

© DMM AndroidのUI開発で利用する ”Jetpack Compose”を利用して UI開発を行えるフレームワーク Compose Multiplatform ( for iOS ) ってなに? 6   iOS ( Alpha ), Android   Windows, MacOS, Linux   Web ( Experimental ) @Composable fun App() { MaterialTheme { Column { Text("Hello World.") Button(onClick = { /* action */ }) { Text("Button") } } } }

Slide 7

Slide 7 text

© DMM 7 引用元: https://blog.jetbrains.com/ja/kotlin/2023/05/compose-multiplatform-for-ios-is-in-alpha/

Slide 8

Slide 8 text

© DMM ・公式チュートリアル  Get started with Compose Multiplatform — tutorial ・プロジェクトのセットアップ  Kotlin Multiplatform Wizard ・GitHubリポジトリ  JetBrains/compose-multiplatform  JetBrains/compose-multiplatform-core ( Jetpack Compose 実装 ) JetBrains公式の情報 8

Slide 9

Slide 9 text

© DMM 9 引用元: https://kmp.jetbrains.com/

Slide 10

Slide 10 text

© DMM iOSアプリを作るまでの流れ Kotlin Multiplatform Wizard で作成されるプロジェクトを元に紹介 10

Slide 11

Slide 11 text

© DMM 11 . ├── composeApp (Androidのディレクトリ) │ └── src │ ├── commonMain │ │ └── kotlin.com.jetbrains.kmpapp ─ App.kt │ ├── androidMain │ │ └── kotlin.com.jetbrains.kmpapp ─ MainActivity.kt │ └── iosMain │ └── kotlin.com.jetbrains.kmpapp ─ MainViewController.kt └── iosApp (iOSのディレクトリ) ├── iosApp ─ iOSApp.swift └── iosApp.xcodeproj (1) 共通のUIを開発する (2) AndroidでUI表示 (3) iOSでUIを利用するための準備 (4) iOSでUI表示 ファイル構成

Slide 12

Slide 12 text

© DMM 12 (1) 共通のUIを開発する: App.kt @Composable fun App() { MaterialTheme { Column( modifier = Modifier.fillMaxSize(), /* 全画面に広げる */ horizontalAlignment = Alignment.CenterHorizontally /* 横中央寄せ */ ) { Text("Hello World.") Button(onClick = { /* action */ }) { Text("Button") } } } }

Slide 13

Slide 13 text

© DMM 13 (2) AndroidでUI表示: MainActivity.kt class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { App() } } }

Slide 14

Slide 14 text

© DMM 14 (3) iOSでUIを利用するための準備 : MainViewController.kt fun MainViewController() = ComposeUIViewController { App() } /* ComposeUIViewControllerの定義 */ fun ComposeUIViewController( content: @androidx.compose.runtime.Composable () -> kotlin.Unit ): platform.UIKit.UIViewController

Slide 15

Slide 15 text

© DMM struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { MainViewControllerKt.MainViewController() } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} } @main struct iOSApp: App { var body: some Scene { WindowGroup { ComposeView().ignoresSafeArea(.keyboard) // Compose has own keyboard handler } } } 15 (4) iOSでUI表示: iOSApp.swift

Slide 16

Slide 16 text

© DMM 16 Android / iOS ビルド結果 @Composable fun App() { MaterialTheme { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally ) { Text("Hello World.") Button(onClick = { /* action */ }) { Text("Button") } } } }

Slide 17

Slide 17 text

© DMM ✍ Composeで作ったUIを UIViewControllerとして表示している

Slide 18

Slide 18 text

© DMM iOS上で動作するまでの仕組み Compose Multiplatform for iOS 部分のコードを抜粋して紹介 18

Slide 19

Slide 19 text

© DMM ComposeUIViewController Jetpack Compose のレイアウトを Swiftで UIViewController として扱うためWrapper 実装の中身 v1.5.12まで ComposeWindow.uikit.kt v1.6.0 から ComposeContainer.uikit.kt ※ 2024/02/29リリースされたバージョン 19

Slide 20

Slide 20 text

© DMM 20 fun ComposeUIViewController( configure: ComposeUIViewControllerConfiguration.() -> Unit = {}, content: @Composable () -> Unit ): UIViewController = ComposeContainer( configuration = ComposeUIViewControllerConfiguration().apply(configure), content = content, ) ComposeUIViewController.uikit.kt

Slide 21

Slide 21 text

© DMM 21 internal class ComposeContainer( private val content: @Composable () -> Unit, ) : CMPViewController(nibName = null, bundle = null) { private fun setContent(content: @Composable () -> Unit) { val mediator = ComposeSceneMediator( container = view, /* = UIViewController.view */ renderingUIViewFactory = ::createSkikoUIView, ... ) } private fun createSkikoUIView(renderRelegate: RenderingUIView.Delegate): RenderingUIView = RenderingUIView(renderDelegate = renderRelegate).apply { … } } ComposeContainer.uikit.kt

Slide 22

Slide 22 text

© DMM 22 ComposeSceneMediator.uikit.kt internal class ComposeSceneMediator( private val container: UIView, ... private val renderingUIViewFactory: (RenderingUIView.Delegate) -> RenderingUIView, ) { init { container.addSubview(rootView) rootView.addSubview(interopViewContainer) /* UIKitViewでUIView実装以外のViewを扱うため */ rootView.addSubview(interactionView) /* キーボードやタップのインタラクションを管理 */ interactionView.addSubview(renderingView) } }

Slide 23

Slide 23 text

© DMM 23 import org.jetbrains.skia.Canvas /* fun createSkikoUIView(...) で生成されるView */ internal class RenderingUIView(...) : UIView(...) { private val metalLayer: CAMetalLayer get() = layer as CAMetalLayer internal val redrawer: MetalRedrawer = MetalRedrawer( metalLayer, callbacks = object : MetalRedrawerCallbacks { override fun render(canvas: Canvas, targetTimestamp: NSTimeInterval) { renderDelegate.render(canvas, targetTimestamp) } } ) } RenderingUIView.uikit.kt

Slide 24

Slide 24 text

© DMM 👀 skia, skiko という見慣れない単語

Slide 25

Slide 25 text

© DMM Skia, Skiko とは? 25 Skia: https://skia.org/, https://github.com/google/skia Flutter でも”利用されていた” C++製のレンダリングエンジン ※現在Flutterは Impeller が標準 Skiko: https://github.com/JetBrains/skiko Skia for Kotlinの略 Skia を Kotlin Multiplatform で扱うためのライブラリ

Slide 26

Slide 26 text

© DMM 登場人物の整理 26  ComposeUIViewController  ComposeをiOSで利用するためのUIViewController  ComposeContainer  👆の内部実装クラス  ComposeSceneMediator  👆のView構成を管理するクラス  RenderingUIView  ComposeのUIが描画されるView ( MetalLayer )  Skia  C++製のレンダリングエンジン  Skiko  👆をKotlinで利用するためのライブラリ

Slide 27

Slide 27 text

© DMM ✍ Composeで作ったUIを “Skiko(Skia)でグラフィックに起こし Metalを使ってUIViewに書き込み” UIViewControllerとして示している

Slide 28

Slide 28 text

© DMM iOSっぽいアプリって作れる? 標準で表現できる/サードパーティが必要なところを紹介 28

Slide 29

Slide 29 text

© DMM JetBrains公式の見解 29 引用元: https://blog.jetbrains.com/ja/kotlin/2023/05/compose-multiplatform-for-ios-is-in-alpha/ クロスプラットフォーム UI フレームワークでは、 要素をどれくらいターゲットプラットフォームの外観に 似せるかが重要な問題となります。 現時点で、JetBrains チームはネイティブの UI 要素と 一般的な外観の UI 要素のどちらを提供するかについては未決定です。

Slide 30

Slide 30 text

© DMM ・現状はマテリアルデザインなUI ・Android/iOS分岐無しで  同じUIを作れる ・iOS標準のとはかなり違う  ( Ripple Effect なども付く) レイアウトの特徴(1) 30

Slide 31

Slide 31 text

© DMM ・UIViewController として扱えるため  SwiftUI / UIKit 環境どちらにも導入可能 ・UIKitView, UIKitViewController コンポーネントを利用することで  Compose の中に UIKit, SwiftUI を入れ込むことも可能  ⭕ Compose ↔ SwiftUI, UIKit の相互呼び出し レイアウトの特徴(2) 31

Slide 32

Slide 32 text

© DMM 画面遷移について 32 ・Android標準で使われるAndroidX Navigationは使えない ・公式的には Voyager というライブラリが推奨されている  ※ iOSのようにプッシュ/モーダル遷移という区別がない ・OS標準に寄せる場合、コード分岐すれば可能  Android: AndroidX Navigation, iOS: NavigationStack

Slide 33

Slide 33 text

© DMM ・Compose Multiplatform 側で管理しているリソースを  SwiftUIで利用するにはサードパーティライブラリが必要  icerockdev/moko-resources ・URL指定して画像を読み込むにはサードパーティライブラリが必要  Kamel-Media/Kamel  ※どちらもWizardから作ったプロジェクトではデフォルト利用 リソース・画像読み込みについて 33

Slide 34

Slide 34 text

© DMM ✍ iOS標準っぽいアプリを作るのは難しいが サードパーティライブラリを利用つつ カスタマイズ前提のUIであればいけそう

Slide 35

Slide 35 text

© DMM 直近の更新情報 35

Slide 36

Slide 36 text

© DMM 36 What's new in Compose Multiplatform 1.6.0 ※2024/02/29リリース ・Aplha版のため、マイナーバージョンの更新でも破壊的変更あり ・iOSに追加サポートされた機能  ・Compose UIテスト, Accessibility, etc… ・iOSのUI改修  ・Composeで描画したUIViewController背景の透明化  ・TextFieldのカーソルがよりiOSネイティブに近い動作に変更 etc … 更新情報

Slide 37

Slide 37 text

© DMM ✍ (個人で使った所感も含め) まだAlpha版で発展途上ではあるが 基本的な開発には問題なさそう

Slide 38

Slide 38 text

© DMM Compose Multiplatform, KMPなどの もう少し突っ込んだ話は懇親会でしましょう! Thank you for listening. 😄