Skip to content

Latest commit

ย 

History

History
137 lines (106 loc) ยท 3.33 KB

File metadata and controls

137 lines (106 loc) ยท 3.33 KB

Add a New Screen

๊ธฐ์กด feature์— ์ƒˆ ํ™”๋ฉด์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋‹จ๊ณ„๋ณ„ ๊ฐ€์ด๋“œ.

Steps

1. Reactor ์ƒ์„ฑ

Presentation/{Feature}/ViewModels/XxxReactor.swift:

import Foundation
import ReactorKit

final class XxxReactor: Reactor {
    enum Action {
        case fetch
    }

    enum Mutation {
        case setData(_ data: [SomeType])
        case setLoading(_ isLoading: Bool)
    }

    struct State {
        var data: [SomeType] = []
        var isLoading: Bool = true
    }

    let initialState: State
    var coordinator: FeatureCoordinator?  // ๋˜๋Š” weak var delegate: XxxReactorDelegate?

    init(coordinator: FeatureCoordinator, state: State = State()) {
        self.coordinator = coordinator
        self.initialState = state
    }

    func mutate(action: Action) -> Observable<Mutation> {
        switch action {
        case .fetch:
            let data = Items.shared.someStream.map { Mutation.setData($0) }
            let loading = Items.shared.isLoading.map { Mutation.setLoading($0) }
            return .merge([data, loading])
        }
    }

    func reduce(state: State, mutation: Mutation) -> State {
        var newState = state
        switch mutation {
        case .setData(let data):
            newState.data = data
        case .setLoading(let isLoading):
            newState.isLoading = isLoading
        }
        return newState
    }
}

2. ViewController ์ƒ์„ฑ

Presentation/{Feature}/ViewControllers/XxxViewController.swift:

import UIKit
import RxSwift

final class XxxViewController: UIViewController {
    private let disposeBag = DisposeBag()

    func bind(to reactor: XxxReactor) {
        // Action ๋ฐ”์ธ๋”ฉ
        self.rx.viewDidLoad
            .map { XxxReactor.Action.fetch }
            .bind(to: reactor.action)
            .disposed(by: disposeBag)

        // State ๋ฐ”์ธ๋”ฉ
        reactor.state.map { $0.isLoading }
            .bind(to: activityIndicator.rx.isAnimating)
            .disposed(by: disposeBag)
    }
}

3. Custom View ์ƒ์„ฑ (ํ•„์š” ์‹œ)

Presentation/{Feature}/Views/XxxView.swift

4. Route ์ถ”๊ฐ€

ํ•ด๋‹น feature์˜ Coordinator์— Route case ์ถ”๊ฐ€:

// XxxCoordinator.swift
enum Route {
    // ... ๊ธฐ์กด routes
    case newScreen(param: SomeType)  // ์ƒˆ route ์ถ”๊ฐ€
}

5. transition() ๊ตฌํ˜„

func transition(for route: Route) {
    switch route {
    // ... ๊ธฐ์กด cases
    case .newScreen(let param):
        let viewController = XxxViewController()
        viewController.bind(to: XxxReactor(param: param, coordinator: self))
        rootViewController.pushViewController(viewController, animated: true)
    }
}

6. ๊ธฐ์กด Reactor์—์„œ Route ํŠธ๋ฆฌ๊ฑฐ

// ํ˜ธ์ถœํ•˜๋Š” ์ธก Reactor์˜ reduce()
case .someAction:
    coordinator?.transition(for: .newScreen(param: data))

Checklist

  • Reactor: Action, Mutation, State ์ •์˜
  • Reactor: mutate(), reduce() ๊ตฌํ˜„
  • ViewController: bind(to:) ๋ฉ”์„œ๋“œ ๊ตฌํ˜„
  • Coordinator: Route case ์ถ”๊ฐ€
  • Coordinator: transition(for:) case ๊ตฌํ˜„
  • ๋กœ์ปฌ๋ผ์ด์ œ์ด์…˜: ko.lproj + en.lproj ๋ฌธ์ž์—ด ์ถ”๊ฐ€
  • SwiftLint: swiftlint --config .swiftlint.yml ํ†ต๊ณผ

์ฐธ๊ณ : TurnipPrices ์˜ˆ์™ธ

TurnipPrices feature๋Š” ํ•˜์œ„ ํด๋” ์—†์ด flat ๊ตฌ์กฐ. ์ด feature์— ์ถ”๊ฐ€ ์‹œ flat์œผ๋กœ ์œ ์ง€ํ•  ๊ฒƒ. โ†’ gotchas.md #2