From d27bddb2caf9e22bb7adad065f356aba13d5e542 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 15 Jun 2020 22:34:42 -0400 Subject: [PATCH] Fix profile header image not showing up on first load The issue occurred because the profile header would kick off a request upon loading, then the profile table would request the initial set of statuses shortly thereafter which would result in reloadData being called which would cancel the request without removing the group, so the request generated by the newly-reloaded header cell would attach a callback to the cancelled request, resulting in the header image never displaying. --- Tusker/Caching/ImageCache.swift | 30 +++++++++++-------- .../Profile/ProfileTableViewController.swift | 28 ++++++++--------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/Tusker/Caching/ImageCache.swift b/Tusker/Caching/ImageCache.swift index 2e04fbd9..7681ab6e 100644 --- a/Tusker/Caching/ImageCache.swift +++ b/Tusker/Caching/ImageCache.swift @@ -18,7 +18,7 @@ class ImageCache { let cache: Cache - var requests = [URL: RequestGroup]() + private var groups = [URL: RequestGroup]() init(name: String, memoryExpiry expiry: Expiry) { let storage = MemoryStorage(config: MemoryConfig(expiry: expiry)) @@ -43,14 +43,18 @@ class ImageCache { completion?(data) return nil } else { - if let completion = completion, let group = requests[url] { + if let completion = completion, let group = groups[url] { return group.addCallback(completion) } else { - let group = RequestGroup(url: url) - let request = group.addCallback(completion) - group.run { (data) in - try? self.cache.setObject(data, forKey: key) + let group = RequestGroup(url: url) { (data) in + if let data = data { + try? self.cache.setObject(data, forKey: key) + } + self.groups.removeValue(forKey: url) } + groups[url] = group + let request = group.addCallback(completion) + group.run() return request } } @@ -61,29 +65,30 @@ class ImageCache { } func cancelWithoutCallback(_ url: URL) { - requests[url]?.cancelWithoutCallback() + groups[url]?.cancelWithoutCallback() } - class RequestGroup { + private class RequestGroup { let url: URL + private let onFinished: (Data?) -> Void private var task: URLSessionDataTask? private var requests = [Request]() - init(url: URL) { + init(url: URL, onFinished: @escaping (Data?) -> Void) { self.url = url + self.onFinished = onFinished } deinit { task?.cancel() } - func run(cache: @escaping (Data) -> Void) { + func run() { task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in guard error == nil, let data = data else { self.complete(with: nil) return } - cache(data) self.complete(with: data) }) task!.resume() @@ -123,11 +128,12 @@ class ImageCache { callback(data) } } + self.onFinished(data) } } class Request { - weak var group: RequestGroup? + private weak var group: RequestGroup? private(set) var callback: ((Data?) -> Void)? private(set) var cancelled: Bool = false diff --git a/Tusker/Screens/Profile/ProfileTableViewController.swift b/Tusker/Screens/Profile/ProfileTableViewController.swift index ec85650c..c1dd5c61 100644 --- a/Tusker/Screens/Profile/ProfileTableViewController.swift +++ b/Tusker/Screens/Profile/ProfileTableViewController.swift @@ -16,20 +16,8 @@ class ProfileTableViewController: EnhancedTableViewController { var accountID: String! - var pinnedStatuses: [(id: String, state: StatusState)] = [] { - didSet { - DispatchQueue.main.async { - self.tableView.reloadData() - } - } - } - var timelineSegments: [[(id: String, state: StatusState)]] = [] { - didSet { - DispatchQueue.main.async { - self.tableView.reloadData() - } - } - } + var pinnedStatuses: [(id: String, state: StatusState)] = [] + var timelineSegments: [[(id: String, state: StatusState)]] = [] var older: RequestRange? var newer: RequestRange? @@ -124,6 +112,12 @@ class ProfileTableViewController: EnhancedTableViewController { self.mastodonController.persistentContainer.addAll(statuses: statuses) { self.pinnedStatuses = statuses.map { ($0.id, .unknown) } + let indexPaths = (0..