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 */; };
|
D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */; };
|
||||||
D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */; };
|
D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */; };
|
||||||
D6A5BB3123BBAD87003BF21D /* JSONResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB3023BBAD87003BF21D /* JSONResponse.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 */; };
|
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */; };
|
||||||
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F613023AE99E000F3CFD3 /* Ambassador.framework */; };
|
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F613023AE99E000F3CFD3 /* Ambassador.framework */; };
|
||||||
D6ACE1AD240C3BAD004EA8E2 /* Embassy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F612D23AE990C00F3CFD3 /* Embassy.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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMesasgeActivity.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1389,6 +1391,7 @@
|
||||||
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */,
|
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */,
|
||||||
D65234C8256189D0001AF9CF /* TimelineLikeTableViewController.swift */,
|
D65234C8256189D0001AF9CF /* TimelineLikeTableViewController.swift */,
|
||||||
D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */,
|
D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */,
|
||||||
|
D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */,
|
||||||
);
|
);
|
||||||
path = Utilities;
|
path = Utilities;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -2060,6 +2063,7 @@
|
||||||
D6E426B325337C7000C02E1C /* CustomEmojiImageView.swift in Sources */,
|
D6E426B325337C7000C02E1C /* CustomEmojiImageView.swift in Sources */,
|
||||||
D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */,
|
D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */,
|
||||||
D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */,
|
D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */,
|
||||||
|
D6A6C10525B6138A00298D0F /* StatusTablePrefetching.swift in Sources */,
|
||||||
D6B053A423BD2C8100A066FA /* AssetCollectionsListViewController.swift in Sources */,
|
D6B053A423BD2C8100A066FA /* AssetCollectionsListViewController.swift in Sources */,
|
||||||
D690797324A4EF9700023A34 /* UIBezierPath+Helpers.swift in Sources */,
|
D690797324A4EF9700023A34 /* UIBezierPath+Helpers.swift in Sources */,
|
||||||
D693DE5723FE1A6A0061E07D /* EnhancedNavigationViewController.swift in Sources */,
|
D693DE5723FE1A6A0061E07D /* EnhancedNavigationViewController.swift in Sources */,
|
||||||
|
|
|
@ -24,6 +24,12 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
return context
|
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 statusSubject = PassthroughSubject<String, Never>()
|
||||||
let accountSubject = PassthroughSubject<String, Never>()
|
let accountSubject = PassthroughSubject<String, Never>()
|
||||||
let relationshipSubject = 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]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
let ids = indexPaths.map { statuses[$0.row].id }
|
||||||
guard let status = mastodonController.persistentContainer.status(for: statuses[indexPath.row].id) else { continue }
|
prefetchStatuses(with: ids)
|
||||||
_ = 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 tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
let ids: [String] = indexPaths.compactMap {
|
||||||
guard let status = mastodonController.persistentContainer.status(for: statuses[indexPath.row].id) else { continue }
|
guard $0.row < statuses.count else {
|
||||||
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
|
return nil
|
||||||
for attachment in status.attachments where attachment.kind == .image {
|
}
|
||||||
ImageCache.attachments.cancelWithoutCallback(attachment.url)
|
return statuses[$0.row].id
|
||||||
}
|
}
|
||||||
}
|
cancelPrefetchingStatuses(with: ids)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import SafariServices
|
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
|
||||||
class ConversationTableViewController: EnhancedTableViewController {
|
class ConversationTableViewController: EnhancedTableViewController {
|
||||||
|
@ -188,24 +187,14 @@ extension ConversationTableViewController: StatusTableViewCellDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ConversationTableViewController: UITableViewDataSourcePrefetching {
|
extension ConversationTableViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
||||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
let ids = indexPaths.map { statuses[$0.row].id }
|
||||||
guard let status = mastodonController.persistentContainer.status(for: statuses[indexPath.row].id) else { continue }
|
prefetchStatuses(with: ids)
|
||||||
_ = 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 tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
let ids: [String] = indexPaths.compactMap { statuses[$0.row].id }
|
||||||
guard let status = mastodonController.persistentContainer.status(for: statuses[indexPath.row].id) else { continue }
|
cancelPrefetchingStatuses(with: ids)
|
||||||
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
|
|
||||||
for attachment in status.attachments where attachment.kind == .image {
|
|
||||||
ImageCache.attachments.cancelWithoutCallback(attachment.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,33 +241,20 @@ extension ProfileStatusesViewController: StatusTableViewCellDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProfileStatusesViewController: UITableViewDataSourcePrefetching {
|
extension ProfileStatusesViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
||||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
let ids = indexPaths.map { item(for: $0).id }
|
||||||
let statusID = item(for: indexPath).id
|
prefetchStatuses(with: ids)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
let ids: [String] = indexPaths.compactMap {
|
||||||
let statusID = item(for: indexPath).id
|
guard $0.section < sections.count,
|
||||||
|
$0.row < sections[$0.section].count else {
|
||||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
return nil
|
||||||
continue
|
|
||||||
}
|
|
||||||
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
|
|
||||||
for attachment in status.attachments where attachment.kind == .image {
|
|
||||||
ImageCache.avatars.cancelWithoutCallback(attachment.url)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
let ids = indexPaths.map { item(for: $0).id }
|
||||||
guard let status = mastodonController.persistentContainer.status(for: item(for: indexPath).id) else {
|
prefetchStatuses(with: ids)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
let ids: [String] = indexPaths.compactMap {
|
||||||
// todo: this means when removing cells, we can't cancel prefetching
|
guard $0.section < sections.count,
|
||||||
// is this an issue?
|
$0.row < sections[$0.section].count else {
|
||||||
guard indexPath.section < sections.count,
|
return nil
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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