apple/UIKit & ReactiveX
[ReactorKit] ReactorKit 공부하기 #4 RxTodo 따라잡기 (2)
lgvv
2022. 9. 8. 02:19
ReactorKit 공부하기 #4 RxTodo 따라잡기 (2)
이번에는 지난 시간 create를 했다면, delete를 진행해 볼 예정.
해당 파일
🌿 구현 코드 🌿
어려웠던 점: Action의 경우에는 정의하기가 그리 어렵지 않았으나, Mutation을 어떻게 줘야할 지 생각을 많이했었음.
//
// 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.editButtonItem.rx.tap
.map { TaskListViewReactor.Action.toggleEditing }
.bind(to: reactor.action)
.disposed(by: disposeBag)
self.addButtonItem.rx.tap
.map { TaskListViewReactor.Action.create } // 1. create액션으로 바꿔서
.bind(to: reactor.action) // 2. 액션에 bind
.disposed(by: disposeBag)
self.tableView.rx.itemDeleted
.map(TaskListViewReactor.Action.deleteTask)
.bind(to: reactor.action)
.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)
reactor.state
.map { $0.isEditing }
.withUnretained(self)
.bind { owner, isEditing in
owner.navigationItem.leftBarButtonItem?.title = isEditing
? "✅완료하기✅"
: "🔮수정하기🔮"
owner.navigationItem.leftBarButtonItem?.style = isEditing
? .done
: .plain
owner.tableView.setEditing(isEditing, animated: true)
}
.disposed(by: disposeBag)
}
}
extension TaskListViewController {
func setupTableView() {
view.addSubview(tableView)
tableView.delegate = self
tableView.register(
TaskCell.self,
forCellReuseIdentifier: TaskCell.identifier
)
tableView.snp.makeConstraints {
$0.edges.equalTo(0)
}
}
}
extension TaskListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
//
// 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 toggleEditing // 토글 에디팅
case deleteTask(IndexPath) // task 삭제
case refresh // 테이블 뷰 리로드
case create // 테이블 아이템 생성
}
// MARK: - Action을 받았을 때 변화
enum Mutation {
case toggleEditing // leftBarItem 토글버튼
case refreshItem // 테이블뷰 리로드
case insertItem // 테이블 뷰에 아이템 추가
case deleteItem(IndexPath) // 테이블 뷰에 아이템 삭제
}
// MARK: - 변화를 받은 후의 상태를 기록
struct State {
var isEditing: Bool = false
var tasks = [Task]() // 현재 상태!
}
// MARK: - 이전 상태와 처리 단위를 받아서 다음 상태를 반환
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .toggleEditing:
return Observable.just(.toggleEditing)
case .refresh:
return Observable.just(.refreshItem)
case .create:
return Observable.just(.insertItem)
case .deleteTask(let indexPath):
return .just(.deleteItem(indexPath))
}
}
// MARK: - 변화에 맞게끔 값을 설정해!
func reduce(state: State, mutation: Mutation) -> State {
var newState = state
switch mutation {
case .toggleEditing:
newState.isEditing = !newState.isEditing
case .refreshItem:
newState.tasks = Task.readTask()
case .insertItem:
Task.createTask()
newState.tasks = Task.readTask()
case .deleteItem(let indexPath):
let task = Task.readTask()[indexPath.row]
Task.deleteTask(task: task, indexPath: indexPath)
newState.tasks = Task.readTask()
}
return newState
}
}
//
// 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)
}
}
}