Swift • Maintained by 5 members • 1,3k+ stars in GitHub • Supporting iOS, macOS, tvOS, watchOS and Linux • 4 extensions provided • github.com/Swinject/Swinject
} You create what you use What you use are created and passed class Person1 { let pet: Cat init(pet: Cat) { self.pet = pet } } let p1 = Person1(pet: Cat()) class Person2 { var pet: Cat? } let p2 = Person2() p2.pet = Cat() class Person3 { func play(pet: Cat) { // Play with pet } } let p3 = Person3() p3.play(pet: Cat()) *OJUJBMJ[FS*OKFDUJPO $POTUSVDUPS*OKFDUJPO 1SPQFSUZ*OKFDUJPO .FUIPE*OKFDUJPO
} protocol Animal { } class Cat: Animal { } class Dog: Animal { } class Person4 { let pet: Animal init(pet: Animal) { self.pet = pet } } let catPerson = Person4(pet: Cat()) let dogPerson = Person4(pet: Dog()) Tight coupling Loose coupling • Only a cat can be a pet • Not flexible • Any animal can be a pet • Flexible
XCTestCase { // MARK: Mock private final class SuccessfulAccountManager: AccountManager { private (set) var currentUser: User? @discardableResult func login(username: String, password: String) -> Bool { currentUser = User(username: "test", fullname: "Test Test") return true } func logout() { currentUser = nil } } // MARK: Tests func testLogin_saysHelloToTheUserOnSuccess() { let accountManager = SuccessfulAccountManager() let viewModel = MainViewModel(accountManager: accountManager) viewModel.login(username: "", password: "") XCTAssertEqual(viewModel.labelText, "Hello Test Test!") } .PDLTVDDFFEJOH UPMPHJOBMXBZT *OKFDUUIFNPDL The message is checked in this example, but checking a state parameter or flag makes a unit test stable.
accountManager.login(username: "", password: "") let viewModel = MainViewModel(accountManager: accountManager) viewModel.login(username: "", password: "") XCTAssertEqual(viewModel.labelText, "Log out first.") } func testLogout_asksToLoginAgain() { let accountManager = SuccessfulAccountManager() accountManager.login(username: "", password: "") let viewModel = MainViewModel(accountManager: accountManager) viewModel.logout() XCTAssertEqual(viewModel.labelText, "Please log in.") } func testLogout_promptsToLoginIfNotLoggedIn() { let accountManager = SuccessfulAccountManager() let viewModel = MainViewModel(accountManager: accountManager) viewModel.logout() XCTAssertEqual(viewModel.labelText, "Log in first.") } } The message is checked in this example, but checking a state parameter or flag makes a unit test stable.
{ // MARK: Mock private final class FailingAccountManager: AccountManager { private (set) var currentUser: User? func login(username: String, password: String) -> Bool { return false } func logout() { } } // MARK: Tests func testLogin_showsErrorOnLoginFailure() { let accountManager = FailingAccountManager() let viewModel = MainViewModel(accountManager: accountManager) viewModel.login(username: "", password: "") XCTAssertEqual(viewModel.labelText, "Failed to log in.") } } *OKFDUUIFNPDL .PDLGBJMJOHUPMPHJOBMXBZT The message is checked in this example, but checking a state parameter or flag makes a unit test stable.
r in A(b: r.resolve(BProtocol.self)!) } container.register(BProtocol.self) { r in B(c: r.resolve(CProtocol.self)!, e: r.resolve(EProtocol.self)!) } Swinject Example $ ' & # " How to use a container #JOTUBODFJTNBOBHFE CZUIFDPOUBJOFS
r in A(b: r.resolve(BProtocol.self)!) } container.register(BProtocol.self) { r in B(c: r.resolve(CProtocol.self)!, e: r.resolve(EProtocol.self)!) } container.register(CProtocol.self) { r in C(f: r.resolve(FProtocol.self)!) } container.register(EProtocol.self) { r in E(f: r.resolve(FProtocol.self)!) } container.register(FProtocol.self) { _ in F() } Swinject Example $ ' & # " How to use a container #JOTUBODFJTNBOBHFE CZUIFDPOUBJOFS
r in A(b: r.resolve(BProtocol.self)!) } container.register(BProtocol.self) { r in B(c: r.resolve(CProtocol.self)!, e: r.resolve(EProtocol.self)!) } container.register(CProtocol.self) { r in C(f: r.resolve(FProtocol.self)!) } container.register(EProtocol.self) { r in E(f: r.resolve(FProtocol.self)!) } container.register(FProtocol.self) { _ in F() } // Anywhere later... let a = container.resolve(AProtocol.self)! Swinject Example $ ' & # " How to use a container #JOTUBODFJTNBOBHFE CZUIFDPOUBJOFS +VTUBTLUPHFU BO"JOTUBODF
of definitions checked by compiler. - No error at runtime for dependency injection. • Cons - Less easy than DI container. %FQFOEFODZ*OKFDUJPOXJUIUIF$BLF1BUUFSOJO4XJGUCZ#PC$PUUSFMM IUUQBDRVJIJSFNFEFQFOEFODZJOKFDUJPOXJUIUIFDBLFQBUUFSOJOTXJGU (SFBUBSUJDMFUPMFBSO$BLF1BUUFSO
coupling • Example Program - State management problem - Refactoring by Dependency Injection • Advanced Features - Dependency Injection Container (dynamic) - Cake Pattern (static)