Dependency Injection: nedir, iOS ve Android'de bağımlılık enjeksiyonu

Yazar: IT Sectr Yayınlanma: 2026-02-18 Okuma süresi: 9 dk

Bağımlılık Enjeksiyonu (DI), bir nesnenin bağımlılıklarını kendisinin oluşturması yerine dışarıdan alması tekniğidir. DI, IoC (Kontrolün Tersine Çevrilmesi) ilkesinin bir uygulamasıdır ve Dagger, Hilt ile Swinject'in temelini oluşturur. Bağımlılık enjeksiyonu kod bağımlılığını azaltır, test etmeyi basitleştirir ve mimariyi esnek hale getirir. Android'de DI, Google'ın Dagger Hilt'i ile standarttır; iOS'te ise Swinject veya manuel enjeksiyon ile yapılır. Daha fazla bilgi için Android DI Kılavuzu'na bakın.

Önemli Noktalar

  • Bağımlılık Enjeksiyonu — bağımlılıklar nesneye dışarıdan iletilir, dahili olarak oluşturulmaz
  • Kontrolün Tersine Çevrilmesi — DI, IoC ilkesini uygular, kontrol akışı konteynere devredilir
  • Dagger Hilt — Google'ın Dagger'ına dayanan Android için DI standardı
  • Swinject — iOS ve Swift için popüler bir DI framework'ü
  • Azaltılmış bağımlılık — bir sınıf somut uygulamalara değil, soyutlamalara bağımlıdır

Dependency Injection Nedir: Özü ve DI Türleri

Bağımlılık Enjeksiyonu, bir nesnenin bağımlılıkları (servisler, depolar, yapılandırmalar) bir kurucu, ayarlayıcı veya arayüz aracılığıyla alması, bunları new ile kendisinin oluşturması yerine geçen bir tekniktir. DI'nin amacı sınıflar arasındaki bağımlılığı azaltmaktır. Bir sınıf bağımlılıkları kendisi oluşturuyorsa, belirli uygulamalara sıkı sıkıya bağlı hale gelir ve bu da test etmeyi ve değişiklik yapmayı zorlaştırır. DI ile sınıf bir soyutlama (protokol/arayüz) ile çalışır ve somut uygulama dışarıdan sağlanır.

Üç enjeksiyon yöntemi — Kurucu Enjeksiyonu (init/constructor aracılığıyla), Ayarlayıcı Enjeksiyonu (özellik/setter aracılığıyla), Arayüz Enjeksiyonu (bir arayüz metodu aracılığıyla). Kurucu Enjeksiyonu tercih edilen yöntemdir: bağımlılıklar imzada açıkça görünür ve nesne her zaman geçerli bir durumda oluşturulur. Ayarlayıcı Enjeksiyonu, varsayılan değere sahip isteğe bağlı bağımlılıklar için kullanılır. Arayüz Enjeksiyonu nadirdir, çoğunlukla DI konteynerleri için kullanılır.

DI TürüYöntemNe Zaman KullanılırÖrnek
KurucuBaşlatıcı parametreleriZorunlu bağımlılıklarinit(service: ServiceProtocol)
ÖzellikSınıf özelliğiİsteğe bağlı bağımlılıklarvar service: ServiceProtocol?
MetotMetot parametresiGeçici bağımlılıklarfunc doWork(with service: Service)

DI Konteyneri — bağımlılıkların oluşturulmasını ve yaşam döngüsünü yöneten bir kütüphane. Konteyner, tür kayıtlarını (her soyut tür somut bir uygulamayla eşlenir) ve çözülmüş bağımlılıklarla nesneler oluşturmak için bir fabrika içerir. Android'de — Dagger/Hilt, iOS'te — Swinject, Needle, Dip. Konteyner kapsamı (scope) yönetebilir: singleton (uygulama başına bir örnek), özellik kapsamı (ekran başına) veya her istekte yeni bir nesne.

Dagger Hilt: Kod Üretimi ile Android için DI

Dagger Hilt, Google'ın Dagger'ı üzerine bir sarmalayıcı olan Android için standart DI kütüphanesidir. Hilt, Dagger'ı basitleştirir: manuel bileşen oluşturmayı kaldırır, @HiltAndroidApp, @AndroidEntryPoint ve @Module ekler. Hilt, Android yaşam döngüsüyle entegre olur: ViewModel, Activity, Fragment, Service, BroadcastReceiver ek açıklamalar aracılığıyla bağımlılıkları alabilir. Kod üretimi derleme zamanında gerçekleşir — Dagger bileşen uygulamalarını oluşturur ve bu da sıfır runtime yükü sağlar.

kotlin
// Uygulama sınıfı
@HiltAndroidApp
class MyApp : Application()

// Modül — bağımlılıkların nasıl oluşturulacağını tanımlar
@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, kurucu aracılığıyla bağımlılık alır
@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 DI'yı etkinleştirir
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
}

