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

Compose for iOS for ZOZOTOWN

Compose for iOS for ZOZOTOWN

ねも

July 14, 2023
Tweet

More Decks by ねも

Other Decks in Programming

Transcript

  1. Compose 
 for iOS 
 for ZOZOTOWN
 2023/07/11 ZOZO Tech

    Meetup - iOS/Android
 株式会社ZOZO
 ZOZOTOWN開発本部 ZOZOTOWNアプリ部 Android1ブロック
 財部彰太
 ZOZOTOWN開発本部 ZOZOTOWNアプリ部 Android2ブロック
 井上晃平
 
 Copyright © ZOZO, Inc. 1
  2. © ZOZO, Inc. 株式会社ZOZO
 ZOZOTOWN開発本部 ZOZOTOWNアプリ部 
 Android2ブロック
 井上 晃平


    最近はワンパンマンにハマっていて、ONE先生版と村田先 生版の両方を読み返した
 
 温泉旅館にいくために、そろそろ自動車免許が欲しい
 3
  3. © ZOZO, Inc. アジェンダ
 1. 発表内容に関する注意と登壇の目的
 2. Compose for iOSの基礎知識


    2.1. JetBrainsが作るマルチプラットフォームの世界
 2.2. Compose for iOSとは
 2.3. レンダリング
 2.4. Compose for iOSの優れている点
 2.5. 今後注意したい動向
 3. ZOZOTOWNをCompose for iOSで作った 3.1. 今回作ったもの 3.2. カルーセル部分の作成 3.3. スライダー部分の作成 3.4. それ以外はハリボテです 4. まとめ 
 4
  4. © ZOZO, Inc. 注意 
 Compose for iOSは2023年7月現在alpha版
 -> 今後大きな変更が加わる可能性がある
 


    目的
 すぐ仕事で使ってみてください!というよりは、今後の可能性をお伝えしたい。
 
 フィードバック窓口
 https://github.com/JetBrains/compose-multiplatform/issues
 
 1 発表内容に関する注意と登壇の目的
 
 
 KotlinConf’23 https://kotlinconf.com/talks/ 5
  5. © ZOZO, Inc. 2.1 JetBrainsが作るマルチプラットフォームの世界
 • Kotlin Multiplatform
 ◦ 複数のプラットフォームでKotlinで書かれたコードを共有する技術


    ◦ 2023年7月現在beta版であり、ドキュメントによるともうほぼstableとのこと
 
 • Kotlin Multiplatform for mobile(KMM)
 ◦ Kotlin Multiplatformのうち、モバイルに特化した場合の呼称
 ◦ データ層のコードや、ViewModelなど、可能な限りの機能をKotlinで作成し、共有する
 ◦ UIの記述はiOS/Android 各プラットフォームに任せている
 
 • Compose Multiplatform
 ◦ AndroidのUIを宣言的に作成できるJetpack Composeを用いて、Desktop, Web, Androidといったプラットフォー ムのUIを記述できるようにするための仕組み
 
 Kotlin Multiplatform https://kotlinlang.org/docs/multiplatform.html
 Compose Multiplatform https://www.jetbrains.com/ja-jp/lp/compose-multiplatform/
 
 8
  6. © ZOZO, Inc. 2.2 Compose for iOSとは
 Compose Multiplatformと
 Kotlin

    Multiplatform for mobileを組み合わせることで、モバイ ルアプリ開発のネットワーク通信からUIの記述までの全範囲 をKotlinで作成し、共有することが可能に!
 
 The Kotlin Blog https://blog.jetbrains.com/ja/kotlin/2023/05/compose-multiplatform-for-ios-is-in-alpha/
 
 12
  7. © ZOZO, Inc. 2.3 レンダリング
 • Android
 ◦ 従来の形でレンダリングされる
 


    • iOS
 ◦ Skikoによってレンダリングされる
 ◦ Skiko
 ▪ SkiaをKotlin Multiplatformの中で使えるようにしたもの
 ◦ Skia
 ▪ Googleによって作成されたオープンソースの2Dグラフィックスライブラリ
 ▪ Chrome、Android、Flutterなど様々なハードウェアやソフトウェアプラットフォームで動作する
 
 
 JetBrains/Skiko https://github.com/JetBrains/skiko
 Skia https://skia.org/
 Shader compilation jank https://docs.flutter.dev/perf/shader
 
 14
  8. © ZOZO, Inc. 2.3 レンダリング
 • FlutterはSkiaを使用した場合、レンダリングパフォーマンス上の課題が発生していた
 ◦ Compose for

    iOSにおいても、Skiaを使用することによるレンダリングパフォーマンスの課題を持つのではない か
 
 • KotlinConfの発表(25分あたり)でも、16msの画面の停止を観測したとの解説がある
 ◦ 今後2msまで縮めていくとのこと
 15 Compose Multiplatform on iOS by: Sebastian Aigner and Nikita Lipsky https://www.youtube.com/watch?v=FWVi4aV36d8 Shader compilation jank https://docs.flutter.dev/perf/shader
  9. © ZOZO, Inc. 2.4 優れている点① Jetpack ComposeのAPIがそのまま使える
 • Android開発で用いるJetpack ComposeのAPIそ

    のものがiOSでも動作する
 
 • 作成したComposableが無駄にならない
 
 The Kotlin Blog https://blog.jetbrains.com/ja/kotlin/2023/05/compose-multiplatform-for-ios-is-in-alpha/ 17 @Composable fun App() { MaterialTheme { var showImage by remember { mutableStateOf(false) } Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { showImage = !showImage }) { Text("Toggle image") } AnimatedVisibility(showImage) { Image( painterResource("compose-multiplatform.xml"), "Compose Multiplatform Logo" } } } } import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.Alignment import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource
  10. © ZOZO, Inc. 2.4 優れている点① Jetpack ComposeのAPIがそのまま使える
 The Kotlin Blog

    https://blog.jetbrains.com/ja/kotlin/2023/05/compose-multiplatform-for-ios-is-in-alpha/ 18
  11. © ZOZO, Inc. 2.4 優れている点② 相互運用性が高い
 • UIKitView
 ◦ iOS側の、地図、WebView、メディアプレイヤー、カメラといったプラットフォーム固有のウィジェットをComposeの

    なかに組み込むことができる
 
 
 • ComposeUIViewController 
 ◦ ComposeをSwift UIアプリケーションに埋め込むことができる
 →段階的な移行が可能に
 
 
 
 
 The Kotlin Blog https://blog.jetbrains.com/ja/kotlin/2023/05/compose-multiplatform-for-ios-is-in-alpha/ 19
  12. © ZOZO, Inc. 2.4 優れている点② 相互運用性が高い
 ComposeUIViewControllerを使うとiOSネイティブとの接続も簡単
 The Kotlin Blog

    https://blog.jetbrains.com/ja/kotlin/2023/05/compose-multiplatform-for-ios-is-in-alpha/ 20 @main struct iOSApp: App { var body: some Scene { WindowGroup { ZStack { Color.white.ignoresSafeArea(.all) // status bar color ContentView() }.preferredColorScheme(.light) } } } struct ContentView: View { var body: some View { ComposeView() .ignoresSafeArea(.all, edges: .bottom) } } struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { Main_iosKt.MainViewController() } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} } iOSプロジェクト側(iosAppパッケージ)
  13. © ZOZO, Inc. fun MainViewController() = ComposeUIViewController { App() }

    @Composable fun App() { MaterialTheme { Screen() } } @Composable fun Screen() { // 実際のホーム画面を構成するコード } 2.4 優れている点② 相互運用性が高い
 sharedモジュール/iosMainパッケージ sharedモジュール/commonMainパッケージ ComposeUIViewControllerが iOSネイティブとComposeを接続!!! compose-multiplatform-ios-android-template https://github.com/JetBrains/compose-multiplatform-ios-android-template/
  14. © ZOZO, Inc. 2.5 今後注意したい動向 テーマ、デザインの方向性が未確定
 • Compose for iOSはキャンバスベースのレンダリング


    ◦ iOSとAndroidアプリケーションはどちらも同じように表示される
 • 現状Compose Multiplatformは全てのプラットフォームでMaterialのウィジェットが適用されている
 • ターゲットのプラットフォームの外観にUIを寄せていく仕組みを作るのか、統一的なUIにしていくかは未確定とのこ と
 
 
 => もしCompose for iOSがiOSへデザインを寄せずにAndroidライクなデザインのみを提供することになったら、Stable になっても導入は難しそう
 The Kotlin Blog https://blog.jetbrains.com/ja/kotlin/2023/05/compose-multiplatform-for-ios-is-in-alpha/ 23
  15. © ZOZO, Inc. 28 
 • カルーセルをスライドした時のテキストのアニメーションの部分
 
 
 •

    複雑なUIである、商品のGrid状のスライダー
 
 
 → 作れます。そう、Compose for iOSならね。
 実装上のポイント

  16. © ZOZO, Inc. 3.2 カルーセル部分の作成
 • HorizontalPagerを使用
 
 
 •

    要素のスライド時に、タイトルとサブテキストだけ
 アニメーション付きでスライドインする
 
 30
  17. © ZOZO, Inc. カルーセルの作成
 31 • HorizontalPagerで横方向のカルーセルを実装する。ページの 設定、ページ間のマージン、ページの表示範囲等を引数で設定 している。 •

    Android開発の時と同じ書き方でカルーセルを実装できる。 @Composable fun Carousel( modifier: Modifier = Modifier, items: List<CarouselItemModel>, ) { val pagerState = rememberPagerState() Column(/* Columnの諸設定 */) { HorizontalPager( pageCount = items.size, state = pagerState, contentPadding = PaddingValues(horizontal = 20.dp), pageSpacing = 12.dp, ) { page -> CarouselItem(/* 後述します */) } // DotsIndicatorの実装 } }
  18. © ZOZO, Inc. カルーセルの要素の作成 
 32 • BoxWithConstraintsでComposableの横幅を計測しつつ、要素を重ねるよ うに配置していく • 引数で受け取ったpageOffsetFractionと、BoxWithConstraintsで計測した

    横幅で、カルーセルのスライド幅を算出している • Modifier.graphicsLayerで、カルーセル自体 < タイトル < サブテキストの順 番でスライドスピードが早くなるようなアニメーションを設定している @Composable fun CarouselItem( // その他の引数 pageOffsetFraction: Float, ) { BoxWithConstraints(/* BoxWithConstraintsの諸設定 */) { val width = LocalDensity.current.run { constraints.maxWidth.toDp() } val pageOffset = width.value * pageOffsetFraction Image(/* カルーセルの画像 */) Column(/* タイトルとサブテキストを保持する Column */) { Text( modifier = Modifier .graphicsLayer { translationX = - pageOffset * 1.8f }, // タイトルのその他の設定 ) Text( modifier = Modifier .graphicsLayer { translationX = - pageOffset * 2.2f }, // サブテキストのその他の設定 ) } } }
  19. © ZOZO, Inc. スライダーの作成
 35 • 引数でrowFixedCountとして受け取った行数を、 LazyHorizontalGridのrowsで渡す • LazyHorizontalGridの内部で、SliderItemというスライダーの要

    素のコンポーザブルを配置する @Composable fun Slider( // その他引数 sliderItemModels: List<sliderItemModel>, rowFixedCount: Int, ) { Column(/* Column 設定 */) { Text(/* セクションのタイトル(アイテムランキング)表示 */) LazyHorizontalGrid( // Modifier設定 rows = GridCells.Fixed(rowFixedCount), ) { sliderItemModels.forEachIndexed { index, model -> item { SliderItem(/* 後述します */) } } } } }
  20. © ZOZO, Inc. スライダーの要素の作成
 36 • SliderItemModelとして表示要素(ブランド名、値段、etc..)を受 け取る • RowとColumnを組み合わせた基本的なレイアウト

    @Composable fun SliderItem( model: SliderItemModel, // その他引数 ) { Column( modifier = modifier.width(120.dp) ) { Image(/* 商品画像 */) Column( modifier = Modifier.fillMaxWidth() ) { Row(/* 1行目のブランド名の部分 */) { Text(/* Brand Name */) } Row(/* 2行目の値段とハートの部分 */) { Text(/* 値段 */) Image(/* ハート */) } } } }
  21. © ZOZO, Inc. 3.4 それ以外はハリボテです
 38 • 実際に押せたり検索できたりはせず、見た目だけ作成 • Columnの内部のZOZOTOWNのロゴを保持しているRowで

    は、左端のSpacerと、通知とカートのアイコンを保持するRow にweight(1f)を指定することでZOZOTOWNのロゴで余った部 分を等分している @Composable fun TopAppBar() { Column( modifier = Modifier .background(color = ZozoColor.GrayScaleFE) ) { Row(/* 設定 */) { Spacer(modifier = Modifier.weight(1f)) Icon(/* ZOZOTOWNのロゴ */) Row( modifier = Modifier.weight(1f) ) { /* 通知とカートのアイコン */ } } SearchBar(/* 検索バー */) TabLayout(/* すべて、シューズ、コスメのTabLayout */) } }
  22. © ZOZO, Inc. 39 • これが画面に表示されるだけで、アプリのホーム画面がそ れっぽくなった • シンプルにRowに画像を3つ載せている @Composable

    fun AttributeSelector( modifier: Modifier = Modifier ) { Row( modifier = modifier.padding(vertical = 12.dp) ) { Image(/* メンズ */) Image(/* レディース */) Image(/* キッズ */) } } メンズ、レディース、キッズの選択UI

  23. © ZOZO, Inc. まとめ
 • KMMと合わせて、Kotlinエコシステムでアプリを作っていけるようになれば嬉しい。ワクワク。 • Swiftをほとんど書かず、Androidアプリを作ってる時と同じ感覚でiOSアプリを作れた。 • やっぱり両OSのコードを共通化できると嬉しい。

    • カメラ等ネイティブの機能をふんだんに活用したりといったことは今回していないので、もしかしたら落とし穴はたくさんある かもしれない。
 Kotlin Multiplatform https://kotlinlang.org/docs/multiplatform.html
 
 42