Dependency Injection: co to je, vkládání závislostí v iOS a Android

Autor: IT Sectr Publikováno: 2026-02-18 Doba čtení: 9 min

Dependency Injection (DI, vkládání závislostí) — technika, při které objekt získává své závislosti zvenčí, místo aby je sám vytvářel. DI je implementací principu IoC (Inversion of Control) a je základem Dagger, Hilt a Swinject. Vkládání závislostí snižuje provázanost kódu, zjednodušuje testování a činí architekturu flexibilní. V Androidu je DI standardem přes Dagger Hilt od Googlu, v iOS — přes Swinject nebo ruční vkládání. Více — v Android DI Guide.

Hlavní body

  • Dependency Injection — závislosti jsou objektu předávány zvenčí, nikoli vytvářeny uvnitř
  • Inversion of Control — DI implementuje princip IoC, řízení toku je předáno kontejneru
  • Dagger Hilt — standard DI pro Android, založený na Dagger od Googlu
  • Swinject — populární DI framework pro iOS a Swift
  • Snižení provázanosti — třída závisí na abstrakcích, ne na konkrétních implementacích

Co je Dependency Injection: podstata a typy DI

Dependency Injection — technika, při které objekt přijímá závislosti (služby, repozitáře, konfigurace) přes konstruktor, setter nebo rozhraní, místo aby je sám vytvářel pomocí new. Cílem DI je snížit provázanost (coupling) mezi třídami. Pokud si třída vytváří závislosti sama, je pevně svázána s konkrétními implementacemi, což ztěžuje testování a úpravy. Při DI třída pracuje s abstrakcí (protocol/interface) a konkrétní implementace je dodávána zvenčí.

Tři způsoby vkládání — Constructor Injection (přes init/constructor), Setter Injection (přes vlastnost/setter), Interface Injection (přes metodu rozhraní). Constructor Injection — preferovaný způsob: závislosti jsou jasně viditelné v signatuře, objekt je vždy vytvořen v platném stavu. Setter Injection se používá pro volitelné závislosti s výchozí hodnotou. Interface Injection — zřídka, hlavně pro DI kontejnery.

Typ DIZpůsobKdy použítPříklad
ConstructorParametry inicializátoruPovinné závislostiinit(service: ServiceProtocol)
PropertyVlastnost třídyVolitelné závislostivar service: ServiceProtocol?
MethodParametr metodyDočasné závislostifunc doWork(with service: Service)

DI kontejner — knihovna, která spravuje vytváření a životní cyklus závislostí. Kontejner obsahuje registraci typů (každý abstraktní typ je mapován na konkrétní implementaci) a továrnu pro vytváření objektů s vyřešenými závislostmi. V Androidu — Dagger/Hilt, v iOS — Swinject, Needle, Dip. Kontejner může spravovat rozsah (Scope): singleton (jedna instance na aplikaci), rozsah funkce (na obrazovku) nebo nový objekt při každém požadavku.

Dagger Hilt: DI pro Android s generováním kódu

Dagger Hilt — vrstva nad Dagger od Googlu, standardní DI knihovna pro Android. Hilt zjednodušuje Dagger: odstraňuje ruční vytváření komponent, přidává @HiltAndroidApp, @AndroidEntryPoint a @Module. Hilt se integruje s životním cyklem Androidu: ViewModel, Activity, Fragment, Service, BroadcastReceiver mohou přijímat závislosti přes anotace. Generování kódu probíhá při kompilaci — Dagger generuje implementace komponent, což poskytuje nulové runtime overhead.

kotlin
// Application class
@HiltAndroidApp
class MyApp : Application()

// Module — definuje, jak vytvářet závislosti
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder().build()

    @Provides
    @Singleton
    fun provideApiService(client: OkHttpClient): ApiService {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .client(client)
            .build()
            .create(ApiService::class.java)
    }
}

// ViewModel přijímá závislost přes konstruktor
@HiltViewModel
class MainViewModel @Inject constructor(
    private val apiService: ApiService
) : ViewModel() {
    private val _state = MutableStateFlow(MainState.Loading)
    val state: StateFlow<MainState> = _state.asStateFlow()

    fun loadData() {
        viewModelScope.launch {
            _state.value = MainState.Success(apiService.getData())
        }
    }
}

// Activity — @AndroidEntryPoint aktivuje DI
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
}