Dagger bileşenleri ve kapsamları — @Singleton (tüm uygulama), @ActivityScoped (Activity başına), @FragmentScoped (Fragment başına), @ViewModelScoped (ViewModel başına). Kapsam seçimi nesnenin ömrünü belirler. @Singleton — işlem başına bir örnek, OkHttpClient ve veritabanları için uygundur. @ActivityScoped — nesne, Activity yaşadığı sürece yaşar, ekran düzeyindeki bağımlılıklar için. @ViewModelScoped — Hilt 2.45+'da yeni, nesne ViewModel yaşadığı sürece yaşar, coroutine kapsamları için kullanışlıdır.

Swinject: Swift ile iOS için DI

Swinject, iOS için popüler bir açık kaynak DI framework'üdür. Swinject, Container, Assemblies ve çeşitli kapsamlar sağlar. Dagger'ın aksine, Swinject çalışma zamanında çalışır — bağımlılıklar kod üretimi olmadan dinamik olarak çözülür. Bu, Swinject'i kurmayı kolaylaştırır, ancak hata ayıklamayı zorlaştırır: çözülmemiş bağımlılık hatası yalnızca çalışma zamanında görünür. Swinject, Kurucu Enjeksiyonu, Özellik Enjeksiyonu ve Metot Enjeksiyonunu destekler.

swift
import Swinject

// Assembly — bir kayıt grubu
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, Kurucu Enjeksiyonu aracılığıyla
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
        }
    }
}

// AppDelegate veya Uygulamada DI kurulumu
let assembler = Assembler([NetworkAssembly(), ServiceAssembly()])
let viewModel = assembler.resolver.resolve(ProfileViewModel.self)!

// UIKit ViewController için Özellik Enjeksiyonu
container.register(UserViewController.self) { r in
    let vc = UserViewController()
    vc.viewModel = r.resolve(ProfileViewModel.self)
    return vc
}

Swinject kapsamları — .transient (her seferinde yeni nesne), .container (konteyner başına singleton), .graph (varsayılan — nesne tek bir bağımlılık grafiği içinde paylaşılır). iOS uygulamaları için .container ve .transient yeterlidir. Swinject ayrıca Assembler'ı da destekler — modüler mimari için Assembly gruplaması. Testler için Assembly, MockAssembly ile değiştirilir ve üretim kodunu değiştirmeden bağımlılık değişimine olanak tanır.

Service Locator ve Manuel Enjeksiyon ile DI Karşılaştırması

DI vs Service Locator — her iki desen de bağımlılık yönetimi sorununu çözer, ancak farklı şekillerde. DI, bağımlılıkları nesneye enjekte eder; Service Locator, nesnenin bağımlılıkları kendisinin talep ettiği küresel bir kayıt defteri sağlar. DI, bağımlılıkları kurucu (veya ayarlayıcı) aracılığıyla açıkça bildirir. Service Locator, bağımlılıkları gizler — bunlar metot içinde talep edilir ve imzayı daha az bilgilendirici hale getirir. DI test etmesi daha kolaydır: kurucuya bir mock geçirmek yeterlidir. Service Locator, her test için küresel kayıt defterinin ayarlanmasını gerektirir.

ÖzellikBağımlılık EnjeksiyonuService LocatorManuel Enjeksiyon
Bağımlılıkların görünürlüğüKurucudaMetot gövdesinde gizliAçık
TestKurucuda MockLocator ayarıKurucuda Mock
Kurulum karmaşıklığıDI konteyneri gerekliKüresel kayıt defteriManuel oluşturma
Runtime yüküDagger — derleme zamanıRuntime aramaYok

DI vs manuel enjeksiyon — DI konteyneri olmadan, bağımlılıklar fabrikalarda veya AppDelegate'te manuel olarak oluşturulur. 5-10 sınıf için manuel enjeksiyon daha basittir — Dagger veya Swinject öğrenmeyi gerektirmez. 50+ sınıf için manuel enjeksiyon sorunlu hale gelir: 5-6 parametreli kurucular, karmaşık oluşturma sırası, kod tekrarı. Bir DI konteyneri bu süreçleri otomatikleştirir ve net bir yaşam döngüsü yönetimi sağlar. Konteyner olmadan manuel enjeksiyon, küçük projeler ve prototipler için iyi bir seçimdir.

Bağımlılık Enjeksiyonu En İyi Uygulamaları

Kurucu Enjeksiyonu — standart. Zorunlu bağımlılıklar için her zaman Kurucu Enjeksiyonunu kullanın. Bu, bağımlılıkları açık hale getirir ve nesne her zaman çalışmaya hazır olur. Ayarlayıcı Enjeksiyonu — yalnızca isteğe bağlı bağımlılıklar için (örneğin, delegate veya listener). Arayüz Enjeksiyonu — kendi DI kütüphanenizi yazmıyorsanız kullanmayın. Kurucu Enjeksiyonu, bir nesnenin geçerli bir durumda oluşturulmasını garanti etmenin tek yoludur.

