Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Clean Swift
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Yoann Coldefy
October 01, 2016
Programming
370
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Clean Swift
iOSでクリーンアーキテクチャー
Yoann Coldefy
October 01, 2016
More Decks by Yoann Coldefy
See All by Yoann Coldefy
ViewControllerの単体テスト
luinily
1
310
Other Decks in Programming
See All in Programming
Building a Meta Ray-Ban display app
akkeylab
0
170
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
200
仕様駆動開発の消費期限
watany
20
8.8k
170k Jobs a Day on GKE: Scaling Mercari's CI Platform - and What's Next for AI-Native Development
junyaokabe
0
120
使いながら育てる Claude Code — 開発フローの1コマンド化 × 繰り返し指摘の自動仕組み化
shiki_kakaku
0
1.9k
Claude Code全社展開のためにやったことn選~プラグイン302個・コミッター271人を支えるために~
kenchan
5
1.6k
komatsuna「分散システムにおけるバグ分析手法」
komatsunaqa
0
270
仕様書を書く前にハーネスを作る - Agent Native開発は「探索を速く、判定を固く」
gotalab555
4
1.7k
楽しそうなつよつよエンジニアと目が死んでる僕/A brilliant engineer having a blast, and dead-eyed me.
3l4l5
2
230
freee が目指す データ マネジメント戦略 AI-Ready 時代を支える 攻めのガバナンスとは
freee
PRO
0
450
Go を使い始めて 2 ヶ月の学び / My first two months with Go
contour_gara
0
360
書籍「プロフェッショナルAI駆動開発」紹介スライド
juntaromatsumoto
0
280
Featured
See All Featured
Leo the Paperboy
mayatellez
8
2.2k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
900
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
260
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
280
Code Reviewing Like a Champion
maltzj
528
40k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
640
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
240
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
350
Are puppies a ranking factor?
jonoalderson
2
3.8k
How to Ace a Technical Interview
jacobian
281
24k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
970
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
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