17 Nov 2025·7 menit membaca

Pengoptimalan performa SwiftUI untuk daftar panjang: perbaikan praktis

Penyempurnaan performa SwiftUI untuk daftar panjang: perbaikan praktis untuk re-render, identitas baris stabil, pagination, pemuatan gambar, dan kelancaran gulir di iPhone lama.

Pengoptimalan performa SwiftUI untuk daftar panjang: perbaikan praktis

Seperti apa “daftar lambat” di aplikasi SwiftUI nyata

Sebuah “daftar lambat” di SwiftUI biasanya bukan bug. Itu terjadi saat UI Anda tidak dapat mengikuti jari. Anda merasakannya saat menggulir: daftar tersendat, frame drop, dan semuanya terasa berat.

Tanda-tanda umum:

  • Guliran tersendat, terutama di perangkat lama
  • Baris berkedip atau sesaat menampilkan konten yang salah
  • Ketukan terasa tertunda, atau aksi geser terlambat
  • Ponsel menjadi hangat dan baterai cepat terkuras
  • Penggunaan memori meningkat semakin lama Anda menggulir

Daftar panjang bisa terasa lambat walau tiap baris terlihat “kecil,” karena biayanya bukan hanya menggambar pixel. SwiftUI masih harus menentukan apa tiap baris itu, menghitung layout, menyelesaikan font dan gambar, menjalankan kode format Anda, dan melakukan diff saat data berubah. Jika salah satu pekerjaan itu terjadi terlalu sering, daftar menjadi hotspot.

Juga berguna memisahkan dua ide. Di SwiftUI, “re-render” sering berarti body view dihitung ulang. Bagian itu biasanya murah. Pekerjaan mahal adalah apa yang dipicu oleh re-hitungan itu: layout berat, decoding gambar, pengukuran teks, atau membangun ulang banyak baris karena SwiftUI mengira identitasnya berubah.

Bayangkan chat dengan 2.000 pesan. Pesan baru datang setiap detik, dan tiap baris memformat timestamp, mengukur teks multi-baris, dan memuat avatar. Walau Anda hanya menambahkan satu item, perubahan state yang tidak terlokalisir bisa menyebabkan banyak baris mengevaluasi ulang, dan beberapa dari mereka harus digambar ulang.

Tujuannya bukan mikro-optimisasi. Anda ingin gulir yang lancar, ketukan instan, dan pembaruan yang hanya menyentuh baris yang benar-benar berubah. Perbaikan di bawah fokus pada identitas stabil, baris yang lebih murah, lebih sedikit pembaruan tak perlu, dan pemuatan yang terkontrol.

Penyebab utama: identitas, kerja per baris, dan badai update

Saat daftar SwiftUI terasa lambat, jarang karena “terlalu banyak baris.” Itu karena pekerjaan ekstra terjadi saat Anda menggulir: membangun ulang baris, menghitung ulang layout, atau memuat gambar berulang kali.

Sebagian besar penyebab utama masuk dalam tiga kategori:

  • Identitas tidak stabil: baris tidak punya id konsisten, atau Anda memakai \\.self untuk nilai yang bisa berubah. SwiftUI tidak bisa mencocokkan baris lama ke yang baru, jadi ia membangun ulang lebih banyak dari yang diperlukan.
  • Terlalu banyak kerja per baris: pemformatan tanggal, penyaringan, mengubah ukuran gambar, atau pekerjaan jaringan/disk di dalam view baris.
  • Badai update: satu perubahan (pengetikan, timer tick, update progress) memicu update state yang sering, dan daftar menyegarkan berulang kali.

Contoh: Anda punya 2.000 order. Tiap baris memformat mata uang, membuat attributed string, dan memulai fetch gambar. Sementara itu, timer “last synced” memperbarui sekali per detik di parent view. Walau data order tidak berubah, timer itu masih dapat meng-invalidate daftar cukup sering sehingga gulir terasa tersendat.

Mengapa List dan LazyVStack bisa terasa berbeda

List lebih dari sekadar scroll view. Itu dirancang sebagai perilaku table/collection dan optimisasi sistem. Seringkali menangani dataset besar dengan memori lebih efisien, tetapi sensitif terhadap identitas dan update yang sering.

ScrollView + LazyVStack memberi Anda kontrol lebih atas layout dan visual, tapi juga lebih mudah tanpa sengaja melakukan pekerjaan layout ekstra atau memicu update mahal. Di perangkat lama, kerja ekstra itu akan terlihat lebih cepat.

Sebelum Anda menulis ulang UI, ukur dulu. Perbaikan kecil seperti ID stabil, memindahkan pekerjaan dari baris, dan mengurangi churn state sering menyelesaikan masalah tanpa mengganti container.

Perbaiki identitas baris sehingga SwiftUI bisa diff efisien

Saat daftar panjang terasa tidak mulus, identitas sering jadi tersangka. SwiftUI memutuskan baris mana yang bisa digunakan ulang dengan membandingkan ID. Jika ID berubah, SwiftUI menganggap baris baru, membuang yang lama, dan membangun ulang lebih banyak dari yang diperlukan. Itu bisa terlihat seperti re-render acak, posisi scroll hilang, atau animasi yang terjadi tanpa alasan.

Kemenangan paling sederhana: buat id tiap baris stabil dan terkait ke sumber data Anda.

Kesalahan umum adalah menghasilkan identitas di dalam view:

ForEach(items) { item in
  Row(item: item)
    .id(UUID())
}

Ini memaksa ID baru setiap render, jadi tiap baris menjadi “berbeda” setiap kali.

Gunakan ID yang sudah ada di model Anda, seperti primary key database, ID server, atau slug stabil. Jika Anda tidak punya, buat sekali saat model dibuat — bukan di dalam view.

struct Item: Identifiable {
  let id: Int
  let title: String
}

List(items) { item in
  Row(item: item)
}

Hati-hati dengan indeks. ForEach(items.indices, id: \\.self) mengikat identitas ke posisi. Jika Anda menyisipkan, menghapus, atau mengurutkan, baris “bergerak,” dan SwiftUI mungkin menggunakan ulang view yang salah untuk data yang salah. Gunakan indeks hanya untuk array yang benar-benar statis.

Jika Anda pakai id: \\.self, pastikan nilai Hashable elemen stabil seiring waktu. Jika hash berubah ketika field diupdate, identitas baris juga berubah. Aturan aman untuk Equatable dan Hashable: dasarkan pada satu ID stabil, bukan properti yang dapat diubah seperti name atau isSelected.

Pemeriksaan sederhana:

  • ID berasal dari sumber data (bukan UUID() di view)
  • ID tidak berubah saat konten baris berubah
  • Identitas tidak bergantung pada posisi array kecuali daftar tidak pernah di-reorder

Kurangi re-render dengan membuat view baris lebih ringan

Daftar panjang sering terasa lambat karena tiap baris melakukan terlalu banyak pekerjaan setiap kali SwiftUI mengevaluasi ulang body-nya. Targetnya sederhana: buat tiap baris murah untuk dibangun ulang.

Biaya tersembunyi yang umum adalah mengoper nilai “besar” ke dalam baris. Struct besar, model sarang dalam, atau properti computed berat dapat memicu kerja ekstra meski UI terlihat tidak berubah. Anda mungkin membangun ulang string, parsing tanggal, mengubah ukuran gambar, atau membuat pohon layout kompleks lebih sering dari yang Anda duga.

Pindahkan pekerjaan mahal keluar dari body

Jika sesuatu lambat, jangan bangun ulang itu di dalam body baris berulang-ulang. Prekomputasi saat data tiba, cache di view model, atau memoize di helper kecil.

Biaya tingkat baris yang cepat menumpuk:

  • Membuat DateFormatter atau NumberFormatter baru per baris
  • Pemformatan string berat di body (join, regex, parsing markdown)
  • Membuat array turunan dengan .map atau .filter di dalam body
  • Membaca blob besar dan mengkonversinya (mis. decoding JSON) di view
  • Layout terlalu kompleks dengan banyak nested stacks dan conditional

Contoh sederhana: simpan formatter statis, dan kirim string yang sudah diformat ke baris.

enum Formatters {
    static let shortDate: DateFormatter = {
        let f = DateFormatter()
        f.dateStyle = .medium
        f.timeStyle = .none
        return f
    }()
}

struct OrderRow: View {
    let title: String
    let dateText: String

    var body: some View {
        HStack {
            Text(title)
            Spacer()
            Text(dateText).foregroundStyle(.secondary)
        }
    }
}

Pisahkan baris dan gunakan Equatable bila sesuai

Jika hanya satu bagian kecil yang berubah (mis. badge count), isolasikan ke subview sehingga sisa baris tetap stabil.

Untuk UI yang driven oleh nilai, membuat subview Equatable (atau membungkusnya dengan EquatableView) bisa membantu SwiftUI melewatkan pekerjaan saat input tidak berubah. Jaga input equatable kecil dan spesifik—jangan melakukan equatable pada seluruh model.

Kontrol update state yang memicu refresh seluruh daftar

Jaga pekerjaan baris tetap di luar UI
Pindahkan pemformatan dan aturan bisnis ke logika backend sehingga baris tetap ringan di perangkat.
Bangun Aplikasi

Kadang baris baik-baik saja, tapi sesuatu terus memberitahu SwiftUI untuk menyegarkan seluruh daftar. Saat menggulir, bahkan update kecil yang berlebih bisa menjadi stutter, terutama di perangkat lama.

Salah satu penyebab umum adalah membuat ulang model terlalu sering. Jika parent view dibuat ulang dan Anda menggunakan @ObservedObject untuk view model yang dimiliki view itu, SwiftUI mungkin membuat ulangnya, mereset subscription, dan memicu publish baru. Jika view memiliki model, gunakan @StateObject sehingga dibuat sekali dan tetap stabil. Gunakan @ObservedObject untuk objek yang disuntik dari luar.

Pembunuh performa lain adalah publishing terlalu sering. Timer, pipeline Combine, dan update progress dapat memicu berkali-kali per detik. Jika properti yang dipublikasikan memengaruhi daftar (atau berada di ObservableObject yang dibagi oleh layar), setiap tick dapat menginvalidasi daftar.

Contoh: Anda punya field pencarian yang memperbarui query setiap ketikan, lalu memfilter 5.000 item. Jika Anda memfilter langsung, daftar berdiff terus saat pengguna mengetik. Debounce query, dan perbarui array terfilter setelah jeda singkat.

Polanya yang biasanya membantu:

  • Jaga nilai yang sering berubah keluar dari objek yang menggerakkan daftar (gunakan objek yang lebih kecil atau @State lokal)
  • Debounce pencarian dan filtering sehingga daftar diperbarui setelah jeda ketikan
  • Hindari publish timer frekuensi tinggi; perbarui lebih jarang atau hanya saat nilai benar-benar berubah
  • Simpan state per-barang lokal (mis. @State di baris) daripada satu nilai global yang berubah terus
  • Pecah model besar: satu ObservableObject untuk data daftar, lain untuk state UI tingkat layar

Idenya sederhana: buat waktu scroll senyap. Jika tidak ada yang berubah penting, daftar tidak perlu diminta melakukan kerja.

Pilih container yang tepat: List vs LazyVStack

Container yang Anda pilih mempengaruhi seberapa banyak kerja yang dilakukan iOS untuk Anda.

List biasanya pilihan paling aman bila UI Anda mirip tabel standar: baris dengan teks, gambar, swipe actions, seleksi, separator, edit mode, dan aksesibilitas. Di bawahnya, ia mendapat optimisasi platform yang telah disetel Apple selama bertahun-tahun.

ScrollView dengan LazyVStack cocok saat Anda butuh layout kustom: kartu, blok konten campuran, header spesial, atau desain feed. “Lazy” berarti membangun baris saat muncul di layar, tapi tidak memberi perilaku yang sama persis seperti List. Dengan dataset sangat besar, itu bisa berarti penggunaan memori lebih tinggi dan gulir lebih tersendat di perangkat lama.

Aturan keputusan sederhana:

  • Gunakan List untuk layar bergaya tabel klasik: pengaturan, inbox, order, daftar admin
  • Gunakan ScrollView + LazyVStack untuk layout kustom dan konten campuran
  • Jika Anda punya ribuan item dan hanya butuh tabel, mulai dengan List
  • Jika butuh kontrol pixel-perfect, coba LazyVStack, lalu ukur memori dan frame drop

Juga perhatikan styling yang diam-diam memperlambat gulir. Efek per-barang seperti shadow, blur, dan overlay kompleks dapat memaksa kerja rendering ekstra. Jika Anda mau efek dalam, terapkan efek berat ke elemen kecil (mis. ikon) bukan seluruh baris.

Contoh konkret: layar “Orders” dengan 5.000 baris sering tetap mulus di List karena baris digunakan ulang. Jika Anda beralih ke LazyVStack dan membuat baris bergaya kartu dengan bayangan besar dan banyak overlay, Anda mungkin melihat jank walau kodenya tampak bersih.

Pagination yang terasa mulus dan menghindari lonjakan memori

Dapatkan identitas benar sejak hari pertama
Rancang ID stabil dan tabel PostgreSQL secara visual sebelum UI SwiftUI Anda menampilkan baris.
Buat Proyek

Pagination membuat daftar panjang tetap cepat karena Anda merender lebih sedikit baris, menyimpan lebih sedikit model di memori, dan memberi SwiftUI pekerjaan diffing lebih sedikit.

Mulailah dengan kontrak paging yang jelas: ukuran halaman tetap (mis. 30–60 item), flag “tidak ada hasil lagi”, dan baris loading yang hanya muncul saat sedang fetch.

Perangkap umum adalah memicu halaman berikutnya hanya saat baris terakhir muncul. Itu sering terlambat, sehingga pengguna mencapai akhir dan melihat jeda. Sebagai gantinya, mulai loading ketika salah satu dari beberapa baris terakhir muncul.

Berikut pola sederhana:

@State private var items: [Item] = []
@State private var isLoading = false
@State private var reachedEnd = false

func loadNextPageIfNeeded(currentIndex: Int) {
    guard !isLoading, !reachedEnd else { return }
    let threshold = max(items.count - 5, 0)
    guard currentIndex >= threshold else { return }

    isLoading = true
    Task {
        let page = try await api.fetchPage(after: items.last?.id)
        await MainActor.run {
            let newUnique = page.filter { p in !items.contains(where: { $0.id == p.id }) }
            items.append(contentsOf: newUnique)
            reachedEnd = page.isEmpty
            isLoading = false
        }
    }
}

Ini menghindari masalah umum seperti baris duplikat (hasil API yang tumpang tindih), kondisi race dari banyak pemanggilan onAppear, dan memuat terlalu banyak sekaligus.

Jika daftar Anda mendukung pull to refresh, reset state paging dengan hati-hati (kosongkan items, reset reachedEnd, batalkan task yang sedang berjalan bila memungkinkan). Jika Anda mengontrol backend, ID stabil dan paging berbasis cursor membuat UI terasa jauh lebih mulus.

Gambar, teks, dan layout: buat rendering baris tetap ringan

Buat layar admin cepat dengan cepat
Buat tool internal dan daftar admin yang tetap responsif bahkan dengan ribuan record.
Coba AppMaster

Daftar panjang jarang terasa lambat karena container daftar. Sebagian besar kasus, penyebabnya adalah baris. Gambar biasanya biang kerok: decoding, mengubah ukuran, dan menggambar bisa melampaui kecepatan scroll, apalagi di perangkat lama.

Jika Anda memuat gambar remote, pastikan pekerjaan berat tidak terjadi di main thread saat scroll. Juga hindari mendownload aset resolusi penuh untuk thumbnail 44–80 pt.

Contoh: layar “Messages” dengan avatar. Jika tiap baris mendownload gambar 2000x2000, memperkecilnya, lalu menerapkan blur atau shadow, daftar akan tersendat walau model datanya sederhana.

Jaga pekerjaan gambar tetap terprediksi

Kebiasaan berdampak tinggi:

  • Gunakan thumbnail yang dihasilkan server atau pra-generate yang mendekati ukuran tampilan
  • Decode dan resize di luar main thread bila memungkinkan
  • Cache thumbnail sehingga scroll cepat tidak mengulang fetch atau decode
  • Gunakan placeholder dengan ukuran final supaya tidak ada flicker atau lompatan layout
  • Hindari modifier mahal pada gambar di baris (shadow berat, mask, blur)

Stabilisasi layout untuk menghindari thrash

SwiftUI bisa menghabiskan lebih banyak waktu mengukur daripada menggambar jika tinggi baris terus berubah. Usahakan baris yang dapat diprediksi: frame tetap untuk thumbnail, batasan baris teks konsisten, dan spasi stabil. Jika teks bisa meluas, batasi (mis. 1–2 baris) sehingga satu pembaruan tidak memaksa pengukuran ulang besar.

Placeholder juga penting. Lingkaran abu-abu yang nantinya menjadi avatar harus menempati frame yang sama, agar baris tidak reflow saat menggulir.

Cara mengukur: pemeriksaan Instruments yang mengungkap hambatan nyata

Pekerjaan performa hanya berdasarkan feeling membuat Anda menebak. Instruments memberi tahu apa yang berjalan di main thread, apa yang dialokasikan saat scroll cepat, dan apa yang menyebabkan frame drop.

Tentukan baseline di perangkat nyata (pilih perangkat lama jika Anda mendukungnya). Lakukan satu aksi yang bisa direplikasi: buka layar, gulir dari atas ke bawah cepat, trigger load-more sekali, lalu gulir kembali ke atas. Catat titik hitch terburuk, puncak memori, dan apakah UI tetap responsif.

Tiga view Instruments yang memberi nilai

Gunakan ini bersama-sama:

  • Time Profiler: cari spike main-thread saat Anda menggulir. Layout, pengukuran teks, parsing JSON, dan decoding gambar di sini sering menjelaskan hitch.
  • Allocations: perhatikan lonjakan objek sementara saat scroll cepat. Itu sering menunjuk ke formatting berulang, attributed string baru, atau membangun ulang model per-barang.
  • Core Animation: konfirmasi dropped frames dan waktu frame panjang. Ini membantu memisahkan tekanan rendering dari pekerjaan data yang lambat.

Saat Anda menemukan spike, klik ke call tree dan tanyakan: apakah ini terjadi sekali per layar, atau sekali per baris, per scroll? Yang kedua adalah yang memecah kelancaran scroll.

Tambahkan signpost untuk event scroll dan pagination

Banyak aplikasi melakukan pekerjaan ekstra saat menggulir (image loads, pagination, filtering). Signpost membantu Anda melihat momen-momen itu di timeline.

import os
let log = OSLog(subsystem: "com.yourapp", category: "list")
os_signpost(.begin, log: log, name: "LoadMore")
// fetch next page
os_signpost(.end, log: log, name: "LoadMore")

Uji ulang setelah setiap perubahan, satu per satu. Jika FPS membaik tapi Allocations memburuk, Anda mungkin menukar stutter dengan tekanan memori. Simpan catatan baseline dan hanya pertahankan perubahan yang menggerakkan angka ke arah yang benar.

Kesalahan umum yang diam-diam membunuh performa daftar

Ubah kelancaran gulir menjadi produk
Buat aplikasi iOS dengan backend nyata sehingga pembaruan daftar tetap dapat diprediksi saat data bertambah.
Mulai Membangun

Beberapa masalah jelas (gambar besar, dataset raksasa). Lainnya baru muncul saat data tumbuh, terutama di perangkat lama.

1) ID baris tidak stabil

Kesalahan klasik adalah membuat ID di dalam view, seperti id: \\.self untuk reference type, atau UUID() di body baris. SwiftUI menggunakan identitas untuk diff update. Jika ID berubah, SwiftUI menganggap baris baru, membangunnya ulang, dan mungkin membuang cache layout.

Gunakan ID stabil dari model (primary key database, ID server, atau UUID yang disimpan sekali saat item dibuat). Jika tidak punya, tambahkan satu.

2) Pekerjaan berat di onAppear

onAppear berjalan lebih sering dari perkiraan karena baris masuk dan keluar saat scroll. Jika tiap baris memulai decoding gambar, parsing JSON, atau lookup database di onAppear, Anda akan mendapat spike berulang.

Pindahkan pekerjaan berat keluar dari baris. Prekomputasi saat data dimuat, cache hasil, dan batasi onAppear ke aksi murah (mis. memicu pagination saat mendekati akhir).

3) Mengikat seluruh daftar ke edit baris

Ketika setiap baris mendapatkan @Binding ke array besar, edit kecil bisa terlihat seperti perubahan besar. Itu bisa menyebabkan banyak baris mengevaluasi ulang, dan kadang seluruh daftar menyegarkan.

Lebih baik mengoper nilai immutable ke baris dan mengirim perubahan kembali dengan aksi ringan (mis. “toggle favorite untuk id”). Simpan state per-barang di baris hanya bila memang tempatnya di sana.

4) Terlalu banyak animasi saat menggulir

Animasi mahal di daftar karena bisa memicu pass layout tambahan. Menerapkan animation(.default, value:) di level tinggi (pada seluruh daftar) atau menganimasikan tiap perubahan kecil bisa membuat gulir terasa lengket.

Jaga sederhana:

  • Batasi animasi ke baris yang berubah saja
  • Hindari animasi saat scroll cepat (terutama untuk selection/highlight)
  • Hati-hati dengan implicit animation pada nilai yang sering berubah
  • Pilih transisi sederhana daripada efek gabungan kompleks

Contoh nyata: daftar chat di mana tiap baris memulai fetch jaringan di onAppear, menggunakan UUID() untuk id, dan menganimasikan perubahan status “seen”. Kombinasi itu menciptakan churn baris konstan. Memperbaiki identitas, caching kerja, dan membatasi animasi sering membuat UI yang sama terasa langsung lebih mulus.

Daftar periksa cepat, contoh sederhana, dan langkah selanjutnya

Jika Anda hanya melakukan beberapa hal, mulai di sini:

  • Gunakan id unik dan stabil untuk tiap baris (bukan indeks array, bukan UUID yang dibuat ulang)
  • Buat pekerjaan baris sangat kecil: hindari format berat, pohon view besar, dan properti computed mahal di body
  • Kontrol publish: jangan biarkan state yang sering berubah (timer, ketikan, progress) meng-invalidate seluruh daftar
  • Muat dengan halaman dan prefetch sehingga memori tetap stabil
  • Ukur sebelum dan sesudah dengan Instruments agar Anda tidak menebak

Bayangkan inbox support dengan 20.000 percakapan. Tiap baris menunjukkan subjek, preview pesan terakhir, timestamp, badge belum dibaca, dan avatar. Pengguna bisa mencari, dan pesan baru datang saat mereka menggulir. Versi lambat biasanya melakukan beberapa hal sekaligus: membangun ulang baris pada setiap ketikan, mengukur teks terlalu sering, dan mem-fetch terlalu banyak gambar terlalu awal.

Rencana praktis yang tidak mengharuskan merobek basis kode:

  • Baseline: rekam scroll singkat dan sesi pencarian di Instruments (Time Profiler + Core Animation).
  • Perbaiki identitas: pastikan model Anda punya id nyata dari server/database, dan ForEach menggunakannya secara konsisten.
  • Tambahkan paging: mulai dengan 50–100 item terbaru, lalu muat lebih banyak saat pengguna mendekati akhir.
  • Optimalkan gambar: gunakan thumbnail lebih kecil, cache hasil, dan hindari decoding di main thread.
  • Ukur ulang: konfirmasi lebih sedikit pass layout, lebih sedikit update view, dan waktu frame yang lebih stabil di perangkat lama.

Jika Anda membangun produk lengkap (app iOS plus backend dan panel admin web), membantu juga merancang model data dan kontrak paging sejak awal. Platform seperti AppMaster (appmaster.io) dibangun untuk alur kerja full-stack itu: Anda bisa mendefinisikan data dan logika bisnis secara visual, dan tetap menghasilkan source code nyata yang bisa Anda deploy atau self-host.

FAQ

