Slide 1

Slide 1 text

Setting Boundaries

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Francisco Díaz @fco_diaz

Slide 4

Slide 4 text

3 iOS Devs 28 hours 1 project == Merge Conflicts

Slide 5

Slide 5 text

What do we want? → Minimize duplication of code. → Develop independently without stepping on each other's toes.

Slide 6

Slide 6 text

Feature verticals: → Big Panic button → Today widget → Knock

Slide 7

Slide 7 text

Big Panic button:

Slide 8

Slide 8 text

What needs to be done? → Create the button. → We need a way to create reports. → Make a backend call to save this information.

Slide 9

Slide 9 text

Today widget:

Slide 10

Slide 10 text

What needs to be done? → Create the extension button. → We need a way to create reports. → Make a backend call to save this information.

Slide 11

Slide 11 text

What was it that we wanted? → Minimize duplication of code. → Develop independently without stepping on each other's toes.

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Let's try again!

Slide 14

Slide 14 text

→ Create the button. → We need a way to create reports. → Make a backend call to save this information.

Slide 15

Slide 15 text

UI / Presentation Create the button.

Slide 16

Slide 16 text

Business logic We need a way to create reports.

Slide 17

Slide 17 text

Backend connection Make a backend call to save this information.

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

To recap: → Minimize duplication of code. → Develop independently without stepping on each other's toes.

Slide 20

Slide 20 text

We can solve any problem by introducing an extra level of indirection — David Wheeler

Slide 21

Slide 21 text

Dependency inversion

Slide 22

Slide 22 text

struct ModelDataManager { let APIClient: APIType init(APIClient: APIType) { self.APIClient = APIClient } }

Slide 23

Slide 23 text

protocol APIType { func createReport(completion: JSONDictionary? -> Void) } struct API { private let manager: Alamofire.Manager init() { manager = Alamofire.Manager() } } extension API: APIType { func createReport(completion: JSONDictionary? -> Void) { manager.request(.POST, "https://some.com/api/report") .responseJSON { response in completion(response) } } }

Slide 24

Slide 24 text

struct ModelDataManager { let APIClient: APIType init(API: APIType) { self.APIClient = API } static func defaultManager() -> ModelDataManager { let APIClient = API() return ModelDataManager(API: APIClient) } func createReport(completionHandler completion: Report? -> Void) { APIClient.createReport() { jsonDictionary in let report = ... // Parse jsonDictionary into Report completion(report) } } }

Slide 25

Slide 25 text

Benefits → Testable. → Decoupled. → Easy to fake our networking layer.

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

struct FakeAPI: APIType { func createReport(completion: JSONDictionary? -> Void) { let dictionary = ["id": 12345] completion(dictionary) } }

Slide 28

Slide 28 text

struct ModelDataManager { let APIClient: APIType init(API: APIType) { self.APIClient = API } static func defaultManager() -> ModelDataManager { // let APIClient = API() let APIClient = FakeAPI() return ModelDataManager(API: APIClient) } }

Slide 29

Slide 29 text

Questions? Slides are available at: https://github.com/fdiaz/settings-boundaries-talk References: Architecture: The Lost Years The Clean Architecture