Notice
Recent Posts
Recent Comments
Link
ยซ   2024/05   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
๊ด€๋ฆฌ ๋ฉ”๋‰ด

lgvv98

[ReactorKit] ReactorKit ๊ณต๋ถ€ํ•˜๊ธฐ #1 ๋ณธ๋ฌธ

apple/๐Ÿฆ• UIKit & ReactiveX

[ReactorKit] ReactorKit ๊ณต๋ถ€ํ•˜๊ธฐ #1

๐Ÿฅ• ์บ๋Ÿฟ๋งจ 2022. 7. 24. 02:39

ReactorKit ๊ณต๋ถ€ํ•˜๊ธฐ #1

 

๐Ÿ“Œ ํ•ด๋‹น ๋ฌธ์„œ๋Š” ReactorKit 3.2.0์„ ๊ธฐ์ค€์œผ๋กœ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. 

 

โœ… ์˜ค๋Š˜์€ ReactorKit์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์Œ,, ์‚ฌ์ „๊ณผ์ œ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด์„œ ์ •๋ง ์˜ค!๋žœ!๋งŒ!์—! RxSwfit๋ฅผ ๋‹ค์‹œ ์‚ฌ์šฉํ–ˆ์—ˆ๋Š”๋ฐ, ๊ธฐ๋ณธ๊ธฐ์— ๋” ์ง‘์ค‘ํ–ˆ๋˜ ๊ฒƒ ๋•Œ๋ฌธ์ธ์ง€ ์˜ค๋žœ๋งŒ์— ์‚ฌ์šฉํ–ˆ์–ด๋„ ์—ฌ๋ ต๊ฒŒ ๋Š๋ผ์ง€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‹ค๋งŒ ์กฐ๊ธˆ ์•„์‰ฌ์› ๋˜ ๋ถ€๋ถ„์ด๋ผ๋ฉด, ํ•™์Šตํ•œ ๋””์ž์ธ ํŒจํ„ด์„ SwiftUI ๊ธฐ๋ฐ˜์˜ ํ”„๋กœ์ ํŠธ์—๋งŒ ์ ์šฉํ•˜๋‹ค ๋‹น์žฅ ์ž˜ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ์ตœ์ ์˜ ๊ตฌ์กฐ๊ฐ€ ์–ด๋–ค ๊ฒƒ์ด์ง€ ๊ณ ๋ฏผํ•˜๋Š” ์‹œ๊ฐ„์ด ๋งŽ์•„์„œ ๊ฐœ๋ฐœ์ด ์กฐ๊ธˆ ์ง€์ฒด๋˜์—ˆ๋‹ค๋Š” ์ ,,, 

RxSwfit๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ReactorKit๋„ ์•Œ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์„œ ๋„์ „ ๊ณ ๊ณ !

 

โœ… ReactorKit ๊นƒํ—ˆ๋ธŒ ๋ฌธ์„œ

https://github.com/ReactorKit/ReactorKit

 

GitHub - ReactorKit/ReactorKit: A library for reactive and unidirectional Swift applications

A library for reactive and unidirectional Swift applications - GitHub - ReactorKit/ReactorKit: A library for reactive and unidirectional Swift applications

github.com

 

โœ… Basic Concept

 

ReactorKit ์„ค๋ช…!

 

๊ทธ๋Ÿฌ๋‹ˆ๊นŒ ReactorKit์€ ๋ฐ˜์‘์ ์ด๊ณ  ๋‹จ๋ฐฉํ–‘์ ์ธ Swift ์•„ํ‚คํ…์ฒ˜๋ฅผ ์œ„ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ!

 

 

โœ… ReactorKit์˜ ๊ธฐ๋Šฅ ๊ฐœ์š”์™€ ์ž‘์„ฑ ์ด์œ ๊ฐ€ ๊ถ๊ธˆํ•˜๋‹ค๋ฉด

https://www.slideshare.net/devxoul/hello-reactorkit

 

Hello, ReactorKit ๏‘‹

Hello, ReactorKit! ๐Ÿ‘‹ Suyeol Jeon https://github.com/devxoul

www.slideshare.net

 

ReactorKit์€ Flux์™€ Reactive Programming์˜ ์กฐํ•ฉ์ด๋‹ค! user์˜ Action๊ณผ View์˜ ์ƒํƒœ๋Š” observable streams์„ ํ†ตํ•ด ๊ฐ๊ฐ์˜ ๋ ˆ์ด์–ด์— ์ „๋‹ฌ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ์ŠคํŠธ๋ฆผ์€ ๋‹จ๋ฐฉํ–ฅ ์ŠคํŠธ๋ฆผ์ด๊ณ , ๋ทฐ๋Š” ์˜ค๋กœ์ง€ emit action๋งŒ ๋ฐฉ์ถœํ•  ์ˆ˜ ์žˆ๊ณ  reactor๋Š” state๋งŒ ๋ฐฉ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑ๋œ๋‹ค!

 

 

โœ… ์˜ˆ์‹œ ์ฝ”๋“œ๋ฅผ ๋ด…์‹œ๋‹ค!

 

1. Reactor๋ฅผ ๋จผ์ € ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

//
//  CounterViewReactor.swift
//  ReactorKitPractice
//
//  Created by Hamlit Jason on 2022/07/23.
//

import Foundation
import RxSwift
import RxCocoa
import ReactorKit

class CounterViewReactor: Reactor {
    /// ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    let initialState = State()
    
    /// ์‚ฌ์šฉ์ž ํ–‰๋™์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    ///
    /// ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฐ›์„ ์•ก์…˜
    enum Action {
        case increase
        case decrease
    }
    
    /// ์ฒ˜๋ฆฌ ๋‹จ์œ„๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    ///
    /// ์•ก์…˜์„ ๋ฐ›์•˜์„ ๋•Œ ๋ณ€ํ™”
    enum Mutation {
        case increaseValue
        case decreaseValue
        case setLoading(Bool)
    }
    
    /// ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.
    ///
    /// ์–ด๋– ํ•œ ๋ณ€ํ™”๋ฅผ ๋ฐ›์€ ์ƒํƒœ!
    struct State {
        var value = 0
        var isLoading = false
    }
    
    /// Action์ด ๋“ค์–ด์˜จ ๊ฒฝ์šฐ ์–ด๋–ค ์ฒ˜๋ฆฌ๋ฅผ ํ•  ๊ฒƒ์ธ์ง€ ๋ถ„๊ธฐ
    ///
    /// Mutation์—์„œ ์ •์˜ํ•œ ์ž‘์—… ๋‹จ์œ„๋“ค์„ ์‚ฌ์šฉํ•˜์—ฌ Observable๋กœ ๋ฐฉ์ถœ
    ///
    /// ์•ก์…˜์— ๋งž๊ฒŒ ํ–‰๋™ํ•ด!
    func mutate(action: Action) -> Observable<Mutation> {
        switch action {
        case .increase:
            return Observable.concat([ // concat์€ ํ‰๋“ฑํ•˜๊ฒŒ ๋จผ์ € ๋“ค์–ด์˜จ ์˜ต์ €๋ฒ„๋ธ”์„ ์ˆœ์„œ๋Œ€๋กœ ๋ฐฉ์ถœ
                Observable.just(.setLoading(true)),
                Observable.just(.increaseValue).delay(.seconds(1), scheduler: MainScheduler.instance),
                Observable.just(.setLoading(false))
            ])
        case .decrease:
            return Observable.concat([
                Observable.just(.setLoading(true)),
                Observable.just(.decreaseValue).delay(.seconds(1), scheduler: MainScheduler.instance),
                Observable.just(.setLoading(false))
            ])
        }
    }
    
    /// ์ด์ „ ์ƒํƒœ์™€ ์ฒ˜๋ฆฌ ๋‹จ์œ„๋ฅผ ๋ฐ›์•„์„œ ๋‹ค์Œ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
    ///
    /// mutate(action: )์ด ์‹คํ–‰๋˜๊ณ  ๋‚œ ํ›„ ๋ฐ”๋กœ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰
    ///
    /// ๋ณ€ํ™”์— ๋งž๊ฒŒ๋” ๊ฐ’์„ ์„ค์ •ํ•ด!
    func reduce(state: State, mutation: Mutation) -> State {
        var newState = state
        switch mutation {
        case .increaseValue:
            newState.value += 1
        case .decreaseValue:
            newState.value -= 1
        case .setLoading(let isLoading):
            newState.isLoading = isLoading
        }
        return newState
    }
}

 

 

2. CounterViewController๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

//
//  CounterViewController.swift
//  ReactorKitPractice
//
//  Created by Hamlit Jason on 2022/07/23.
//

import UIKit
import RxSwift
import RxCocoa
import ReactorKit
import SnapKit

class CounterViewController: UIViewController, View {
    
    var disposeBag = DisposeBag()
    let counterViewReactor = CounterViewReactor()
    
    lazy var increaseButton = UIButton()
    lazy var countLabel = UILabel()
    lazy var decreaseButton = UIButton()
    lazy var loadingIndicator = UIActivityIndicatorView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupView()
        bind(reactor: counterViewReactor)
    }
    
    func bind(reactor: CounterViewReactor) {
        bindAction(reactor: reactor)
        bindState(reactor: reactor)
    }
    
    private func bindAction(reactor: CounterViewReactor) {
        increaseButton.rx.tap
            .map { CounterViewReactor.Action.increase }
            .bind(to: reactor.action)
            .disposed(by: disposeBag)
        
        decreaseButton.rx.tap
            .map { CounterViewReactor.Action.decrease }
            .bind(to: reactor.action)
            .disposed(by: disposeBag)
    }
    
    private func bindState(reactor: CounterViewReactor) {
        reactor.state
            .map { "\($0.value)" }
            .distinctUntilChanged()
            .bind(to: countLabel.rx.text)
            .disposed(by: disposeBag)
        
        reactor.state
            .map { $0.isLoading }
            .distinctUntilChanged()
            .bind(to: loadingIndicator.rx.isAnimating)
            .disposed(by: disposeBag)
        
        reactor.state
            .map { !$0.isLoading }
            .distinctUntilChanged()
            .bind(to: loadingIndicator.rx.isHidden)
            .disposed(by: disposeBag)
    }
}

extension CounterViewController {
    var margin: CGFloat {
        get { return 10.0 }
    }
    
    func setupView() {
        view.addSubview(increaseButton)
        increaseButton.setImage(UIImage(systemName: "plus"), for: .normal)
        
        increaseButton.snp.makeConstraints {
            $0.width.height.equalTo(30)
            $0.centerY.equalToSuperview()
            $0.leading.equalTo(margin)
        }
        
        view.addSubview(countLabel)
        countLabel.text = "0"
        
        countLabel.snp.makeConstraints {
            $0.center.equalToSuperview()
        }
        
        view.addSubview(decreaseButton)
        decreaseButton.setImage(UIImage(systemName: "minus"), for: .normal)
        
        decreaseButton.snp.makeConstraints {
            $0.width.height.equalTo(30)
            $0.centerY.equalToSuperview()
            $0.trailing.equalTo(-margin)
        }
        
        view.addSubview(loadingIndicator)
        
        loadingIndicator.snp.makeConstraints {
            $0.centerX.equalToSuperview()
            $0.centerY.equalToSuperview().offset(100)
        }
    }
}

typealias Reactor๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

ํ•ด๋‹น ์ฝ”๋“œ ํŒŒ์ผ์€ ์•„๋ž˜ ์ฃผ์†Œ์˜ ReactorKit ํŒŒํŠธ์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

https://github.com/lgvv/DesignPattern

 

GitHub - lgvv/DesignPattern: โœจ ๋””์ž์ธ ํŒจํ„ด์„ ๊ณต๋ถ€ํ•ฉ๋‹ˆ๋‹ค!

โœจ ๋””์ž์ธ ํŒจํ„ด์„ ๊ณต๋ถ€ํ•ฉ๋‹ˆ๋‹ค! Contribute to lgvv/DesignPattern development by creating an account on GitHub.

github.com

 

Comments