Swinject: cos'è, principi di Dependency Injection e come funziona

Autore: IT Sectr Pubblicato: 2026-05-04 Tempo di lettura: 8 min

Swinject è un container DI per Swift che implementa il pattern Dependency Injection nelle applicazioni iOS. Il framework automatizza la creazione e l'iniezione di dipendenze, eliminando la gestione manuale di oggetti e factory. Secondo Swinject su GitHub, la libreria supporta Constructor Injection, Property Injection e Method Injection con un flessibile sistema di scope per la gestione del ciclo di vita.

Punti chiave

  • Swinject — un container DI per Swift che automatizza l'iniezione di dipendenze nei progetti iOS.
  • Dependency Injection — un pattern in cui un oggetto riceve le sue dipendenze dall'esterno invece di crearle internamente.
  • Container — il componente centrale di Swinject che memorizza un registro dei servizi registrati e delle loro factory.
  • Service — un'astrazione sotto forma di protocollo per la quale il container memorizza un'implementazione concreta.
  • ObjectScope — un meccanismo che determina la durata dell'istanza: graph, container o transient.

Cos'è Swinject e Dependency Injection

Swinject è un container DI open source per il linguaggio Swift, progettato per semplificare l'iniezione di dipendenze nelle applicazioni per iOS, macOS e watchOS. Il framework utilizza l'approccio Service Locator: i servizi vengono registrati in un container centrale e il container risolve automaticamente il grafo delle dipendenze quando viene richiesta un'istanza.

Dependency Injection (DI) è un pattern di progettazione in cui un oggetto riceve le sue dipendenze dall'esterno invece di crearle internamente. Ciò riduce l'accoppiamento tra i componenti, semplifica i test unitari e consente di sostituire le implementazioni senza modificare il codice del consumatore.

Secondo Martin Fowler (2004), DI è un caso specifico di Inversion of Control e viene implementato tramite iniezione tramite costruttore, proprietà o metodo. Swinject automatizza questo processo, eliminando la necessità di scrivere manualmente factory e service locator.

Utilizza Swinject in progetti con tre o più servizi che hanno dipendenze incrociate, dove la costruzione manuale di oggetti porta a codice di inizializzazione gonfio e ridotta testabilità.

Swinject si integra strettamente con l'ecosistema Apple e supporta tutte le versioni di Swift a partire dalla 3.0. Il framework è compatibile con Objective-C tramite bridge, consentendo di introdurlo in progetti esistenti in linguaggio misto senza una migrazione completa del codice. Ciò è particolarmente rilevante per applicazioni grandi con più di cinque anni di sviluppo.

Come funziona il container Swinject

Il container Swinject è implementato dalla classe Container, che memorizza un registro dei servizi registrati. Quando viene chiamato il metodo resolve, il container crea un oggetto, risolvendo ricorsivamente tutte le sue dipendenze attraverso il grafo di registrazione.

Container e Service

Container è l'oggetto centrale in cui vengono registrate le corrispondenze tra astrazioni e loro implementazioni. Un Service è un protocollo che definisce un contratto, mentre un Component è una classe che implementa questo protocollo. La registrazione viene effettuata utilizzando il metodo register, che accetta il tipo di servizio e una factory.

swift
let container = Container()
container.register(Networking.self) { _ in
    NetworkService()
}
let service = container.resolve(Networking.self)

Il metodo resolve restituisce un'istanza dell'implementazione concreta registrata per il protocollo specificato. Se una dipendenza non è registrata, il container lancia un errore fatale per il rapido rilevamento del problema durante lo sviluppo.

Registrazione e servizi nominati

Ogni registrazione crea una voce con una funzione factory e uno scope selezionato. Un singolo servizio può avere più registrazioni con nomi diversi, consentendo di selezionare un'implementazione specifica per nome — utile per diversi ambienti (sviluppo, staging, produzione).

Il processo di risoluzione delle dipendenze funziona in modo ricorsivo: quando il container crea un'istanza di Component, analizza il suo inizializzatore e per ogni parametro chiama resolve con il tipo corrispondente. Se una dipendenza ha anche le proprie dipendenze, il processo continua fino a quando l'intero grafo non è completamente costruito. La profondità di annidamento è limitata solo dalla memoria disponibile, ma in pratica raramente supera i cinque livelli.