Bir sınıf — bir sorumluluk. Bir sınıfın kurucusu 5+ parametre gerektiriyorsa, sınıf muhtemelen Tek Sorumluluk İlkesini ihlal ediyordur. Sınıfı daha az bağımlılığı olan birden çok sınıfa bölün. Bir işaret: 6 farklı hizmete sahip bir ServiceManager sınıfı yazıyorsanız — bu God Object anti-desenidir. İş mantığını Use Cases'lere (Interactors) çıkarın, her biri 1-2 bağımlılığa sahip olsun.

kotlin
// ❌ Kötü: 6 bağımlılık — 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
)

// ✅ İyi: 1-2 bağımlılıklı Use Cases
class ProfileViewModel @Inject constructor(
    private val loadProfileUseCase: LoadProfileUseCase,
    private val trackAnalyticsUseCase: TrackAnalyticsUseCase
)

Kapsam ve yaşam döngüsü — her bağımlılık için doğru kapsamı seçin. Singleton'lar: OkHttpClient, veritabanı, SharedPreferences. Özellik kapsamı: depolar, Use Cases (durumsuz iseler). Transient: Value Objects, DateFormatter, ayrıştırıcılar. Kapsam hataları yaygın bir sorundur: ekran durumunu depolayan bir singleton, sızıntılara neden olur. Android'de, Hilt'in @ActivityScoped'i bunu çözer; Swinject'te .container'ı dikkatli kullanın.

Sıkça Sorulan Sorular

New kullanabiliyorken neden DI'ya ihtiyacım var?

new, sınıflar arasında sıkı bir bağ oluşturur — kodu değiştirmeden uygulamayı değiştiremezsiniz. Test etmek zorlaşır: gerçek bir hizmet yerine mock enjekte edemezsiniz. SRP ihlal edilir: sınıf hem iş mantığından hem de bağımlılık oluşturmadan sorumludur. DI, bağımlılıkları dışarıdan enjekte ederek ve soyutlamalarla çalışarak bu sorunları çözer.

Dagger Hilt mi yoksa Koin mi — Android için hangisi seçilmeli?

Dagger Hilt, Google'ın standardıdır, kod üretimi ile derleme zamanı DI, daha iyi performans ve Jetpack entegrasyonu sağlar. Koin, çalışma zamanı DI'dır, kurulumu daha kolaydır ancak daha yavaştır ve çalışma zamanı hatalarına sahiptir. Üretim projeleri için Hilt'i seçin. Koin, prototipler ve küçük uygulamalar için uygundur.

Swinject, iOS için tek DI seçeneği mi?

Hayır. iOS için mevcut olanlar: Swinject (çalışma zamanı, popüler), Needle (Uber'dan derleme zamanı), Dip (hafif), Weaver (Sourcery tabanlı). Apple, yerleşik bir DI konteyneri sağlamaz, ancak init aracılığıyla manuel enjeksiyon standart bir uygulamadır. SwiftUI için, harici kütüphaneler olmadan Environment veya @StateObject aracılığıyla manuel DI genellikle yeterlidir.

DI bir framework olmadan kullanılabilir mi?

Evet. Kurucu aracılığıyla manuel enjeksiyon, framework olmadan DI'dır. Service Locator, framework olmadan bir alternatiftir. Fabrikalar ve Factory Method da DI biçimleridir. Bir framework (Dagger, Swinject), rutin kayıt ve bağımlılık çözümünü otomatikleştirir, ancak 10-20 sınıf için manuel DI yeterlidir.

DI bir desen mi yoksa ilke mi?

DI, Kontrolün Tersine Çevrilmesi ilkesini uygulayan bir tekniktir (şablondur). GoF desenlerinin aksine, DI'nın katı bir 3-4 sınıf yapısı yoktur. DI, bağımlılıkları düzenlemenin bir yoludur, bir tasarım deseni değildir. DI konteynerleri (Dagger, Swinject), bu tekniği otomatikleştiren framework'lerdir.

Özet

  • DI — kurucu, ayarlayıcı veya metot aracılığıyla dışarıdan bağımlılık enjekte etme tekniği
  • Dagger Hilt — derleme zamanı kod üretimi ve @HiltViewModel ile Android için DI standardı
  • Swinject — Container, Assembly ve kapsamlarla iOS için çalışma zamanı DI
  • Kurucu Enjeksiyonu — zorunlu bağımlılıklar için tercih edilen yöntem
  • Kapsam — durumsuz hizmetler için Singleton, ekran düzeyindeki bağımlılıklar için özellik kapsamı
  • Test — DI, kodu değiştirmeden bağımlılıkları mock ile değiştirmeyi basitleştirir

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun