Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

© ZOZO, Inc. 株式会社ZOZO
 ZOZOTOWN開発本部 ZOZOTOWNアプリ部 Android1ブロック
 財部 彰太
 食べる事が好きです。
 2

Slide 3

Slide 3 text

© ZOZO, Inc. 株式会社ZOZO
 ZOZOTOWN開発本部 ZOZOTOWNアプリ部 
 Android2ブロック
 井上 晃平
 最近はワンパンマンにハマっていて、ONE先生版と村田先 生版の両方を読み返した
 
 温泉旅館にいくために、そろそろ自動車免許が欲しい
 3

Slide 4

Slide 4 text

© 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

Slide 5

Slide 5 text

© ZOZO, Inc. 注意 
 Compose for iOSは2023年7月現在alpha版
 -> 今後大きな変更が加わる可能性がある
 
 目的
 すぐ仕事で使ってみてください!というよりは、今後の可能性をお伝えしたい。
 
 フィードバック窓口
 https://github.com/JetBrains/compose-multiplatform/issues
 
 1 発表内容に関する注意と登壇の目的
 
 
 KotlinConf’23 https://kotlinconf.com/talks/ 5

Slide 6

Slide 6 text

© ZOZO, Inc. 6 Compose for iOSの基礎知識

Slide 7

Slide 7 text

© ZOZO, Inc. 7 JetBrainsが作る
 マルチプラットフォームの世界

Slide 8

Slide 8 text

© 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

Slide 9

Slide 9 text

© ZOZO, Inc. 9 Compose for iOSとは

Slide 10

Slide 10 text

© ZOZO, Inc. Compose MultiplatformのiOSへの対応がalpha版に!!
 Compose Multiplatform https://www.jetbrains.com/ja-jp/lp/compose-multiplatform/ 10 Button( onClick = {   //hogehoge } ) { Text("Hello $platform") } 2.2 Compose for iOSとは


Slide 11

Slide 11 text

© ZOZO, Inc. compose-multiplatform-ios-android-template https://github.com/JetBrains/compose-multiplatform-ios-android-template/#readme 11 2.2 Compose for iOSとは
 


Slide 12

Slide 12 text

© 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

Slide 13

Slide 13 text

© ZOZO, Inc. 13 レンダリング

Slide 14

Slide 14 text

© 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

Slide 15

Slide 15 text

© 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

Slide 16

Slide 16 text

© ZOZO, Inc. 16 Compose for iOSの優れている点

Slide 17

Slide 17 text

© 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

Slide 18

Slide 18 text

© 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

Slide 19

Slide 19 text

© 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

Slide 20

Slide 20 text

© 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パッケージ)

Slide 21

Slide 21 text

© 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/

Slide 22

Slide 22 text

© ZOZO, Inc. 22 今後注意したい動向

Slide 23

Slide 23 text

© 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

Slide 24

Slide 24 text

© ZOZO, Inc. 24 ZOZOTOWNをCompose for iOSで作った

Slide 25

Slide 25 text

© ZOZO, Inc. 25 今回作ったもの

Slide 26

Slide 26 text

© ZOZO, Inc. 3.1 今回作った物
 Compose Multiplatformにより一つのコードで、iOSとAndroid版のホーム画面を作成
 26 iOS版
 Android版


Slide 27

Slide 27 text

© ZOZO, Inc. 27 動画だとこんな感じ


Slide 28

Slide 28 text

© ZOZO, Inc. 28 
 ● カルーセルをスライドした時のテキストのアニメーションの部分
 
 
 ● 複雑なUIである、商品のGrid状のスライダー
 
 
 → 作れます。そう、Compose for iOSならね。
 実装上のポイント


Slide 29

Slide 29 text

© ZOZO, Inc. 29 カルーセル部分の作成

Slide 30

Slide 30 text

© ZOZO, Inc. 3.2 カルーセル部分の作成
 ● HorizontalPagerを使用
 
 
 ● 要素のスライド時に、タイトルとサブテキストだけ
 アニメーション付きでスライドインする
 
 30

Slide 31

Slide 31 text

© ZOZO, Inc. カルーセルの作成
 31 ● HorizontalPagerで横方向のカルーセルを実装する。ページの 設定、ページ間のマージン、ページの表示範囲等を引数で設定 している。 ● Android開発の時と同じ書き方でカルーセルを実装できる。 @Composable fun Carousel( modifier: Modifier = Modifier, items: List, ) { val pagerState = rememberPagerState() Column(/* Columnの諸設定 */) { HorizontalPager( pageCount = items.size, state = pagerState, contentPadding = PaddingValues(horizontal = 20.dp), pageSpacing = 12.dp, ) { page -> CarouselItem(/* 後述します */) } // DotsIndicatorの実装 } }

Slide 32

Slide 32 text

© 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 }, // サブテキストのその他の設定 ) } } }

Slide 33

Slide 33 text

© ZOZO, Inc. 33 スライダー部分の作成

Slide 34

Slide 34 text

© ZOZO, Inc. 3.3 スライダー部分の作成
 34 ● LazyHorizontalGridを使用 ● GridCells.Fixedで2を設定することで、2行の横方 向のスライダーを実現

Slide 35

Slide 35 text

© ZOZO, Inc. スライダーの作成
 35 ● 引数でrowFixedCountとして受け取った行数を、 LazyHorizontalGridのrowsで渡す ● LazyHorizontalGridの内部で、SliderItemというスライダーの要 素のコンポーザブルを配置する @Composable fun Slider( // その他引数 sliderItemModels: List, rowFixedCount: Int, ) { Column(/* Column 設定 */) { Text(/* セクションのタイトル(アイテムランキング)表示 */) LazyHorizontalGrid( // Modifier設定 rows = GridCells.Fixed(rowFixedCount), ) { sliderItemModels.forEachIndexed { index, model -> item { SliderItem(/* 後述します */) } } } } }

Slide 36

Slide 36 text

© 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(/* ハート */) } } } }

Slide 37

Slide 37 text

© ZOZO, Inc. 37 それ以外はハリボテです

Slide 38

Slide 38 text

© 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 */) } }

Slide 39

Slide 39 text

© ZOZO, Inc. 39 ● これが画面に表示されるだけで、アプリのホーム画面がそ れっぽくなった ● シンプルにRowに画像を3つ載せている @Composable fun AttributeSelector( modifier: Modifier = Modifier ) { Row( modifier = modifier.padding(vertical = 12.dp) ) { Image(/* メンズ */) Image(/* レディース */) Image(/* キッズ */) } } メンズ、レディース、キッズの選択UI


Slide 40

Slide 40 text

© ZOZO, Inc. 40 完成!!


Slide 41

Slide 41 text

© ZOZO, Inc. 41 まとめ

Slide 42

Slide 42 text

© ZOZO, Inc. まとめ
 ● KMMと合わせて、Kotlinエコシステムでアプリを作っていけるようになれば嬉しい。ワクワク。 ● Swiftをほとんど書かず、Androidアプリを作ってる時と同じ感覚でiOSアプリを作れた。 ● やっぱり両OSのコードを共通化できると嬉しい。 ● カメラ等ネイティブの機能をふんだんに活用したりといったことは今回していないので、もしかしたら落とし穴はたくさんある かもしれない。
 Kotlin Multiplatform https://kotlinlang.org/docs/multiplatform.html
 
 42

Slide 43

Slide 43 text

No content