Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Clean Swift
Yoann Coldefy
October 01, 2016
Programming
0
260
Clean Swift
iOSでクリーンアーキテクチャー
Yoann Coldefy
October 01, 2016
Tweet
Share
More Decks by Yoann Coldefy
See All by Yoann Coldefy
ViewControllerの単体テスト
luinily
1
240
Other Decks in Programming
See All in Programming
iOSアプリの技術選択2022
tattn
3
1.3k
Micro Frontends with Module Federation: Beyond the Basics @jax2022
manfredsteyer
PRO
0
250
人類には難しいZynqで組み込みRust
ikemori
0
460
Yumemi.apk #6 ~ゆめみのAndroidエンジニア 日頃の成果大発表会!~ Session 2
blendthink
1
200
roadmap to rust 2024
matsu7874
1
390
New Relicを使った Observabilityの実現方法と活用例 / gocon 2022 spring after talk
budougumi0617
0
790
あなたの会社の古いシステム、なんとかしませんか?~システム刷新から考えるDX化への道筋とバリエーション~/webinar20220420-systems
grapecity_dev
0
110
ゼロから作る Protocol Buffer のパーサーとレキサー / Writing Protocol Buffer Parser/Lexer in Go from scratch
yoheimuta
1
150
スクラムの窓から眺めてみた エンジニアリングマネジメント / em-meetup#10 scrum with em
shinden
1
400
Microsoft Teams の 会議アプリ開発のはじめかた / How to start Microsoft Teams app development
karamem0
0
1.3k
あなたの会社の古いシステム、なんとかしませんか?~システム刷新から考えるDX化への道筋とバリエーション~/webinar20220420-grapecity
grapecity_dev
0
120
Reactでアプリケーションを構築する多様化
sakito
4
2.6k
Featured
See All Featured
It's Worth the Effort
3n
172
25k
Put a Button on it: Removing Barriers to Going Fast.
kastner
56
2.3k
Large-scale JavaScript Application Architecture
addyosmani
499
110k
Debugging Ruby Performance
tmm1
65
10k
How to name files
jennybc
39
58k
Streamline your AJAX requests with AmplifyJS and jQuery
dougneiner
125
8.5k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
224
49k
Visualization
eitanlees
124
11k
Build your cross-platform service in a week with App Engine
jlugia
219
17k
The Art of Programming - Codeland 2020
erikaheidi
31
5.8k
The Invisible Side of Design
smashingmag
289
48k
Building an army of robots
kneath
299
40k
Transcript
Clean Swift
ࣗݾհ • ໊લɿίʔυώʔϤΞϯ • ग़ɿGrenoble • ৬ۀɿιϑτΤΞʔΤϯδχΞʔ • ։ൃܦݧɿDelphiʢ7ʣɺSwiftʢʣ •
Facebookɿhttps://www.facebook.com/yoann.coldefy • Twitterɿ@luinily
• View Controllerେ͖͘ͳΔ • View Controller͕ෳࡶʹͳΔ • อक͠ʹ͍͘ • ༷มߋʹରԠ͠ʹ͍͘
• ΫϦʔϯߏ • Λ͚Δ • ґଘΛੵۃతʹ͑Δ
• ୯ମςετ • ςετۦಈͰࣗવʹมߋ͍͢͠ߏங • ϦϑΝΫτϦϯάͯ҆͠৺
iOSͰͲ͏͢Ε͍͍ʁ
View Controller Interactor Presenter VIP cycle
View Controller Interactor Presenter VIP cycle Viewͷίϯτϩʔϧ
View Controller Interactor Presenter VIP cycle ϞσϧͱͷΓͱΓ
View Controller Interactor Presenter VIP cycle දࣔ༻ͷσʔλΛܗ
View Controller Interactor Presenter VIP cycle
Input protocol Output protocol Data Data Data Data
View Controller View Model Request View
class ApodViewController: UIViewController { var output: ApodViewControllerOutput! }
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) } }
class ApodViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() fetchTodaysApod()
} func fetchTodaysApod() { let request = TodayApodRequest() output.fetchTodayApod(request: request) } }
Interactor Worker Worker Worker Request Response Business Logic
class ApodInteractor: ApodInteractorInput { var output: ApodInteractorOutput! private var
_apodWorker: ApodWorker }
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) }
class ApodInteractor: ApodInteractorInput { func fetchTodayApod(request: TodayApodRequest) { _apodWorker.fetchTodayAPOD()
{ apodData, error in let response = ApodResponse(apodData: apodData) self.output.presentApod(response: response) } } }
Presenter Response View Model
class ApodPresenter: ApodPresenterInput { weak var output: ApodPresenterOutput! }
func test_presentApod_callsDisplayAPOD() func test_presentApod_viewModelContainsTitle() func test_presentApod_viewModelContainsFormatedDate() { {…} //
Assert XCTAssertEqual(mockOutput.apodViewModel?.date, "201607݄18") }
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) } }
View Controller View Model Request View
func test_displayApod_TitleIsDisplayed() { // Arrange loadView() let viewModel =
prepareViewModel() // Act target.displayApod(viewModel: viewModel) // Assert XCTAssertEqual(target.titleLabel.text, viewModel.title) }
extension ApodViewController: ApodViewControllerInput { func displayApod(viewModel: ApodDataViewModel) { titleLabel.text
= viewModel.title explanationTextView.text = viewModel.explanation } }
• 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 } }
protocol ApodViewControllerOutput { func fetchTodayApod(request: TodayApodRequest) func fetchRandomApod(request: RandomApodRequest)
} protocol ApodInteractorInput { func fetchTodayApod(request: TodayApodRequest) func fetchRandomApod(request: RandomApodRequest) }
extension ApodInteractor: ApodViewControllerOutput { }
·ͱΊ • ςετۦಈ։ൃ͕Ͱ͖Δ • σΟάϨʔυ͠ʹ͍͘ • ֤Ϋϥεͷ͕͖ͬΓ͍ͯ͠ΔͷͰɺό άमਖ਼มߋ͕͍͢͠
• Ϋϥεؒͷґଘੑ͕͍ͨΊɺผͷΫϥ ε͕ೖΓม͍͑͢ • View ControllerϏϡʔͷ੍ޚͱೖྗؔ ͷίʔυ͔͠ͳ͍
• ίʔυ͕݁ߏ૿͑ͯ͠·͍·͢ • ςετॻ͘ͷͰɺඞཁ࣌ؒ૿͑·͢ • ࠷ॳϋʔυϧ͕ͪΐͬͱߴΊ
http://clean-swift.com https://github.com/luinily/spaceApp http://qiita.com/takahia/items/67b9e1229682127d924e