Komponenty Dagger a rozsahy viditelnosti — @Singleton (pro celou aplikaci), @ActivityScoped (pro Activity), @FragmentScoped (pro Fragment), @ViewModelScoped (pro ViewModel). Volba rozsahu určuje životnost objektu. @Singleton — jedna instance na proces, vhodná pro OkHttpClient a databázi. @ActivityScoped — objekt žije dokud žije Activity, pro závislosti obrazovky. @ViewModelScoped — novinka Hilt 2.45+, objekt žije dokud žije ViewModel, vhodné pro coroutine rozsahy.

Swinject: DI pro iOS ve Swiftu

Swinject — populární DI framework pro iOS s otevřeným zdrojovým kódem. Swinject poskytuje Container, Assemblies a různé rozsahy. Na rozdíl od Dagger, Swinject pracuje v runtime — závislosti jsou řešeny dynamicky bez generování kódu. To činí Swinject jednodušším na konfiguraci, ale ztěžuje ladění: chyba nevyřešené závislosti se projeví až v runtime. Swinject podporuje Constructor Injection, Property Injection a Method Injection.

swift
import Swinject

// Assembly — skupina registrací
class NetworkAssembly: Assembly {
    func assemble(container: Container) {
        container.register(NetworkServiceProtocol.self) { _ in
            NetworkService()
        }.inObjectScope(.container) // singleton

        container.register(UserRepositoryProtocol.self) { r in
            UserRepository(
                networkService: r.resolve(NetworkServiceProtocol.self)!
            )
        }
    }
}

// ViewModel přes Constructor Injection
class ProfileViewModel: ObservableObject {
    private let repository: UserRepositoryProtocol

    init(repository: UserRepositoryProtocol) {
        self.repository = repository
    }

    @Published var user: User?
    func loadUser() {
        repository.fetchUser { [weak self] user in
            self?.user = user
        }
    }
}

// Konfigurace DI v AppDelegate nebo App
let assembler = Assembler([NetworkAssembly(), ServiceAssembly()])
let viewModel = assembler.resolver.resolve(ProfileViewModel.self)!

// Property Injection pro UIKit ViewController
container.register(UserViewController.self) { r in
    let vc = UserViewController()
    vc.viewModel = r.resolve(ProfileViewModel.self)
    return vc
}

Rozsahy Swinject — .transient (nový objekt pokaždé), .container (singleton na kontejner), .graph (výchozí — objekt je sdílen v rámci jednoho grafu závislostí). Pro iOS aplikace stačí .container a .transient. Swinject také podporuje Assembler — seskupování Assembly pro modulární architekturu. Pro testy jsou Assembly nahrazeny MockAssembly, což umožňuje nahrazení závislostí bez změny produkčního kódu.

Srovnání DI s Service Locator a ručním vkládáním

DI vs Service Locator — oba vzory řeší problém správy závislostí, ale různými způsoby. DI vkládá závislosti do objektu, Service Locator poskytuje globální registr, ze kterého si objekt závislosti sám vyžádá. DI explicitně deklaruje závislosti přes konstruktor (nebo setter). Service Locator skrývá závislosti — jsou vyžadovány uvnitř metody, což činí signaturu méně informativní. DI se snadněji testuje: stačí vložit mock do konstruktoru. Service Locator vyžaduje konfiguraci globálního registru pro každý test.

VlastnostDependency InjectionService LocatorRuční vkládání
Zřejmost závislostíV konstruktoruSkryté v těle metodyExplicitní
TestováníMock v konstruktoruKonfigurace LocatorMock v konstruktoru
Složitost konfiguraceVyžaduje DI kontejnerGlobální registrRuční vytváření
Runtime overheadDagger — compile-timeRuntime lookupŽádný

DI vs ruční vkládání — bez DI kontejneru jsou závislosti vytvářeny ručně v továrnách nebo AppDelegate. Pro 5-10 tříd je ruční vkládání jednodušší — nevyžaduje učení Dagger nebo Swinject. Pro 50+ tříd se ruční vkládání stává problémem: konstruktory s 5-6 parametry, složité pořadí vytváření, duplicita kódu. DI kontejner tyto procesy automatizuje a poskytuje jasný životní cyklus. Ruční vkládání bez kontejneru — dobrá volba pro malé projekty a prototypy.

Nejlepší praktiky Dependency Injection

