SwiftUI dentro UIKit: collegare due mondi

Come portare in SwiftUI solo il contenuto di una UITableViewCell, usando UIHostingController e notifiche Combine, senza problemi di scrolling.

Read in English

Tradotto dall’inglese con l’aiuto dell’IA e rivisto dall’autore.Leggi l’originale

Il logo arancione di Swift con un'ombra scura su uno sfondo sfumato blu.

Ieri sera ho partecipato a una room su Clubhouse molto interessante, in cui si parlava di SwiftUI e di interoperabilità con Swift. Il resto dell'articolo è un proof-of-concept che esplora alcune idee emerse durante la chat con Dimitri Giani, Stefano Mondino e Andrea Mengoli.

SwiftUI offre ottime prestazioni e rende semplice costruire anche layout complessi, ma spostare un intero progetto da UIKit a SwiftUI non è sempre fattibile, per molte ragioni (maturità del framework, tempi di consegna, e così via). Allora perché non prendere il meglio dei due mondi?

Esistono decine di tutorial e articoli sull'uso di UIHostingController che, ad oggi, sembra l'unica strada percorribile per realizzare quanto detto sopra. Ieri sera però ci siamo concentrati su un punto di vista un po' diverso:

Spostare in SwiftUI solo porzioni di codice molto piccole.

Per esempio, una delle cose più odiate da ogni sviluppatore iOS è dover usare lo Storyboard per disegnare le celle di una UITableView/UICollectionView.

Non sarebbe bello migrare in SwiftUI solo il contenuto di una UITableViewCell, per migliorare prestazioni, manutenibilità e semplicità, rendendo così a prova di futuro almeno una parte del nostro codice? Era la proposta di Dimitri Giani.

Tutti, io compreso, eravamo scettici: UIHostingController sembrava un approccio molto complicato e sovradimensionato per un compito così critico dal punto di vista delle prestazioni (parliamo dei componenti riutilizzabili di una UIScrollView).

Inutile dire che mi sbagliavo. Almeno in un primissimo test, descritto nel resto di questo articolo, l'approccio funziona benissimo, con qualche accorgimento, e senza alcun problema di prestazioni.

Schizzo di uno Storyboard: una table view con una cella prototipo vuota e una view SwiftUI disegnata a mano, indicata come rootView di un UIHostingController

Sopra c'è una bozza di ciò che vogliamo ottenere. Come si vede, la UITableViewCell deve conoscere la proprietà rootView del corrispondente UIHostingController e, poiché ogni istanza di UITableViewCell deve essere riutilizzabile, dobbiamo mantenere questa proprietà unica.

Codice di CustomTableViewCell: crea un UIHostingController, ne vincola la view alla contentView in awakeFromNib e invia una notifica in configure

Rendiamo la nostra CustomTableViewCell consapevole di questo creando una costante private che istanzia un UIHostingController e configurando l'oggetto nella primissima fase di awakeFromNib.

Il metodo configure(title:sfIcon:) è solo un segnaposto che sfrutta l'approccio Combine / publisher per comunicare con la corrispondente view SwiftUI. Qui inviamo una semplice Notification quando dobbiamo configurare la SwiftUIView.

Da notare che dobbiamo specificare la proprietà object, perché vogliamo gestire solo una specifica istanza dell'oggetto.

Sì, lo ammetto, sembra davvero un hack...

Codice di GuestView, una HStack SwiftUI con un'immagine e un titolo che si aggiorna con un publisher di NotificationCenter e adotta Equatable

La controparte SwiftUI è molto semplice. Creiamo solo un contenitore HStack che ospita sia l'immagine di anteprima sia il titolo. Regoliamo anche il frame in modo da occupare tutto lo spazio disponibile senza ulteriori vincoli (la UIView che ci contiene sarà responsabile delle dimensioni del contenitore, N.d.R.). onReceive aggiorneremo i relativi stati, facendo in modo che la view si aggiorni da sola dove serve.

Noterete che ho fatto adottare Equatable alla struct stessa, aggiungendo sia un id sia il relativo metodo in override. Serve per distinguere se dobbiamo scartare o prendere in considerazione la notifica pubblicata ricevuta da NotificationCenter.

Ed è tutto. Anche se questa soluzione è un'architettura di cella molto hackish e sovradimensionata, sono davvero colpito dai risultati: lo scrolling è fluido e senza il minimo glitch.

Sono abbastanza sicuro che esistano altri modi per ottenere lo stesso risultato con meno boilerplate e/o in modo molto più elegante (soprattutto per la comunicazione da UIKit a SwiftUI).

Non vedo l'ora di leggere i vostri commenti e di vedere i vostri commit. Il codice completo è disponibile qui su GITHUB.

Continua a leggere

Tutti gli articoli
  1. TextField e UIViewRepresentable
  2. Un gioco TicTacToe in SwiftUI
  3. Programmare con SwiftUI
↑ ↓ naviga↵ apriesc chiudi