17 nov 2025·8 min di lettura

Ottimizzazione delle prestazioni SwiftUI per liste lunghe: rimedi pratici

Ottimizzazione delle prestazioni SwiftUI per liste lunghe: soluzioni pratiche per rerender, identità stabile delle righe, paginazione, caricamento immagini e scorrimento fluido su iPhone più vecchi.

Ottimizzazione delle prestazioni SwiftUI per liste lunghe: rimedi pratici

Come si presentano le “liste lente” nelle app SwiftUI reali

Una “lista lenta” in SwiftUI di solito non è un bug. È il momento in cui l'interfaccia non riesce a stare al passo con il dito. Lo noti mentre scorri: la lista esita, i frame saltano e tutto sembra pesante.

Segnali tipici:

  • Lo scorrimento va a scatti, specialmente su dispositivi più vecchi
  • Le righe sfarfallano o mostrano brevemente contenuti sbagliati
  • I tap sembrano in ritardo, o le azioni swipe partono tardi
  • Il telefono si scalda e la batteria cala più del previsto
  • L'uso di memoria cresce quanto più scorri

Le liste lunghe possono sembrare lente anche quando ogni riga appare “piccola”, perché il costo non è solo disegnare pixel. SwiftUI deve comunque capire cosa è ogni riga, calcolare il layout, risolvere font e immagini, eseguire il tuo codice di formattazione e fare il diff quando i dati cambiano. Se uno di questi lavori avviene troppo spesso, la lista diventa un punto critico.

Conviene anche separare due idee. In SwiftUI, un “re-render” spesso significa che il body di una vista viene ricalcolato. Quella parte è di solito economica. Il lavoro costoso è ciò che il ricalcolo innesca: layout pesante, decodifica immagini, misurazione del testo o ricostruzione di molte righe perché SwiftUI pensa che la loro identità sia cambiata.

Immagina una chat con 2.000 messaggi. Arrivano nuovi messaggi ogni secondo e ogni riga formatta timestamp, misura testo multilinea e carica avatar. Anche se aggiungi solo un elemento, un cambiamento di stato con ambito troppo ampio può far riesaminare molte righe e alcune di esse ridisegnare.

L'obiettivo non è la micro-ottimizzazione. Vuoi scorrimento fluido, tap istantanei e aggiornamenti che toccano solo le righe effettivamente cambiate. Le correzioni qui sotto si concentrano su identità stabile, righe più leggere, meno aggiornamenti non necessari e caricamenti controllati.

Le cause principali: identità, lavoro per riga e tempeste di aggiornamenti

Quando una lista SwiftUI sembra lenta, raramente è per “troppi elementi”. È lavoro extra che avviene mentre scorri: ricostruire righe, ricalcolare layout o ricaricare immagini ripetutamente.

La maggior parte delle cause rientra in tre categorie:

  • Identità instabile: le righe non hanno un id coerente, o usi \.self per valori che possono cambiare. SwiftUI non riesce ad abbinare le vecchie righe con quelle nuove, quindi ricostruisce più del necessario.
  • Troppo lavoro per riga: formattazione date, filtri, ridimensionamento immagini o lavoro di rete/disk nella vista di riga.
  • Tempeste di aggiornamenti: un cambiamento (digitazione, tick di un timer, aggiornamento di progresso) scatena aggiornamenti di stato frequenti e la lista si rinfresca ripetutamente.

Esempio: hai 2.000 ordini. Ogni riga formatta la valuta, costruisce una stringa attribuita e avvia un fetch immagine. Nel frattempo, un timer "ultimo sync" aggiorna una volta al secondo nella vista padre. Anche se i dati degli ordini non cambiano, quel timer può invalidare la lista abbastanza spesso da rendere lo scorrimento scattoso.

Perché List e LazyVStack possono comportarsi diversamente

List è più di una scroll view. È pensata attorno al comportamento di tabelle/collection e ottimizzazioni di sistema. Spesso gestisce grandi dataset con meno memoria, ma può essere sensibile all'identità e agli aggiornamenti frequenti.

ScrollView + LazyVStack ti dà più controllo sul layout e l'aspetto, ma è anche più facile fare involontariamente lavoro di layout extra o scatenare aggiornamenti costosi. Su dispositivi vecchi, quel lavoro in più si vede prima.

