Merge pull request #1844 from sixten/clear-local-files

Clear local files when reseting local storage
This commit is contained in:
Satindar Dhillon 2023-02-27 14:33:16 -08:00 committed by GitHub
commit ad5b6eac85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 107 additions and 29 deletions

View file

@ -6,8 +6,6 @@ import Utils
import Views
@MainActor final class HomeFeedViewModel: NSObject, ObservableObject {
let dateFormatter = DateFormatter.formatterISO8601
var currentDetailViewModel: LinkItemDetailViewModel?
private var fetchedResultsController: NSFetchedResultsController<LinkedItem>?
@ -110,7 +108,7 @@ import Views
func syncItems(dataService: DataService) async {
let syncStart = Date.now
let lastSyncDate = dateFormatter.date(from: dataService.lastItemSyncTime) ?? Date(timeIntervalSinceReferenceDate: 0)
let lastSyncDate = dataService.lastItemSyncTime
try? await dataService.syncOfflineItemsWithServerIfNeeded()
@ -124,7 +122,7 @@ import Views
self.isLoading = false
}
} else {
dataService.lastItemSyncTime = DateFormatter.formatterISO8601.string(from: syncStart)
dataService.lastItemSyncTime = syncStart
}
// If possible start prefetching new pages in the background

View file

@ -39,7 +39,7 @@ struct ManageAccountView: View {
)
Button(
action: {
dataService.resetCoreData()
dataService.resetLocalStorage()
},
label: { Text(LocalText.manageAccountResetCache) }
)

View file

@ -543,8 +543,7 @@
}
public static func pathForAudioDirectory(itemID: String) -> URL {
FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
URL.om_documentsDirectory
.appendingPathComponent("audio-\(itemID)/")
}

View file

@ -56,8 +56,7 @@ struct SpeechDocument: Decodable {
}
static func audioDirectory(pageId: String) -> URL {
FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
URL.om_documentsDirectory
.appendingPathComponent("audio-\(pageId)")
}
}
@ -213,12 +212,10 @@ struct SpeechSynthesizer {
let data = try await downloadData(session: session, request: speechItem.urlRequest)
let tempPath = FileManager.default
.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let tempPath = URL.om_cachesDirectory
.appendingPathComponent(UUID().uuidString + ".mp3")
let tempSMPath = FileManager.default
.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let tempSMPath = URL.om_cachesDirectory
.appendingPathComponent(UUID().uuidString + ".speechMarks")
do {

View file

@ -38,7 +38,7 @@ public final class Authenticator: ObservableObject {
}
public func logout(dataService: DataService, isAccountDeletion: Bool = false) {
dataService.resetCoreData()
dataService.resetLocalStorage()
clearCreds()
Authenticator.unregisterIntercomUser?()
isLoggedIn = false

View file

@ -29,9 +29,22 @@ public final class DataService: ObservableObject {
persistentContainer.viewContext
}
@AppStorage(UserDefaultKey.lastItemSyncTime.rawValue) public var lastItemSyncTime: String = {
DateFormatter.formatterISO8601.string(from: Date(timeIntervalSinceReferenceDate: 0))
}()
public var lastItemSyncTime: Date {
get {
guard
let str = UserDefaults.standard.string(forKey: UserDefaultKey.lastItemSyncTime.rawValue),
let date = DateFormatter.formatterISO8601.date(from: str)
else {
return Date(timeIntervalSinceReferenceDate: 0)
}
return date
}
set {
logger.trace("last item sync updated to \(newValue)")
let str = DateFormatter.formatterISO8601.string(from: newValue)
UserDefaults.standard.set(str, forKey: UserDefaultKey.lastItemSyncTime.rawValue)
}
}
public init(appEnvironment: AppEnvironment, networker: Networker) {
self.appEnvironment = appEnvironment
@ -43,7 +56,7 @@ public final class DataService: ObservableObject {
backgroundContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
if isFirstTimeRunningNewAppBuild() {
resetCoreData()
resetLocalStorage()
} else {
persistentContainer.loadPersistentStores { _, error in
if let error = error {
@ -102,10 +115,58 @@ public final class DataService: ObservableObject {
}
}
public func resetCoreData() {
lastItemSyncTime = DateFormatter.formatterISO8601.string(from: Date(timeIntervalSinceReferenceDate: 0))
private func clearDownloadedFiles() {
let relevantTypes = ["pdf", "mp3", "speechMarks"]
let fileMgr = FileManager()
logger.trace("removing cached downloads")
// clear the temporary files in the caches directory
if let cacheFileURLs = try? fileMgr.contentsOfDirectory(
at: URL.om_cachesDirectory,
includingPropertiesForKeys: .none,
options: .skipsHiddenFiles
) {
logger.trace("\(cacheFileURLs.count) file URLs in caches directory")
for fileURL in cacheFileURLs where relevantTypes.contains(fileURL.pathExtension) {
logger.trace("removing \(fileURL.absoluteString)")
try? fileMgr.removeItem(at: fileURL)
}
}
// and also the copies written to Documents
let resourceKeys = Set<URLResourceKey>([.nameKey, .isDirectoryKey])
if let documentsFileURLs = try? fileMgr.contentsOfDirectory(
at: URL.om_documentsDirectory,
includingPropertiesForKeys: Array(resourceKeys),
options: .skipsHiddenFiles
) {
logger.trace("\(documentsFileURLs.count) file URLs in documents directory")
for fileURL in documentsFileURLs {
guard
let resourceValues = try? fileURL.resourceValues(forKeys: resourceKeys),
let isDirectory = resourceValues.isDirectory,
let name = resourceValues.name
else {
continue
}
if isDirectory {
if name.hasPrefix("audio-") {
logger.trace("removing \(fileURL.absoluteString)")
try? fileMgr.removeItem(at: fileURL)
}
} else if relevantTypes.contains(fileURL.pathExtension) {
logger.trace("removing \(fileURL.absoluteString)")
try? fileMgr.removeItem(at: fileURL)
}
}
}
}
public func resetLocalStorage() {
lastItemSyncTime = Date(timeIntervalSinceReferenceDate: 0)
clearCoreData()
clearDownloadedFiles()
persistentContainer = PersistentContainer.make()
persistentContainer.loadPersistentStores { _, error in

View file

@ -93,7 +93,7 @@ public extension DataService {
}
}
DispatchQueue.main.sync {
self.lastItemSyncTime = DateFormatter.formatterISO8601.string(from: Date.now)
self.lastItemSyncTime = Date.now
onComplete()
}
}

View file

@ -21,8 +21,7 @@ public extension DataService {
var localPdfURL: URL?
let tempPath = FileManager.default
.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let tempPath = URL.om_cachesDirectory
.appendingPathComponent(UUID().uuidString + ".pdf")
try await backgroundContext.perform { [weak self] in

View file

@ -10,8 +10,7 @@ import QuickLookThumbnailing
public enum PDFUtils {
public static func copyToLocal(url: URL) throws -> String {
let subPath = UUID().uuidString + ".pdf"
let dest = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
let dest = URL.om_documentsDirectory
.appendingPathComponent(subPath)
try FileManager.default.copyItem(at: url, to: dest)
@ -20,8 +19,7 @@ public enum PDFUtils {
public static func moveToLocal(url: URL) throws -> String {
let subPath = UUID().uuidString + ".pdf"
let dest = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
let dest = URL.om_documentsDirectory
.appendingPathComponent(subPath)
try FileManager.default.moveItem(at: url, to: dest)
@ -29,8 +27,7 @@ public enum PDFUtils {
}
public static func localPdfURL(filename: String) -> URL? {
let url = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
let url = URL.om_documentsDirectory
.appendingPathComponent(filename)
return url

View file

@ -0,0 +1,27 @@
import Foundation
public extension URL {
// swiftlint:disable:next identifier_name
static var om_documentsDirectory: URL {
if #unavailable(iOS 16, tvOS 16, macOS 13) {
guard let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
fatalError("Could not determine the user's documents directory")
}
return url
} else {
return URL.documentsDirectory
}
}
// swiftlint:disable:next identifier_name
static var om_cachesDirectory: URL {
if #unavailable(iOS 16, tvOS 16, macOS 13) {
guard let url = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
fatalError("Could not determine the user's caches directory")
}
return url
} else {
return URL.cachesDirectory
}
}
}