ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Rxswift operators - flatmap, flatmapLatest, merge
    개발/Swift 2022. 11. 11. 13:39

    참조 : http://adamborek.com/thinking-rxswift/

    Flatmap

    검색결과에 따라 API 호출후

    노래 리스트를 응답 받아 그려야하는 상황이다.

    Observable<String> 
    -> Observable<[Track]> 
    -> Observable<[TrackRenderable]>:
    

    그러면 이런 흐름으로 map operator를 사용하여Observable을 전달받아 처리하면 된다.

    그러나 검색어에 대한 bservable<String> 은 동기적이고

    Observable<[Track]> 은 API응답이므로 비동기적이다.

    flatmap을 사용해 해결할수있다.

    searchBar.rx.text.orEmpty.flatmap { query in
    	return API.fetch(query:query) // Observable<Resutl> 리턴 해야함
    }
    

    Observable 을 응답값으로 받는 방법은 여기나와있다

    http://adamborek.com/practical-introduction-rxswift/

    FlatmapLatest

    응답 딜레이에 따른 에러

    1. let 을 입력 → let 노래 리스트 요청
    2. let it go 입력 → let it go 노래 리스트 요청
    3. let it go 노래 리스트 응답
    4. let 노래 리스트 응답

    이런경우 원하는 결과 값은 let it go 리스트 이지만 let 리스트가 결과적으로 받아오게 된다.

    flatmaplatest를 사용하면 이전 구독을 모두 취소한다. 따라서 새 데이터가 오래된 응답에의해 대체될 수가 없게된다.

    searchBar.rx.text.orEmpty.flatmapLatest { query in
    	return API.fetch(query:query) // Observable<Resutl> 리턴 해야함
    }.map { tracks in
        return tracks.map(TrackRenderable.init)
    } //Observable<[TrackRenderable]>
    

    merge

    만약 텍스트가 입력될때는 기존 리스트를 지우고 다시 받아오려한다면 어떻게 해야할까

    기존에는 검색을 하면서 리스트가 수정 되겠지만 지우는 기능은 없다.

    비어있는 노래 리스트를 리턴하는 Observable을 만들어

    merge를 통해 합쳐주면 된다

    //텍스트가 수정될때
    let searchTextChanged = searchBar.rx.text.orEmpty.asObservable().skip(1)
    
    //기존 검색 로직
    let tracksFromSpotify = searchTextChanged
        .flatMapLatest { query in
            	return API.fetch(query:query) 
        }.map { tracks in
            return tracks.map(TrackRenderable.init)
    }
    //지우는 로직
    let clearTracksOnQueryChanged = searchTextChanged
        .map { _ in return [TrackRenderable]() }
    // 병합
    
    let tracks = Observable.of(tracksFromSpotify, clearTracksOnQueryChanged).merge()
    

    만약 Pullto Refresh를 여기다 구현한다 해보자.

    refresh를 요청하면 마지막 searchtext를 가지고 쿼리를 해야한다.

    let refreshControl = UIRefreshControl()
     tableView.addSubview(refreshControl)
    
    let didPullToRefresh: Observable<Void> =  refreshControl.rx.controlEvent(.valueChanged)
        .map { [refreshControl] in
            return refreshControl.isRefreshing
        }.filter { $0 == true }
        .map { _ in return () }
    
    //refresh 끝나면 refreshControl.endRefreshing()
    

    테이블뷰에 refresh control 을 넣어서 쉽게 사용할수있다.

    기존에는 searchText가 바뀌면 쿼리했지만 이제는 merge를 통해 refresh 호출시에도 쿼리할것이다

    여기서 Observable<String> (searchTextChanged) 의 마지막 값을 가져와야하는데

    let refreshLastQuery = didPullToRefresh
        .withLatestFrom(searchTextChanged)
    
    //didPullToRefresh: Observable<Void>
    //searchTextChanged: Observable<String> 
    

    이렇게 사용할수 있다.

    merge를 사용하면

    let tracksFromSpotify = Observable.of(searchTextChanged, refreshLastQuery).merge()
    		.flatMapLatest { query in
            	return API.fetch(query:query) 
        }.map { tracks in
            return tracks.map(TrackRenderable.init)
    		}.do(onNext: { [refreshControl] _ in
            refreshControl.endRefreshing()
        })
    

    이렇게 사용 가능하다.

    댓글

Designed by Tistory.