Prima di riscrivere l'interfaccia, misura. Correzioni piccole come ID stabili, spostare il lavoro fuori dalle righe e ridurre il churn di stato spesso risolvono il problema senza cambiare il contenitore.

Correggi l'identità delle righe così SwiftUI può fare diff efficiente

Quando una lista lunga è scattosa, spesso la colpa è l'identità. SwiftUI decide quali righe riusare confrontando gli ID. Se quegli ID cambiano, SwiftUI tratta le righe come nuove, scarta le vecchie e ricostruisce più del necessario. Questo può apparire come re-render casuali, perdita di posizione nello scroll o animazioni che partono senza motivo.

La vittoria più semplice: fai in modo che l'id di ogni riga sia stabile e legato alla tua fonte dati.

Un errore comune è generare l'identità dentro la vista:

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

Questo forza un nuovo ID a ogni render, quindi ogni riga diventa “diversa” ogni volta.

Preferisci ID già presenti nel tuo modello, come una chiave primaria del database, un ID del server o uno slug stabile. Se non ne hai uno, crealo una sola volta quando crei il modello — non dentro la vista.

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

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

Fai attenzione agli indici. ForEach(items.indices, id: \.self) lega l'identità alla posizione. Se inserisci, cancelli o riordini, le righe “si muovono” e SwiftUI può riusare la vista sbagliata per i dati sbagliati. Usa gli indici solo per array veramente statici.

Se usi id: \.self, assicurati che il valore Hashable sia stabile nel tempo. Se l'hash cambia quando un campo viene aggiornato, anche l'identità della riga cambia. Una regola sicura per Equatable e Hashable: basali su un singolo ID stabile, non su proprietà modificabili come name o isSelected.

Controlli di buon senso:

  • Gli ID provengono dalla fonte dati (non da UUID() nella vista)
  • Gli ID non cambiano quando il contenuto della riga cambia
  • L'identità non dipende dalla posizione dell'array a meno che la lista non venga mai riordinata

Riduci i re-render rendendo le righe più leggere

Una lista lunga spesso sembra lenta perché ogni riga fa troppo lavoro ogni volta che SwiftUI ricalcola il suo body. L'obiettivo è semplice: rendere ogni riga economica da ricostruire.

Un costo nascosto comune è passare valori “grandi” nella riga. Struct pesanti, modelli profondamente annidati o proprietà calcolate costose possono scatenare lavoro extra anche quando l'interfaccia sembra immutata. Potresti ricostruire stringhe, parsare date, ridimensionare immagini o produrre alberi di layout complessi più spesso di quanto immagini.

Sposta il lavoro pesante fuori dal body

Se qualcosa è lento, non ricostruirlo dentro il body della riga più e più volte. Precomputalo quando arrivano i dati, cachalo nel view model o memoizzalo in un piccolo helper.

Costi a livello di riga che si sommano in fretta:

  • Creare un nuovo DateFormatter o NumberFormatter per ogni riga
  • Formattazione pesante di stringhe in body (join, regex, parsing markdown)
  • Costruire array derivati con .map o .filter dentro body
  • Leggere blob grandi e convertirli (es. decodifica JSON) nella vista
  • Layout troppo complesso con molte VStack/HStack annidate e conditionals

Un esempio semplice: tieni i formatter statici e passa stringhe già formattate nella riga.

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)
        }
    }
}

Spezza le righe e usa Equatable quando conviene

Se cambia solo una piccola parte (come un badge), isolala in una sotto-vista così il resto della riga resta stabile.

Per UI guidate da valori, rendere una sotto-vista Equatable (o avvolgerla con EquatableView) può aiutare SwiftUI a saltare lavoro quando gli input non sono cambiati. Mantieni gli input equatabili piccoli e specifici — non l'intero modello.

Controlla gli aggiornamenti di stato che innescano il refresh dell'intera lista

Sviluppa più velocemente rispetto all'ottimizzazione manuale delle liste
Modella i tuoi dati e la paginazione una volta sola, poi genera insieme codice iOS, web e backend.
Prova AppMaster

A volte le righe vanno bene, ma qualcosa continua a dire a SwiftUI di rinfrescare l'intera lista. Durante lo scorrimento, anche piccoli aggiornamenti extra possono trasformarsi in scatti, specialmente su dispositivi vecchi.

