우리가 보통 rx를 사용할때 다양한 오퍼레이터로 이어준 스트림을 방출하고자할때 각각의 종류에 따라 subscribe, drive, emit 등등을 사용하게 된다
방출된 값에 따른 프로퍼티를 사용하고자할때 우리는 주로 2가지의 방법을 사용하게 된다
- guard let self = self~
- withUnretained(obj)
나는 위 방법중에서 2번을 주로 사용하였는데 subscribe(with:), drive(with:)와 같이 하나의 오퍼레이터로 단축시킬 수 있었다
또한 2번 방법을 사용할때에 Driver에는 withUnretained를 사용할 수 없어서 asObservable로 변환 이후 사용했었는데 아주 유용한 오퍼레이터같다
보통 서버를 이용해서 어떤 데이터를 내려보내야할때 파라미터로 page값을 받아서 한번에 받아오는 양을 조절하게 된다
프론트딴에서 이러한 페이징처리를 할때 스크롤뷰의 delegate를 이용하게 되는데 이때 가장 많이 사용되는 코드가 다음과 같다
homeCollectionView.rx.didScroll
.subscribe(with: self, onNext: { owner, _ in
let offSetY = owner.homeCollectionView.contentOffset.y
let contentHeight = owner.homeCollectionView.contentSize.height
if offSetY > (contentHeight - owner.homeCollectionView.frame.size.height) {
owner.viewModel.fetchMoreDatas.onNext(())
}
})
.disposed(by: disposeBag)
헌데 딱 이 코드만을 가지고 viewModel에 데이터를 요청하게되면 문제가 생긴다
그건 바로 끝까지 스크롤시에 offSetY값이 해당 조건문에 계속 걸려 무한정으로 fetchMoreDatas를 호출하게되는 문제점이다
didScroll대신에 didEndDragging을 이용하는 것이였다
위 코드에서 didScroll -> didEndDragging으로만 변경하면 스크롤하고 한번 떼었을때의 offsetY값만을 판단하여 내가 원하는 딱 한번 데이터를 요청하게 된다
-> 하지만 위 같은 방법엔 문제가 있다. 바로바로 스크롤하여 데이터를 요청하고싶은 클라이언트의 사용에 문제가 발생한다
-> 즉 didScroll을 통해서 바로바로 데이터를 요청해야하는건 맞지만 viewModel내부에서 특정 조건을 걸어줘야만 무한정 요청하는 부분을 막을 수 있다는 생각이 들었다
let inquireRecommendationMeeting = currentPage
.flatMapLatest { page in
if try !isLastPage.value() && !isLoading.value() { // 마지막 페이지가 아니고 로딩중이 아닐때
isLoading.onNext(true)
return MeetingService.shared.inquireRecommendationMeeting(page: page)
}
return .empty()
}
- 필터 혹은 분류타입 혹은 검색에 따른 요청을 할때 단 한번이라도 isLastPage값이 true로 변경되어지게되면 다시는 false로 변경되어지지않는 문제가 발생하였다
let requestSearch = Observable.combineLatest(searchKeyword, currentPage, searchFilterType, searchSortType)
.do(onNext: { _ in
isLastPage.onNext(false)
currentPage.onNext(1)
})
.flatMapLatest { (keyword, page, filterType, sortType) in
if try !isLastPage.value() && !isLoading.value() { // 마지막 페이지가 아니고 로딩중이 아닐때
isLoading.onNext(true)
return RecruitmentService.shared.searchRecruitment(searchParameter: .init(keyword: keyword, page: page, type: filterType.text, sort: sortType.text))
}
return .empty()
}
스냅킷을 이용하여 오토레이아웃을 주로 작성하는데 좌우에 대한 컨트롤을 할때 leading trailing값을 사용한다
leading trailing을 이용할 수도 있지만 directionalHorizontal을 이용하면 코드를 단축시킬 수 있다
interestListCollectionView.snp.makeConstraints {
$0.top.equalTo(headerView.snp.bottom)
$0.directionalHorizontalEdges.bottom.equalToSuperview()
}
homeCollectionView.snp.makeConstraints {
$0.edges.equalToSuperview().inset(16)
}
항상 해당 UI를 슈퍼뷰에 상하좌우 붙이고싶을때 위와 같은 코드를 많이 사용하였다
하지만 edges를 뜯어보면 top, left, right, bottom을 의미한다
즉 edges는 Localized를 위한 leading trailing을 사용하지않는다
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch selectedCategoryType {
case .chart:
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height / 4 - 6)
case .grid:
return CGSize(width: collectionView.frame.width / 2 - 6, height: collectionView.frame.height / 2.5)
}
}
위와 같은 코드는 아마 많은 분들이 사용해보셨을 코드일 것이다
하지만 이처럼 간단한 경우가 아닌 복잡한 경우에는 이야기가 달라진다
정수 / 정수 일때 몫이 나온다는 사실을 알고있었을 것이다
$0.height.equalTo(ceil(Double(model.count) / Double(4)) * (48 + 4 + 21) + (ceil(Double(model.count) / Double(4)) - 1) * 16 + Double(24 + 24))