Metodi di iniezione delle dipendenze in Swinject

Swinject supporta tre metodi principali di iniezione delle dipendenze, ciascuno applicabile a seconda del contesto architetturale.

Constructor Injection

Constructor Injection inietta le dipendenze tramite i parametri dell'inizializzatore. Questo è il metodo preferito, che garantisce che un oggetto sia sempre in uno stato valido dal momento della creazione. Swinject risolve automaticamente tutte le dipendenze passate al costruttore.

swift
class LoginViewModel {
    private let authService: AuthProtocol

    init(authService: AuthProtocol) {
        self.authService = authService
    }
}

container.register(AuthProtocol.self) { _ in
    AuthService()
}
container.register(LoginViewModel.self) { r in
    LoginViewModel(authService: r.resolve(AuthProtocol.self)!)
}

Property Injection

Property Injection inietta le dipendenze impostando le proprietà dell'oggetto dopo l'inizializzazione. Viene utilizzato quando una dipendenza è opzionale o non può essere passata tramite il costruttore, ad esempio, quando si lavora con Storyboard, dove il view controller viene creato automaticamente. Swinject supporta l'annotazione @Inject per l'iniezione automatica delle proprietà in fase di esecuzione senza una chiamata esplicita a resolve.

Quando si utilizza Property Injection, è importante assicurarsi che la dipendenza sia impostata prima del primo accesso all'oggetto. Altrimenti, la proprietà rimarrà nil, portando a un crash imprevisto. Swinject risolve questo problema attraverso il meccanismo Implicitly Unwrapped Optional e una validazione rigorosa nella fase di risoluzione del grafo delle dipendenze.

Method Injection

Method Injection inietta le dipendenze tramite i parametri del metodo. Viene utilizzato per servizi necessari solo per eseguire una singola operazione e che non devono essere memorizzati come stato permanente dell'oggetto. Questo è il metodo meno comune ma utile per i callback.

Scope in Swinject e loro scopo

ObjectScope è un meccanismo che determina la durata di un'istanza creata all'interno del container Swinject. Il framework fornisce tre scope integrati con la possibilità di crearne di personalizzati tramite ObjectScopeProtocol.

ObjectScope.graph

Lo scope graph è il valore predefinito. Ogni chiamata a resolve crea una nuova istanza che vive solo per la durata della risoluzione del grafo delle dipendenze. È una scelta sicura per i servizi senza stato poiché elimina le perdite di memoria dovute alla memorizzazione nella cache.

ObjectScope.container

Lo scope container è un singleton all'interno del container. L'istanza viene creata una volta al primo resolve e restituita per tutte le richieste successive. Adatto per servizi con stato condiviso: cache dati, logger, impostazioni dell'applicazione.

ObjectScope.transient

Lo scope transient crea una nuova istanza ad ogni chiamata a resolve senza memorizzazione nella cache. Utilizzato per oggetti leggeri che non necessitano di essere riutilizzati — ad esempio, moduli che gestiscono una specifica richiesta HTTP.

ScopeDurataUtilizzo consigliato
graphPer la durata della risoluzione del grafoServizi senza stato per impostazione predefinita
containerPer tutta la vita del containerSingleton: cache, logger, client di rete
transientSenza cacheOggetti leggeri per uso singolo

Swinject nei progetti iOS

L'integrazione di Swinject in un progetto iOS reale inizia con l'inizializzazione del container all'avvio dell'applicazione — in AppDelegate o nella scena. Si consiglia di strutturare le registrazioni tramite Assembly: una classe o struttura separata che raggruppa i servizi correlati.

Secondo un sondaggio della Swift Developer Community (2025), il 43% degli sviluppatori iOS utilizza container DI in progetti commerciali per gestire le dipendenze del livello di rete, dei repository e dei coordinatori di navigazione. Swinject rimane la soluzione più popolare grazie alla sua sintassi minima e alla compatibilità con Objective-C.

Storyboard Injection è una caratteristica unica di Swinject: il container inietta automaticamente le dipendenze nei view controller creati da Storyboard senza codice aggiuntivo in AppDelegate. Utilizza un risolutore speciale passato a UIStoryboard tramite il metodo init(container:), che intercetta la creazione del view controller e inietta le dipendenze registrate.