Apa perbaikan tercepat ketika daftar SwiftUI saya terasa tersendat saat digulir?

Mulailah dengan memperbaiki identitas baris. Gunakan id stabil dari model Anda dan hindari menghasilkan ID di dalam view, karena perubahan ID membuat SwiftUI menganggap baris sebagai baru dan membangun ulang lebih sering dari yang diperlukan.

Apakah SwiftUI lambat karena terlalu sering “re-render”?

Rekomputasi body biasanya murah; yang mahal adalah apa yang dipicu olehnya. Layout berat, pengukuran teks, decoding gambar, dan membangun ulang banyak baris akibat identitas yang tidak stabil biasanya menyebabkan drop frame.

Bagaimana cara memilih `id` yang stabil untuk `ForEach` dan `List`?

Jangan gunakan UUID() di dalam baris atau bergantung pada indeks array untuk identity jika data dapat disisipkan, dihapus, atau diurutkan ulang. Pilih ID dari server/database atau UUID yang disimpan di model saat dibuat, sehingga ID tetap sama antar pembaruan.

Bisakah `id: \\.self` memperburuk performa daftar?

Bisa. Terutama jika nilai hash berubah saat properti yang bisa diedit berubah — SwiftUI mungkin melihatnya sebagai baris berbeda. Jika perlu menggunakan Hashable, dasarkan pada satu identifier yang stabil, bukan properti yang dapat berubah seperti name, isSelected, atau teks turunan.

Apa yang harus saya hindari lakukan di dalam `body` sebuah baris?

Pindahkan pekerjaan berat keluar dari body. Format tanggal dan angka sebelumnya, hindari membuat formatter baru per baris, dan jangan membangun array turunan besar dengan map/filter di dalam view; hitung sekali di model atau view model dan kirim nilai siap-tampil ke baris.

Kenapa `onAppear` saya sering terpanggil di daftar panjang?

onAppear berjalan lebih sering daripada yang diperkirakan karena baris muncul dan hilang saat menggulir. Jika tiap baris memulai pekerjaan berat di onAppear (decoding gambar, baca database, parsing), Anda akan mendapat spike berulang; batasi onAppear untuk tugas ringan seperti memicu pagination saat mendekati akhir.

Apa yang menyebabkan “update storms” yang membuat gulir terasa lengket?

Nilai yang sering berubah dan dipublish bersama daftar dapat membuatnya terinvalidasi berulang kali, walau data baris tidak berubah. Jaga agar timer, state pengetikan, dan progress update tidak berada di objek utama yang menggerakkan daftar; debounce pencarian; dan pecah ObservableObject besar menjadi beberapa bagian jika perlu.

Kapan saya harus memilih `List` vs `LazyVStack` untuk dataset besar?

Gunakan List saat UI Anda bersifat tabel: baris standar, swipe action, seleksi, separator—karena List mendapat optimisasi platform. Gunakan ScrollView + LazyVStack saat butuh layout kustom, tapi ukur memori dan dropped frames karena mudah melakukan pekerjaan layout ekstra.

Apa pendekatan pagination sederhana yang tetap lancar?

Mulailah memuat lebih awal daripada menunggu baris terakhir muncul — mulai saat pengguna mencapai threshold mendekati akhir, dan lindungi dari trigger duplikat. Pilih ukuran halaman yang wajar, lacak isLoading dan reachedEnd, dan deduplikasi hasil dengan ID stabil untuk menghindari baris duplikat dan diff ekstra.

Bagaimana saya mengukur apa yang benar-benar memperlambat daftar SwiftUI saya?

Buat baseline di perangkat nyata dan gunakan Instruments untuk menemukan spike main-thread dan lonjakan alokasi saat scroll cepat. Time Profiler menunjukkan apa yang menghalangi scroll, Allocations mengungkap churn per-barang, dan Core Animation mengonfirmasi dropped frames sehingga Anda tahu apakah bottleneck di rendering atau pekerjaan data.

Mudah untuk memulai
Ciptakan sesuatu yang menakjubkan

Eksperimen dengan AppMaster dengan paket gratis.
Saat Anda siap, Anda dapat memilih langganan yang tepat.

Memulai