-
[ReactorKit#2] Example : Counter 코드작성 (스토리보드 없이)iOS Dev/ReactorKit 2020. 6. 7. 02:42반응형
https://github.com/chongin12/Counter-Prac << 전체 소스코드
우선 ReactorKit, RxSwift, RxCocoa, SnapKit 을 pod을 이용하여 설치를 할 것이다. .xcodeproj가 있는 폴더에서 pod init 명령어를 실행시켜주고, 생성된 Podfile에 다음과 같이 적어준다.
pod 'ReactorKit' pod 'RxSwift' pod 'RxCocoa' pod 'SnapKit'
pod install 명령어로 설치를 한 다음 생성된 .xcworkspace 파일을 열어준다.
우선 스토리보드 없이 코드를 작성하려면 스토리보드를 삭제해야 한다. Main.storyboard, Launch.storyboard 다 휴지통으로 보내주고, 프로젝트 정보 General - Deployment Info - Main Interface를 비워준다.
Info.plist에 들어가서 Information Property List - Application Scene Manifest - Scene Configuration - Application Session Role - Item 0 (Default Configuration) 에 들어가서 Storyboard Name 부분 행을 아예 지워준다. (마우스 오버레이 후 -(빼기)버튼 눌러도 된다.)
그 다음 SceneDelegate.swift로 가서 func scene() 부분을 고쳐주어야 한다.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). guard let _ = (scene as? UIWindowScene) else { return } }
이렇게 생긴 거를
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(frame: windowScene.coordinateSpace.bounds) window?.windowScene = windowScene window?.rootViewController = ViewController() window?.makeKeyAndVisible() }
이렇게 바꿔준다.
커밋 추가 (message : storyboard 제거)
이제 뷰를 하나 만들어야 한다.
CounterView.swift 파일 하나 만들어주고, 다음 내용을 집어넣는다.// // CounterView.swift // Counter-Prac // // Created by 정종인 on 2020/06/06. // Copyright © 2020 swmaestro10th. All rights reserved. // import Foundation import UIKit import SnapKit class CounterView: UIView { private let backgroundView: UIView = { let v = UIView() v.backgroundColor = .white return v; }() private let valueLabel: UILabel = { let v = UILabel() v.text = "0" v.textAlignment = .center return v }() private let increaseButton: UIButton = { let v = UIButton(type: .roundedRect) v.backgroundColor = .brown return v }() private let decreaseButton: UIButton = { let v = UIButton(type: .roundedRect) v.backgroundColor = .brown return v }() private let activityIndicatorView: UIActivityIndicatorView = { let v = UIActivityIndicatorView(style: .medium) return v }() func setup() { setupUI() setBind() } } extension CounterView { private func setupUI() { addSubviews() setLayout() } private func setBind() { //delegate, datasource } private func addSubviews() { self.addSubview(self.backgroundView) self.backgroundView.addSubview(self.valueLabel) self.backgroundView.addSubview(self.increaseButton) self.backgroundView.addSubview(self.decreaseButton) self.backgroundView.addSubview(self.activityIndicatorView) } private func setLayout() { self.backgroundView.snp.makeConstraints { $0.top.left.bottom.right.equalTo(safeAreaLayoutGuide) } self.valueLabel.snp.makeConstraints { $0.center.equalToSuperview() $0.width.height.equalTo(45) } self.increaseButton.snp.makeConstraints { $0.centerY.equalToSuperview() $0.trailing.equalTo(-30) $0.width.height.equalTo(45) } self.decreaseButton.snp.makeConstraints { $0.centerY.equalToSuperview() $0.leading.equalTo(30) $0.width.height.equalTo(45) } self.activityIndicatorView.snp.makeConstraints { $0.centerX.equalToSuperview() $0.top.equalTo(valueLabel).offset(30) $0.width.height.equalTo(45) } } }
이걸 보여지게 하려면 뷰 컨트롤러에서 이 뷰를 불러와야 한다.
ViewController의 내용을 다음과 같이 바꾼다.
// // ViewController.swift // Counter-Prac // // Created by 정종인 on 2020/06/05. // Copyright © 2020 swmaestro10th. All rights reserved. // import UIKit class ViewController: UIViewController { private var counterView = CounterView() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. counterView.setup() } override func loadView() { self.view = counterView } }
커밋 추가 (message : view 구성)
이제 본격적으로 ReactorKit을 활용해본다.
CounterViewReactor.swift파일을 하나 만들고, Reactor 프로토콜을 추가해본다.
Action, Mutation, State, mutate(), reduce()를 구현한다.
코드 :
// // CounterViewReactor.swift // Counter-Prac // // Created by 정종인 on 2020/06/06. // Copyright © 2020 swmaestro10th. All rights reserved. // import Foundation import ReactorKit class CounterViewReactor: Reactor { enum Action { case increase case decrease } enum Mutation { case increaseValue case decreaseValue case isLoading(Bool) } struct State { var value: Int var isLoading: Bool } var initialState: State init() { self.initialState = State(value: 0, isLoading: false) } func mutate(action: Action) -> Observable<Mutation> { switch action { case .increase: return Observable.concat([ Observable.just(Mutation.isLoading(true)), Observable.just(Mutation.increaseValue), Observable.just(Mutation.isLoading(false)) ]) case .decrease: return Observable.concat([ Observable.just(Mutation.isLoading(true)), Observable.just(Mutation.decreaseValue), Observable.just(Mutation.isLoading(false)) ]) } } func reduce(state: State, mutation: Mutation) -> State { var state = state switch mutation { case .increaseValue: state.value += 1 case .decreaseValue: state.value -= 1 case .isLoading(let isLoading): state.isLoading = isLoading } return state } }
커밋 추가(message : reactor 추가)
이제 View 부분을 작성해야 한다. 다시 CounterView로 와서, disposeBag 변수를 하나 추가 해주고, View 프로토콜을 추가한 코드를 extension에 짜준다.
// // CounterView.swift // Counter-Prac // // Created by 정종인 on 2020/06/06. // Copyright © 2020 swmaestro10th. All rights reserved. // import Foundation import UIKit import SnapKit import ReactorKit import RxCocoa class CounterView: UIView { weak var vc: CounterViewController? init(controlBy viewController: CounterViewController) { self.vc = viewController super.init(frame: UIScreen.main.bounds) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private let baseView: UIView = { let v = UIView() v.backgroundColor = .white return v }() private let backgroundView: UIView = { let v = UIView() v.backgroundColor = .white return v }() private let valueLabel: UILabel = { let v = UILabel() v.text = "0" v.textAlignment = .center return v }() private let increaseButton: UIButton = { let v = UIButton(type: .roundedRect) v.setTitle("+", for: .normal) return v }() private let decreaseButton: UIButton = { let v = UIButton(type: .roundedRect) v.setTitle("-", for: .normal) return v }() private let activityIndicatorView: UIActivityIndicatorView = { let v = UIActivityIndicatorView(style: .medium) return v }() var disposeBag: DisposeBag = DisposeBag() func setup() { setupUI() setBind() } } extension CounterView { private func setupUI() { addSubviews() setLayout() } private func setBind() { //delegate, datasource } private func addSubviews() { self.addSubview(self.baseView) self.baseView.addSubview(self.backgroundView) self.backgroundView.addSubview(self.valueLabel) self.backgroundView.addSubview(self.increaseButton) self.backgroundView.addSubview(self.decreaseButton) self.backgroundView.addSubview(self.activityIndicatorView) } private func setLayout() { self.baseView.snp.makeConstraints { $0.top.left.bottom.right.equalToSuperview() } self.backgroundView.snp.makeConstraints { $0.top.left.bottom.right.equalTo(safeAreaLayoutGuide) } self.valueLabel.snp.makeConstraints { $0.center.equalToSuperview() $0.width.height.equalTo(45) } self.increaseButton.snp.makeConstraints { $0.centerY.equalToSuperview() $0.trailing.equalTo(-30) $0.width.height.equalTo(45) } self.decreaseButton.snp.makeConstraints { $0.centerY.equalToSuperview() $0.leading.equalTo(30) $0.width.height.equalTo(45) } self.activityIndicatorView.snp.makeConstraints { $0.centerX.equalToSuperview() $0.top.equalTo(valueLabel).offset(30) $0.width.height.equalTo(45) } } } extension CounterView: View { func bind(reactor: CounterViewReactor) { increaseButton.rx.tap .map { Reactor.Action.increase } .bind(to: reactor.action) .disposed(by: disposeBag) decreaseButton.rx.tap .map { Reactor.Action.decrease } .bind(to: reactor.action) .disposed(by: disposeBag) reactor.state .map { $0.value } .distinctUntilChanged() .map { "\($0)" } .bind(to: valueLabel.rx.text) .disposed(by: disposeBag) reactor.state .map { $0.isLoading } .distinctUntilChanged() .bind(to: activityIndicatorView.rx.isAnimating) .disposed(by: disposeBag) } }
새로운 CounterViewController.swift를 작성하고, view를 연결하고, counterView의 reactor도 정의를 해준다.
// // CounterViewController.swift // Counter-Prac // // Created by 정종인 on 2020/06/07. // Copyright © 2020 swmaestro10th. All rights reserved. // import Foundation import UIKit class CounterViewController: UIViewController { private lazy var counterView = CounterView(controlBy: self) override func viewDidLoad() { super.viewDidLoad() counterView.setup() counterView.reactor = CounterViewReactor() } override func loadView() { self.view = counterView } }
그 다음, SceneDelegate에 ViewController()부분도 CounterViewcontroller부분으로 바꿔주어야 한다.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(frame: UIScreen.main.bounds) window?.windowScene = windowScene window?.rootViewController = CounterViewController() window?.makeKeyAndVisible() }
그밖에도 delay추가 등 세부적인 코드 추가 후 빌드.
커밋 추가(message: finish)
https://github.com/chongin12/Counter-Prac
chongin12/Counter-Prac
ReactorKit Example Counter Practice. Contribute to chongin12/Counter-Prac development by creating an account on GitHub.
github.com
반응형'iOS Dev > ReactorKit' 카테고리의 다른 글
[ReactorKit#5] Example : RxTodo 구조 분석[미완] (2) 2020.06.21 [ReactorKit#4] Example : GithubSearch 코드작성 (스토리보드 없이) (0) 2020.06.07 [ReactorKit#3] Example : GithubSearch 구조 분석 (0) 2020.06.07 [ReactorKit#1] Example : Counter 구조 분석 (0) 2020.06.06 [ReactorKit#0] ReactorKit 시작하기 (2) 2020.06.05