[RxSwift+MVVM] 18. Segmented control 로 네트워크 통신시, 쿼리 전달

miori·2022년 5월 10일
0

RxSwiftinProject

목록 보기
2/2

꾸준히 가야돼! "Rx를 기깔나게 쓰는 신입개발자 도전" 🚀

구현 명세

  • 책 검색을 할수 있어야 한다. (책이름으로)
  • 작가 이름을 검색시에는 작가가 출판한 책이 결과물로 나와야 한다.

피그마 파일

우선, 피그마로 대략적으로 그려본 뷰이다. (물론 디자이너분이랑 비교가 안되지만, 혼자 작업할때 피그마로 대략적으로 만들 뷰를 그려보면 구조가 잡혀, 시간을 투자하여 피그마로 그려보는 편이다)

  • 구현 명세를 따르면, 예를 들어 "문석기" 를 검색하면 아래 와 같은 검색 결과가 나오면 된다.
    • 책 이름에 "문석기" 라는 워딩이 포함된 책 (없으면 alert)
    • "문석기"라는 이름의 작가가 출판한 책

  • 하지만 좀 더 빠른 검색결과와 정확한 검색 결과를 제공하기 위해 책/작가 를 선택할 수 있는 segmented control 을 추가해 주기로 하였다.

API

활용한 API

문제점

이 API를 postman으로 테스트 해본 결과, 이상한 점을 발견하였다.
"파이썬" 이라는 키워드로 검색을 하였을 때 startIndex 쿼리가 바뀔때마다 totalItems 수가 랜덤하게 계속 변했다.

이렇게 되면 추후에 무한 스크롤을 구현할때, 어려움이 발생할거 같았고 해결방안으로 검색 쿼리를 추가해주었다.
실제로, 공식문서를 참고해보면 검색어 쿼리를 넘겨줄때, 특정 필드를 추가할 수 있다고 안내하고 있고, 이에 따라 "intitle:", 과 "inauthor:" 필드를 추가해보았다.

그 결과, totalItems 수가 변하는 이슈가 해결이 되었다.

깃허브 이슈링크 에 자세히 적어두었다.

Code

mvvm 패턴에 따라 적용하였다.

view

- segmented control

class BookTableViewHeaderView : UITableViewHeaderFooterView {
    
    let totalCountLabel = UILabel()
    let bottomBorder = UIView()
    let searchTargetSegment = UISegmentedControl(items: ["책","작가"])
    
    let disposeBag = DisposeBag()
    
    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)
        
        setAttribute()
        setLayout()
    }
 }

위으 코드와 같이 UITableViewHeaderFooterView 에 UISegmentedControl을 추가해 주었다.

view model

struct BookTableHeaderViewModel {
    
    let segmentIndex = BehaviorRelay<Int>(value: 0)
    let selectedIndex : Observable<Int>
    
    init() {
        self.selectedIndex = segmentIndex
            .asObservable()
    }
}
  • Relay 객체는 .completed, .error를 발생하지 않고 Dispose되기 전까지 계속 작동하기 때문에 UI 등에서 사용하기 적절하다.
  • 따라서 초기값을 가지는 BehaviorRelay를 사용하였다.
  • 그리고 segmented control로 부터 받은 값을 내보내기 위해 selectedIndex 를 선언해주었다.

view 에서 binding

다시 view 로 돌아와, view 와 view model 을 바인딩 해주었다.

    func bind(_ viewModel : BookTableHeaderViewModel) {

        self.searchTargetSegment.rx.selectedSegmentIndex
            .bind(to: viewModel.segmentIndex)
            .disposed(by: disposeBag)
        
    }
  • segmented control 값이 변경될때마다 바인딩이 되게끔 구현하였다.

main view model

  • 이제 메인 뷰 컨트롤러와 관련된 뷰모델이다.
  • 여기서는 네트워크 통신을 진행하여, 데이터를 원하는 형식으로 바꾸는 과정을 진행한다.

우선, 네트워크 관련 부분이다.

- 네트워킹

// 검색어와, 검색타겟(책/작가)  + startIndex 값을 받아, convertSearchQuery 를 통해 SearchQuery 형태로

let searchQueryStruct = Observable
    .combineLatest(searchBarViewModel.shouldLoadResult, bookTableHeaderViewModel.selectedIndex, bookTableViewModel.fetchStratIndex, resultSelector: model.convertSearchQuery)
  • 서치바에 입력된 값 : searchBarViewModel.shouldLoadResult
  • segmented Control 값 : bookTableHeaderViewModel.selectedIndex
  • 무한 스크롤을 위한 값 : bookTableViewModel.fetchStratIndex

3가지 요소를 combineLatest 연산자를 활용해 model.convertSearchQuery 를 통해 원하는 struct 로 변하게끔 구현하였다.

  • model.convertSearchQuery 는 위의 3개의 요소를 받아 하나의 struct 로 변환해주는 함수 이다.
let searchBookResult = searchQueryStruct
    .flatMapLatest(model.searchBooks)
    .share()
  • 그 결과 얻어진 struct를 이전 observable을 무시하게 되는 flatmaplatest 연산자를 활용해 네트워크 통신을 진행하게끔 구현하였다.
  • model.searchBooks 는 api통신을 가능하게 해주는 함수이다.

결과 영상


전체코드는 깃허브 에서 확인 가능합니다.

profile
iS를 공부하는 miori 입니다.

0개의 댓글