iOS SwiftUI vs Android Jetpack Compose
Talking about both iOS and Android in one session we see similarities (even if there are none?). This post is similar to Kotlin is Swift.
A good introduction to SwiftUI
Now that we have both Apple and Google building decorative UIs let’s do a comparison.
→ → Please Note → → I believe this is the first time in history that a post covers SwiftUI and Jetpack Compose so it will be updated regularly as my understanding and both platforms evolve over time. Some of the sections have “???” TBD ←←←
IMPORTANT: Both make a hard break from the past. SwiftUI no longer uses UIKit / Storyboards and Jetpack Compose no longer uses View / XML.
Components
Simple
— Text with Formatting —
Jetpack Compose
fun MyText(Text(text = "Hello Compose!")
style = +themeTextStyle { h3 }) // set style
SwiftUI
Text("Hello SwiftUI!")
.font(.title) // set style
.color(.green) // you can keep adding modifiers //
.lineLimit(nil)
.multilineTextAlignment(.center)// render a gradient
.background(LinearGradient(gradient: Gradient(colors: [.white, .red, .black]), startPoint: .leading, endPoint: .trailing), cornerRadius: 0)// You can add formaters
static let taskDateFormat: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter
}()
— Image —
Jetpack Compose
Container(width = image.width.toDp(), height = image.height.toDp()) {
Draw { canvas, _ ->
canvas.drawImage(image, Offset.zero, Paint())
}
}
SwiftUI
Image(star.thumbnail)
Image(systemName: "star.fill")
.resizable() // you can put modifiers on the image
.aspectRatio(contentMode: .fill)
— DrawShape —
Jetpack Compose
fun DrawScaledRect(scale: Float, color: Color) {
Draw { canvas, parentSize ->
val centerX = parentSize.width.value / 2
val centerY = parentSize.height.value / 2
paint.color = color
canvas.drawRect(
Rect(
centerX - halfSize * scale, centerY - halfSize * scale,
centerX + halfSize * scale, centerY + halfSize * scale
), paint
)
}
}
SwiftUI
Rectangle()
.fill(Color.red)
.frame(width: 200, height: 200)// 50x50 blue circle
Circle()
.fill(Color.blue)
.frame(width: 50, height: 50)
— Slider —
Jetpack Compose
??? TBD
SwiftUI
struct ContentView : View {
@State var rate: Double = 0
var body: some View {
VStack {
Slider(value: $rate, from: 1, through: 10, by:1)
Text("Your rate is \(rate)")
}
}
}
— Selector —
Jetpack Compose
SwiftUI
??? TBD
Complex / Compound UI
Building UI component from other components
Jetpack Compose
@Composable
fun Checkbox(...) {
Wrap {
Ripple {
Toggleable(value = value, onToggle = onClick) {
Padding(padding = CheckboxDefaultPadding) {
Container(width = CheckboxSize, height = CheckboxSize) {
DrawCheckbox(value = value, color = color)
}
}
}
}
}
}
SwiftUI
struct UserDetails: View { ...
struct ProfilePicture: View { ....HStack {
ProfilePicture(imageName: user.profilePicture)
UserDetails(user: user)
}
— UI as Properties—
Jetpack Compose
val txt = Text(text = "txt", style = +themeTextStyle { h5 })
val but = Button(text = "but", onClick = {})
SwiftUI
struct ContentView : View {
let title = Text("TxT Title")
.font(.largeTitle)
let subtitle = Text("My Subtitle")
.foregroundColor(.secondary)
var body: some View {
VStack {
title
subtitle
}
}
}
Layout
— Documentation —
Jetpack Comopose
— TBD — Jetpack Compose documentation is still being written
SwiftUI
— Handling Different Size —
Jetpack Compose
????
SwiftUI
@Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?if horizontalSizeClass == .compact {
return Text("Compact")
} else {
return Text("Regular")
}
— Handling Rotation —
Jetpack Compose
SwiftUI
Frame
— Add a view —
Jetpack Compose
setContent { CraneWrapper {
@Composeable {
fun MyText(Text(text = "Hello Compose!",
style = +themeTextStyle { h3 }) // set style
}
}
}
SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello SwiftUI!")
.font(.title) // Set style
.color(.green)
}
}
— Adding rows —
Jetpack Compose
Row {
myText("Hello")
myButton("a button")
}
FlexRow()- A widget that places its children in a horizontal sequence, assigning children widths according to their flex weights.
SwiftUI
VStack {
Text("Turtle Rock").font(.title)
Text("Joshua Tree National Park").font(.subheadline)
}
— Adding Columns —
Jetpack Compose
Column {
Text(text = "my txt", style = +themeTextStyle { h5 })
Button(text = "b hi", onClick = {})
}
SwiftUI
HStack {
Text("Joshua Tree National Park").font(.subheadline)
Text("California").font(.subheadline)
}
— Adding Depth by Stacking Components —
Jetpack Compose
Stack {
aligned(Alignment.BottomRight) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
size = childSize[0],
position = childPosition[0],
positionedLatch = positionedLatch
)
}
Container(width = doubleSizeDp, height = doubleSizeDp) {
SaveLayoutInfo(
size = childSize[1],
position = childPosition[1],
positionedLatch = positionedLatch
)
}
}
}
SwiftUI
// an image with text on top
ZStack() {
Image("example-image")
Text("Hacking with Swift")
.font(.largeTitle)
.background(Color.black)
.foregroundColor(.white)
}
— Padding —
Jetpack Compose
Padding(padding = EdgeInsets(left = 20.dp, top = 20.dp)) {
Row {
myButton("B00")
myText(" Top Hello ")
myButton("B0")
}
}
SwiftUI
Text("SwiftUI")
.padding(100)
— Spacing —
Jetpack Compose
- WidthSpacer() — Component that represents an empty space with fixed width and zero height. See FixSpacer below.
- HeightSpacer() — Component that represents an empty space with fixed height and zero width. See FixedSpacer below.
//fun FixedSpacer(width: Dp, height: Dp)
Row {
myButton("B00")
myText(" Top Hello ")
FixedSpacer(20.dp,20.dp)
myButton("B0")
}
}
SwiftUI
VStack {
Text("Hello World")
Text("Hello World")
Spacer()
Text("Hello World")
}
— Alignment —
Jetpack Compose
Align(Alignment.TopCenter) {
Row {
Text(text = "Hello Compose!",style = +themeTextStyle { h3 })
}
}
SwiftUI
VStack(alignment: .leading) {
Text("SwiftUI")
Text("rocks")
}
— Returning Different View Types —
Jetpack Compose
??? TBD
SwiftUI
var body: some View {
if Bool.random() {
Image("example-image")
} else {
Text("Better luck next time")
}
}
— Adding Logic to Views —
Jetpack Compose
@Composable
fun Greeting(name: String) {
For(count in 0..3)
Text ("$count Hello $name!")
}
SwiftUI
VStack {
ForEach(colors.identified(by: \.self)) {
color in Text(color.description.capitalized)
.padding()
.background(color)
}
}
State & Data Binding
Jetpack Compose
val amount = +state { 0 }
val isHappy = +state { true }
SwiftUI
@State var isHappy = true
@State var amount = 0
Event handling
— Button Tap —
Jetpack Compose
Button(text = "txt", onClick = {// your action here})
Swift
Button(action: {
// your action here
}) {
Text("Button title")
}
Please watch is space as I will be adding to it as time allows.
— Gesture Recognizer —
Jetpack Compose
val recognizer = MultiTapGestureRecognizer(longTapDelay = Duration.zero)
val textSpan = TextSpan(recognizer = recognizer)
Swift
Text("Tap me!") // could be any view like Image
.tapAction { //double tap: .tapAction(count: 2)
print("Tapped!")
}Text("Tap me!")
.gesture(
DragGesture(minimumDistance: 50)
.onEnded { _ in print("Dragged!")})
.gesture(
LongPressGesture(minimumDuration: 2)
.onEnded { _ in print("Pressed!") })
View Lists
Jetpack Compose
SwiftUI
Think of this as the “adapter”
struct RestaurantRow: View {
var name: String
var body: some View {
Text("Restaurant: \(name)")
}
}
Fill the list with the adapter
List {
RestaurantRow(name: "Joe's Original")
RestaurantRow(name: "The Real Joe's Original")
RestaurantRow(name: "Original Joe's")
}
— Navigation —
Jetpack Compose
???
SwiftUI
NavigationView {
Text("SwiftUI")
.navigationBarTitle(Text("Welcome"))
}
— Alert —
Jetpack Compose
Card(color = cardInternalColor) {
Padding(padding = 12.dp) {
Column {
Row(mainAxisAlignment = MainAxisAlignment.SpaceBetween){
Text(text = "Alerts", style = +themeTextStyle { ...
TransparentButton(text = "See All", onClick = { })
}
....
SwiftUI
@State var showingAlert = falseButton(action: {
self.showingAlert = true
}) {
Text("Show Alert")
}
.presentation($showingAlert) {
Alert(title: Text("Message"), message: Text("Hi You"), dismissButton: .default(Text("OK!")))
}
Much more to come … with images and documentation for each.
~Ash