티스토리 뷰

🍏/RxSwift

[RxSwift-Operator] Map과 FlatMap

eungding 2019. 4. 22. 15:31
반응형

Map vs FlatMap 

1) Map get value from stream and return another value of whatever type, result is Observable<whatever type>.

2) FlatMap get value from stream and return an Observable of whatever type.

 

Map

map은 이벤트를 바꾼다. E Type이벤트를 R Type이벤트로 바꾼다 

    public func map<R>(_ transform: @escaping (Self.E) throws -> R) -> RxSwift.Observable<R>

FlatMap

flatMap은 이벤트를 다른 observable로 바꾼다 

    public func flatMap<O: ObservableConvertibleType>(_ selector: @escaping (E) throws -> O)
        -> Observable<O.E>

 

 

map은 클로저에 연산 값을, flatMap은 Observable을 리턴시켜줘야한다

 

예를 들어 Observable<Int> 타입인 numberObservable이 있는데,

number값을 String으로 변환한 Observable<String>을 얻고 싶다면 map과 flatMap은 클로저 리턴 값을 다르게 구성해줘야한다  

let numberObservable = Observable<Int>.just(10)

let observableAfterMap = numberObservable.map { String($0) }
let observableAfterFlatMap = numberObservable.flatMap { Observable<String>.just(String($0)) }

 

하지만 보통 다음과 같은 상황에서 flatMap을 쓰지 않는다 

observable로 부터 emit되는 item을 다른 타입으로 바꾸고 싶을때 (observable이 아니라) map이 사용된다 

 

In general, When you have an item emitted from observable and you want to transform them into other type but not an observable then map is preferred. 

 

 

Map 자주 쓰이는 예제

1)

Float타입의 이벤트를 String타입의 이벤트로 바꿔줄 때 쓰이는 map 

map연산을 하면, Observable<Float>에서 Observable<String>으로 바뀐다 

        slider.rx.value.asObservable()
            .map { String($0) }
            .bind(to: label.rx.text)
            .disposed(by: disposeBag)

 

2)

textField가 키보드에서 Return을 누르고 들어갈때 그 textField의 값을 가져오는 경우

map연산으로 Observable<()>에서 Observable<String?>으로 바꿔준다 

 

        cityNameTextField.rx.controlEvent(.editingDidEndOnExit)
            .asObservable()
            .map {
                return self.cityNameTextField.text
            }.subscribe(onNext: { city in
                print(city)
            }).disposed(by: disposeBag)

 

3) 

 

서버 응답으로 받은 데이터를 PersonModel로 디코드해준 결과인 Observable<PersonModel> 

이 모델에 있는 age값을 label에 뿌려주고 싶은 경우 

Observable<PersonModel>.map { "\($0.age)" }
                 .bind(to: ageLabel.rx.text)
                 .disposed(by: disposeBag)

 

 

 

FlatMap 자주 쓰이는 예제 

buttonTapObservable을 API Call 한 결과인 answers를 가지고 있는 Observable<[String]>으로 바꿔줄 때 쓰이는 flatMap 

    let answers = Variable<[String]>.init([])
        
    button.rx.tap.asObservable().flatMap { _ -> Observable<[String]> in
            return fetchAllAnswers()
            }.subscribe(onNext: { newAnswers in
                answers.value = newAnswers
            }).disposed(by: disposeBag)
            
            
    func fetchAllAnswers() -> Observable<[String]> {
        let api = Observable<[String]>.create { observer in
            let answers = API.allAnswers()
            observer.onNext(answers)
            observer.onCompleted()
            return Disposables.create()
        }
        return api
    }

(참고: https://stackoverflow.com/questions/36838248/rxswift-how-to-recall-an-api)

 

 

Map과 FlatMap 같이 쓰이는 예제 

let url = URL(string: ....)

Observable.just(url)
    .flatMap { url -> Observable<Data> in
        let request = URLRequest(url: url)
        return URLSession.shared.rx.data(request: request)
    }.map { data -> [SomeModel]? in
        return try? JSONDecoder().decode(SomeModelList.self, from: data).models
    }.subscribe(onNext: { [weak self] models in
        if let models = models {
            self?.models = models
            DispatchQueue.main.async {
                self?.tableView.reloadData()
            }
        }
    }).disposed(by: disposeBag)
반응형
댓글