Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Jetpack Compose vs SwiftUI (Android Worldwide 2...
Search
Mohit S
October 26, 2021
Programming
1.2k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Jetpack Compose vs SwiftUI (Android Worldwide 2021)
Mohit S
October 26, 2021
More Decks by Mohit S
See All by Mohit S
Guide to Improving Compose Performance
heyitsmohit
0
350
Building Shared UIs across Platforms with Compose
heyitsmohit
1
740
Building Multiplatform Apps with Compose
heyitsmohit
2
650
Building StateFlows with Jetpack Compose
heyitsmohit
6
2k
Building Android Testing Infrastructure
heyitsmohit
1
660
Migrating to Kotlin State & Shared Flows
heyitsmohit
1
910
Using Square Workflow for Android & iOS
heyitsmohit
1
550
Building Android Infrastructure Teams at Scale
heyitsmohit
3
450
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
690
Other Decks in Programming
See All in Programming
AgentCore CLI で進化した AWS での AI エージェントの作り方 : 必要な機能を必要な時に
icoxfog417
PRO
3
370
Kiroで創り、AgentCoreで繋ぐ!AWSで実践する「AI-DLC」から「AIエージェント統合」までの最新地図
licux
4
770
20260914 AIエージェント時代のPlatform Engineering LLM基盤とプロダクトの責務境界線
kanfab1
7
2.2k
Go × SIMDで高速化するベクトル検索 ~ルーフラインモデルでSIMDが効く境界を探れ! ~
po3rin
1
2.9k
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
520
スマートフォンでモールス信号を送受信する 〜スマートフォンのLEDとカメラで作る光通信の設計と実装〜
atsuki_seo
0
170
Workers Cache を知る
syumai
0
210
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
500
マイコン向けの軽量Ruby「PicoRuby」で各種デバイスを制御するネイティブアプリの実現手法
bash0c7
0
440
Jetpack Compose メカニズム
skydoves
0
470
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
170
すこし踏み込む CancellationToken
htkym
2
910
Featured
See All Featured
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
820
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.5k
Paper Plane (Part 1)
katiecoart
PRO
2
11k
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2.2k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
250
1.3M
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
How to Talk to Developers About Accessibility
jct
2
550
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
610
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.6k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
700
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
670
Transcript
Mohit Sarveiya Jetpack Compose vs SwiftUI @heyitsmohit
Jetpack Compose vs SwiftUI • Concurrency • Managing State •
Navigation
Benefits • Understand different approaches in Compose & SwiftUI •
Collaborate with iOS Dev on architecture.
Building Android & iOS App Example App
iOS Android
• Display Images • Search
• Like an Image • Display location, tags
Jetpack Compose • MutableStateOf • Remember • Side Effects •
@State • @ObservedObject • @Published SwiftUI
Jetpack Compose Views, Managing State
• Display Images • Search
Images List Screen View Model State
Images List Screen View Model State
val uiState = MutableStateFlow<ImagesUiState>()
val uiState = MutableStateFlow<ImagesUiState>()
sealed class ImagesUiState { object Loading: ImagesUiState() }
sealed class ImagesUiState { data class Success( val images:
List<ImageData> ): ImagesUiState() }
sealed class ImagesUiState { data class Error( val errorMessage:
String ): ImagesUiState() }
val uiState = MutableStateFlow(ImageUiState.Loading)
Images List Screen View Model State
init { viewModelScope.launch { val images = repository.getImages() _uiState.value =
ImagesUiState.Success(images = images) } }
init { viewModelScope.launch { val images = repository.getImages() _uiState.value =
ImagesUiState.Success(images = images) } }
init { viewModelScope.launch { val images = repository.getImages() _uiState.value =
ImagesUiState.Success(images = images) } }
init { viewModelScope.launch { val images = repository.getImages() uiState.value =
ImagesUiState.Success(images = images) } }
init { viewModelScope.launch { val images = repository.getImages() uiState.value =
ImagesUiState.Success(images = images) } }
Images List Screen View Model State
@Composable fun ImagesListScreen() { )
Images List Screen View Model State
Streams of data • LiveData -> observeAsState • Flow ->
collectAsState • Observable -> subscribeAsState
@Composable fun ImagesListScreen() { val uiState = uiState.collectAsState().value )
@Composable fun ImagesListScreen() { when (uiState) { } )
@Composable fun ImagesListScreen() { when (uiState) { ImagesUiState.Loading ImagesUiState.Success
ImagesUiState.Error } )
ImagesUiState.Loading -> { CircularProgressIndicator() }
ImagesUiState.Loading -> { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally ) { CircularProgressIndicator() } }
ImagesUiState.Loading -> { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally ) { CircularProgressIndicator() } }
Grid Search
ImagesUiState.Success -> { }
ImagesUiState.Success -> { Column { } }
Column { SearchView { } LazyVerticalGrid() { } }
Column { SearchView { } LazyVerticalGrid() { } }
LazyVerticalGrid( cells = GridCells.Fixed(3) ) { }
LazyVerticalGrid( cells = GridCells.Fixed(3) ) { items(uiState.images) {
} }
LazyVerticalGrid( cells = GridCells.Fixed(3) ) { items(uiState.images) { Image( painter
= rememberImagePainter(it.url) ) }
Image( painter = rememberImagePainter(it.url), modifier = Modifier .size(128.dp) .padding(8.dp) .contentScale
= ContentScale.Crop )
Image( painter = rememberImagePainter(it.url), modifier = Modifier .size(128.dp) .padding(8.dp) .contentScale
= ContentScale.Crop )
• Search for images • Update grid with results
Search View View Model Event
Search View View Model State
Grid Search
Search View • Remember and update search text • Perform
Search
@Composable fun SearchView() { OutlinedTextField( value = , onValueChange =
{ } ) }
OutlinedTextField( value = , onValueChange = { } )
OutlinedTextField( value = , onValueChange = { } )
val queryState = remember { mutableStateOf("") }
val queryState = remember { mutableStateOf("") } OutlinedTextField( value =
queryState.value, onValueChange = { queryState.value = it } )
val queryState = remember { mutableStateOf("") } OutlinedTextField( value =
queryState.value, onValueChange = { queryState.value = it } )
Search View • Remember and update search text • Perform
Search
Search View View Model Event
@Composable fun SearchView( onSearch: (String) -> Unit ) { }
OutlinedTextField( keyboardActions = KeyboardActions( onDone = { onSearch(queryState.value) } )
)
OutlinedTextField( keyboardActions = KeyboardActions( onDone = { onSearch(queryState.value) } )
)
Column { SearchView { imagesViewModel.searchImages(query) } }
class ImagesViewModel() fun searchImages(query: String) { } }
viewModelScope.launch { uiState.value = ImagesUiState.Loading val images = repository.searchImages(query =
query) uiState.value = ImagesUiState.Success(images = images) }
viewModelScope.launch { uiState.value = ImagesUiState.Loading val images = repo.searchImages(query =
query) uiState.value = ImagesUiState.Success(images = images) }
viewModelScope.launch { uiState.value = ImagesUiState.Loading val images = repo.searchImages(query =
query) uiState.value = ImagesUiState.Success(images = images) }
None
• Display location, tags • Like an Image
@Composable fun ImageDetailsScreen() { }
@Composable fun ImageDetailsScreen(imageId: Int?) { }
Image Details Screen View Model Side Effect
@Composable fun ImageDetailsScreen(imageId: Int?) { }
LaunchedEffect(key1 = imageId) { }
LaunchedEffect(key1 = imageId) { viewModel.getImage(imageId) }
val uiState = uiState.collectAsState().value
when (uiState) { ImageDetailsUiState.Loading ImageDetailsUiState.Success ImageDetailsUiState.Error }
• Display location, tags • Like an Image
Image Toolbar
Scaffold( topBar = { }, content = { }
)
Scaffold( topBar = { TopAppBar( title = { Text(text =
"Images") } } )
Scaffold( actions = { Icon( Icons.Default.FavoriteBorder ) } )
• Like Image -> • Unlike Image ->
class ImageDetailsViewModel { var isLiked = mutableStateOf(false) }
fun likeImage(imageId: Int) { }
fun likeImage(imageId: Int) { isLiked.value = true }
fun likeImage(imageId: Int) { isLiked.value = true viewModelScope.launch { val
result = repo.likeImage(imageId) result.onFailure { isLiked.value = false } } }
fun likeImage(imageId: Int) { isLiked.value = true viewModelScope.launch { val
result = repo.likeImage(imageId) result.onFailure { isLiked.value = false } } }
fun likeImage(imageId: Int) { isLiked.value = true viewModelScope.launch { val
result = repo.likeImage(imageId) result.onFailure { isLiked.value = false } } }
val isLikeState = remember { viewModel.isLiked }
Icon( if (isLikeState.value.not()) Icons.Default.FavoriteBorder else Icons.Filled.Favorite )
Icon( modifier = Modifier.clickable { viewModel.likeImage(imageId) } )
• Display location, tags • Like an Image
None
Nav Host Images List View Images Details View
Images List View Images Details View
val navController = rememberNavController()
val navController = rememberNavController() NavHost(navController) { }
NavHost(navController) { composable("images_list") { } composable(“images_details") { } }
NavHost(navController) { composable("images_list") { ImagesListScreen() } composable(“images_details") { ImageDetailsScreen()
} }
composable("images_list") { ImagesListScreen { } }
composable("images_list") { ImagesListScreen { navController.navigate(“images_details/{imageId}“) } }
composable(“images_details") { ImagesDetailsScreen( backStackEntry.arguments ?. getString(“imageId") ?. toInt(), ... )
}
None
Jetpack Compose • MutableStateOf • Remember • Side Effects •
@State • @ObservedObject • @Published SwiftUI
Jetpack Compose • MutableStateOf • Remember • Side Effects
SwiftUI Views, Concurrency, Managing State, Navigation
• Display Images • Search
Observed Object View Observe
Observed Object View Update
class ImagesRepository: ObservableObject { }
class ImagesRepository: ObservableObject { @Published var images: [ImageData] = []
}
Swift Concurrency • Async/Await • Tasks
func getImages() async { }
func getImages() async { images = apiService.getImages() }
func getImages() async { images = apiService.getImages() } Publishing changes
from background threads is not allowed; make sure to publish values from the main thread !
@MainActor func getImages() async { images = apiService.getImages() }
Observed Object View Observe
struct ContentView: View { }
struct ContentView: View { }
struct ContentView: View { var body: some View { }
}
struct ContentView: View { @StateObject var repository = ImagesRepository() }
struct ContentView: View { @StateObject var repository = ImagesRepository() }
Best Practice Use StateObject when creating objects.
struct ContentView: View { @StateObject var repository = ImagesRepository()
}.task { }
struct ContentView: View { @StateObject var repository = ImagesRepository()
}.task { await repository.getImages() }
struct ContentView: View { @State var showProgressBar = true
}
ZStack { }
ZStack { if (showProgressBar) { } }
ZStack { if (showProgressBar) { ProgressView() .progressViewStyle( CircularProgressViewStyle(tint: .blue) )
} }
struct ContentView: View { @State var showProgressBar = true
}.task { await repository.getImages() showProgressBar = false }
struct ContentView: View {
• Display Images • Search • Like an image
LazyVGrid(spacing: 10) { }
LazyVGrid(spacing: 10) { ForEach(repo.images, id: \.id) { item in }
}
LazyVGrid(spacing: 10) { ForEach(repo.images, id: \.id) { item in Image(item.url)
.resizable() .frame(minWidth: 0, maxWidth: .infinity) } }
• Display Images • Search • Like an image
@State var searchText = ""
LazyVGrid(spacing: 10) { }.searchable()
LazyVGrid(spacing: 10) { }.searchable(text: $searchText)
LazyVGrid(spacing: 10) { }.searchable(text: $searchText) Two way binding
@State var searchText = “" var searchResults: [ImageData] { if
searchText.isEmpty { return repository.images } else { return filterImages(searchText) }
• Display Images • Search • Like an image
struct ImageDetailsView: View { let imageData: ImageData }
struct ImageDetailsView: View { @State var isLiked: Bool = false
}
struct ImageDetailsView: View { @State var isLiked: Bool =
false var body: some View { }.onAppear { } }
struct ImageDetailsView: View { @State var isLiked: Bool =
false var body: some View { }.onAppear { isLiked = imageData.isLiked } }
struct ImageDetailsView: View {
Button(action: { isLiked.toggle() }) { Image(systemName: isLiked ? "heart.fill" :
"heart") }
struct ImageDetailsView: View { @State var isLiked: Bool =
false var body: some View { }.onChange(of: isLiked) { } }
@State var likeTask: Task<Void, Never>? = nil
.onChange(of: isLiked) { newValue in if (newValue) { likeTask ?.
cancel() likeTask = Task { await repository.likeImage(id: imageData.id) } } }
.onChange(of: isLiked) { newValue in if (newValue) { likeTask ?.
cancel() likeTask = Task { await repository.likeImage(id: imageData.id) } } }
.onChange(of: isLiked) { newValue in if (newValue) { likeTask ?.
cancel() likeTask = Task { await repository.likeImage(id: imageData.id) } } }
• Display Images • Search • Like an image
None
NavigationView { }
NavigationView { NavigationLink( destination: ImageDetailsView( repository: repository, imageData: item )
) }
None
toolbar { ToolbarItem { Button(action: { }) { Image(systemName:
"square.grid.2x2") } } }
Jetpack Compose • MutableStateOf • Remember • Side Effects •
@State • @ObservedObject • @Published SwiftUI
Resources • github.com/msya/ImagesComposeApp • github.com/msya/ImagesSwiftUIApp
Thank You! www.codingwithmohit.com @heyitsmohit