A Cheat Sheet for Dual Native Mobile Development

Chase
8 min readJun 30, 2023

--

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.

The words Cheat Sheet followed by an Apple emoji and Android head with two equals in between

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.

A screenshot of the Auto Import section of the Android Studio settings with the Auto Import and Auto Clean Up check boxes 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")
)

--

--