Constructor Injection — standard. Vždy používejte Constructor Injection pro povinné závislosti. To činí závislosti explicitními a objekt vždy připraveným k práci. Setter Injection — pouze pro volitelné závislosti (např. delegate nebo listener). Interface Injection — nepoužívejte, pokud nepíšete vlastní DI knihovnu. Constructor Injection — jediný způsob, jak zaručit, že je objekt vytvořen v platném stavu.

Jedna třída — jedna odpovědnost. Pokud konstruktor třídy vyžaduje 5+ parametrů, třída pravděpodobně porušuje Single Responsibility Principle. Rozdělte třídu na několik s méně závislostmi. Signál: pokud píšete třídu ServiceManager se 6 různými službami — to je anti-vzor God Object. Přesuňte obchodní logiku do Use Cases (Interactors), každý s 1-2 závislostmi.

kotlin
// ❌ Špatně: 6 závislostí — God Object
class ProfileViewModel @Inject constructor(
    private val api: ApiService,
    private val db: Database,
    private val analytics: Analytics,
    private val prefs: Preferences,
    private val location: LocationProvider,
    private val notification: NotificationManager
)

// ✅ Dobře: Use Cases s 1-2 závislostmi
class ProfileViewModel @Inject constructor(
    private val loadProfileUseCase: LoadProfileUseCase,
    private val trackAnalyticsUseCase: TrackAnalyticsUseCase
)

Rozsah a životní cyklus — vyberte správný rozsah pro každou závislost. Singleton: OkHttpClient, databáze, SharedPreferences. Rozsah funkce: repozitáře, Use Cases (pokud nemají stav). Transient: Value Objects, DateFormatter, analyzátory. Chyba rozsahu — častý problém: singleton ukládající stav obrazovky vede k únikům paměti. V Android Hilt @ActivityScoped řeší tento problém, v Swinject — .container s opatrností.

Často kladené otázky

Proč je potřeba DI, když lze jednoduše vytvářet pomocí new?

New vytváří pevné propojení mezi třídami — nemůžete nahradit implementaci bez změny kódu. Testování je obtížné: nelze vložit mock místo skutečné služby. SRP je porušeno: třída odpovídá jak za obchodní logiku, tak za vytváření závislostí. DI řeší tyto problémy prostřednictvím vkládání závislostí zvenčí a práce s abstrakcemi.

Dagger Hilt nebo Koin — co vybrat pro Android?

Dagger Hilt — standard od Googlu, compile-time DI s generováním kódu, lepší výkon a integrace s Jetpack. Koin — runtime DI, jednodušší na konfiguraci, ale pomalejší a s runtime chybami. Vybírejte Hilt pro produkční projekty. Koin je vhodný pro prototypy a malé aplikace.

Je Swinject jediné DI pro iOS?

Ne. Pro iOS jsou k dispozici: Swinject (runtime, populární), Needle (compile-time od Uberu), Dip (odlehčený), Weaver (založený na Sourcery). Apple neposkytuje vestavěný DI kontejner, ale ruční vkládání přes init je standardní praxí. Pro SwiftUI často stačí ruční DI přes Environment nebo @StateObject bez externích knihoven.

Lze DI použít bez frameworku?

Ano. Ruční vkládání přes konstruktor — to je DI bez frameworku. Service Locator — alternativa bez frameworku. Továrny a Factory Method — také forma DI. Framework (Dagger, Swinject) automatizuje rutinní registraci a řešení závislostí, ale pro 10-20 tříd je ruční DI dostačující.

Je DI vzor nebo princip?

DI — je technika (vzor) implementující princip Inversion of Control. Na rozdíl od GoF vzorů nemá DI přísnou strukturu 3-4 tříd. DI je způsob organizace závislostí, nikoli návrhový vzor. DI kontejnery (Dagger, Swinject) jsou frameworky, které tuto techniku automatizují.

Shrnutí

  • DI — technika vkládání závislostí zvenčí přes konstruktor, setter nebo metodu
  • Dagger Hilt — standard DI pro Android s compile-time generováním kódu a @HiltViewModel
  • Swinject — runtime DI pro iOS s Container, Assembly a rozsahy
  • Constructor Injection — preferovaný způsob pro povinné závislosti
  • Rozsah — Singleton pro bezstavové služby, rozsah funkce pro závislosti obrazovky
  • Testování — DI zjednodušuje nahrazení závislostí mockem bez změny kódu

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také