Prefetch on a background queue to avoid blocking main queue with
CoreData lookups
This commit is contained in:
parent
0b008489f7
commit
2e88b266d9
|
@ -227,6 +227,7 @@
|
|||
D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */; };
|
||||
D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */; };
|
||||
D6A5BB3123BBAD87003BF21D /* JSONResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */; };
|
||||
D6A6C10525B6138A00298D0F /* StatusTablePrefetching.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */; };
|
||||
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */; };
|
||||
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F613023AE99E000F3CFD3 /* Ambassador.framework */; };
|
||||
D6ACE1AD240C3BAD004EA8E2 /* Embassy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F612D23AE990C00F3CFD3 /* Embassy.framework */; };
|
||||
|
@ -587,6 +588,7 @@
|
|||
D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProfileTests.swift; sourceTree = "<group>"; };
|
||||
D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegatingResponse.swift; sourceTree = "<group>"; };
|
||||
D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONResponse.swift; sourceTree = "<group>"; };
|
||||
D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTablePrefetching.swift; sourceTree = "<group>"; };
|
||||
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSceneDelegate.swift; sourceTree = "<group>"; };
|
||||
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
|
||||
D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMesasgeActivity.swift; sourceTree = "<group>"; };
|
||||
|
@ -1389,6 +1391,7 @@
|
|||
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */,
|
||||
D65234C8256189D0001AF9CF /* TimelineLikeTableViewController.swift */,
|
||||
D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */,
|
||||
D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */,
|
||||
);
|
||||
path = Utilities;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2060,6 +2063,7 @@
|
|||
D6E426B325337C7000C02E1C /* CustomEmojiImageView.swift in Sources */,
|
||||
D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */,
|
||||
D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */,
|
||||
D6A6C10525B6138A00298D0F /* StatusTablePrefetching.swift in Sources */,
|
||||
D6B053A423BD2C8100A066FA /* AssetCollectionsListViewController.swift in Sources */,
|
||||
D690797324A4EF9700023A34 /* UIBezierPath+Helpers.swift in Sources */,
|
||||
D693DE5723FE1A6A0061E07D /* EnhancedNavigationViewController.swift in Sources */,
|
||||
|
|
|
@ -24,6 +24,12 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
|||
return context
|
||||
}()
|
||||
|
||||
private(set) lazy var prefetchBackgroundContext: NSManagedObjectContext = {
|
||||
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
|
||||
context.parent = self.viewContext
|
||||
return context
|
||||
}()
|
||||
|
||||
let statusSubject = PassthroughSubject<String, Never>()
|
||||
let accountSubject = PassthroughSubject<String, Never>()
|
||||
let relationshipSubject = PassthroughSubject<String, Never>()
|
||||
|
|
|
@ -162,24 +162,19 @@ extension BookmarksTableViewController: StatusTableViewCellDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
extension BookmarksTableViewController: UITableViewDataSourcePrefetching {
|
||||
extension BookmarksTableViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
guard let status = mastodonController.persistentContainer.status(for: statuses[indexPath.row].id) else { continue }
|
||||
_ = ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
_ = ImageCache.attachments.get(attachment.url, completion: nil)
|
||||
}
|
||||
}
|
||||
let ids = indexPaths.map { statuses[$0.row].id }
|
||||
prefetchStatuses(with: ids)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
guard let status = mastodonController.persistentContainer.status(for: statuses[indexPath.row].id) else { continue }
|
||||
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
ImageCache.attachments.cancelWithoutCallback(attachment.url)
|
||||
let ids: [String] = indexPaths.compactMap {
|
||||
guard $0.row < statuses.count else {
|
||||
return nil
|
||||
}
|
||||
return statuses[$0.row].id
|
||||
}
|
||||
cancelPrefetchingStatuses(with: ids)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
import Pachyderm
|
||||
|
||||
class ConversationTableViewController: EnhancedTableViewController {
|
||||
|
@ -188,24 +187,14 @@ extension ConversationTableViewController: StatusTableViewCellDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
extension ConversationTableViewController: UITableViewDataSourcePrefetching {
|
||||
extension ConversationTableViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
guard let status = mastodonController.persistentContainer.status(for: statuses[indexPath.row].id) else { continue }
|
||||
_ = ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
_ = ImageCache.attachments.get(attachment.url, completion: nil)
|
||||
}
|
||||
}
|
||||
let ids = indexPaths.map { statuses[$0.row].id }
|
||||
prefetchStatuses(with: ids)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
guard let status = mastodonController.persistentContainer.status(for: statuses[indexPath.row].id) else { continue }
|
||||
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
ImageCache.attachments.cancelWithoutCallback(attachment.url)
|
||||
}
|
||||
}
|
||||
let ids: [String] = indexPaths.compactMap { statuses[$0.row].id }
|
||||
cancelPrefetchingStatuses(with: ids)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,33 +241,20 @@ extension ProfileStatusesViewController: StatusTableViewCellDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
extension ProfileStatusesViewController: UITableViewDataSourcePrefetching {
|
||||
extension ProfileStatusesViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
let statusID = item(for: indexPath).id
|
||||
|
||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||
continue
|
||||
}
|
||||
|
||||
_ = ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
_ = ImageCache.attachments.get(attachment.url, completion: nil)
|
||||
}
|
||||
}
|
||||
let ids = indexPaths.map { item(for: $0).id }
|
||||
prefetchStatuses(with: ids)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
let statusID = item(for: indexPath).id
|
||||
|
||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||
continue
|
||||
}
|
||||
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
ImageCache.avatars.cancelWithoutCallback(attachment.url)
|
||||
let ids: [String] = indexPaths.compactMap {
|
||||
guard $0.section < sections.count,
|
||||
$0.row < sections[$0.section].count else {
|
||||
return nil
|
||||
}
|
||||
return item(for: $0).id
|
||||
}
|
||||
cancelPrefetchingStatuses(with: ids)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,32 +150,20 @@ extension TimelineTableViewController: StatusTableViewCellDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
extension TimelineTableViewController: UITableViewDataSourcePrefetching {
|
||||
extension TimelineTableViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
guard let status = mastodonController.persistentContainer.status(for: item(for: indexPath).id) else {
|
||||
continue
|
||||
}
|
||||
_ = ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
_ = ImageCache.attachments.get(attachment.url, completion: nil)
|
||||
}
|
||||
}
|
||||
let ids = indexPaths.map { item(for: $0).id }
|
||||
prefetchStatuses(with: ids)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
// todo: this means when removing cells, we can't cancel prefetching
|
||||
// is this an issue?
|
||||
guard indexPath.section < sections.count,
|
||||
indexPath.row < sections[indexPath.section].count,
|
||||
let status = mastodonController.persistentContainer.status(for: item(for: indexPath).id) else {
|
||||
continue
|
||||
}
|
||||
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
ImageCache.attachments.cancelWithoutCallback(attachment.url)
|
||||
let ids: [String] = indexPaths.compactMap {
|
||||
guard $0.section < sections.count,
|
||||
$0.row < sections[$0.section].count else {
|
||||
return nil
|
||||
}
|
||||
return item(for: $0).id
|
||||
}
|
||||
cancelPrefetchingStatuses(with: ids)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// StatusTablePrefetching.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 1/18/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
protocol StatusTablePrefetching: TuskerNavigationDelegate {
|
||||
}
|
||||
|
||||
extension StatusTablePrefetching {
|
||||
|
||||
func prefetchStatuses(with ids: [String]) {
|
||||
let context = apiController.persistentContainer.prefetchBackgroundContext
|
||||
context.perform {
|
||||
guard let statuses = getStatusesWith(ids: ids, in: context) else {
|
||||
return
|
||||
}
|
||||
for status in statuses {
|
||||
_ = ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
_ = ImageCache.attachments.get(attachment.url, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancelPrefetchingStatuses(with ids: [String]) {
|
||||
let context = apiController.persistentContainer.prefetchBackgroundContext
|
||||
context.perform {
|
||||
guard let statuses = getStatusesWith(ids: ids, in: context) else {
|
||||
return
|
||||
}
|
||||
for status in statuses {
|
||||
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
|
||||
for attachment in status.attachments where attachment.kind == .image {
|
||||
ImageCache.attachments.cancelWithoutCallback(attachment.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate func getStatusesWith(ids: [String], in context: NSManagedObjectContext) -> [StatusMO]? {
|
||||
let request: NSFetchRequest<StatusMO> = StatusMO.fetchRequest()
|
||||
request.predicate = NSPredicate(format: "id IN %@", ids)
|
||||
return try? context.fetch(request)
|
||||
}
|
Loading…
Reference in New Issue