Una causa comune è ricreare il modello troppo spesso. Se una vista padre viene ricostruita e hai usato @ObservedObject per un view model che la vista possiede, SwiftUI può ricrearlo, resettare le subscription e scatenare nuove pubblicazioni. Se la vista possiede il modello, usa @StateObject in modo che venga creato una sola volta e resti stabile. Usa @ObservedObject per oggetti iniettati da fuori.

Un altro killer silenzioso sono le publish troppo frequenti. Timer, pipeline Combine e aggiornamenti di progresso possono sparare molte volte al secondo. Se una proprietà pubblicata influenza la lista (o risiede in un ObservableObject condiviso dallo schermo), ogni tick può invalidare la lista.

Esempio: hai un campo di ricerca che aggiorna query a ogni battuta e filtra 5.000 elementi. Se filtri immediatamente, la lista si ridiffa costantemente mentre l'utente digita. Debounce per la query e aggiorna l'array filtrato dopo una breve pausa.

Pattern che generalmente aiutano:

  • Tieni valori che cambiano rapidamente fuori dall'oggetto che guida la lista (usa oggetti più piccoli o @State locale)
  • Debounce per ricerca e filtraggio in modo che la lista si aggiorni dopo una pausa di digitazione
  • Evita publish ad alta frequenza; aggiorna meno spesso o solo quando il valore è effettivamente cambiato
  • Mantieni lo stato per riga locale (es. @State nella riga) invece che un valore globale che cambia costantemente
  • Separa i modelli grandi: un ObservableObject per i dati della lista, un altro per lo stato UI a livello di schermo

L'idea è semplice: rendi il tempo di scorrimento tranquillo. Se nulla di importante è cambiato, la lista non dovrebbe essere chiamata a fare lavoro.

Scegli il contenitore giusto: List vs LazyVStack

Il contenitore che scegli influisce su quanto lavoro fa iOS per te.

List è in genere la scelta più sicura quando la tua UI somiglia a una tabella standard: righe con testo, immagini, azioni swipe, selezione, separatori, edit mode e accessibilità. Sotto il cofano beneficia di ottimizzazioni di piattaforma che Apple ha raffinato per anni.

Una ScrollView con LazyVStack è ottima quando ti servono layout personalizzati: card, blocchi di contenuto misto, header speciali o un feed stile social. “Lazy” significa che costruisce le righe quando entrano in vista, ma non offre lo stesso comportamento di List in ogni caso. Con dataset molto grandi questo può comportare un maggiore uso di memoria e scorrimento più scattoso su dispositivi datati.

Una regola semplice:

  • Usa List per schermate tabellari classiche: impostazioni, inbox, ordini, liste admin
  • Usa ScrollView + LazyVStack per layout personalizzati e contenuti misti
  • Se hai migliaia di elementi e ti serve solo una tabella, parti con List
  • Se ti serve controllo pixel-perfect, prova LazyVStack e poi misura memoria e frame drops

Fai attenzione anche a stili che rallentano silenziosamente lo scorrimento. Effetti per riga come shadow, blur e overlay complessi possono forzare lavoro di rendering extra. Se vuoi profondità, applica effetti pesanti a elementi piccoli (es. un'icona) invece che all'intera riga.

Esempio concreto: una schermata “Ordini” con 5.000 righe spesso resta fluida in List perché le righe vengono riutilizzate. Se passi a LazyVStack e costruisci righe in stile card con grandi ombre e overlay multipli, potresti vedere jank anche se il codice sembra pulito.

Paginazione che scorre bene ed evita picchi di memoria

Lancia con gli essenziali inclusi
Usa auth preconfezionata e pagamenti Stripe per concentrarti sulle prestazioni, non sull'infrastruttura.
Inizia

La paginazione mantiene le liste lunghe veloci perché renderizzi meno righe, tieni meno modelli in memoria e dai a SwiftUI meno lavoro di diff.

Inizia con un contratto di paging chiaro: una dimensione pagina fissa (ad esempio 30-60 elementi), un flag “nessun altro risultato” e una riga di caricamento che appare solo mentre fetchi.

Una trappola comune è avviare la pagina successiva solo quando appare l'ultima riga. Spesso è troppo tardi, quindi l'utente arriva alla fine e vede una pausa. Invece, avvia il caricamento quando appare una delle ultime righe.

Pattern semplice:

@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
        }
    }
}

Questo evita problemi comuni come righe duplicate (risultati API sovrapposti), condizioni di race da molteplici chiamate onAppear, e caricamento eccessivo.

Se la lista supporta pull-to-refresh, resettare lo stato di paging con cura (svuota items, resetta reachedEnd, cancella task in corso se possibile). Se controlli il backend, ID stabili e paginazione basata su cursor rendono l'interfaccia notevolmente più fluida.

Immagini, testo e layout: mantieni leggero il rendering delle righe

Crea schermate admin veloci in pochi passi
Costruisci strumenti interni e liste amministrative che restano reattive anche con migliaia di record.
Prova AppMaster

Le liste lunghe raramente sono lente a causa del contenitore. La maggior parte delle volte il problema è la riga. Le immagini sono il colpevole più comune: decodifica, ridimensionamento e disegno possono non tenere il passo con la velocità di scorrimento, soprattutto su dispositivi più vecchi.

Se carichi immagini remote, assicurati che il lavoro pesante non avvenga sul main thread durante lo scroll. Evita anche di scaricare asset in piena risoluzione per una miniatura da 44–80 pt.

Esempio: una schermata “Messaggi” con avatar. Se ogni riga scarica un'immagine 2000x2000, la ridimensiona e applica blur o shadow, la lista andrà a scatti anche se il modello dati è semplice.

Mantieni prevedibile il lavoro sulle immagini

Abitudini ad alto impatto:

  • Usa thumbnail generate server-side o pre-generate vicine alla dimensione mostrata
  • Decodifica e ridimensiona off the main thread quando possibile
  • Cache delle miniature così lo scroll veloce non ri-fetch o ri-decode
  • Usa un placeholder che abbia la stessa dimensione finale per evitare flicker e salti di layout
  • Evita modificatori costosi sulle immagini nelle righe (ombre pesanti, maschere, blur)

Stabilizza il layout per evitare thrash

SwiftUI può passare più tempo a misurare che a disegnare se l'altezza della riga continua a cambiare. Cerca di mantenere le righe prevedibili: frame fissi per le miniature, limiti di linee coerenti e spaziature stabili. Se il testo può espandersi, limitane le righe (ad es. 1–2) così un singolo aggiornamento non forza misurazioni extra.

I placeholder contano anche qui. Un cerchio grigio che diventa avatar dopo dovrebbe occupare lo stesso frame, così la riga non riformatta a metà scroll.

Come misurare: controlli con Instruments che rivelano i veri colli di bottiglia

Lavorare sulle prestazioni è azzardoso se ti basi solo sul “sento che va a scatti”. Instruments ti dice cosa gira sul main thread, cosa viene allocato durante lo scroll veloce e cosa causa frame persi.

Definisci una baseline su un dispositivo reale (meglio ancora uno più vecchio se lo supporti). Fai un'azione ripetibile: apri la schermata, scorri velocemente dall'inizio alla fine, attiva il caricamento di una pagina, poi scorri indietro. Nota i punti di maggior hitch, il picco di memoria e se l'UI resta responsiva.

Le tre viste di Instruments che pagano

Usale insieme:

  • Time Profiler: cerca i picchi sul main thread mentre scorri. Layout, misurazione testo, parsing JSON e decodifica immagine qui spesso spiegano il problema.
  • Allocations: osserva le ondate di oggetti temporanei durante lo scroll veloce. Spesso indica formatting ripetuto, nuove attributed string o ricostruzione di modelli per riga.
  • Core Animation: conferma i frame persi e i lunghi tempi di frame. Questo aiuta a separare pressione sul rendering da lavoro sui dati.

Quando trovi un picco, clicca nell'albero delle chiamate e chiediti: succede una volta per schermata, o una volta per riga, per ogni scroll? La seconda è quella che rompe lo scorrimento fluido.

Aggiungi signpost per eventi di scroll e paginazione

Molte app fanno lavoro extra durante lo scroll (caricamenti immagini, paginazione, filtraggio). I signpost ti aiutano a vedere quei momenti sulla 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")

Ritest dopo ogni modifica, una alla volta. Se FPS migliora ma Allocations peggiora, potresti aver scambiato stutter per pressione di memoria. Tieni appunti di baseline e conserva solo le modifiche che muovono i numeri nella direzione giusta.

Errori comuni che uccidono silenziosamente le prestazioni delle liste

Riduci gli update storm alla fonte
Automatizza i workflow con processi di business visuali e mantieni la logica dell'app coerente ovunque.
Avvia progetto

Alcuni problemi sono ovvi (immagini grandi, dataset enormi). Altri emergono solo quando i dati crescono, specialmente su dispositivi datati.

1) ID righe instabili

Un errore classico è creare ID dentro la vista, come id: \.self per tipi reference o UUID() nel corpo della riga. SwiftUI usa l'identità per fare il diff. Se l'ID cambia, SwiftUI tratta la riga come nuova, la ricostruisce e può scartare il layout cache.

Usa un ID stabile dal tuo modello (chiave primaria DB, ID server o una UUID memorizzata creata una sola volta quando l'item viene creato). Se non ce l'hai, aggiungilo.

2) Lavoro pesante dentro onAppear

onAppear viene eseguito più spesso di quanto si pensi perché le righe vanno e vengono mentre scorri. Se ogni riga avvia decodifica immagini, parsing JSON o lookup DB in onAppear, otterrai picchi ripetuti.

Sposta il lavoro pesante fuori dalla riga. Precomputa quando carichi i dati, metti in cache i risultati e tieni onAppear limitato ad azioni leggere (come triggerare la paginazione quando sei vicino alla fine).

3) Binding dell'intera lista per modifiche alle righe

Quando ogni riga riceve un @Binding su un grande array, una piccola modifica può sembrare un grande cambiamento. Questo può causare la riesecuzione di molte righe e talvolta il refresh dell'intera lista.

Preferisci passare valori immutabili alla riga e inviare i cambi con una action leggera (es. “toggle favorite per id”). Mantieni lo stato per riga locale solo quando gli appartiene davvero.

4) Troppe animazioni durante lo scroll

Le animazioni sono costose in una lista perché possono scatenare ulteriori passaggi di layout. Applicare animation(.default, value:) in alto (sull'intera lista) o animare ogni piccolo stato cambia può rendere lo scorrimento appiccicoso.

Regole semplici:

  • Limita le animazioni alla singola riga che cambia
  • Evita di animare durante lo scroll veloce (soprattutto per selezione/highlight)
  • Stai attento con le animazioni implicite su valori che cambiano frequentemente
  • Preferisci transizioni semplici invece di effetti combinati complessi

Un esempio reale: una lista in stile chat dove ogni riga avvia un fetch in onAppear, usa UUID() per id e anima i cambi di stato “letto”. Quella combinazione crea churn costante. Sistemare l'identità, fare caching del lavoro e limitare le animazioni spesso rende la stessa UI istantaneamente più fluida.

Checklist rapida, un esempio pratico e i passi successivi

Se puoi fare solo poche cose, inizia da qui:

  • Usa un id unico e stabile per ogni riga (non l'indice, non una UUID appena generata)
  • Mantieni il lavoro della riga piccolo: evita formattazioni pesanti, grandi alberi di vista e proprietà calcolate costose in body
  • Controlla le pubblicazioni: non lasciare che stato che cambia rapidamente (timer, digitazione, progresso) invalida l'intera lista
  • Carica a pagine e prefetch in modo che la memoria resti piatta
  • Misura prima e dopo con Instruments così non stai indovinando

Immagina una inbox di supporto con 20.000 conversazioni. Ogni riga mostra un soggetto, anteprima dell'ultimo messaggio, timestamp, badge non letto e un avatar. Gli utenti possono cercare e arrivano nuovi messaggi mentre scorrono. La versione lenta solitamente fa alcune cose insieme: ricostruisce righe a ogni battuta, rimesura testo troppo spesso e scarica troppe immagini troppo presto.

Un piano pratico che non richiede di buttare via il codebase:

  • Baseline: registra uno scroll breve e una sessione di ricerca in Instruments (Time Profiler + Core Animation).
  • Sistema l'identità: assicurati che il modello abbia un vero id dal server/database e ForEach lo usi coerentemente.
  • Aggiungi paginazione: parti con i più recenti 50–100 elementi, poi carica di più quando l'utente si avvicina alla fine.
  • Ottimizza le immagini: usa thumbnail più piccoli, metti in cache i risultati e evita la decodifica sul main thread.
  • Riesegui le misure: conferma meno passaggi di layout, meno aggiornamenti di vista e tempi di frame più stabili su dispositivi più vecchi.

Se stai costruendo un prodotto completo (app iOS più backend e pannello admin web), aiuta anche progettare il modello dati e il contratto di paging fin da subito. Piattaforme come AppMaster (appmaster.io) sono pensate per quel workflow full-stack: puoi definire dati e logica di business in modo visuale e comunque generare codice sorgente reale che puoi distribuire o ospitare autonomamente.

FAQ

Qual è la soluzione più rapida quando la mia lista SwiftUI scorre a scatti?

Inizia correggendo l'identità delle righe. Usa un id stabile preso dal modello e evita di generare ID nella vista, perché cambiare gli ID costringe SwiftUI a trattare le righe come nuove e a ricostruirne molte più del necessario.

SwiftUI è lento perché "riallega/rerenderizza" troppo?

Un ricalcolo di body è di solito economico; la parte costosa è ciò che quel ricalcolo innesca. Layout pesante, misurazione del testo, decodifica delle immagini e la ricostruzione di molte righe a causa di identità instabile sono ciò che tipicamente causa i cali di frame.

Come scelgo un `id` stabile per `ForEach` e `List`?

Non usare UUID() dentro la riga né fare affidamento sugli indici degli array se i dati possono essere inseriti, cancellati o riordinati. Preferisci un ID del server/database o una UUID memorizzata sul modello al momento della sua creazione, così l'ID resta identico negli aggiornamenti.

`id: \.self` può rendere le liste più lente?

Può peggiorare le prestazioni, specialmente se l'hash del valore cambia quando campi modificabili vengono aggiornati: SwiftUI può considerarlo come una riga diversa. Se usi Hashable, basalo su un singolo identificatore stabile invece che su proprietà come name, isSelected o testo derivato.

Cosa dovrei evitare di fare dentro il `body` di una riga?

Sposta il lavoro pesante fuori da body. Preformatta date e numeri, evita di creare nuovi formatter per ogni riga, e non costruire grandi array derivati con map/filter dentro la vista; calcola una volta nel modello o nel view model e passa valori piccoli e pronti per la visualizzazione alla riga.

Perché il mio `onAppear` viene chiamato così spesso in una lista lunga?

onAppear viene chiamato più spesso di quanto si pensi perché le righe entrano ed escono dallo schermo durante lo scorrimento. Se ogni riga avvia lavoro pesante lì (decodifica immagini, letture database, parsing), otterrai picchi ripetuti; limita onAppear ad azioni leggere come richiamare la paginazione vicino alla fine.

Cosa causa gli "update storms" che rendono lo scorrimento appiccicoso?

Qualsiasi valore pubblicato ad alta frequenza condiviso con la lista può invalidarla ripetutamente, anche se i dati delle righe non sono cambiati. Tieni timer, stato di digitazione e aggiornamenti di progresso fuori dall'oggetto principale che guida la lista, applica debounce alla ricerca, e suddividi grandi ObservableObject in oggetti più piccoli quando serve.

Quando dovrei usare `List` vs `LazyVStack` per dataset grandi?

Usa List quando l'interfaccia è in stile tabella (righe standard, azioni swipe, selezione, separatori) e vuoi le ottimizzazioni di sistema. Usa ScrollView + LazyVStack per layout personalizzati, ma misura memoria e cali di frame: è più facile lì fare lavoro di layout extra che peggiora lo scorrimento.

Qual è un approccio semplice alla paginazione che resta fluido?

Inizia prima dell'ultima riga vera e propria: avvia il caricamento quando l'utente raggiunge una soglia vicino alla fine e proteggi da trigger duplicati. Mantieni le dimensioni delle pagine ragionevoli, traccia isLoading e reachedEnd, e deduplica i risultati tramite ID stabili per evitare righe duplicate e diff inutili.

Come misuro cosa sta realmente rallentando la mia lista SwiftUI?

Fai una baseline su un dispositivo reale e usa Instruments per trovare picchi sul main thread e ondate di allocazioni durante lo scroll veloce. Time Profiler mostra cosa blocca lo scorrimento, Allocations rivela churn per riga e Core Animation conferma i frame persi, così sai se il collo di bottiglia è nel rendering o nel lavoro sui dati.

Facile da avviare
Creare qualcosa di straordinario

Sperimenta con AppMaster con un piano gratuito.
Quando sarai pronto potrai scegliere l'abbonamento appropriato.

Iniziare