Nei progetti grandi, Swinject può essere combinato con coordinatori di navigazione: il coordinatore riceve il container e crea schermate risolvendo le loro dipendenze tramite resolve, mantenendo un singolo punto di configurazione per l'intera scena.

L'architettura Assembly è il pattern consigliato per organizzare le registrazioni. Ogni Assembly raggruppa servizi correlati (ad esempio, NetworkingAssembly, DatabaseAssembly) e può dipendere da altri Assembly. Durante l'inizializzazione del container, tutti gli Assembly vengono caricati e registrano i propri servizi, fornendo una chiara separazione delle responsabilità e semplificando la navigazione attraverso la configurazione DI in progetti grandi con decine di servizi.

Per il debug del grafo DI, Swinject fornisce l'estensione SwinjectPropertyLoader, che carica la configurazione da un file plist, e SwinjectStoryboard — integrazione con gli storyboard tramite una versione speciale di UIStoryboard. Questi strumenti sono particolarmente utili durante la migrazione di un progetto esistente dalla costruzione manuale di oggetti a DI: lo sviluppatore può registrare gradualmente i servizi, verificando il grafo delle dipendenze tramite test e registrazione degli errori di risoluzione senza interrompere lo sviluppo delle funzionalità principali.

Swinject fornisce anche l'integrazione con RxSwift e Combine tramite l'estensione SwinjectAutoregistration per la risoluzione automatica delle dipendenze basata sui tipi di parametri dell'inizializzatore senza registrazione esplicita della factory. Ciò riduce la quantità di codice di registrazione per i servizi semplici: basta chiamare container.register(ServiceProtocol.self) senza specificare una factory e Swinject costruirà automaticamente la factory basandosi sulla riflessione Signal fornita dal runtime Swift. Questo approccio è consigliato per servizi i cui costruttori accettano solo tipi di base e non richiedono logica di creazione complessa.

Domande frequenti

In cosa si differenzia Swinject dagli altri framework DI per Swift?

Swinject è scritto in puro Swift senza generazione di codice o riflessione. A differenza di Needle, non richiede generazione di sorgenti e, rispetto a Dip, fornisce supporto integrato per Storyboard Injection, semplificando l'integrazione in progetti UIKit esistenti.

Come installare Swinject tramite Swift Package Manager?

Aggiungi il pacchetto tramite URL github.com/Swinject/Swinject attraverso Xcode nel menu File — Add Packages. È disponibile anche l'installazione tramite CocoaPods e Carthage. Dopo l'installazione, importa il modulo Swinject e crea un'istanza di Container.

Si può usare Swinject nei progetti SwiftUI?

Sì, Swinject è completamente compatibile con SwiftUI. Le dipendenze vengono iniettate tramite gli inizializzatori di View o tramite Environment, dove il container viene passato come EnvironmentObject. Swinject non dipende da UIKit e funziona ugualmente bene con entrambi i framework.

Come usare Swinject per i test unitari?

Crea un container separato per i test, sostituendo i servizi reali con mock. Swinject consente di sovrascrivere le registrazioni senza modificare il codice dei consumatori. Ogni test riceve un container isolato con un insieme minimo di dipendenze.

Quale scope scegliere per un servizio di analisi?

Per le analisi, utilizza lo scope container in modo che tutti gli schermi inviino eventi attraverso una singola istanza. Ciò garantisce una coda di invio unificata e il corretto funzionamento dell'aggregazione batch senza duplicazione dei dati tra diversi consumatori.

Riepilogo

  • Swinject — un container DI per Swift che automatizza l'iniezione di dipendenze tramite Container e ObjectScope.
  • Dependency Injection riduce l'accoppiamento del codice, semplifica i test e consente di sostituire le implementazioni senza modificare i consumatori.
  • Container — un registro di servizi che supporta register per la registrazione e resolve per il recupero dell'istanza.
  • Constructor Injection è il metodo di iniezione preferito, che garantisce lo stato valido dell'oggetto.
  • ObjectScope gestisce la durata: graph (predefinito), container (singleton) e transient (senza cache).
  • Storyboard Injection inietta automaticamente le dipendenze nelle scene UIKit senza configurazione manuale.
  • Per i test unitari, utilizza un container separato con implementazioni mock dei servizi.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche