SwiftUI & Jetpack Compose

YLabZ
5 min readJun 11, 2019

--

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

--

--

Responses (3)