Dependency Injection


```swift:Original.swift
class User {
}

class ApiManager {
    static let shared = ApiManager()

    private init() {}

    func getUsers(completion: @escaping (Result<[User], Error>) -> Void) {
        let user = User()
        /* Do task which takes time such as get users over internet. */
        completion(.success([user]))
    }
}

class Client {
    func execute() {
        let api = ApiManager.shared
        api.getUsers { result in
            /* do something */
        }
    }
}

class ClientTest {
    func testClient() {
        let client = Client()
        client.execute()
    }
}
```
For testing purpose, it is not good idea to use real ApiManger instance when unit test is focusing Client class and especially ApiManager does some heavy tasks.
So, we want to replace real ApiManager to dummy instance.

```swift:InstanceInject.swift
class User {
}

class ApiManager {
    static let shared = ApiManager()

    // Don't hide initializer so that multiple manager can be created.
    //private init() {}

    func getUsers(completion: @escaping (Result<[User], Error>) -> Void) {
        let user = User()
        /* Do task which takes time such as get users over internet. */
        completion(.success([user]))
    }
}

class Client {
    // add property
    var api: ApiManager = .shared

    func execute() {
        // use referenced singleton manager via property
        self.api.getUsers { result in
            /* do something */
        }
    }
}

class ClientTest {
    func testClient() {
        let client = Client()
        // Inject dummy instance.
        client.api = ApiManagerStub()
        // Now execute method uses injected manager during test
        client.execute()
        /* do some testing */
    }
}

class ApiManagerStub: ApiManager {
    override func getUsers(completion: @escaping (Result<[User], Error>) -> Void) {
        // return immediately
        completion(.success([]))
    }
}
```
Or, set dependency in initializer.  This way is much straight forward.

```swift:initinjection.swift
class Client {
    private let api: ApiManager

    init(api: ApiManager = .shared) {
        self.api = api
    }

    func execute() {
        self.api.getUsers { result in
            /* do something */
        }
    }
}

class ClientTest {
    func testClient() {
        // Set dummy instance at initialization time.
        let client = Client(api: ApiManagerStub())
        client.execute()
        /* do some testing */
    }
}
```
What happens if ApiManager is provided as independent library and cannot modify it?
No problem.  We can use protocol and class extension to inject.

```swift:protocolInject.swift
protocol Api {
    func getUsers(completion: @escaping (Result<[User], Error>) -> Void)
}

extension ApiManager: Api {}

class ApiManagerStub: Api {
    func getUsers(completion: @escaping (Result<[User], Error>) -> Void) {
        // return immediately
        completion(.success([]))
    }
}

class Client {
    var api: Api = ApiManager.shared

    func execute() {
        self.api.getUsers { result in
            /* do something */
        }
    }
}

class ClientTest {
    func testClient() {
        let client = Client()
        // Inject dummy instance.
        client.api = ApiManagerStub()
        client.execute()
        /* do some testing */
    }
}
```

Other approach is injection by closure.

```swift:closureInjection.swift
typealias Api = (@escaping (Result<[User], Error>) -> Void) -> Void

class Client {
    var getUsers: Api = ApiManager.shared.getUsers(completion:)

    func execute() {
        self.getUsers { result in
            /* do something */
        }
    }
}

class ClientTest {
    func testClient() {
        let client = Client()
        // Inject dummy instance.
        client.getUsers = ApiManagerStub().getUsers(completion:)
        // or directly inject closure
        client.getUsers = { completion in
            completion(.success([]))
        }
        client.execute()
        /* do some testing */
    }
}

class ApiManagerStub {
    func getUsers(completion: @escaping (Result<[User], Error>) -> Void) {
        // return immediately
        completion(.success([]))
    }
}
```
Notice (completion:) is not necessary.