Slide 1

Slide 1 text

Compose MultiplatformとSwiftUIで作る ハイブリッドモバイルアプリ: コード共有とUI融合の実践 Yena Hwang Assistant Manager @ Kinto Technologies

Slide 2

Slide 2 text

Introduction

Slide 3

Slide 3 text

Introduction @Yellow_yena @YenaHwang432434 @viviennehwang Yena Hwang Android Development Assistant Manager / KMP Team Lead KINTO Technologies ( a Toyota Group Company)

Slide 4

Slide 4 text

From Android to KMP-Evolution Our team started with Android development, and now we focus on Kotlin Multiplatform to deliver cross-platform experiences. Members Yao Xie – Android / Compose Multiplatform Yonghui Chen – Android / iOS / Kotlin Multiplatform Garamoi Choi– Android / Kotlin Multiplatform / API @hemoptysisheart KMP Team

Slide 5

Slide 5 text

AI/AR SDK 見積り 申し込み 保存・共有 通知 my route KINTOかんたん申し込み AI/AR SDK Project

Slide 6

Slide 6 text

Scan QR Code & Ask your questions here! Slido code #67258131 https://app.sli.do/event/455CaD4m pV23uyVgNjT2CZ

Slide 7

Slide 7 text

Agenda Why Compose Multiplatform? Why Hybrid Architecture? Implementation Strategies Sample code Performance Comparison Pitfalls & Best Practices

Slide 8

Slide 8 text

Agenda Why Compose Multiplatform? Why Hybrid Architecture? Implementation Strategies Sample code Performance Comparison Pitfalls & Best Practices

Slide 9

Slide 9 text

Agenda Why Compose Multiplatform? Why Hybrid Architecture? Implementation Strategies Sample code Performance Comparison Pitfalls & Best Practices

Slide 10

Slide 10 text

Agenda Why Compose Multiplatform? Why Hybrid Architecture? Implementation Strategies Sample code Performance Comparison Pitfalls & Best Practices

Slide 11

Slide 11 text

Agenda Why Compose Multiplatform? Why Hybrid Architecture? Implementation Strategies Sample code Performance Comparison Pitfalls & Best Practices

Slide 12

Slide 12 text

Agenda Why Compose Multiplatform? Why Hybrid Architecture? Implementation Strategies Sample code Performance Comparison Pitfalls & Best Practices

Slide 13

Slide 13 text

Agenda Why Compose Multiplatform? Why Hybrid Architecture? Implementation Strategies Sample code Performance Comparison Pitfalls & Best Practices

Slide 14

Slide 14 text

Context Last year, we began a new product with our existing code - Jetpack x SwiftUI.From the start, the prototype had to be: • High Speed: very short timeline • Low Cost: small dev team • High Quality: UI performance equal to native apps High Quality Low Cost High Speed

Slide 15

Slide 15 text

Context But as you know, “High Speed, Low Cost, and High Quality” often feels like an impossible triangle — you usually only get two. That was the challenge we faced. High Quality Low Cost High Speed Slow High Cost X IMPOSSIBLE Low Quality

Slide 16

Slide 16 text

KMP + CMP We saw an opportunity to go a step further – share both logic & UI layer across platforms. The solution?

Slide 17

Slide 17 text

This approach promised a rare mix: • Ship a prototype quickly with a small team • Native-level performance • Reuse Jetpack Compose components • Shared code for logic and UI • Higher efficiency to meet the deadline It looked like a practical way to break the “impossible triangle”.

Slide 18

Slide 18 text

What is CMP?

Slide 19

Slide 19 text

KMP: Business logic, API serviceand data management CMP: UI screens of lists / cards / forms, data-heavy details Native: Navigation / gestures, Map / Camera / Wallet, strong platform styling Quick heuristics A pragmatic blueprint for building hybrid mobile apps with CMP

Slide 20

Slide 20 text

BEST OF BOTH WORLDS: • Platform-specific UI/UX guidelines • Native component integration (Maps, Camera) • Gradual migration path • Team expertise utilization USE CASES: • Existing native apps • Platform-specific design requirements • Complex native integrations Why Hybrid Architecture?

