SwiftDependencyInjection 1.3

SwiftDependencyInjection 1.3

TestsTested
LangLanguage SwiftSwift
License MIT
ReleasedLast Release Jan 2024
SPMSupports SPM

Maintained by Martin Eberl.



  • By
  • Martin Eberl

SwiftDependencyInjection




Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Requirements

Installation

SwiftDependencyInjection is available through CocoaPods. To install
it, simply add the following line to your Podfile:

pod 'SwiftDependencyInjection'

Author

Martin Eberl, [email protected]

License

SwiftDependencyInjection is available under the MIT license. See the LICENSE file for more info.

Usage

  1. Implement a module:
import SwiftDependencyInjection

protocol FooProvider {
    var foo: Foo { get }
}

final class FooModule: Module, FooProvider {
    weak var delegate: ModuleDelegate?

    lazy var foo: Foo = {
        return Foo()
        }()
}

Use a protocol (here FooProvider) to help to communicate and request injection types.

  1. Requesting the implementations of the protocol (e.g. in your ViewController or in any other viewmodels):
Injector.shared.inject(self).with(FooProvider.self)

This helps the injector to provide you the required dependencies. Add .with(…Provider.self) to request more dependencies.

  1. Implement the Injectable protocol:
func inject(inject: Any) {
    if let inject = inject as? FooProvider {
        foo = inject.foo
    }
}

Module Dependencies

Let’s say some of your modules require dependencies from other modules. You than may want the injector to do the hard work and resolve those dependencies.

Here we have a LectureProvider which provides a Repository of lectures to students.

import SwiftDependencyInjection

protocol LectureProvidable {
    var repository: LectureRepository { get }
}

final class LectureProviderModule: Module, LectureProvidable {
    weak var delegate: ModuleDelegate?

    //lazy makes a var to a singleton
    lazy var repository: LectureRepository = {
        return LectureRepository()
    }()
}

Now based on the numbers of our students, we want to get a room or in case the auditorium is still too small, we need to hold the lecture open air

enum Room: Int {
    case lectureRoom = 20
    case lectureHall = 50
    case auditorium = 300

    statuc func for(_ numberOfStudents: Int) -> Room? {
        if numberOfStudents <= Room.lectureRoom.rawValue {
            return .lectureRoom
        }
        if numberOfStudents <= Room.lectureHall.rawValue {
            return .lectureHall
        }
        if numberOfStudents <= Room.lectureHall.rawValue {
            return .auditorium
        }
        return nil
    }

    func holdLecture(_ lector: Lector) {} 
}

import SwiftDependencyInjection

protocol RoomProvidable {
    func room(for lectureId: String) -> Room?
}

final class RoomProviderModule: Module, RoomProvidable {
    weak var delegate: ModuleDelegate?
    weak var lectureRepository: LectureRepository?

    init() {
        //this is how you tell the the Injector, what types of provideables are
        //required for e.g. the RoomProvideable
        //The Roomprovideable is not being provided, unless it's dependencies can 
        //be resolved
        requires(for: RoomProvidable)?
            .this(LectureProvidable.self)
          //.this(... .self) we would add here more dependencies if we needed to
    }

    func room(for lectureId: Int) -> Room? {
        guard let lecture = lectureRepository.lectures(with: lectureId) else {
            return nil
        }
        return Room.for(lecture.subscibedStudents)
    }

    func inject(inject: Any) {
        if let inject = inject as? LectureProvidable {
            lectureRepository = inject.repository
        }
    }
}

Now we need to register the Modules

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

        //we wont to register the Moduels
        //The Injector has a built in dependency resolve, 
        //so it resolved the conflict, that the RoomProvider is 
        //first instantiated, even though it needs the LecutureProvider Module.
        //When the LectureProviderModule provides it's Provideables, the RoomProvider is
        //automatically injected with the required dependencies
        RoomProviderModule().provide()
        LectureProviderModule().provide()
        
        return true
    }
    ...
}

And finally we want our lector to provide a lecture, on which students may subscribe and a room should be selected automatically based on the number of students

final class Lector {
    var roomProvidable: RoomProvidable?
    var lectureProvidable: LectureProvidable?

    init() {
        //That's how you say, what dependencies you need
        Injector.shared.inject(self)
            .with(RoomProvidable.self)
            .with(LectureProvidable.self)
    }

    //this is required, because that's how the Injector assigns the dependencies
    func inject(inject: Any) {
        if let inject = inject as? RoomProvidable {
            roomProvidable = inject
        }
        if let inject = inject as? LectureProvidable {
            lectureProvidable = inject
        }
    }

    func provideLecture(lectureId: Int) {
        guard let lectureProvidable = lectureProvidable else {
            return
        }
        lectureProvideable.provide(Lecture(id: lectureId, lector: this, Date(2017, 12, 03), estimatedHours: 2))
    }

    func holdLecture(lectureId: Int) {
        guard let roomProvidable = roomProvidable,
            let room = roomProvidable.room(for: lectureId) else {
            //hold lecture outside
            return
        }
        room.holdLecture(this)
    }
}

New in Version 1.2.0

Class Inject
With only a few lines of code, you can more easily add dependencies

private var fooProvider = Inject<FooProvider>()

...

fooProvider.value

I’d love to hear, if you’d need any further samples or have improovement proposals