Episode ini membahas SwiftUI: dasar views dan layout secara deklaratif, state management dengan @State, @Binding, @ObservedObject, dan @EnvironmentObject, animasi serta transisi untuk UI yang adaptif, dan interoperabilitas antara UIKit dan SwiftUI.

SwiftUI mengubah cara kalian membangun antarmuka: dari imperatif ("lakukan ini, lalu itu") menjadi deklaratif ("begini yang kalian inginkan"). Episode 17 membahas SwiftUI dan declarative UI — mulai dari konsep dasar views dan layout, state management dengan property wrappers, hingga animasi dan interoperabilitas dengan UIKit.
Dengan SwiftUI, kalian mendeskripsikan UI sebagai fungsi dari state: saat state berubah, SwiftUI menghitung ulang tampilan dan memperbarui apa yang berubah. Memahami model state ini adalah kunci menguasai SwiftUI.
View di SwiftUI adalah struct yang mendeskripsikan tampilan dan perilakunya:
import SwiftUI
struct GreetingView: View {
var body: some View {
VStack(spacing: 12) {
Text("Halo, SwiftUI")
.font(.largeTitle)
Text("Belajar deklaratif")
}
.padding()
}
}struct GreetingView: View menyatakan bahwa view adalah data — kombinasi komponen dengan modifier seperti .font dan .padding. VStack menyusun anaknya secara vertikal. UI ini akan tampil sama di iPhone, iPad, Mac, Apple Watch, dan Apple TV.
Komposisi layout dibangun dari tiga stack dasar:
HStack {
Image(systemName: "heart.fill")
Text("Menyukai Swift")
}
.padding(12)HStack { ... } menempatkan icon dan teks sejajar. Stack dikombinasikan bebas membentuk layout yang kompleks, dengan Spacer dan frame untuk mengatur distribusi ruang.
@State adalah sumber kebenaran lokal view. Saat nilai berubah, view di-render ulang:
struct CounterView: View {
@State private var jumlah = 0
var body: some View {
VStack {
Text("Jumlah: \(jumlah)")
TombolTambah(jumlah: $jumlah)
}
}
}
struct TombolTambah: View {
@Binding var jumlah: Int
var body: some View {
Button("Tambah") {
jumlah += 1
}
}
}@State private var jumlah = 0 menyimpan state lokal view, dan @Binding var jumlah: Int membiarkan view anak membaca serta mengubah state milik view induk. Binding menjaga satu sumber kebenaran tanpa memindahkan state ke anak.
Untuk state yang dibagikan lebih luas, episode 16 memperkenalkan @StateObject. Ketika object state dimiliki di tempat lain, gunakan @ObservedObject; dan untuk menyuntikkan ke seluruh hierarki, gunakan @EnvironmentObject:
@main
struct AppUtama: App {
@StateObject var sesi = SesiPengguna()
var body: some Scene {
WindowGroup {
KontenView()
.environmentObject(sesi)
}
}
}.environmentObject(sesi) membuat sesi tersedia bagi setiap view di bawahnya tanpa meneruskannya satu per satu. View yang membutuhkannya cukup mendeklarasikan @EnvironmentObject var sesi: SesiPengguna. Ini cara paling praktis untuk state global seperti sesi login dan tema.
SwiftUI menganimasikan perubahan state secara otomatis dengan .animation (implisit) atau withAnimation (eksplisit):
struct AnimasiView: View {
@State private var diperbesar = false
var body: some View {
Button("Tekan") {
withAnimation(.spring(response: 0.4, dampingFraction: 0.6)) {
diperbesar.toggle()
}
}
.scaleEffect(diperbesar ? 1.4 : 1.0)
}
}withAnimation(.spring(...)) { diperbesar.toggle() } menganimasikan perubahan yang terjadi di dalam bloknya — di sini, perubahan scale pada tombol. Pilih durasi dan kurva yang sesuai; spring memberi efek alami untuk interaksi pengguna.
Perubahan tampilan antar state juga bisa dianimasikan dengan transisi:
if ditampilkan {
DetailView()
.transition(.opacity.combined(with: .slide))
}.transition(.opacity.combined(with: .slide)) menggabungkan efek memudar dan geser saat view muncul atau menghilang. Transisi membutuhkan animasi di sekitarnya — tanpa withAnimation atau .animation, perubahan terjadi seketika.
SwiftUI menyesuaikan diri dengan ukuran layar lewat kemampuan responsif: HStack bisa beralih ke VStack bergantung pada horizontal size class. Gunakan @Environment untuk membaca ukuran, dan ViewThatFits untuk memilih layout yang cocok dengan ruang yang tersedia.
SwiftUI dan UIKit hidup berdampingan dalam satu project. UIViewRepresentable membungkus UIKit view agar bisa dipakai SwiftUI, dan UIViewControllerRepresentable membungkus View Controller:
import UIKit
import SwiftUI
struct MapViewWrapper: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
return MKMapView()
}
func updateUIView(_ uiView: MKMapView, context: Context) {}
}makeUIView(context:) membuat view UIKit dan updateUIView(_:context:) memperbaruinya saat SwiftUI berubah. Interop ini berarti kalian bisa mengadopsi SwiftUI bertahap tanpa membuang UIKit lama — arah sebaliknya, menyematkan SwiftUI di UIKit, memakai UIHostingController.
Info
Property wrapper adalah inti model state SwiftUI. Mulailah dengan @State untuk state lokal, @Binding untuk meneruskan, lalu @StateObject dan @EnvironmentObject untuk state yang dibagikan. Urutan ini menjaga arsitektur kalian tetap sederhana.
Inti yang harus dibawa pulang:
@State untuk state lokal, @Binding untuk meneruskan perubahan.@ObservedObject dan @EnvironmentObject membagikan state dalam hierarki.withAnimation dan .transition menghidupkan perubahan state.UIViewRepresentable menjembatani UIKit dan SwiftUI dalam satu project.Di episode 18 selanjutnya kita akan membahas modern tooling dan build automation — manajemen Xcode project dan build settings, continuous integration dengan Xcode Cloud, GitHub Actions, dan Bitrise, code signing dengan provisioning profiles, serta Fastlane untuk otomasi build dan deployment. Pipeline kalian akan berjalan otomatis!