-
ReactorKit 으로 로그인 페이지 만들기Swift/RxSwift 2022. 10. 14. 01:58
Login Controller
// // ViewController.swift // ReactorPractice // // Created by Mac mini on 2022/10/13. // import Foundation import UIKit import RxSwift import RxCocoa import Then import ReactorKit import RxViewController import RxGesture import SnapKit class ViewController: UIViewController, View { internal var disposeBag = DisposeBag() private let behaviorRelay = BehaviorRelay<Void>(value: ()) override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white setupLayout() } init(reactor: LoginReactor) { super.init(nibName: nil, bundle: nil) self.reactor = reactor } func bind(reactor: LoginReactor) { // MARK: - View -> Reactor // initialize state when viewWillAppear called self.rx.viewWillAppear .map { _ in Reactor.Action.initialize } .bind(to: reactor.action) .disposed(by: disposeBag) emailTF.rx.text.orEmpty .map { Reactor.Action.typeEmail($0)} .bind(to: reactor.action) .disposed(by: disposeBag) passwordTF.rx.text.orEmpty .map { Reactor.Action.typePassword($0)} .bind(to: reactor.action) .disposed(by: disposeBag) loginBtn.rx.tap .map { Reactor.Action.login } .bind(to: reactor.action) .disposed(by: disposeBag) // MARK: - Reactor -> View reactor.state.map { $0.email } .distinctUntilChanged() .bind(to: emailTF.rx.text ) .disposed(by: disposeBag) reactor.state.map { $0.password } .distinctUntilChanged() .bind(to: passwordTF.rx.text ) .disposed(by: disposeBag) // spinner reactor.state.map { $0.isSpinnerRunning } .distinctUntilChanged() .subscribe(onNext: { [weak self] shouldRun in guard let self = self else { return } shouldRun ? self.spinner.startAnimating() : self.spinner.stopAnimating() }) .disposed(by: disposeBag) // login reactor.state.map { $0.shouldLogin } .distinctUntilChanged() .subscribe(onNext: { [weak self] shouldLogin in guard let self = self else { return } if shouldLogin { self.moveToNextPage() } }) .disposed(by: disposeBag) // hide keyboard when empty view tapped self.view.rx.tapGesture() .subscribe(onNext: { [weak self] _ in self?.hideKeyboard() }) .disposed(by: disposeBag) } private func hideKeyboard() { self.view.endEditing(true) } private func setupLayout() { [ titleLabel, emailTF, passwordTF, loginBtn, spinner ].forEach { self.view.addSubview($0)} titleLabel.snp.makeConstraints { make in make.top.equalToSuperview().offset(200) make.centerX.equalToSuperview() make.width.equalToSuperview() make.height.equalTo(50) } emailTF.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(36) make.leading.equalToSuperview().inset(48) make.trailing.equalToSuperview().inset(48) make.height.equalTo(56) } passwordTF.snp.makeConstraints { make in make.top.equalTo(emailTF.snp.bottom).offset(24) make.leading.equalToSuperview().inset(48) make.trailing.equalToSuperview().inset(48) make.height.equalTo(56) } loginBtn.snp.makeConstraints { make in make.bottom.equalToSuperview().inset(70) make.height.equalTo(56) make.leading.equalToSuperview().inset(48) make.trailing.equalToSuperview().inset(48) } spinner.transform = CGAffineTransform(scaleX: 2, y: 2) spinner.snp.makeConstraints { make in make.center.equalToSuperview() make.width.height.equalTo(100) } } private func moveToNextPage() { let secondController = SecondViewController() navigationController?.pushViewController(secondController, animated: true) } private let titleLabel = UILabel().then { $0.textColor = .black $0.text = "Login Page" $0.textAlignment = .center $0.font = UIFont.systemFont(ofSize: 20) $0.numberOfLines = 2 } private let emailTF = UITextField().then { $0.placeholder = "E-mail" $0.backgroundColor = .gray $0.keyboardType = .emailAddress $0.autocapitalizationType = .none $0.autocorrectionType = .no } private let passwordTF = UITextField().then { $0.placeholder = "Password" $0.backgroundColor = .gray $0.isSecureTextEntry = true } private let loginBtn = UIButton().then { $0.setTitle("Login", for: .normal) $0.setTitleColor(.white, for: .normal) $0.backgroundColor = .red $0.layer.cornerRadius = 8 } var spinner = UIActivityIndicatorView().then { $0.hidesWhenStopped = true $0.color = .magenta } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
LoginReactor
// // LoginReactor.swift // ReactorPractice // // Created by Mac mini on 2022/10/13. // import Foundation import RxCocoa import RxSwift import ReactorKit // TODO: make UserService. class LoginReactor: Reactor { private let tempId = "id@gmail.com" private let tempPassword = "password!" public enum Action { case login case typeEmail(String) case typePassword(String) case initialize } public enum Mutation { case login case updateEmail(String) case updatePassword(String) case shouldShowSpinner(Bool) case shouldInitialize } public struct State { var email: String var password: String var isSpinnerRunning: Bool var shouldLogin: Bool var errorMsg: String init() { self.email = "" self.password = "" self.isSpinnerRunning = false self.shouldLogin = false self.errorMsg = "" } } public var initialState: State = State() func mutate(action: Action) -> Observable<Mutation> { switch action { case .login: return Observable.concat([ Observable.just(Mutation.shouldShowSpinner(true)), // delay 2 sec to show spinner clearly Observable.just(Mutation.login).delay(RxTimeInterval.seconds(2), scheduler: MainScheduler.instance), Observable.just(Mutation.shouldShowSpinner(false)) ]) case .typeEmail(let email): return .just(.updateEmail(email)) case .typePassword(let password): return .just(.updatePassword(password)) case .initialize: return .just(.shouldInitialize) } } func reduce(state: State, mutation: Mutation) -> State { var state = state switch mutation { case .login: if self.tempId == state.email && self.tempPassword == state.password { state.shouldLogin = true } case .updateEmail(let email): state.email = email case .updatePassword(let password): state.password = password case .shouldShowSpinner(let shouldShow): state.isSpinnerRunning = shouldShow if shouldShow == false { state.shouldLogin = false } case .shouldInitialize: state = State() } return state } deinit { print("loginReactor deinit") } public init() { print("loginReactor initialized.") } }
'Swift > RxSwift' 카테고리의 다른 글
ReactorKit 에서 API 활용하기 (0) 2022.10.15 RxAlamofire (0) 2022.10.03 RxSwift Observable, Subjects and Relays (0) 2022.10.01 UITableView (with RxSwift) (2) 2022.09.30 Login Validation (simple..) RxSwift 구현 (0) 2022.09.29