Swinject är en DI-container för Swift som implementerar mönstret Dependency Injection i iOS-applikationer. Ramverket automatiserar skapandet och injiceringen av beroenden, vilket eliminerar manuell hantering av objekt och fabriker. Enligt uppgifter från Swinject på GitHub stöder biblioteket Constructor Injection, Property Injection och Method Injection med ett flexibelt system av scopes för livstidshantering.
Huvudpunkter
Swinject är en öppen källkods DI-container för språket Swift, utformad för att förenkla beroendeinjektion i applikationer för iOS, macOS och watchOS. Ramverket använder Service Locator-metoden: tjänster registreras i en central container och containern löser automatiskt beroendegrafen när en instans begärs.
Dependency Injection (DI) är ett designmönster där objektet tar emot sina beroenden utifrån istället för att skapa dem inom sig. Detta minskar kopplingen mellan komponenter, förenklar modulära tester och möjliggör utbyte av implementationer utan att ändra konsumentens kod.
Enligt Martin Fowler (2004) är DI ett specialfall av Inversion of Control och implementeras genom injektion via konstruktor, egenskap eller metod. Swinject automatiserar denna process och eliminerar manuellt skrivande av fabriker och tjänstelokalisatorer.
Tillämpa Swinject i projekt med tre eller fler tjänster som har korsberoenden, där manuell objektkonstruktion leder till tillväxt av initialiseringskod och minskad testbarhet.
Swinject integreras nära med Apple-ekosystemet och stöder alla versioner av Swift från 3.0 och framåt. Ramverket är kompatibelt med Objective-C via broar, vilket möjliggör införande i befintliga projekt skrivna på blandat språk utan fullständig kodmigrering. Detta är särskilt relevant för stora applikationer med en utvecklingshistoria på mer än fem år.
Swinject-containern är implementerad av klassen Container, som lagrar registret över registrerade tjänster. Vid anrop av resolve-metoden skapar containern objektet och löser alla dess beroenden rekursivt enligt registreringsgrafen.
Container är det centrala objektet där överensstämmelser mellan abstraktion och dess implementation registreras. Service är ett protokoll som definierar kontraktet och Component är en klass som implementerar detta protokoll. Registrering görs via register-metoden som tar emot tjänstetypen och fabriken.
let container = Container()
container.register(Networking.self) { _ in
NetworkService()
}
let service = container.resolve(Networking.self)
resolve-metoden returnerar en instans av den konkreta implementation som registrerats för det angivna protokollet. Om beroendet inte är registrerat kastar containern ett allvarligt fel för snabb upptäckt av problemet i utvecklingsfasen.
Varje registrering skapar en post med en fabriksfunktion och ett valt scope. En tjänst kan ha flera registreringar med olika namn, vilket gör det möjligt att välja en specifik implementation efter namn — användbart för olika miljöer (utveckling, staging, produktion).
Beroendelösningsprocessen (resolution) fungerar rekursivt: när containern skapar en instans av Component analyserar den dess initierare och för varje parameter anropar resolve av motsvarande typ. Om beroendet också har egna beroenden fortsätter processen tills hela grafen är fullständigt byggd. Kapslingsdjupet begränsas endast av tillgängligt minne, men överstiger i praktiken sällan fem nivåer.
Swinject stöder tre huvudsakliga sätt att injicera beroenden, var och en tillämplig beroende på arkitektoniskt sammanhang.
Constructor Injection — injektion av beroenden via initieringsparametrar. Detta är det föredragna sättet, som garanterar att objektet alltid är i korrekt tillstånd från skapelseögonblicket. Swinject löser automatiskt alla beroenden som skickas till konstruktorn.
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 — injektion genom att ställa in objektets egenskaper efter dess initiering. Används när beroendet är valfritt eller inte kan skickas via konstruktorn, till exempel vid arbete med Storyboard, där visningskontrollern skapas automatiskt. Swinject stöder @Inject-annoteringen för automatisk egenskapsinjektion via runtime utan explicit anrop av resolve.
Vid användning av Property Injection är det viktigt att säkerställa att beroendet har ställts in före första åtkomsten till objektet. Annars kommer egenskapen att förbli nil, vilket leder till en oväntad krasch. Swinject löser detta problem genom mekanismen Implicitly Unwrapped Optional och strikt verifiering i fasen av beroendegrafens lösning.
Method Injection — injektion via metodparametrar. Tillämpas för tjänster som endast behövs för att utföra en operation och inte bör lagras som permanent objektstatus. Detta är det minst utbredda men för callbackar användbara injektionssättet.
ObjectScope — mekanismen som bestämmer livstiden för en skapad instans inuti Swinject-containern. Ramverket tillhandahåller tre inbyggda scopes med möjlighet att skapa anpassade scopes via protokollet ObjectScopeProtocol.
graph-scopen — standardvärdet. Vid varje resolve-anrop skapas en ny instans som bara lever under tiden för beroendegrafens lösning. Detta är ett säkert val för tillståndslösa tjänster eftersom det eliminerar minnesläckor orsakade av cachning.
container-scopen — singleton inom containern. Instansen skapas en gång vid första resolve och returneras vid alla efterföljande förfrågningar. Lämplig för tjänster med delat tillstånd: datacache, logger, applikationsinställningar.
transient-scopen — varje resolve-anrop skapar en ny instans utan cachning. Används för lätta objekt som inte behöver återanvändas — till exempel för moduler som arbetar med en specifik HTTP-förfrågan.
| Scope | Livstid | Rekommenderad användning |
|---|---|---|
| graph | Under graflösningstiden | Tillståndslösa tjänster som standard |
| container | Hela containerns livstid | Singleton: cache, logger, nätverksklient |
| transient | Utan cachning | Lätta objekt för engångsbruk |
Integrering av Swinject i ett verkligt iOS-projekt börjar med initiering av containern vid applikationsstart — i AppDelegate eller scen. Det rekommenderas att strukturera registreringar via Assembly: en separat klass eller struktur som grupperar relaterade tjänster.
Enligt en undersökning från Swift Developer Community (2025) använder 43% av iOS-utvecklare DI-containrar i kommersiella projekt för att hantera beroenden i nätverkslagret, datalager och navigeringskoordinatorer. Swinject förblir den mest populära lösningen tack vare minimal syntax och kompatibilitet med Objective-C.
Storyboard Injection — en unik förmåga hos Swinject: containern injicerar automatiskt beroenden i visningskontrollers som skapas från Storyboard, utan extra kod i AppDelegate. För detta används en speciell resolver som skickas till UIStoryboard via metoden init(container:), som fångar upp skapandet av visningskontrollern och injicerar de registrerade beroenden.
I stora projekt kan Swinject kombineras med navigeringskoordinatorer: koordinatorn tar emot containern och skapar skärmar, löser deras beroenden via resolve, vilket bibehåller en enda konfigurationspunkt för hela scenen.
Arkitektur med Assembly — det rekommenderade mönstret för organisering av registreringar. Varje Assembly grupperar relaterade tjänster (t.ex. NetworkingAssembly, DatabaseAssembly) och kan vara beroende av andra Assemblies. Vid initiering av containern laddas alla Assemblies och registrerar sina tjänster, vilket ger en tydlig ansvarsfördelning och förenklar navigering i DI-konfigurationen i stora projekt med dussintals tjänster.
För felsökning av DI-grafen tillhandahåller Swinject tillägget SwinjectPropertyLoader, som laddar konfiguration från en plist-fil, och SwinjectStoryboard — integration med storyboards via en speciell version av UIStoryboard. Dessa verktyg är särskilt användbara i fasen av att överföra ett befintligt projekt från manuell objektkonstruktion till DI: utvecklaren kan gradvis registrera tjänster, kontrollera beroendegrafen genom tester och loggning av lösningsfel, utan att stoppa utvecklingen av applikationens huvudfunktioner.
Swinject tillhandahåller också integration med RxSwift och Combine via tillägget SwinjectAutoregistration för automatisk lösning av beroenden efter typer av initieringsparametrar utan explicit registrering av fabriker. Detta minskar mängden registreringskod för enkla tjänster: det räcker att anropa container.register(ServiceProtocol.self) utan att ange fabrik, och Swinject bygger självständigt fabriken baserat på Signal-reflektion som tillhandahålls av Swift-körtidsmiljön. Detta tillvägagångssätt rekommenderas för tjänster vars konstruktor endast accepterar basetyper och inte kräver komplex logik vid skapande.
Vanliga frågor
Swinject är skriven i ren Swift utan kodgenerering och reflektion. Till skillnad från Needle kräver den inte generering av källkod, och jämfört med Dip — ger den inbyggt stöd för Storyboard Injection, vilket förenklar integrering i befintliga UIKit-projekt.
Lägg till paketet via URL github.com/Swinject/Swinject via Xcode i menyn File — Add Packages. Installation via CocoaPods och Carthage är också tillgänglig. Efter installation, importera Swinject-modulen och skapa en instans av Container.
Ja, Swinject är fullt kompatibel med SwiftUI. Beroenden injiceras via View-initierare eller via Environment, där containern skickas som EnvironmentObject. Swinject är inte beroende av UIKit och fungerar likadant med båda ramverken.
Skapa en separat container för tester och ersätt verkliga tjänster med mockar. Swinject tillåter överskrivning av registreringar utan att ändra konsumenternas kod. Varje test får en isolerad container med en minimal uppsättning beroenden.
För analys använd container-scopen, så att alla skärmar skickar händelser via en instans. Detta garanterar en enhetlig sändningskö och korrekt batch-aggregation utan dubbelring av data mellan olika konsumenter.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också