From cda4c9eb29300ca90e6fd200e3669d70465d9e6b Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 24 Mar 2024 21:25:11 -0700 Subject: [PATCH] Make ArticleThemeDownloader a struct with static funcs, which fixes the concurrency warning about the static shared property (which got removed). --- Mac/AppDelegate.swift | 2 +- .../AppDelegate+Scriptability.swift | 2 +- .../ArticleThemeDownloader.swift | 62 +++++++++---------- iOS/AppDelegate.swift | 2 +- iOS/SceneDelegate.swift | 2 +- 5 files changed, 34 insertions(+), 36 deletions(-) diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index 9e86b58da..1af012228 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -338,7 +338,7 @@ import Sparkle shuttingDown = true saveState() - ArticleThemeDownloader.shared.cleanUp() + ArticleThemeDownloader.cleanUp() accountManager.sendArticleStatusAll() { self.isShutDownSyncDone = true diff --git a/Mac/Scriptability/AppDelegate+Scriptability.swift b/Mac/Scriptability/AppDelegate+Scriptability.swift index b05c312d0..1594ebaba 100644 --- a/Mac/Scriptability/AppDelegate+Scriptability.swift +++ b/Mac/Scriptability/AppDelegate+Scriptability.swift @@ -62,7 +62,7 @@ extension AppDelegate : AppDelegateAppleEvents { } do { - try ArticleThemeDownloader.shared.handleFile(at: location) + try ArticleThemeDownloader.handleFile(at: location) } catch { NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error]) } diff --git a/Shared/ArticleStyles/ArticleThemeDownloader.swift b/Shared/ArticleStyles/ArticleThemeDownloader.swift index d6714e169..47c712477 100644 --- a/Shared/ArticleStyles/ArticleThemeDownloader.swift +++ b/Shared/ArticleStyles/ArticleThemeDownloader.swift @@ -9,11 +9,11 @@ import Foundation import Zip -public class ArticleThemeDownloader { - +struct ArticleThemeDownloader { + public enum ArticleThemeDownloaderError: LocalizedError { case noThemeFile - + public var errorDescription: String? { switch self { case .noThemeFile: @@ -21,27 +21,42 @@ public class ArticleThemeDownloader { } } } - - public static let shared = ArticleThemeDownloader() - private init() {} - - public func handleFile(at location: URL) throws { + + static func handleFile(at location: URL) throws { createDownloadDirectoryIfRequired() let movedFileLocation = try moveTheme(from: location) let unzippedFileLocation = try unzipFile(at: movedFileLocation) NotificationCenter.default.post(name: .didEndDownloadingTheme, object: nil, userInfo: ["url" : unzippedFileLocation]) } - - + + /// Removes downloaded themes, where themes == folders, from `Application Support/NetNewsWire/Downloads`. + static func cleanUp() { + guard let filenames = try? FileManager.default.contentsOfDirectory(atPath: downloadDirectory().path) else { + return + } + for path in filenames { + do { + if FileManager.default.isFolder(atPath: downloadDirectory().appendingPathComponent(path).path) { + try FileManager.default.removeItem(atPath: downloadDirectory().appendingPathComponent(path).path) + } + } catch { + print(error) + } + } + } +} + +private extension ArticleThemeDownloader { + /// Creates `Application Support/NetNewsWire/Downloads` if needed. - private func createDownloadDirectoryIfRequired() { + static func createDownloadDirectoryIfRequired() { try? FileManager.default.createDirectory(at: downloadDirectory(), withIntermediateDirectories: true, attributes: nil) } /// Moves the downloaded `.tmp` file to the `downloadDirectory` and renames it a `.zip` /// - Parameter location: The temporary file location. /// - Returns: Destination `URL`. - private func moveTheme(from location: URL) throws -> URL { + static func moveTheme(from location: URL) throws -> URL { var tmpFileName = location.lastPathComponent tmpFileName = tmpFileName.replacingOccurrences(of: ".tmp", with: ".zip") let fileUrl = downloadDirectory().appendingPathComponent("\(tmpFileName)") @@ -52,7 +67,7 @@ public class ArticleThemeDownloader { /// Unzips the zip file /// - Parameter location: Location of the zip archive. /// - Returns: Enclosed `.nnwtheme` file. - private func unzipFile(at location: URL) throws -> URL { + static func unzipFile(at location: URL) throws -> URL { do { let unzipDirectory = URL(fileURLWithPath: location.path.replacingOccurrences(of: ".zip", with: "")) try Zip.unzipFile(location, destination: unzipDirectory, overwrite: true, password: nil, progress: nil, fileOutputHandler: nil) // Unzips to folder in Application Support/NetNewsWire/Downloads @@ -68,11 +83,10 @@ public class ArticleThemeDownloader { } } - /// Performs a deep search of the unzipped directory to find the theme file. /// - Parameter searchPath: directory to search /// - Returns: optional `String` - private func findThemeFile(in searchPath: String) -> String? { + static func findThemeFile(in searchPath: String) -> String? { if let directoryContents = FileManager.default.enumerator(atPath: searchPath) { while let file = directoryContents.nextObject() as? String { if file.hasSuffix(".nnwtheme") { @@ -86,23 +100,7 @@ public class ArticleThemeDownloader { /// The download directory used by the theme downloader: `Application Support/NetNewsWire/Downloads` /// - Returns: `URL` - private func downloadDirectory() -> URL { + static func downloadDirectory() -> URL { FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("NetNewsWire/Downloads", isDirectory: true) } - - /// Removes downloaded themes, where themes == folders, from `Application Support/NetNewsWire/Downloads`. - public func cleanUp() { - guard let filenames = try? FileManager.default.contentsOfDirectory(atPath: downloadDirectory().path) else { - return - } - for path in filenames { - do { - if FileManager.default.isFolder(atPath: downloadDirectory().appendingPathComponent(path).path) { - try FileManager.default.removeItem(atPath: downloadDirectory().appendingPathComponent(path).path) - } - } catch { - print(error) - } - } - } } diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index 6eab50d98..a546b04b8 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -345,7 +345,7 @@ private extension AppDelegate { accountManager.suspendNetworkAll() accountManager.suspendDatabaseAll() - ArticleThemeDownloader.shared.cleanUp() + ArticleThemeDownloader.cleanUp() CoalescingQueue.standard.performCallsImmediately() for scene in UIApplication.shared.connectedScenes { diff --git a/iOS/SceneDelegate.swift b/iOS/SceneDelegate.swift index e794298ab..6d9aa39fa 100644 --- a/iOS/SceneDelegate.swift +++ b/iOS/SceneDelegate.swift @@ -192,7 +192,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let location = location else { return } do { - try ArticleThemeDownloader.shared.handleFile(at: location) + try ArticleThemeDownloader.handleFile(at: location) } catch { NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error]) }