Slide 21

Slide 21 text

Platform- specific design requirements

Slide 22

Slide 22 text

Architecture Overview

Slide 23

Slide 23 text

Project Structure my-cmp-app/ ├── composeApp/ │ ├── src/ │ │ ├── commonMain/ // CMP Shared code │ │ ├── androidMain/ // Android-specific │ │ └── iosMain/ // iOS-specific ├── iosApp/ │ ├── Views/ // SwiftUI views │ ├── Screens/ // Native screens │ └── ComposeViewContainer.swift └── shared/ // KMP domain layer ├── viewmodels/ └── models/

Slide 24

Slide 24 text

End-to-End Blueprint • Module layout for clean separation between shared and platform-specific code • Shared ViewModel contracts to decouple logic from UI • Bidirectional UI interop: • Embed CMP views in Native screens • Embed Native components inside CMP screens • Navigation & state management strategies that work across platforms

Slide 25

Slide 25 text

This hybrid structure allows seamless tab switching and demonstrates how shared and native UI components can coexist. MainScreen uses a bottom navigation bar with four tabs: Tab UI Implementation Application CMP Lineup CMP HelpCenter CMP Settings Native (SwiftUI on iOS) Demo

Slide 26

Slide 26 text

Migrating from Native Navigation to CMP-based Hybrid Navigation • Refactor existing navigation stacks to route between native & CMP • Abstract platform-specific navigation logic behind shared interfaces

Slide 27

Slide 27 text

Route-Based Navigation Strategy: Define routes as string constants Platform-specific navigation implementations Bridge between SwiftUI and Compose

Slide 28

Slide 28 text

Kotlin Navigation Implementation

Slide 29

Slide 29 text

iOS Navigation Integration

Slide 30

Slide 30 text

iOS Navigation in Practice

Slide 31

Slide 31 text

Modularization with Koin

Slide 32

Slide 32 text

Koin Benefits: Clean dependency management Reusability across platforms Module isolation Testability

Slide 33

Slide 33 text

Platform Specific DI

Slide 34

Slide 34 text

Initializing Koin

Slide 35

Slide 35 text

Dynamically Load Feature Modules with Koin

Slide 36

Slide 36 text

DUPLICATE BUSINESS LOGIC ACROSS PLATFORMS For validation (e.g. email, phone, postal code, password strength, date formats), we needed implement multiple validators.

Slide 37

Slide 37 text

TOTAL 24 x2! If implemented separately: Android: 24 validators in Kotlin iOS: 24 validators in Swift → 48 implementations total → 48 ValidationResults total → plus 48 sets of unit tests

Slide 38

Slide 38 text

The KMP solution Move all validation logic into the shared KMP module. Implement each validator once in Kotlin. Expose as common functions/classes that can be called from: • CMP screens • Android/iOS Native screens

Slide 39

Slide 39 text

SwiftUI + CMP Integration

Slide 40

Slide 40 text

Embedding Compose into SwiftUI 1 2

Slide 41

Slide 41 text

Embedding Compose into SwiftUI 3

Slide 42

Slide 42 text

Embedding SwiftUI into Compose 1

Slide 43

Slide 43 text

Embedding SwiftUI into Compose 2

Slide 44

Slide 44 text

MapView Showcase

Slide 45

Slide 45 text

DECISION MATRIX: CMP vs Native CRITERIA COMPOSE UI NATIVE UI Complex Lists Platform UX Map Integration Custom Animations Development Speed Team Expertise

Slide 46

Slide 46 text

Platform views in Flutter come with performance trade-offs - Flutter documentation

Slide 47

Slide 47 text

Difference between CMP & Flutter OVERLAY 🥲 INLINE RENDERING Fast and efficient CMP nesting Native vs Flutter nesting Native

Slide 48

Slide 48 text

