I'm using custom PullToRefresh to refresh the whole screen when user wants. PullToRefresh performs specific operation but freezes for a second while doing so. When I remove .sink from method that is performed on refresh everything works fine.
Method that causes this bug:
private func updateFitnessClasses() {
if let firstCompany = userCompanies.first {
updateFitnessClassesInteractor
.update(companyID: firstCompany.id)
.combineLatest(fitnessClassesRepository.fitnessClassesSections,
fitnessClassesRepository.calendarDayItems)
.replaceError(with: ((), [], []))
.sink(receiveValue: { [weak self] _, calendarSections, calendarRows in
self?.fitnessClassesSections = calendarSections
self?.calendarRows = calendarRows
})
.store(in: cancelBag)
}
}
Where fitnessClassesRepository.fitnessClassesSections
and fitnessClassesRepository.fitnessClassesSections
are AnyPublisher<[CalendarSection], Error>
constructed elsewhere. I use these two publishers in another method that fires in view's .onAppear
and UI do not freeze there.
Also UpdateFitnessClassesInteractor's.update()
looks like this:
func update(companyID: Int) -> AnyPublisher<Void, Error> {
fitnessClassCache
.lastUpdateTimestamp(forCompanyID: companyID)
.prefix(1)
.flatMap { self.apiClient.sendRequest(.fitnessClasses(timestamp: $0, companyID: companyID)) }
.map { $0.data }
.map { self.fitnessClassCache.updateCache(with: $0) }
.eraseToAnyPublisher()
}
There is an API call and then new data is saved to database from which it is later fetched in fitnessClassesRepository
methods.
It turns out that my fitnessClassesRepository
publishers - fitnessClassesSections
and calendarDayItems
- were performing some heavy calculations synchronous on the main thread so the UI freezed for the time of doing those calculations. Having changed these calculations to be performed on background thread by adding .receive(on: DispatchQueue.global())
in the proper place and adding .receive(on: DispatchQueue.main)
in my updateFitnessClasses
method fixed the problem.
Finally, my updateFitnessClasses
method looks like this:
private func updateFitnessClasses() {
if let firstCompany = userCompanies.first {
updateFitnessClassesInteractor
.update(companyID: firstCompany.id)
.combineLatest(fitnessClassesRepository.fitnessClassesSections,
fitnessClassesRepository.calendarDayItems)
.replaceError(with: ((), [], []))
.receive(on: DispatchQueue.main)
.sink(receiveValue: { [weak self] _, calendarSections, calendarRows in
self?.fitnessClassesSections = calendarSections
self?.calendarRows = calendarRows
})
.store(in: cancelBag)
}
}