Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
yeahg-dev opened this issue Jan 25, 2023 · 0 comments
Open

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

yeahg-dev opened this issue Jan 25, 2023 · 0 comments
Assignees
Labels
UI/UX UI, UX개선

Comments

@yeahg-dev
Copy link
Owner

yeahg-dev commented Jan 25, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
UI/UX UI, UX개선
Projects
None yet
Development

No branches or pull requests

1 participant