Difference between Flutter & CMP Host UI → Embedded UI Rendering path Performance impact Android: AndroidView inside Compose (same View system). iOS: CMP Skia surface hosts a UIKit view via UIKitView. Very Low (Android) / Low– Moderate (iOS) Android: ComposeView in a ViewGroup (Compose runs on platform renderer). iOS: SwiftUI/UIViewController hosts ComposeUIViewController (Skia surface inside). Low (Android) / Moderate (iOS) Flutter renders via Skia/Impeller; native view injected as PlatformView (texture/layer composition). Moderate; visible stutter on heavy views (Map, WebView). Native Activity/VC hosts Flutter engine surface. Extra startup cost (engine warm- up), stable after warm-up

Slide 49

Slide 49 text

The Merits of CMP CMP Use the native MapView as-is, and render icons/routes with the native API. Moves smoothly with no performance issues. • Place native components such as MKMapView directly • Can be replaced on a per- component basis • Maintain native performance and accessibility iOS Native Compose Host UI

Slide 50

Slide 50 text

Complex Hybrid UI: Performance Comparison Framework Startup Time Memory Usage UI Smoothness Native CMP + Native Flutter+ Native

Slide 51

Slide 51 text

Every solution breeds new problems. — Arthur Bloch

Slide 52

Slide 52 text

HTTP/TLS/redirec ts/cookies Symptom/Cause: Engines differ; cookie persistence, redirects, TLS pinning vary by OS. Fix: Shared config for timeouts/cache/logging; inject per-platform engine+security policy via DI or expect/actual.

Slide 53

Slide 53 text

Regex differences Symptom/Cause: JVM uses java.util.regex, iOS/Native uses a different engine (ICU). Features like \R, variable-width lookbehind, some Unicode classes behave differently or are unsupported on iOS. Fix: Avoid “spicy” features; rewrite into simpler sub-patterns; add a small adapter so you can swap engines later if needed.

Slide 54

Slide 54 text

Time zones & DST Symptom/Cause: Time zone databases and formatting rules differ across platforms. Fix: Do all calculations in kotlinx-datetime; keep formatting as a platform concern via expect/actual.

Slide 55

Slide 55 text

Fonts & typography (CJK/emoji) Symptom/Cause: Platform fallback differs → line height, truncation, emoji width vary. Fix: Bundle fonts, declare families/weights explicitly, set lineHeight/letterSpacing explicitly.

Slide 56

Slide 56 text

Truncation/measurement differences Symptom/Cause: iOS/Skiko text layout isn’t bit-identical to Android. Fix: Use maxLines + TextOverflow.Ellipsis; avoid “exact fit” assumptions; do screenshot regression on key screens.

Slide 57

Slide 57 text

Scroll & gesture physics Symptom/Cause: iOS inertia is “slipperier”, nested scroll feels different. Caution: Abstract scroll physics knobs and branch by platform, simplify containers on complex nested scroll pages.

Slide 58

Slide 58 text

Practices: CMP/KMP lets us flexibly shift between shared and native code—maximizing reuse in prototypes, then moving toward native as products mature. CMP/KMP

Slide 59

Slide 59 text

Fully leverage CMP flexibility Start Stage (≈99% shared) Goal: ship fast, validate ideas with minimal resources. Limited Dev Resources

Slide 60

Slide 60 text

Fully leverage CMP flexibility Growth Stage (≈80% shared) Goal: scale product while enhancing native UX where it matters. Standard dev resources

Slide 61

Slide 61 text

Fully leverage CMP flexibility Maturity Stage (≈50% shared) Goal: optimize performance, long- term maintainability, and team specialization. Abundant dev resources

Slide 62

Slide 62 text

Gradual Adoption: For Mature Products Start Small 1% Safest entry point: introduce KMP/CMP in the smallest, low-risk areas.

Slide 63

Slide 63 text

Gradual Adoption: For Mature Products Start Small 1% Safest entry point: introduce KMP/CMP in the smallest, low-risk areas. Stepwise Expansion 20% Expand adoption while ensuring stability and confidence.

Slide 64

Slide 64 text

Gradual Adoption: For Mature Products Start Small 1% Safest entry point: introduce KMP/CMP in the smallest, low-risk areas. Stepwise Expansion 20% Expand adoption while ensuring stability and confidence. End Stage e.g. 50% Reach a sustainable mix of shared and native code.

Slide 65

Slide 65 text

Thank you.