Slide 1

Slide 1 text

Clean Swift

Slide 2

Slide 2 text

ࣗݾ঺հ • ໊લɿίʔυώʔϤΞϯ • ग़਎ɿGrenoble • ৬ۀɿιϑτΤΞʔΤϯδχΞʔ • ։ൃܦݧɿDelphiʢ7೥ʣɺSwiftʢ൒೥ʣ • Facebookɿhttps://www.facebook.com/yoann.coldefy • Twitterɿ@luinily

Slide 3

Slide 3 text

• View Controller͸େ͖͘ͳΔ • View Controller͕ෳࡶʹͳΔ • อक͠ʹ͍͘ • ࢓༷มߋʹରԠ͠ʹ͍͘

Slide 4

Slide 4 text

• ΫϦʔϯߏ૝ • ੹೚Λ෼͚Δ • ґଘΛੵۃతʹ཈͑Δ

Slide 5

Slide 5 text

• ୯ମςετ • ςετۦಈͰࣗવʹมߋ͠΍͍͢ߏங • ϦϑΝΫτϦϯάͯ͠΋҆৺

Slide 6

Slide 6 text

iOSͰ͸Ͳ͏͢Ε͹͍͍ʁ

Slide 7

Slide 7 text

View Controller Interactor Presenter VIP cycle

Slide 8

Slide 8 text

View Controller Interactor Presenter VIP cycle Viewͷίϯτϩʔϧ

Slide 9

Slide 9 text

View Controller Interactor Presenter VIP cycle Ϟσϧͱͷ΍ΓͱΓ

Slide 10

Slide 10 text

View Controller Interactor Presenter VIP cycle දࣔ༻ͷσʔλΛ੔ܗ

Slide 11

Slide 11 text

View Controller Interactor Presenter VIP cycle

Slide 12

Slide 12 text

Input protocol Output
 protocol Data Data Data Data

Slide 13

Slide 13 text

View Controller View Model Request View

Slide 14

Slide 14 text

class ApodViewController: UIViewController { var output: ApodViewControllerOutput! }

Slide 15

Slide 15 text

class ApodViewControllerTests { override func setUp() { super.setUp() window = UIWindow() setupApodViewController() outputSpy = MockApodViewControllerOutput() target.output = outputSpy } func test_viewDidLoad_fetchTodaysApodIsCalled() { // Arrange // Act loadView() // Assert XCTAssertTrue(outputSpy.fetchTodayApodCalled) } }

Slide 16

Slide 16 text

class ApodViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() fetchTodaysApod() } func fetchTodaysApod() { let request = TodayApodRequest() output.fetchTodayApod(request: request) } }

Slide 17

Slide 17 text

Interactor Worker Worker Worker Request Response Business Logic

Slide 18

Slide 18 text


 class ApodInteractor: ApodInteractorInput { var output: ApodInteractorOutput! private var _apodWorker: ApodWorker }

Slide 19

Slide 19 text


 func test_fetchTodayApod_callsApodWorkerFetchTodayApod() { // Arrange let request = TodayApodRequest() // Act target.fetchTodayApod(request: request) // Assert XCTAssertTrue(mochApodWorker.fetchTodayApodCalled) } func test_fetchTodayApod_callsOutputPresentApod() { // Arrange let request = TodayApodRequest() // Act target.fetchTodayApod(request: request) // Assert XCTAssertTrue(mochOutput.presentOutputCalled) }

Slide 20

Slide 20 text


 class ApodInteractor: ApodInteractorInput { func fetchTodayApod(request: TodayApodRequest) { _apodWorker.fetchTodayAPOD() { apodData, error in let response = ApodResponse(apodData: apodData) self.output.presentApod(response: response) } } }

Slide 21

Slide 21 text

Presenter Response View Model

Slide 22

Slide 22 text


 class ApodPresenter: ApodPresenterInput { weak var output: ApodPresenterOutput! }

Slide 23

Slide 23 text


 func test_presentApod_callsDisplayAPOD() func test_presentApod_viewModelContainsTitle() func test_presentApod_viewModelContainsFormatedDate() { {…} // Assert XCTAssertEqual(mockOutput.apodViewModel?.date, "2016೥07݄18೔") }

Slide 24

Slide 24 text


 class ApodPresenter: ApodPresenterInput { func presentApod(response: ApodResponse) { let apodViewModel = makeApodViewModel(apodData: response.apodData) output.displayApod(viewModel: apodViewModel) } private func makeApodViewModel(apodData: ApodData) -> ApodDataViewModel { let title = apodData.title let date = dateToString(date: apodData.date) let explanation = apodData.explanation let copyright = apodData.copyright return ApodDataViewModel(title: title, date: date, explanation: explanation, copyright: copyright) } }

Slide 25

Slide 25 text

View Controller View Model Request View

Slide 26

Slide 26 text


 func test_displayApod_TitleIsDisplayed() { // Arrange loadView() let viewModel = prepareViewModel() // Act target.displayApod(viewModel: viewModel) // Assert XCTAssertEqual(target.titleLabel.text, viewModel.title) }

Slide 27

Slide 27 text


 extension ApodViewController: ApodViewControllerInput { func displayApod(viewModel: ApodDataViewModel) { titleLabel.text = viewModel.title explanationTextView.text = viewModel.explanation } }

Slide 28

Slide 28 text

• InteractorɺPresenterͳͲͷઃఆͱ݁ͼ͚ͭ class ApodConfigurator { func configure(viewController: ApodViewController) { let router = ApodRouter() router.viewController = viewController let presenter = ApodPresenter() presenter.output = viewController let interactor = configureInteractor(presenter: presenter) viewController.output = interactor viewController.router = router } }

Slide 29

Slide 29 text


 protocol ApodViewControllerOutput { func fetchTodayApod(request: TodayApodRequest) func fetchRandomApod(request: RandomApodRequest) } protocol ApodInteractorInput { func fetchTodayApod(request: TodayApodRequest) func fetchRandomApod(request: RandomApodRequest) }

Slide 30

Slide 30 text


 extension ApodInteractor: ApodViewControllerOutput { }

Slide 31

Slide 31 text

·ͱΊ • ςετۦಈ։ൃ͕Ͱ͖Δ • σΟάϨʔυ͠ʹ͍͘ • ֤Ϋϥεͷ੹೚͕͸͖ͬΓ͍ͯ͠ΔͷͰɺό άमਖ਼΍มߋ͕͠΍͍͢

Slide 32

Slide 32 text

• Ϋϥεؒͷґଘੑ͕௿͍ͨΊɺผͷΫϥ ε͕ೖΓม͑΍͍͢ • View Controller͸Ϗϡʔͷ੍ޚͱೖྗؔ܎ ͷίʔυ͔͠ͳ͍

Slide 33

Slide 33 text

• ίʔυ͕݁ߏ૿͑ͯ͠·͍·͢ • ςετॻ͘ͷͰɺඞཁ࣌ؒ΋૿͑·͢ • ࠷ॳ͸ϋʔυϧ͕ͪΐͬͱߴΊ

Slide 34

Slide 34 text

http://clean-swift.com https://github.com/luinily/spaceApp http://qiita.com/takahia/items/67b9e1229682127d924e