If you want to develop in both native languages at the same time, this article includes several fundamental code examples for both Swift and Kotlin.
Before we get started, please take a couple seconds to follow me and 👏 clap for the article so that we can help more people learn about this useful content.
Basic Setup
When writing code in Android Studio, there is one preference that you will want to turn on in your settings, that is Auto Import and Auto Clean Up.
Unlike SwiftUI where most components are included when you “import SwiftUI” at the top of the file, Jetpack Compose is based on Kotlin/Java where you will import every component that you use. Having the Auto import settings turned on will save you a lot of time trying to figure out which version of a thing to import (I learned this the hard way). The screenshot below has the options we want to turn on highlighted.
I intend for this to be a living document, changing and improving as my knowledge grows, so if you don’t find what you’re looking for today, feel free to save this article to your bookmarks and check back later to see if it has been added.
More Resources
If you want a more in depth guide on the language difference between Swift and Kotlin then check out this article from Kodeco: https://www.kodeco.com/6754-a-comparison-of-swift-and-kotlin-languages
If you want a full list of the modifiers that are available (and in what scope) in Jetpack Compose, check out their documentation: https://developer.android.com/jetpack/compose/modifiers-list
If you are new to SwiftUI you can learn more about how to build apps using SwiftUI here: https://medium.com/@jpmtech/basics-of-swiftui-c8eab0edf13b
A quick note on the format of the article, I will give a code example in SwiftUI, then give the same example using Jetpack Compose directly below it.
If you get value from this article, please consider following me, clapping for this article, or sharing it to help others more easily find the article.
If you have any questions on the topic, or know of another way to accomplish the same task, feel free to respond to the post or share it with a friend and get their opinion on it.
If you want to learn more about Swift and SwiftUI, you can check out the other articles I have written here: https://medium.com/@jpmtech
If you want to see apps that have been built with Swift and SwiftUI, you can check out my apps here: https://jpmtech.io/apps
Thank you for taking the time to check out my work! Now let’s get to the content that brought you here.
Create a View Component
SwiftUI
import SwiftUI
struct NewView: View {
var body: some View {
// other components go here
}
}
Jetpack Compose
import androidx.compose.runtime.Composable
@Composable
fun NewView() {
// other components go here
}
Create a View Component and pass parameters
SwiftUI
import SwiftUI
struct NewView: View {
let myTextParameter: String
var body: some View {
Text(myTextParameter)
}
}
Jetpack Compose
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@Composable
fun NewView(myTextParameter: String) {
Text(myTextParameter)
}
Display views that stack vertically
SwiftUI
VStack {
// code here
}
Jetpack Compose
Column {
// code here
}
Display views that stack horizontally
SwiftUI
HStack {
//code here
}
Jetpack Compose
Row {
//code here
}
Displays views on top of each other (a depth stack)
SwiftUI
ZStack {
//code here
}
Jetpack Compose
Box {
//code here
}
A spacer that fills all available space
SwiftUI
Spacer()
Jetpack Compose (note: this spacer is only available in the Column and Row scope)
Spacer(Modifier.weight(1f))
Button
SwiftUI
Button {
//code here
} label: {
Text("click me")
}
Jetpack Compose
Button(onClick = {
//code here
}) {
Text(text = "click me")
}
Displaying text
SwiftUI
Text("I’m text")
Jetpack Compose
Text(text = "I’m text")
Getting text from user input using a local state variable
SwiftUI
@State var userInput = ""
//...
TextField("input name", text: $userInput)
Jetpack Compose
var userInput = remember {
mutableStateOf("")
}
//...
TextField(
value = userInput.value,
onValueChange = {userInput.value = it},
label = { Text(text = "input name") }
)
A Boolean Toggle/Switch
SwiftUI
@State var isOn = true
//...
Toggle("the toggle is", isOn: $isOn)
Jetpack Compose: Note Jetpack Compose has multiple options for boolean switches.
// Option 1: Switch (similar to iOS)
val isOn = remember {
mutableStateOf(false)
}
//...
Switch(checked = isOn.value, onCheckedChange = {
isOn.value = it
})
// Option 2: Checkbox
var isOn = remember {
mutableStateOf(true)
}
//...
Row {
Checkbox(
checked = isOn.value,
onCheckedChange = {isOn.value = it}
Text(
text = "the toggle is",
modifier = Modifier.align(Alignment.CenterVertically)
)
}
Building a date picker
SwiftUI
@State var selectedDate = Date.now
//...
DatePicker("select a date", selection: $selectedDate, displayedComponents: .date)
Jetpack Compose (currently not available in Material 3 yet, so we will build our own that looks and acts similar to the one on iOS)
@Composable
fun CustomDatePicker(labelText: String, selection: MutableState<ZonedDateTime>) {
val context = LocalContext.current
//the month is off by 1 on this older built in date picker,
//which is the reason for the random (+/-)1 values
val datePickerDialog = DatePickerDialog(
context,
{_: DatePicker, selectedYear: Int, selectedMonth: Int, selectedDayOfMonth: Int ->
selection.value = ZonedDateTime.of(
selectedYear,
selectedMonth + 1,
selectedDayOfMonth,
selection.value.hour,
selection.value.minute,
selection.value.second,
selection.value.nano,
selection.value.zone
)
}, selection.value.year, selection.value.monthValue - 1, selection.value.dayOfMonth
)
Row {
Text(
text = labelText,
modifier = Modifier.align(Alignment.CenterVertically)
)
Spacer(Modifier.weight(1f))
Button(onClick = {
datePickerDialog.show()
}) {
Text(DateTimeFormatter.ofPattern("M/d/yyyy").format(selection.value)
}
}
}
// in the view we want to use it in
var selectedDate = remember {
mutableStateOf(ZonedDateTime.now())
}
//...
CustomDatePicker(labelText = "select a date", selection = selectedDate)
Two-way Binding
SwiftUI
//parent view
@State var myValue = true
//...
ChildView(myBinding: $myValue)
//child view
@Binding var myBinding: Bool
//...
Button {
myBinding = false
} label: {
Text("change binding to false")
}
Jetpack Compose
//parent view
var myValue = remember {
mutableStateOf(true)
//...
ChildView(myBinding = myValue)
//child view
@Composable
ChildView(myBinding: MutableState<Boolean>) {
Button(onClick = {
myBinding = false
}) {
Text(text = "change binding to false")
}
}
Display images/icons that are already included on the device
SwiftUI
If you want to learn more about built in images for iOS called SF Symbols, feel free to check out this article: https://medium.com/@jpmtech/how-to-use-custom-colors-in-an-sf-symbol-18b72451b15f
Image(systemName: "person")
Jetpack Compose
Icon(imageVector = Icons.Default.Person, contentDescription = null)
Adding padding to a view
SwiftUI
Text("vertical specific").padding(.vertical, 16)
Text("horizontal default").padding(.horizontal)
Text("leading specific").padding(.leading, 16)
Text("trailing default").padding(.trailing)
Text("default padding on all sides").padding()
Jetpack Compose
//vertical
modifier=Modifier.padding(vertical=16.dp)
//horizontal
modifier=Modifier.padding(horizontal=16.dp)
//leading
modifier=Modifier.padding(start=16.dp)
//end
modifier=Modifier.padding(end=16.dp)
//Jetpack Compose does not have a default padding
Make a view scrollable
SwiftUI
ScrollView {
// content
}
Jetpack Compose
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
) {
// content
}
Display a loading indicator
SwiftUI
ProgressView()
Jetpack Compose
CircularProgressIndicator()
Creating an Asynchronous Function
SwiftUI
func doAThing() async {
// ...
}
Jetpack Compose
suspend fun doAThing() {
// ...
}
Displaying a single selection list
SwiftUI
struct ContentView: View {
@State var selectedItem = "One"
let pickerItems = ["One", "Two", "Three", "Four", "Five"]
var body: some View {
VStack {
Picker("Select an Item", selection: $selectedItem) {
ForEach(pickerItems, id: \.self) { item in
Text(item)
}
}
}
.padding()
}
}
Jetpack Compose
// Single selection lists aren't really defined well in Jetpack Compose,
// the only thing that is close is a drop down menu. So we will make our
// own that aligns with the API from Apple.
@Composable
fun Picker(label: String, selection: MutableState<String>, items: List<String>) {
val isExpanded = remember {
mutableStateOf(false)
}
Column {
OutlinedButton(onClick = { isExpanded.value = true }) {
Text("$label: ${selection.value}")
Icon(imageVector = Icons.Default.ArrowDropDown, contentDescription = null)
}
DropdownMenu(
expanded = isExpanded.value,
onDismissRequest = {
isExpanded.value = false
}
) {
items.forEachIndexed { itemIndex, itemText ->
DropdownMenuItem(
text = {
Text(text = itemText)
},
onClick = {
selection.value = items[itemIndex]
isExpanded.value = false
}
)
}
}
}
}
// then it can be used/called in the following way
@Composable
fun MyView() {
Picker(label = "Number of People", selection = numberOfPeople, items = listOf("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"))
}
Displaying a photo picker
SwiftUI — You can check out my full article on this at the link below:
Jetpack Compose — You can check out my full article on this at the link below:
Navigation
SwiftUI:
Jetpack Compose:
Add a Bottom Navigation/Tab Bar component
SwiftUI:
Jetpack Compose:
Share a link from your app
SwiftUI:
Jetpack Compose:
Add gestures to elements in your app
SwiftUI:
Jetpack Compose:
Storing Data Locally in your app
SwiftUI (using either CoreData or SwiftData):
Jetpack Compose:
UI Testing
SwiftUI:
Jetpack Compose:
Add Test IDs to components that don’t impact accessibility
SwiftUI:
Text("Hello, world!")
.accessibilityIdentifier("myCustomTestID")
Jetpack Compose:
Text(
text = "Hello, world!",
modifier = Modifier.testTag("myCustomTestID")
)