Purge cache (IOS-14)

This commit is contained in:
Nathan Mattes 2023-06-28 09:02:14 +02:00
parent 951f46353f
commit 6b12224578
3 changed files with 70 additions and 82 deletions

View File

@ -10,7 +10,7 @@ protocol AboutViewControllerDelegate: AnyObject {
class AboutViewController: UIViewController { class AboutViewController: UIViewController {
let tableView: UITableView let tableView: UITableView
let sections: [AboutSettingsSection] private(set) var sections: [AboutSettingsSection] = []
var tableViewDataSource: UITableViewDiffableDataSource<AboutSettingsSection, AboutSettingsEntry>? var tableViewDataSource: UITableViewDiffableDataSource<AboutSettingsSection, AboutSettingsEntry>?
weak var delegate: AboutViewControllerDelegate? weak var delegate: AboutViewControllerDelegate?
@ -20,17 +20,6 @@ class AboutViewController: UIViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(AboutMastodonTableViewCell.self, forCellReuseIdentifier: AboutMastodonTableViewCell.reuseIdentifier) tableView.register(AboutMastodonTableViewCell.self, forCellReuseIdentifier: AboutMastodonTableViewCell.reuseIdentifier)
sections = [
AboutSettingsSection(entries: [
.evenMoreSettings,
.contributeToMastodon,
.privacyPolicy
]),
AboutSettingsSection(entries: [
.clearMediaCache(AppContext.shared.currentDiskUsage())
])
]
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
let tableViewDataSource = UITableViewDiffableDataSource<AboutSettingsSection, AboutSettingsEntry>(tableView: tableView) { [weak self] tableView, indexPath, itemIdentifier in let tableViewDataSource = UITableViewDiffableDataSource<AboutSettingsSection, AboutSettingsEntry>(tableView: tableView) { [weak self] tableView, indexPath, itemIdentifier in
@ -58,6 +47,20 @@ class AboutViewController: UIViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
update(with:
[AboutSettingsSection(entries: [
.evenMoreSettings,
.contributeToMastodon,
.privacyPolicy
]),
AboutSettingsSection(entries: [
.clearMediaCache(AppContext.shared.currentDiskUsage())
])]
)
}
func update(with sections: [AboutSettingsSection]) {
self.sections = sections
var snapshot = NSDiffableDataSourceSnapshot<AboutSettingsSection, AboutSettingsEntry>() var snapshot = NSDiffableDataSourceSnapshot<AboutSettingsSection, AboutSettingsEntry>()
@ -66,7 +69,7 @@ class AboutViewController: UIViewController {
snapshot.appendItems(section.entries) snapshot.appendItems(section.entries)
} }
tableViewDataSource?.apply(snapshot) tableViewDataSource?.apply(snapshot, animatingDifferences: false)
} }
} }

View File

@ -2,6 +2,7 @@
import UIKit import UIKit
import AuthenticationServices import AuthenticationServices
import MastodonCore
protocol SettingsCoordinatorDelegate: AnyObject { protocol SettingsCoordinatorDelegate: AnyObject {
func logout(_ settingsCoordinator: SettingsCoordinator) func logout(_ settingsCoordinator: SettingsCoordinator)
@ -71,9 +72,18 @@ extension SettingsCoordinator: AboutViewControllerDelegate {
case .privacyPolicy: case .privacyPolicy:
delegate?.openPrivacyURL(self) delegate?.openPrivacyURL(self)
case .clearMediaCache(_): case .clearMediaCache(_):
//TODO: appContext.purgeCache()
//FIXME: maybe we should inject an AppContext/AuthContext here instead of delegating everything to SceneCoordinator? //FIXME: maybe we should inject an AppContext/AuthContext here instead of delegating everything to SceneCoordinator?
break AppContext.shared.purgeCache()
viewController.update(with:
[AboutSettingsSection(entries: [
.evenMoreSettings,
.contributeToMastodon,
.privacyPolicy
]),
AboutSettingsSection(entries: [
.clearMediaCache(AppContext.shared.currentDiskUsage())
])]
)
} }
} }
} }

View File

@ -126,76 +126,51 @@ extension AppContext {
return formatter return formatter
}() }()
private static let purgeCacheWorkingQueue = DispatchQueue(label: "org.joinmastodon.app.AppContext.purgeCacheWorkingQueue") public func purgeCache() {
ImageDownloader.defaultURLCache().removeAllCachedResponses()
public func purgeCache() -> AnyPublisher<ByteCount, Never> { let fileManager = FileManager.default
Publishers.MergeMany([ let temporaryDirectoryURL = fileManager.temporaryDirectory
AppContext.purgeAlamofireImageCache(), let fileKeys: [URLResourceKey] = [.fileSizeKey, .isDirectoryKey]
AppContext.purgeTemporaryDirectory(),
]) if let directoryEnumerator = fileManager.enumerator(
.reduce(0, +) at: temporaryDirectoryURL,
.eraseToAnyPublisher() includingPropertiesForKeys: fileKeys,
options: .skipsHiddenFiles) {
for case let fileURL as URL in directoryEnumerator {
guard let resourceValues = try? fileURL.resourceValues(forKeys: Set(fileKeys)),
resourceValues.isDirectory == false else {
continue
}
try? fileManager.removeItem(at: fileURL)
}
}
} }
// In Bytes
public func currentDiskUsage() -> Int { public func currentDiskUsage() -> Int {
let alamoFireDiskBytes = ImageDownloader.defaultURLCache().currentDiskUsage let alamoFireDiskBytes = ImageDownloader.defaultURLCache().currentDiskUsage
//TODO: Add temp directory files
return alamoFireDiskBytes
}
private static func purgeAlamofireImageCache() -> AnyPublisher<ByteCount, Never> { var tempFilesDiskBytes = 0
Future<ByteCount, Never> { promise in let fileManager = FileManager.default
AppContext.purgeCacheWorkingQueue.async { let temporaryDirectoryURL = fileManager.temporaryDirectory
// clean image cache for AlamofireImage let fileKeys: [URLResourceKey] = [.fileSizeKey, .isDirectoryKey]
let diskBytes = ImageDownloader.defaultURLCache().currentDiskUsage
ImageDownloader.defaultURLCache().removeAllCachedResponses() if let directoryEnumerator = fileManager.enumerator(
let currentDiskBytes = ImageDownloader.defaultURLCache().currentDiskUsage at: temporaryDirectoryURL,
let purgedDiskBytes = max(0, diskBytes - currentDiskBytes) includingPropertiesForKeys: fileKeys,
promise(.success(purgedDiskBytes)) options: .skipsHiddenFiles) {
for case let fileURL as URL in directoryEnumerator {
guard let resourceValues = try? fileURL.resourceValues(forKeys: Set(fileKeys)),
resourceValues.isDirectory == false else {
continue
}
tempFilesDiskBytes += resourceValues.fileSize ?? 0
} }
} }
.eraseToAnyPublisher()
return alamoFireDiskBytes + tempFilesDiskBytes
} }
private static func purgeTemporaryDirectory() -> AnyPublisher<ByteCount, Never> {
Future<ByteCount, Never> { promise in
AppContext.purgeCacheWorkingQueue.async {
let fileManager = FileManager.default
let temporaryDirectoryURL = fileManager.temporaryDirectory
let resourceKeys = Set<URLResourceKey>([.fileSizeKey, .isDirectoryKey])
guard let directoryEnumerator = fileManager.enumerator(
at: temporaryDirectoryURL,
includingPropertiesForKeys: Array(resourceKeys),
options: .skipsHiddenFiles
) else {
promise(.success(0))
return
}
var fileURLs: [URL] = []
var totalFileSizeInBytes = 0
for case let fileURL as URL in directoryEnumerator {
guard let resourceValues = try? fileURL.resourceValues(forKeys: resourceKeys),
let isDirectory = resourceValues.isDirectory else {
continue
}
guard !isDirectory else {
continue
}
fileURLs.append(fileURL)
totalFileSizeInBytes += resourceValues.fileSize ?? 0
}
for fileURL in fileURLs {
try? fileManager.removeItem(at: fileURL)
}
promise(.success(totalFileSizeInBytes))
}
}
.eraseToAnyPublisher()
}
} }