Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clean Swift Workshop

Chiaote Ni
November 08, 2020

Clean Swift Workshop

Chiaote Ni

November 08, 2020
Tweet

More Decks by Chiaote Ni

Other Decks in Programming

Transcript

  1. Clean Swift
    A better architecture to make project testable
    iOS@Taipei
    艾倫 Ni & William Kuo

    View full-size slide

  2. What is Clean Swift?

    View full-size slide

  3. ViewController

    View full-size slide

  4. Interactor
    Presenter
    ViewController

    View full-size slide

  5. ViewController
    Interactor
    Presenter
    Display

    Logic
    Business

    Logic
    Presentation

    Logic
    ViewController
    Interactor
    Presenter
    Display

    Logic
    Business

    Logic
    Presenta
    tion

    Logic

    View full-size slide

  6. ViewControll
    Interact
    Presenter
    Display

    Logic
    Business

    Logic
    Presentation

    Logic
    Worker
    Worker
    Worker

    View full-size slide

  7. Worker
    ORM /
    DB
    Local
    API
    Network
    / API
    Network
    API
    ViewControll
    Interact
    Presenter
    Display

    Logic
    Business

    Logic
    Presentation

    Logic

    View full-size slide

  8. Worker
    ViewControll
    Interact
    Presenter
    Display

    Logic
    Business

    Logic
    Presentation

    Logic
    I13N
    Calculate
    Thumbnail
    resize
    Parse video’s
    metadata

    View full-size slide

  9. Interactor
    Presenter
    ViewController
    Request
    Response
    ViewModel

    View full-size slide

  10. viewContorller
    interactor
    presentor
    in
    out
    in
    out
    DisplayLogic
    in
    out
    Boundary Boundary
    Boundary
    BusinessLogic
    Presentation
    Logic

    View full-size slide

  11. protocol MeetupEventListDisplayLogic: AnyObject {
    func displayMeetupEvents(viewModel: MeetupEventList.FetchEvents.ViewModel)
    func displayUpdateHistoryEvent(viewModel: MeetupEventList.UpdateHistoryEvent.ViewModel)
    }
    final class MeetupEventListViewController: UIViewController, MeetupEventListDisplayLogic { }
    protocol MeetupEventListBusinessLogic {
    func fetchMeetupEvents(request: MeetupEventList.FetchEvents.Request)
    func tapFavorite(request: MeetupEventList.TapFavorite.Request)
    func subscribeFavoriteUpdate(request: MeetupEventList.SubscribeFavoriteUpdate.Request)
    func unsubscribeFavoriteUpdate(request: MeetupEventList.UnsubscribeFavoriteUpdate.Request)
    }
    final class MeetupEventListInteractor: MeetupEventListBusinessLogic { }
    protocol MeetupEventListPresentationLogic {
    func presentMeetupEvents(response: MeetupEventList.FetchEvents.Response)
    func presentUpdateHistoryEvent(response: MeetupEventList.UpdateHistoryEvent.Response)
    }
    final class MeetupEventListPresenter: MeetupEventListPresentationLogic { }

    View full-size slide

  12. BusinessLogic
    in
    out
    Boundary
    private func
    private func private func
    private func
    Test

    View full-size slide

  13. What’s more?

    View full-size slide

  14. ViewController
    Interactor DataStore
    Router
    Data
    Passing
    Routing

    View full-size slide

  15. Workshop Practice

    View full-size slide

  16. 基礎版 進階版

    View full-size slide

  17. Interactor
    Presenter
    ViewController
    Request
    Response
    ViewModel
    struct Request { }
    struct ViewModel {
    let historyEvents: []
    let recentlyEvents: []
    }
    struct Response {
    let recentlyEvents: [EventResponseItem]
    let historyEvents: [EventResponseItem]
    }

    View full-size slide

  18. enum MeetupEventList {
    enum FetchEvents {
    struct Request { }
    struct Response {
    let recentlyEvents: [EventResponseItem]
    let historyEvents: [EventResponseItem]
    }
    struct ViewModel {
    let historyEvents: [DisplayHistoryEvent]
    let recentlyEvents: [DisplayRecentlyEvent]
    }
    }
    }

    View full-size slide

  19. Presentation Model

    View full-size slide

  20. struct DisplayRecentlyEvent {
    let id: String
    let title: String
    let dateText: String
    let hostName: String
    let coverImageURL: URL?
    }

    View full-size slide

  21. struct DisplayHistoryEvent {
    let id: String
    let title: String
    let dateText: String
    let hostName: String
    let coverImageURL: URL?
    let favoriteButtonColor: UIColor
    }

    View full-size slide

  22. class MeetupEventListViewController: UIViewController {
    }
    var recentlyEvents: [MeetupEvent] = []
    var historyEvents: [MeetupEvent] = []
    ViewController (MVC)

    View full-size slide

  23. var recentlyEvents: [MeetupEventList.DisplayRecentlyEvent] = []
    var historyEvents: [MeetupEventList.DisplayHistoryEvent] = []
    protocol MeetupEventListDisplayLogic: AnyObject {
    func displayMeetupEvents(
    viewModel: MeetupEventList.FetchEvents.ViewModel
    )
    }
    class MeetupEventListViewController: UIViewController,
    MeetupEventListDisplayLogic {
    }
    ViewController (Clean Swift)

    View full-size slide

  24. meetupEventListAPI.fetchMeetupEvents { [weak self] result in
    guard let self = self else { return }
    switch result {
    case let .success((recentlyEvents, historyEvents)):
    self.recentlyEvents = self.recentlyEvents
    self.historyEvents = self.historyEvents
    self.tableView.reloadData()
    case let .failure(error):
    print("#### error -> \(error)")
    }
    }
    loadData( ) (MVC)

    View full-size slide

  25. let request: MeetupEventList.FetchEvents.Request = .init()
    interactor?.fetchMeetupEvents(request: request)
    loadData( ) (Clean Swift)

    View full-size slide

  26. func configureCell(with meetupEvent: MeetupEvent) {
    titleLabel.text = meetupEvent.title
    hostNameLabel.text = meetupEvent.hostName
    if let eventDate = meetupEvent.date {
    dateLabel.text = getDateText(with: eventDate)
    } else {
    dateLabel.text = ""
    }
    coverImageView.image = nil
    if let coverImageUrl = meetupEvent.coverImageLink,
    let url = URL(string: coverImageUrl) {
    coverImageView.isHidden = false
    coverImageView.kf.setImage(with: url)
    } else {
    coverImageView.isHidden = true
    }
    }
    Configure Cell (MVC)

    View full-size slide

  27. func configureCell(
    with meetupEvent: MeetupEventList.DisplayRecentlyEvent
    ) {
    titleLabel.text = meetupEvent.title
    hostNameLabel.text = meetupEvent.hostName
    dateLabel.text = meetupEvent.dateText
    coverImageView.image = nil
    if let url = meetupEvent.coverImageURL {
    coverImageView.isHidden = false
    coverImageView.kf.setImage(with: url)
    } else {
    coverImageView.isHidden = true
    }
    }
    Configure Cell (Clean Swift)

    View full-size slide

  28. protocol MeetupEventListBusinessLogic {
    func fetchMeetupEvents(
    request: MeetupEventList.FetchEvents.Request
    )
    }
    class MeetupEventListInteractor: MeetupEventListBusinessLogic{
    var fetchMeetupEventWorker: MeetupEventListAPIWorker
    func fetchMeetupEvents(
    request:MeetupEventList.FetchEvents.Request
    ) { }
    }
    Interactor (Clean Swift)

    View full-size slide

  29. protocol MeetupEventListPresentationLogic {
    func presentMeetupEvents(
    response: MeetupEventList.FetchEvents.Response
    )
    }
    class MeetupEventListPresenter:MeetupEventListPresentationLogic {
    func presentMeetupEvents(
    response: MeetupEventList.FetchEvents.Response
    ){ }
    }
    Presenter (Clean Swift)

    View full-size slide

  30. 活動時間: 每週⼆ pm: 8:30 ~ 10:00 (每⽉第⼀週休)
    活動地點:線上聚會 || Meet.jobs 辦公室
    (感謝Meet.jobs 提供場地)

    View full-size slide

  31. 聯絡資訊(email) 聯絡資訊(LinkedIn)
    我們在招⼈~

    beanfun!的iOS團隊需要你


    ⽬前我們正在以Clean Swift為主架構

    重構整個beanfun! App
    ⽬前預計明年1⽉會上線
    如果你對這個機會有興趣,想了解更多資訊
    歡迎email給我,或加我LinkedIn聊聊唷

    View full-size slide

  32. 練習專案 完成版 Clean Swift 討論群

    View full-size slide

  33. Thanks for your attention.

    View full-size slide