Make ArticleThemeDownloader a struct with static funcs, which fixes the concurrency warning about the static shared property (which got removed).

This commit is contained in:
Brent Simmons 2024-03-24 21:25:11 -07:00
parent 8ad09228db
commit cda4c9eb29
5 changed files with 34 additions and 36 deletions

View File

@ -338,7 +338,7 @@ import Sparkle
shuttingDown = true shuttingDown = true
saveState() saveState()
ArticleThemeDownloader.shared.cleanUp() ArticleThemeDownloader.cleanUp()
accountManager.sendArticleStatusAll() { accountManager.sendArticleStatusAll() {
self.isShutDownSyncDone = true self.isShutDownSyncDone = true

View File

@ -62,7 +62,7 @@ extension AppDelegate : AppDelegateAppleEvents {
} }
do { do {
try ArticleThemeDownloader.shared.handleFile(at: location) try ArticleThemeDownloader.handleFile(at: location)
} catch { } catch {
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error]) NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error])
} }

View File

@ -9,7 +9,7 @@
import Foundation import Foundation
import Zip import Zip
public class ArticleThemeDownloader { struct ArticleThemeDownloader {
public enum ArticleThemeDownloaderError: LocalizedError { public enum ArticleThemeDownloaderError: LocalizedError {
case noThemeFile case noThemeFile
@ -22,26 +22,41 @@ public class ArticleThemeDownloader {
} }
} }
public static let shared = ArticleThemeDownloader() static func handleFile(at location: URL) throws {
private init() {}
public func handleFile(at location: URL) throws {
createDownloadDirectoryIfRequired() createDownloadDirectoryIfRequired()
let movedFileLocation = try moveTheme(from: location) let movedFileLocation = try moveTheme(from: location)
let unzippedFileLocation = try unzipFile(at: movedFileLocation) let unzippedFileLocation = try unzipFile(at: movedFileLocation)
NotificationCenter.default.post(name: .didEndDownloadingTheme, object: nil, userInfo: ["url" : unzippedFileLocation]) 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. /// Creates `Application Support/NetNewsWire/Downloads` if needed.
private func createDownloadDirectoryIfRequired() { static func createDownloadDirectoryIfRequired() {
try? FileManager.default.createDirectory(at: downloadDirectory(), withIntermediateDirectories: true, attributes: nil) try? FileManager.default.createDirectory(at: downloadDirectory(), withIntermediateDirectories: true, attributes: nil)
} }
/// Moves the downloaded `.tmp` file to the `downloadDirectory` and renames it a `.zip` /// Moves the downloaded `.tmp` file to the `downloadDirectory` and renames it a `.zip`
/// - Parameter location: The temporary file location. /// - Parameter location: The temporary file location.
/// - Returns: Destination `URL`. /// - Returns: Destination `URL`.
private func moveTheme(from location: URL) throws -> URL { static func moveTheme(from location: URL) throws -> URL {
var tmpFileName = location.lastPathComponent var tmpFileName = location.lastPathComponent
tmpFileName = tmpFileName.replacingOccurrences(of: ".tmp", with: ".zip") tmpFileName = tmpFileName.replacingOccurrences(of: ".tmp", with: ".zip")
let fileUrl = downloadDirectory().appendingPathComponent("\(tmpFileName)") let fileUrl = downloadDirectory().appendingPathComponent("\(tmpFileName)")
@ -52,7 +67,7 @@ public class ArticleThemeDownloader {
/// Unzips the zip file /// Unzips the zip file
/// - Parameter location: Location of the zip archive. /// - Parameter location: Location of the zip archive.
/// - Returns: Enclosed `.nnwtheme` file. /// - Returns: Enclosed `.nnwtheme` file.
private func unzipFile(at location: URL) throws -> URL { static func unzipFile(at location: URL) throws -> URL {
do { do {
let unzipDirectory = URL(fileURLWithPath: location.path.replacingOccurrences(of: ".zip", with: "")) 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 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. /// Performs a deep search of the unzipped directory to find the theme file.
/// - Parameter searchPath: directory to search /// - Parameter searchPath: directory to search
/// - Returns: optional `String` /// - 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) { if let directoryContents = FileManager.default.enumerator(atPath: searchPath) {
while let file = directoryContents.nextObject() as? String { while let file = directoryContents.nextObject() as? String {
if file.hasSuffix(".nnwtheme") { if file.hasSuffix(".nnwtheme") {
@ -86,23 +100,7 @@ public class ArticleThemeDownloader {
/// The download directory used by the theme downloader: `Application Support/NetNewsWire/Downloads` /// The download directory used by the theme downloader: `Application Support/NetNewsWire/Downloads`
/// - Returns: `URL` /// - Returns: `URL`
private func downloadDirectory() -> URL { static func downloadDirectory() -> URL {
FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("NetNewsWire/Downloads", isDirectory: true) 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)
}
}
}
} }

View File

@ -345,7 +345,7 @@ private extension AppDelegate {
accountManager.suspendNetworkAll() accountManager.suspendNetworkAll()
accountManager.suspendDatabaseAll() accountManager.suspendDatabaseAll()
ArticleThemeDownloader.shared.cleanUp() ArticleThemeDownloader.cleanUp()
CoalescingQueue.standard.performCallsImmediately() CoalescingQueue.standard.performCallsImmediately()
for scene in UIApplication.shared.connectedScenes { for scene in UIApplication.shared.connectedScenes {

View File

@ -192,7 +192,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
let location = location else { return } let location = location else { return }
do { do {
try ArticleThemeDownloader.shared.handleFile(at: location) try ArticleThemeDownloader.handleFile(at: location)
} catch { } catch {
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error]) NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error])
} }