TIL
[Swift]ReactorKit, Coordinator 그리고 화면 이동과 데이터 전달
Kim Roks
2025. 1. 9. 19:20
개요
이번 포스팅에서는 테이블 뷰의 아이템을 선택했을 때, 코디네이터(Coordinator) 패턴을 사용하여 화면을 전환하고, 필요한 데이터를 전달하는 방법을 다룹니다.
이 글에서는 ReactorKit, RxSwift, 그리고 Coordinator 패턴을 사용하여 AView에서 BView로 데이터(User)를 전달하는 예시를 설명합니다.
각 디자인패턴이나 라이브러리의 상세한 설명은 생략됩니다.
액션 정의하기
먼저, A뷰와 B뷰가 있고, A뷰에서 B뷰로 이동한다고 가정합니다. 전달할 데이터는 User 형식으로 정의
데이터 모델 정의
struct User: Decodable {
let name: String?
let age: Int?
}
AView의 Reactor 정의
AView의 Reactor는 비동기 처리를 통해 데이터를 이미 받아왔다는 가정하에 진행
import ReactorKit
import RxSwift
// 해당 리액터에서 비동기 처리가 이미 되어있다는 가정하에 진행
class AViewReactor: Reactor {
enum Action {
// IndexPath 를 전달
case itemSelected(IndexPath)
}
enum Mutation {
// 기존 테이블 뷰에 네트워킹으로 받아놓은 데이터를 전달하기 위함
case pushToItemView(User)
}
struct State {
var selectedItem: User?
var items: [User]
}
let initialState: State
init(items: [User]) {
self.initialState = State(items: items)
}
func mutate(action: Action) -> Observable<Mutation> {
switch action {
// 해당 아이템이 갖고 있는 데이터를 사용하기 위해 indexPath를 받음
case .itemSelected(let indexPath):
let user = currentState.items[indexPath.row]
return .just(Mutation.pushToItemView(user))
}
}
func reduce(state: State, mutation: Mutation) -> State {
var newState = state
switch mutation {
case .pushToItemView(let user):
newState.selectedItem = user
}
return newState
}
}
코디네이터 정의
AView와 BView의 화면 전환을 담당하는 코디네이터를 정의합니다.
AView의 코디네이터 정의
protocol ACoordinator: Coordinator {
func navigateToBView(with data: User)
}
final class DefaultACoordinator: ACoordinator {
weak var parentCoordinator: Coordinator?
var childCoordinators: [Coordinator] = []
var navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
// 초기 화면 설정
}
func didFinish() {
// 코디네이터 종료 시 처리
}
func navigateToBView(with data: User) {
let bViewCoordinator = DefaultBViewCoordinator(navigationController: navigationController)
bViewCoordinator.parentCoordinator = self
childCoordinators.append(bViewCoordinator)
// BView의 Coordinator는 start 시 data를 받아 이동하도록 합니다.
bViewCoordinator.start(with: data)
}
}
BView의 코디네이터 정의
protocol BViewCoordinator: Coordinator {
func start(with data: User)
}
final class DefaultBViewCoordinator: BViewCoordinator {
weak var parentCoordinator: (any Coordinator)?
var childCoordinators: [any Coordinator] = []
var navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
// 초기 화면 설정
}
func start(with data: User) {
// BView는 생성 시 data를 받도록 작성했습니다.
let bView = BView(data: data)
bView.coordinator = self
navigationController.pushViewController(bView, animated: true)
}
func didFinish() {
parentCoordinator?.remove(child: self)
}
}
// BViewController 정의
final class BView: UIViewController {
weak var coordinator: BViewCoordinator?
var user: User?
init(data: User) {
self.user = data
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
self.title = user?.name
// user 정보를 이용해 추가적인 UI 구성 가능
}
}
AViewController에서 화면 이동 호출하기
이제 AViewController에서 선택된 아이템을 Reactor와 바인딩하고, 상태 변화에 따라 화면 이동을 처리합니다.
import UIKit
import RxSwift
import RxCocoa
import ReactorKit
// 기타 코드는 생략, 가독성을 위해 View 프로토콜을 extension에서 채택
extension AViewController: View {
func bind(reactor: AViewReactor) {
// 액션이 일어날 테이블 뷰
myTableView.rx.itemSelected
.map { Reactor.Action.itemSelected($0) }
.bind(to: reactor.action)
.disposed(by: disposeBag)
// 상태 변화에 따른 화면 이동 처리
reactor.state
.map { $0.selectedItem }
.compactMap { $0 } // nil 값을 지우기 위함
.subscribe(onNext: { [weak self] data in
// 뷰가 가지고 있는 코디네이터에서 해당 메서드를 호출하며, data를 전달
self?.coordinator?.navigateToBView(with: data)
})
.disposed(by: disposeBag)
}
}
결론
이번 포스팅에서는 테이블 뷰의 아이템 선택 시 Reactor와 Coordinator 패턴을 사용하여 데이터를 전달하고 화면을 전환하는 방법을 살펴보았습니다. 이 방법을 통해 뷰와 비즈니스 로직을 분리하고, 네비게이션 로직을 좀 더 모듈화하여 관리할 수 있습니다.
참조: https://benoitpasquier.com/data-between-views-using-coordinator-pattern-swift/