Skip to content

Instantly share code, notes, and snippets.

@GeekTree0101
Last active July 12, 2019 03:00
Show Gist options
  • Save GeekTree0101/48b454be535d00428ad54d076ba51405 to your computer and use it in GitHub Desktop.
Save GeekTree0101/48b454be535d00428ad54d076ba51405 to your computer and use it in GitHub Desktop.
CleanSwift Example
class DisplayCellNode: ASCellNode {
// UI
let buttonNode = ASButtonNode()
// Props
weak var interactor: InteractorLogics!
private var presenter: DisplayCellPresenter = .init()
override func didLoad() {
super.didLoad()
buttonNode.addTarget(self, didTapButton:, .touchUpInside)
}
@objc func didTapButton() {
// Request -> Response
let response = interactor.didTapButton(id: "user-12345")
// Response -> ViewModel
let viewModel = response
.then { [weak self] user in
return self?.username(user: user) ?? brokenPromise()
}
.recover { [weak self] error in
return self?.errorMessage(error: error) ?? brokenPromise()
}
// ViewModel -> Rendering
viewModel
.done { [weak self] username in
self?.buttonNode.text = username
self?.setNeedsLayout()
}
}
}
// TDD: Stub Testing
protocol InteractorLogics {
func didTapButton(id: String) -> Promise<User>
}
class Interactor: InteractorLogics {
private var worker: WorkerLogics
init(worker: WorkerLogics) {
self.worker = worker
}
}
extension Interactor {
func didTapButton(id: String) -> Promise<User> {
return worker.getUser(id: id)
}
}
// TDD: Stub Testing
struct DisplayCellPresenter {
func username(user: User) -> String {
return user.username
}
func errorMessage(error: Error) -> String {
return error.errorMessage
}
}
protocol WorkerLogics {
func getUser(id: String) -> Promise<User>
}
class Worker: WorkerLogics {
func getUser(id: String) -> Promise<User> {
return service.get(API.user(id: userID))
}
}
@GeekTree0101
Copy link
Author

개발 및 생산성

  • 뷰들은 각자 독립된 Presenter를 가진다.
  • 하나의 Scene에 있는 모든 뷰들은 하나의 인터렉터(비즈니스로직)를 공유한다.
  • 뷰의 상태값 변화에 대한 로직은 사이드 이펙트 없이 목적지향적이고 독립적으로 수행되어야한다.

TDD

  • 인터렉터는 Worker를 Stubbing하여 input(Request to worker) -> output(Response from worker) 테스트를 한다.
  • 프리젠터는 input에 대한 값을 mocking하여 output(ViewModel)을 테스트한다.
  • 인터렉터와 프리젠터는 독립적으로 테스트가 가능해야한다.

@GeekTree0101
Copy link
Author

GeekTree0101 commented Jul 12, 2019

  • 버그를 찾아 빠르고 쉽게 수정할 수 있습니다. (Find and fix bugs faster and easier.)
  • 확신을 가지고 기존 동작을 변경할 수 있습니다. (Change existing behaviors with confidence.)
  • 새로운 기능을 쉽게 추가 할 수 있습니다. (Add new features easily.
  • 단일 책임으로 더 짧은 방법을 작성할 수 있습니다. (Write shorter methods with single responsibility.)
  • 클래스 종속성을 기존 경계와 분리합니다. (Decouple class dependencies with established boundaries.)
  • 비즈니스 로직을 뷰 컨트롤러에서 인터랙터로 분리함으로써 컨트롤러가 Massive해지는 걸 방지할 수 있습니다. (Extract business logic from view controllers into interactors.)
  • Wokrer 및 Service Object의 형태로 재사용 가능한 구성 요소를 구축합니다. (Build reusable components with workers and service objects.)
  • 처음부터 factored code로 작성합니다. (Write factored code from the start.)
  • 신속하고 유지 보수가 가능한 단위 테스트를 작성할 수 있습니다. (Write fast and maintainable unit tests.)
  • 회귀 분석에 대해서 자신감을 가지고 테스트 가능합니다. (Have confidence in your tests to catch regression.)

@GeekTree0101
Copy link
Author

  • 복잡한 기술적 기교를 줄이고
  • 직관적이고 읽기 쉬운
  • 적은 코드로 많은 테스트
  • 버그 추적하기 쉬운

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment