Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Compose Multiplatform for iOS開発でぶつかった壁
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
ねも
January 24, 2024
Technology
0
1.7k
Compose Multiplatform for iOS開発でぶつかった壁
2024/1/23に行われたZOZO Tech Meetup -Android-における発表資料です。
ねも
January 24, 2024
Tweet
Share
More Decks by ねも
See All by ねも
ノンプログラマのための ~アルゴリズムパズル プログラマのための数学パズル入門~
kohei_inoue
0
84
ノンデザイナーズ・デザインブックを読んだので名刺作ってみた
kohei_inoue
0
93
Compose Multiplatform for iOSで音声再生しようぜ!!
kohei_inoue
0
340
夏 × Jetpack Compose
kohei_inoue
0
87
Compose for iOS for ZOZOTOWN
kohei_inoue
0
1.7k
Other Decks in Technology
See All in Technology
名刺メーカーDevグループ 紹介資料
sansan33
PRO
0
1k
Agent Skils
dip_tech
PRO
0
110
Oracle Cloud Observability and Management Platform - OCI 運用監視サービス概要 -
oracle4engineer
PRO
2
14k
ブロックテーマ、WordPress でウェブサイトをつくるということ / 2026.02.07 Gifu WordPress Meetup
torounit
0
190
超初心者からでも大丈夫!オープンソース半導体の楽しみ方〜今こそ!オレオレチップをつくろう〜
keropiyo
0
110
AIと新時代を切り拓く。これからのSREとメルカリIBISの挑戦
0gm
1
2.6k
~Everything as Codeを諦めない~ 後からCDK
mu7889yoon
3
420
GitHub Issue Templates + Coding Agentで簡単みんなでIaC/Easy IaC for Everyone with GitHub Issue Templates + Coding Agent
aeonpeople
1
240
SREじゃなかった僕らがenablingを通じて「SRE実践者」になるまでのリアル / SRE Kaigi 2026
aeonpeople
6
2.5k
Embedded SREの終わりを設計する 「なんとなく」から計画的な自立支援へ
sansantech
PRO
3
2.5k
コスト削減から「セキュリティと利便性」を担うプラットフォームへ
sansantech
PRO
3
1.5k
StrandsとNeptuneを使ってナレッジグラフを構築する
yakumo
1
120
Featured
See All Featured
Done Done
chrislema
186
16k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
170
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
1.8k
RailsConf 2023
tenderlove
30
1.3k
How to build a perfect <img>
jonoalderson
1
4.9k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
3.9k
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
0
3.4k
Building an army of robots
kneath
306
46k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
750
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.6k
Transcript
Compose Multiplatform for iOS ։ൃͰͿ͔ͭͬͨน XʢچTwitterʣ: https://twitter.com/nemo_855 GitHub: https://github.com/nemo-855 Qiita:
https://qiita.com/nemo-855 Android ΤϯδχΞ Ҫ্ ߊฏ / Ͷ 1
Ҫ্ߊฏ @nemo_855 XͰϑΥϩʔ͓ئ͍͠·͢🙏 • ZOZOTOWN։ൃຊ෦ ZOZOTOWN։ൃ1෦ Android2ϒϩοΫ • Α͏͘VIVANTΛݟ࢝Ί·ͨ͠ 2
ݸਓͰiOSͱAndroidΞϓϦΛ࡞͠·ͨ͠ʂ 3 iOS൛ Android൛
Jetpack Composeͱಉ͡ॻ͖ຯͰ iOSΞϓϦΛ࡞ΕΔͷ͍͢͝ʂʂ 4 iOS൛ Android൛
͚ͩͲɺɺɺ 5 iOS൛ Android൛
🍎 ࣍ 6 🍋 TextFieldΛλοϓͨ࣌͠ͷڍಈ 🍋 Android Context 🍋 ݖݶϦΫΤετͷਏΈ
🍍 TextFieldΛλοϓͨ࣌͠ͷڍಈ ͦͷ1 TextFieldΛλοϓͨ࣌͠ʹɺ ը໘֎ʹεΫϩʔϧͯ͠͠·͏ɻ 7
ComposeUiViewControllerͷconfigureҾʹ onFocusBehavior.DoNothingΛࢦఆ͢Εྑ͍ 🍍 TextFieldΛλοϓͨ࣌͠ͷڍಈ ͦͷ2 fun PostViewController( viewModel: PostViewModel, closePage:
() -> Unit, ): UIViewController { return ComposeUIViewController( configure = { onFocusBehavior = OnFocusBehavior.DoNothing }, ) { PostPage( context = IosKmpContext, viewModel = viewModel, closePage = closePage ) } } 8
໌֬ʹ͜ͷόάʹ͍ͭͯͷ GitHub issue͕ແ͔ͬͨ ↓ #3128, #3537, #3856ͳͲͷ ؔ࿈͢Δissue͔Βਪଌͯ͠ࢼߦࡨޡ͢ Δ͔͠ͳ͍ 🍍
TextFieldΛλοϓͨ࣌͠ͷڍಈ ͦͷ3 9
🍐 Android Context ͦͷ1 Compose Multiplatformͷڞ༗ϞδϡʔϧͰɺ ը૾Λѻ͏ࡍʹͲ͏ͯ͠ Android Context͕ඞཁʹͳΔ 10
🍐 Android Context ͦͷ2 11 actual fun Uri.getImageFlow(context: KmpContext): Flow<ImageBitmap>
{ return flow { val bitmap = this.getBitmapOrNull(context.contentResolver) if (bitmap != null) { emit(bitmap) } } } private fun Uri.getBitmapOrNull(contentResolver: ContentResolver): ImageBitmap? { return kotlin.runCatching { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val source = ImageDecoder.createSource(contentResolver, this) ImageDecoder.decodeBitmap(source) } else { MediaStore.Images.Media.getBitmap(contentResolver, this) } }.getOrNull()?.asImageBitmap() }
🍐 Android Context ͦͷ3 // commonMain expect abstract class KmpContext
// androidMain actual typealias KmpContext = android.content.Context // iosMain actual abstract class KmpContext object IosContext : KmpContext() kotlinlang SlackνϟϯωϧͰ հ͞Ε͍ͯͨख๏ ӈਤͷΑ͏ͳKmpContextΛ࡞͢Δ 12
🍐 Android Context ͦͷ3 // commonMain expect abstract class KmpContext
// androidMain actual typealias KmpContext = android.content.Context // iosMain actual abstract class KmpContext object IosContext : KmpContext() AndroidଆͰAndroidͷContextɺ iOSଆͰμϛʔͷContextΛ༻͢Δ 13
🍐 Android Context ͦͷ4 AndroidଆͷࣄΛڞ௨Ϟδϡʔϧʹແཧཧ ࣋ͪࠐΜͰ͍ΔͷͰෳࡶͳॲཧʹͳ͍ͬͯΔ Compose MultiplatformಠࣗͷਏΈ 14
ը૾ͷΞΫηεͷࡍʹͦΕͧΕͷOSͰݖݶ ΛϦΫΤετ͠ͳ͍ͱ͍͚ͳ͍͕ɺ ͦΕΒͷॲཧΛڞ௨Խ͢Δ͜ͱ͕͍͠ ↓ ίϯϙʔωϯτͷߏΛ͢Δ 🥭 ݖݶϦΫΤετͷਏΈ ͦͷ1 15
🥭 ݖݶϦΫΤετͷਏΈ ͦͷ3 val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()) {
// ͜͜ͰݖݶͷεςʔλεʹԠͯ͡ذॲཧ } val permission = if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.TIRAMISU) { READ_EXTERNAL_STORAGE } else { READ_MEDIA_IMAGES } launcher.launch(permission) PHPhotoLibrary.requestAuthorization { status in // ίίͰݖݶͷεςʔλεʹԠͯ͡ذॲཧ } Android iOS 16
🥭 ݖݶϦΫΤετͷਏΈ ͦͷ3 val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()) {
// ͜͜ͰݖݶͷεςʔλεʹԠͯ͡ذॲཧ } val permission = if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.TIRAMISU) { READ_EXTERNAL_STORAGE } else { READ_MEDIA_IMAGES } launcher.launch(permission) PHPhotoLibrary.requestAuthorization { status in // ίίͰݖݶͷεςʔλεʹԠͯ͡ذॲཧ } Android iOS ݖݶϦΫΤετͷॲཧ & ݖݶεςʔλεͷछྨ͕ҟͳΔͷͰɺ expect / actualͰڞ௨Խ͕͠ਏ͍ 17
commonMain 🥭 ݖݶϦΫΤετͷਏΈ ͦͷ4 androidMain iosApp Template Page View View
ը໘ભҠॲཧ ʢCompose Navigationʣ View ը໘ભҠॲཧ (SwiftUI) 18
commonMain 🥭 ݖݶϦΫΤετͷਏΈ ͦͷ5 androidMain iosApp Template Page View View
ը໘ભҠॲཧ ʢCompose Navigationʣ View ը໘ભҠॲཧ (SwiftUI) ɾը໘ͷঢ়ଶͷཧ ɾViewModelͷอ࣋ expect / actualͰڞ௨ԽͰ͖ͳ͍ॲཧ ྫʣ ݖݶϦΫΤετॲཧ 19
🥭 ݖݶϦΫΤετͷਏΈ ͦͷ6 @Composable fun PostView(closeView: () -> Unit) {
// ViewModelऔಘॲཧ val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()) { // no-op } LaunchedEffect(Unit) { if (!PermissionChecker(context).isGallaryPermissionGranted()) { PermissionRequester().requestGallaryPermission(launcher) } } PostPage( context = context, viewModel = viewModel, closePage = closeView, ) } 20
🥭 ݖݶϦΫΤετͷਏΈ ͦͷ7 private struct PostView: UIViewControllerRepresentable { // লུ:
ϓϩύςΟͷॳظԽॲཧ func makeUIViewController(context: Context) -> UIViewController { if (!isGallaryPermissionGranted()) { requestGallaryPermission(completion: { isGranted in // no-op }) } return MainKt.PostViewController( viewModel: viewModel, closePage: closeView ) } // লུ: updateUIViewController, dismantleUIViewController } 21
͕ࣗͿ͔ͭͬͨนΛհ͢Δ͜ͱͰɺ ผͷਓ͕ಉ͡༰ͰࠔΒͳ͍Α͏ʹͳͬͯཉ͍͠ʂʂ 22
🍇 ࢀߟ ✓ https://github.com/JetBrains/compose-multiplatform/issues/3128 ✓ https://github.com/JetBrains/compose-multiplatform/issues/3537 ✓ https://github.com/JetBrains/compose-multiplatform/issues/3856 ✓ https://kotlinlang.org/community/
23
24