RxSwift 실험

Fastcampus RxSwift 강좌를 통해 만난, Rx Swift로 구현해보는 작은 실험들 👀

RGB Slider 만들기

✔️ 1차 실험

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {

    @IBOutlet weak var colorView: UIView!
    @IBOutlet weak var redSlider: UISlider!
    @IBOutlet weak var greenSlider: UISlider!
    @IBOutlet weak var blueSlider: UISlider!
    @IBOutlet weak var redValueLabel: UILabel!
    @IBOutlet weak var greenValueLabel: UILabel!
    @IBOutlet weak var blueValueLabel: UILabel!
    
    var disposeBag: DisposeBag = DisposeBag()
    
    var redValue: CGFloat = 0.5
    var greenValue: CGFloat = 0.5
    var blueValue: CGFloat = 0.5
    
    override func viewDidLoad() {
        super.viewDidLoad()
        colorView.backgroundColor = UIColor(red: redValue, green: greenValue, blue: blueValue, alpha: 1)
        bind()
    }

}

extension ViewController {
   
    func bind() {
        
        redSlider.rx.value.subscribe(onNext: { value in
            self.redValueLabel.text = "\(value)"
            self.redValue = CGFloat(value)
            self.setColor(r: CGFloat(value), g: self.greenValue, b: self.blueValue)
        })
        .disposed(by: disposeBag)
        
        greenSlider.rx.value.subscribe(onNext: { value in
            self.greenValueLabel.text = "\(value)"
            self.greenValue = CGFloat(value)
            self.setColor(r: self.redValue, g: CGFloat(value), b: self.blueValue)
        }).disposed(by: disposeBag)
        
        blueSlider.rx.value.subscribe(onNext: { value in
            self.blueValueLabel.text = "\(value)"
            self.blueValue = CGFloat(value)
            self.setColor(r: self.redValue, g: self.greenValue, b: CGFloat(value))
        }).disposed(by: disposeBag)
        
    }
    
    func setColor(r: CGFloat, g: CGFloat, b: CGFloat) {
        colorView.backgroundColor = UIColor(red: r, green: g, blue: b, alpha: 1)
    }
    
}

✔️ 2차 실험

// extension Reactive
extension Reactive where Base: UIView {
    var backgroundColor: Binder<UIColor> {
        return Binder(self.base) { view, color in
            view.backgroundColor = color
        }
    }
}
let color = Observable<UIColor>.combineLatest(redSlider.rx.value, greenSlider.rx.value, blueSlider.rx.value) { (r,g,b) -> UIColor in
    return UIColor(displayP3Red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: 1)
}
    
color.bind(to: colorView.rx.backgroundColor).disposed(by: disposeBag)

Rx 구구단 만들기

✔️ 나의 풀이 :: textField를 rx 로 바꿔서 For 문을 돌려 구구단을 만들고 label에 bind 시켰다.

func bind() {
    textField.rx.text.orEmpty.asObservable()
        .map({ (str) -> String in
            guard let number = Int(str) else { return "" }
            var result: String = ""
            for i in 1...9 {
                result += "✏️ \(i) × \(number) = \(i*number) \n"
            }
            return result
        })
        .bind(to: label.rx.text).disposed(by: disposeBag)
}

✔️ 다른 사람의 풀이 :: 배열을 만들어 map -> reduce -> bind

func bind2() {
    textField.rx.text.orEmpty.asObservable()
        .map({ (str) -> String in
            guard let number = Int(str) else { return "" }
            var result: String = ""
            let numArr = [1,2,3,4,5,6,7,8,9]
            result = numArr.map({ (num) -> String in
                return "\(number) * \(num) = \(number*num)"
            }).reduce("", {$0 + "\n" + $1})
            return result
        })
        .bind(to: label.rx.text).disposed(by: disposeBag)
}
    

✔️ 강사님의 풀이 :: Observable을 리턴하여 바인드

func bind3() {
    textField.rx.text.orEmpty.asObservable()
        .flatMap({(value) -> Observable<Int> in
            guard let result = Int(value) else { return Observable.empty() }
            return Observable.just(result)
        }).flatMap({(dan: Int) -> Observable<[String]> in
            Observable.from([1,2,3,4,5,6,7,8,9]).map({ (num) -> String in
                return "\(dan) * \(num) = \(dan*num)"
            }).toArray()
        }).map({ steps -> String in
            return steps.reduce("", { (prev: String, next: String) -> String in
                return prev + "\n" + next
            })
        })
        .bind(to: label.rx.text).disposed(by: disposeBag)
}
    




RxCocoa :: Binder

//
//  Binder.swift
//  RxCocoa
//
//  Created by Krunoslav Zaher on 9/17/17.
//  Copyright © 2017 Krunoslav Zaher. All rights reserved.
//

import RxSwift

/**
 Observer that enforces interface binding rules:
 * can't bind errors (in debug builds binding of errors causes `fatalError` in release builds errors are being logged)
 * ensures binding is performed on a specific scheduler

 `Binder` doesn't retain target and in case target is released, element isn't bound.
 
 By default it binds elements on main scheduler.
 */
public struct Binder<Value>: ObserverType {
    public typealias E = Value
    
    private let _binding: (Event<Value>) -> ()

    /// Initializes `Binder`
    ///
    /// - parameter target: Target object.
    /// - parameter scheduler: Scheduler used to bind the events.
    /// - parameter binding: Binding logic.
    public init<Target: AnyObject>(_ target: Target, scheduler: ImmediateSchedulerType = MainScheduler(), binding: @escaping (Target, Value) -> ()) {
        weak var weakTarget = target

        _binding = { event in
            switch event {
            case .next(let element):
                _ = scheduler.schedule(element) { element in
                    if let target = weakTarget {
                        binding(target, element)
                    }
                    return Disposables.create()
                }
            case .error(let error):
                bindingError(error)
            case .completed:
                break
            }
        }
    }

    /// Binds next element to owner view as described in `binding`.
    public func on(_ event: Event<Value>) {
        _binding(event)
    }

    /// Erases type of observer.
    ///
    /// - returns: type erased observer.
    public func asObserver() -> AnyObserver<Value> {
        return AnyObserver(eventHandler: on)
    }
}