Slide 1

Slide 1 text

Dependency Injection in Swift TOKYO iOS MEETUP, February 13, 2016 @yoichitgy - Yoichi Tagaya

Slide 2

Slide 2 text

Dependency Injection in Scala http://www.infoq.com/news/2015/06/play-24-dependency-injection Typesafe's Play team has released version 2.4 "Damiya" of their web framework... By embracing dependency injection, the refactoring towards better modularization that was started in 2.3 has continued in this release... Play has already supported dependency injection in earlier versions, but now it comes out of the box and its use is even encouraged. "Play 2.4 Moves to Dependency Injection and Java 8"

Slide 3

Slide 3 text

DEMO - Simple Weather App OpenWeatherMap API - http://openweathermap.org https://github.com/Swinject/SwinjectSimpleExample

Slide 4

Slide 4 text

What's Dependency? $MBTT# $MBTT" Reference Dependency "MBNPpSF /FUXPSL 8FBUIFS 'FUDIFS Reference External System Dependency In the demo app:

Slide 5

Slide 5 text

Hard to test...

Slide 6

Slide 6 text

Problem of Dependency: Simple Example Coupled Components class Cat { let name: String init(name: String) { self.name = name } func sound() -> String { return "Meow!" } } class PetOwner { let pet = Cat(name: "Mimi") func play() -> String { return "I'm playing with \(pet.name). \(pet.sound())" } } let petOwner = PetOwner() print(petOwner.play()) // prints "I'm playing with Mimi. Meow!" Hardcoded Assume we are developing a pet game...

Slide 7

Slide 7 text

PetOwner cannot have a dog...

Slide 8

Slide 8 text

Decoupled Components protocol AnimalType { var name: String { get } func sound() -> String } class Cat: AnimalType { let name: String init(name: String) { self.name = name } func sound() -> String { return "Meow!" } } class PetOwner { let pet: AnimalType init(pet: AnimalType) { self.pet = pet } func play() -> String { return "I'm playing with \(pet.name). \(pet.sound())" } } let catOwner = PetOwner(pet: Cat(name: "Mimi")) print(catOwner.play()) // prints "I'm playing with Mimi. Meow!" AnimalType is Passed (Injected) Dependency to protocol not to implementation (or loosely coupled components)

Slide 9

Slide 9 text

Decoupled Components class Dog: AnimalType { let name: String init(name: String) { self.name = name } func sound() -> String { return "Bow wow!" } } let dogOwner = PetOwner(pet: Dog(name: "Hachi")) print(dogOwner.play()) // prints "I'm playing with Hachi. Bow wow!" (or loosely coupled components) PetOwner can have a dog!

Slide 10

Slide 10 text

Dependency Injection is... https://en.wikipedia.org/wiki/Dependency_injection In software engineering, dependency injection is a software design pattern that implements inversion of control for resolving dependencies. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. Wikipedia: Dependency injection

Slide 11

Slide 11 text

A Bit about Inversion of Control • I'm talking to you.
 / You're talking to me. • You call libraries.
 / Frameworks call you. • You create what you use.
 / What you use are created (and passed).

Slide 12

Slide 12 text

Three Major Injection Patterns 1. Initializer Injection (Constructor Injection) 2. Property Injection 3. Method Injection

Slide 13

Slide 13 text

1. Initializer Injection (Constructor Injection) class PetOwner { let pet: AnimalType init(pet: AnimalType) { self.pet = pet } } let catOwner = PetOwner(pet: Cat(name: "Mimi")) Preferred (No 2-step initialization)

Slide 14

Slide 14 text

2. Property Injection class PetOwner { var pet: AnimalType? init() { } } let catOwner = PetOwner() catOwner.pet = Cat(name: "Mimi") UIViewController with UIStoryboard init?(coder aDecoder: NSCoder)

Slide 15

Slide 15 text

3. Method Injection class PetOwner { private(set) var pet: AnimalType? init() { } func setPet(pet: AnimalType) { self.pet = pet } } let catOwner = PetOwner() catOwner.setPet(Cat(name: "Mimi")) class PetOwner { func play(pet: AnimalType) -> String { return "I'm playing with \(pet.name). \(pet.sound())" } } let petOwner = PetOwner() petOwner.play(Cat(name: "Mimi")) or

Slide 16

Slide 16 text

3. Method Injection (another pattern) struct DateManager { private let formatter: NSDateFormatter = { let formatter = NSDateFormatter() formatter.dateStyle = .MediumStyle return formatter }() func today(date: NSDate = NSDate()) -> String { return formatter.stringFromDate(date) } } let dateManager = DateManager() // Production print(dateManager.today()) // prints "Feb 13, 2016" // Test XCTAssertEqual( "Jan 1, 1970", dateManager.today(NSDate(timeIntervalSince1970: 0.0))) Date can be injected for test

Slide 17

Slide 17 text

DEMO - Fixed Unit Tests with Dependency Injection

Slide 18

Slide 18 text

DI (Dependency Injection) Container Examples: • Spring (Java/Scala) • Dagger (Java/Scala) • Guice (Java/Scala) • Ninject (C#) Popular in Java or static languages. Less popular in Ruby or dynamic languages. What's DI Container?

Slide 19

Slide 19 text

Problem: Beginning of Development /FUXPSL $MBTT" Reference

Slide 20

Slide 20 text

Problem: Soon You'll See Dependency Graph /FUXPSL $MBTT" Reference %BUBCBTF 5JNF 4USVDU# $MBTT$ Easy way to manage the dependency graph???

Slide 21

Slide 21 text

DI Container /FUXPSL $MBTT" %BUBCBTF 5JNF 4USVDU# $MBTT$ Dependency graph %*$POUBJOFS Register at the beginning Resolve when you need *OTUBODFPG $MBTT"

Slide 22

Slide 22 text

DI Container for Swift import Swinject let container = Container() container.register(AnimalType.self) { _ in Cat(name: "Mimi") } container.register(PetOwner.self) { r in PetOwner(pet: r.resolve(AnimalType.self)!) } https://github.com/Swinject/Swinject Swinject let petOwner = container.resolve(PetOwner.self)! print(petOwner.play()) // prints "I'm playing with Mimi. Meow!" Registration: Resolution: Cat is instantiated automatically when PetOwner is instantiated!

Slide 23

Slide 23 text

DEMO Using Swinject as DI Container

Slide 24

Slide 24 text

Summary • Dependency Injection is a pattern to decouple (loosely-couple) components. • DI Container stores dependency graph and provides instances upon requests. • Software architecture with Dependency Injection is easier to test, maintain and organize.

Slide 25

Slide 25 text

Further Readings • https://www.natashatherobot.com/unit-testing-swift- dependency-injection/ • http://www.warski.org/blog/2011/04/di-in-scala-cake- pattern-pros-cons/ • https://yoichitgy.github.io/post/dependency-injection- in-mvvm-architecture-with-reactivecocoa-part-1- introduction/