Skip to content

SavedAppDetailTableViewCell 데이터 바인딩 지연 개선  #24

@yeahg-dev

Description

@yeahg-dev

Before & After

개선 전 개선 후
개선전  개선후 low27

문제 상황 : Cell에 데이터 표시 지연

  • cellForRowAt에서 네트워크 및 렐름에서 CellModeld을 비동기적으로 받아오는 Publisher를 만든 후, cell에 bind해주고 스트림을 시작합니다.
  • cell이 디큐되기 직전에 비동기작업이 시작되므로, cell에 데이터를 표시하기까지 지연이 발생합니다.
extension AppFolderDetailViewModel: UITableViewDataSource {
    
    func tableView(
        _ tableView: UITableView,
        numberOfRowsInSection section: Int)
    -> Int
    {
        if let savedApps,
           savedApps.isEmpty {
            showEmptyView.send(true)
        } else {
            showEmptyView.send(false)
        }
        return savedApps?.count ?? 0
    }
    
    func tableView(
        _ tableView: UITableView,
        cellForRowAt indexPath: IndexPath)
    -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(
            withClass: SavedAppDetailTableViewCell.self,
            for: indexPath)
        guard let savedApp = savedApps?[safe: indexPath.row] else {
            return cell
        }
        
        // TODO: - Error Handling
        let cellModel = appFolderUsecase.readSavedAppDetail(of: savedApp)
            .map { savedAppDetail in
                return SavedAppDetailTableViewCellModel(savedAppDetail: savedAppDetail)
            }
            .assertNoFailure()
            .eraseToAnyPublisher()
        
        cell.bind(cellModel)
        return cell
    }

}

해결 방법 : UITableViewDataSourcePrefetching 사용

  • UITableViewDataSourcePrefetching를 채택하여 tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) 메서드내에서 [indexPath]에 해당하는 savedApp들의 cellModel을 다운로드하여 배열에 저장합니다.
extension AppFolderDetailViewModel: UITableViewDataSourcePrefetching {
    
    func tableView(
        _ tableView: UITableView,
        prefetchRowsAt indexPaths: [IndexPath])
    {
        guard let savedApps else {
            return
        }
        Publishers.Sequence<[IndexPath], Never>(sequence: indexPaths)
            .map({ indexPath in
                return (indexPath.row, savedApps[indexPath.row])
            })
            .flatMap { (index, savedApp) -> AnyPublisher<(Int, SavedAppDetail), Error> in
                return self.appFolderUsecase.readSavedAppDetail(of: savedApp, index: index)
            }
            .map{ savedAppDetail in
                return (savedAppDetail.0, SavedAppDetailTableViewCellModel(savedAppDetail: savedAppDetail.1))
            }
            .assertNoFailure()
            .sink { [unowned self] cellModel in
                self.fetchedCellModels[cellModel.0] = cellModel.1
            }.store(in: &cancellable)
    }

}
  • tableView( _ tableView: UITableView, cellForRowAt indexPath: IndexPath)에서 만약 prefetch한 cellModel이 있다면 해당 cellModel을 사용해 바인드하고, 없다면 스트림을 시작합니다.
extension AppFolderDetailViewModel: UITableViewDataSource {
    
    func tableView(
        _ tableView: UITableView,
        numberOfRowsInSection section: Int)
    -> Int
    {
        if let savedApps,
           savedApps.isEmpty {
            showEmptyView.send(true)
        } else {
            showEmptyView.send(false)
        }
        return savedApps?.count ?? 0
    }
    
    func tableView(
        _ tableView: UITableView,
        cellForRowAt indexPath: IndexPath)
    -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(
            withClass: SavedAppDetailTableViewCell.self,
            for: indexPath)
        guard let savedApp = savedApps?[safe: indexPath.row] else {
            return cell
        }
        
        if let cellModel = fetchedCellModels[indexPath.row] {
            cell.bind(cellModel)
        } else {
            let cellModel = appFolderUsecase.readSavedAppDetail(of: savedApp)
                .map { savedAppDetail in
                    return SavedAppDetailTableViewCellModel(savedAppDetail: savedAppDetail)
                }
                .assertNoFailure()
                .eraseToAnyPublisher()

            cell.bind(cellModel)
        }

        return cell
    }

}

추가 보완점

prefetchRowsAt메서드는 사용자가 스크롤을 해야 호출되기 때문에 맨 처음 보여지는 셀은 여전히 지연이 발생합니다.

Metadata

Metadata

Assignees

Labels

UI/UXUI, UX개선

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions