apple/UIKit & ReactiveX
[ReactorKit] ReactorKit 공부하기 #3 RxTodo 따라잡기 (1)
lgvv
2022. 9. 7. 21:08
ReactorKit 공부하기 #3 RxTodo 따라잡기 (1)
아무튼 ReactorKit RxTodo 예제 고고
이번시간에 하려고 한 것
아래 폴더로 구성 Service 부분은 현재 예제를 구현하기에 그렇게 필요하지 않아서 패스!
🌿 구현 🌿
이제 리액터 사용법이 이해가 되기 시작했움!
리액터 정리!!
ViewController에서 map을 통해 원하는 Action으로 변경해서 reactor에 전달
reactor에서는 mutate함수를 통해서 이전 상태를 받아서 다음 상태를 반환!
reduce는 해당 로직처리!
그리고 변경된 값은 viewController에서 map을 통해 reactor.state를 가져다가 사용하기!
👉 ViewController
//
// TaskListViewController.swift
// AppleCollectionView
//
// Created by Hamlit Jason on 2022/09/07.
//
import UIKit
import ReactorKit
final class TaskListViewController: UIViewController, View {
var disposeBag = DisposeBag()
let reactor: TaskListViewReactor
// MARK: - Views
let addButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: nil, action: nil)
let tableView = UITableView()
// MARK: - Initialize
init(reactor: TaskListViewReactor) {
self.reactor = reactor
super.init(nibName: nil, bundle: nil)
self.navigationItem.leftBarButtonItem = self.editButtonItem
self.navigationItem.rightBarButtonItem = self.addButtonItem
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
bind(reactor: reactor)
// MARK: - 초기 테이블 뷰 로딩을 위해서
reactor.action.onNext(TaskListViewReactor.Action.refresh)
}
func bind(reactor: TaskListViewReactor) {
// MARK: - Action
self.addButtonItem.rx.tap
.map { TaskListViewReactor.Action.create } // 1. create액션으로 바꿔서
.bind(to: reactor.action) // 2. 액션에 bind
.disposed(by: disposeBag)
// MARK: - State
reactor.state
.map { _ in reactor.currentState.tasks }
.bind(to: tableView.rx.items(
cellIdentifier: TaskCell.identifier,
cellType: TaskCell.self)
) { index, item, cell in
cell.configureCell(with: item)
}
.disposed(by: disposeBag)
}
}
extension TaskListViewController {
func setupTableView() {
view.addSubview(tableView)
tableView.register(
TaskCell.self,
forCellReuseIdentifier: TaskCell.identifier
)
tableView.snp.makeConstraints {
$0.edges.equalTo(0)
}
}
}
여기서 viewDidLoad에 reactor를 한번 보내주는데, 그래야 테이블 뷰 그릴 수 있음!
사실 extension + Rx를 활용하면 viewDidLoad마저도 rx로 바꿀 수 있어서 그렇게 해도 된다.
근데 개인적으로는 LifeCycle과 관련한 코드는 rx로 묶으면 뭔가 보기가 불편해져서 그냥 한눈에 보이라고 저렇게 두는 편!
👉 Reactor
//
// TaskListViewReactor.swift
// AppleCollectionView
//
// Created by Hamlit Jason on 2022/09/07.
//
import Foundation
import ReactorKit
class TaskListViewReactor: Reactor {
var initialState = State()
// MARK: - User의 행동을 정의
enum Action {
case refresh // 테이블 뷰 리로드
case create // 테이블 아이템 생성
}
// MARK: - Action을 받았을 때 변화
enum Mutation {
case refreshItem // 테이블뷰 리로드
case insertItem // 테이블 뷰에 아이템 추가
}
// MARK: - 변화를 받은 후의 상태를 기록
struct State {
// 현재 상태!
var tasks = [Task]()
}
// MARK: - 이전 상태와 처리 단위를 받아서 다음 상태를 반환
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .refresh:
return Observable.just(.refreshItem)
case .create:
return Observable.just(.insertItem)
}
}
// MARK: - 변화에 맞게끔 값을 설정해!
func reduce(state: State, mutation: Mutation) -> State {
var newState = state
switch mutation {
case .refreshItem:
newState.tasks = Task.readTask()
case .insertItem:
Task.createTask()
newState.tasks = Task.readTask()
}
return newState
}
}
👉 cell
//
// TaskCell.swift
// AppleCollectionView
//
// Created by Hamlit Jason on 2022/09/07.
//
import UIKit
class TaskCell: UITableViewCell {
// MARK: - Views
var contentLabel = UILabel()
// MARK: - Initialize
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupContentLabel(contentLabel)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureCell(with item: Task) {
self.contentLabel.text = item.content
}
}
extension TaskCell {
private func setupContentLabel(_ sender: UILabel) {
contentView.addSubview(sender)
sender.font = UIFont.preferredFont(forTextStyle: .body, compatibleWith: .current)
sender.textColor = .systemTeal
sender.snp.makeConstraints {
$0.centerY.equalToSuperview()
$0.leading.equalToSuperview().offset(15)
}
}
}