Post

[Day 50] RxSwift와 SearchController를 활용한 검색기능

RxSwift와 Clean Architecture를 사용한 앱 개발 일기

[Day 50] RxSwift와 SearchController를 활용한 검색기능

😭 왜 데이터가 안넘어가지…?

오늘은 SearchController를 사용해서 검색기능을 구현하려고햇는데 검색결과 화면으로 검색어를 넘겨주려고 했는데 데이터가 나오지 않았다…

예를 들어, 홈 화면의 텍스트 필드에 "아이언맨"을 입력을 해도 searchBartext가 비어있었다… 데이터는 아래처럼 넘겨 줬다.

HomeView에서

1
2
3
4
5
6
let container = DIContainer()
lazy var searchResultsVC = container.makeSearchViewController(query: self.searchTextRelay)

lazy var searchController = UISearchController (searchResultsController: searchResultsVC).then {
  ...
}

이렇게 DIContainer를 사용해서 값을 가지고 데이터를 넘겨주고 텍스트 변화 감지는 UISearchBarUpdating를 사용해서 아래처럼 넘겨줬다

1
2
3
4
5
func updateSearchResults(for searchController: UISearchController) {
  searchController.searchBar.rx.text.orEmpty
    .bind(to: searchTextRelay)
    .disposed(by: disposeBag)
}

🐞 디버깅 과정

이제 SearchViewController로 가서

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private func bindViewModel() {
  queryRelay
    .map {
      .search($0)
    }
    .bind(to: viewModel.action)
    .disposed(by: disposeBag)

  viewModel.state
    .subscribe(onNext: { state in
      print("받은 데이터", state)
    })
    .disposed(by: disposeBag)
}

이렇게 해서 로그를 찍어봤는데… 결과는

1
2
받은 데이터 State(movieresults: [], podcastresults: [], searchResult: [iTunes_SearchApp.SearchResult(movies: [], podcasts: [])], errorMessage: nil)
-[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:]  perform input operation requires a valid sessionID. inputModality = Keyboard, inputOperation = <null selector>, customInfoType = UIEmojiSearchOperations

이렇게 로그가 찍혔다.

⁉️ 문제의 원인

문제는 데이터가 SearchControllertextSearchViewController로 넘어가지 않았다.

그래서 뭐가 문제일까…. 하고 고민을 했더니 넘겨주는 방식이 잘못됐던 것이다…

updateSearchResults로 데이터를 넘겨주면 안됐었다…..

나는 검색어가 바뀔때 마다 호출한다 라고 생각을하고 updateSearchResults를 사용해서 데이터를 넘겨줘야지 하고 사용을 했는데 이건 전혀 Rx스럽지 않은 거였다, 그 이유는 아래와 같다

updateSearchResults는 검색어가 바뀌면 호출되는 함수인데 여기서 바인딩을 걸어버리니까 검색어가 바뀔때 마다 계속해서 새로운 바인딩을 추가하는 셈이 되버려서 memory leak이나 unexpected behavior가 발생 해버린것이였다.. 그래서 데이터는 넘어가지 않았던것…

해결방법

그래서 아까 팀원들이랑 이야기를 나누면서 들었던 이야기가 있었다. 바인딩은 바인딩 함수에서만 한번만 걸어줘야한다고. 그 말이 생각이나서 바로 이렇게 옮겨줬다…

1
2
3
4
5
6
7
private func bindViewModel() {
  searchController.searchBar.rx.text.orEmpty
    .bind(to: searchTextRelay)
    .disposed(by: disposeBag)
  
 ...
}

이렇게 바인딩은 한번만 하게끔 바인딩 함수 안에 코드를 작성하였더니?!

1
2
전달받은 검색어: 스파이더맨
받은 데이터 State(movieresults: [iTunes_SearchApp.Movie(trackName: "Spider-Man: Far From Home", artistName: "Jon Watts", longDescription: "Peter Parker returns in Spider-Man: Far From Home, ...

데이터가 잘 넘어왔다!!!

사실 중간에 Entity명에 오타가 났어서 에러 한번 난건 비밀

맺으며: 춘식이와 함께하는 스마트한 개발

아직 RxSwift가 문법이 익숙하지 않아서 어떤곳에 코드를 작성을 해야하는지, 어떻게 작성을 해야하는지 감이 잘 안잡히지만 이렇게 오류도 내면서, 사용해보면서 하면 이해가 되기 시작하고있다.

이번에 Reactive Rrogramming을 배우면서 정말 너무 어렵고 힘들었는데, 확실히 사람마다 학습하는 방식이 다르겠지만, 조금씩 코드를 따라치면서라도 해보니까 조금씩 다시 자신감이 생기는거같다.

오늘도 오류 내며 자란 춘식이였습니다. 🐾 더 열심히 노력 해서 좋은 iOS Developer가 되자!

혹시 이 글을 보시는 분들 중 더 유용한 팁이 있다면 댓글로 많이 알려주세요!

This post is licensed under CC BY 4.0 by the author.