From a1b01384d334ace1cd3b80e089c217960b0c82e7 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Mon, 20 Sep 2021 19:34:25 +0800 Subject: [PATCH] refactors downloader code for macOS & iOS More consistent code across platforms. --- Mac/AppDelegate.swift | 11 +++ .../AppDelegate+Scriptability.swift | 32 +-------- NetNewsWire.xcodeproj/project.pbxproj | 6 ++ .../ArticleThemeDownloader.swift | 70 +++++++++++++++++++ iOS/SceneDelegate.swift | 66 +++++------------ 5 files changed, 106 insertions(+), 79 deletions(-) create mode 100644 Shared/ArticleStyles/ArticleThemeDownloader.swift diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index afdbbfd56..b7b41e015 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -125,6 +125,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(inspectableObjectsDidChange(_:)), name: .InspectableObjectsDidChange, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(importDownloadedTheme(_:)), name: .didEndDownloadingTheme, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(didWakeNotification(_:)), name: NSWorkspace.didWakeNotification, object: nil) appDelegate = self @@ -375,6 +376,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, @objc func didWakeNotification(_ note: Notification) { fireOldTimers() } + + @objc func importDownloadedTheme(_ note: Notification) { + guard let userInfo = note.userInfo, + let url = userInfo["url"] as? URL else { + return + } + DispatchQueue.main.async { + self.importTheme(filename: url.path) + } + } // MARK: Main Window diff --git a/Mac/Scriptability/AppDelegate+Scriptability.swift b/Mac/Scriptability/AppDelegate+Scriptability.swift index b8ad7ee3d..41a7e22bc 100644 --- a/Mac/Scriptability/AppDelegate+Scriptability.swift +++ b/Mac/Scriptability/AppDelegate+Scriptability.swift @@ -56,40 +56,12 @@ extension AppDelegate : AppDelegateAppleEvents { if let themeURL = URL(string: themeURLString) { let request = URLRequest(url: themeURL) let task = URLSession.shared.downloadTask(with: request) { location, response, error in - var downloadDirectory = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first! - try? FileManager.default.createDirectory(at: downloadDirectory, withIntermediateDirectories: true, attributes: nil) - let tmpFileName = UUID().uuidString + ".zip" - downloadDirectory.appendPathComponent("\(tmpFileName)") - if location == nil { + guard let location = location else { return } do { - try FileManager.default.moveItem(at: location!, to: downloadDirectory) - - var unzippedDir = downloadDirectory - unzippedDir = unzippedDir.deletingLastPathComponent() - unzippedDir.appendPathComponent("newtheme.nnwtheme") - - try Zip.unzipFile(downloadDirectory, destination: unzippedDir, overwrite: true, password: nil, progress: nil, fileOutputHandler: nil) - try FileManager.default.removeItem(at: downloadDirectory) - - let decoder = PropertyListDecoder() - let plistURL = URL(fileURLWithPath: unzippedDir.appendingPathComponent("Info.plist").path) - - let data = try Data(contentsOf: plistURL) - let plist = try decoder.decode(ArticleThemePlist.self, from: data) - - // rename - var renamedUnzippedDir = unzippedDir.deletingLastPathComponent() - renamedUnzippedDir.appendPathComponent(plist.name + ".nnwtheme") - if FileManager.default.fileExists(atPath: renamedUnzippedDir.path) { - try FileManager.default.removeItem(at: renamedUnzippedDir) - } - try FileManager.default.moveItem(at: unzippedDir, to: renamedUnzippedDir) - DispatchQueue.main.async { - self.importTheme(filename: renamedUnzippedDir.path) - } + try ArticleThemeDownloader.handleFile(at: location) } catch { print(error) } diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 80ff1568c..be116d4ca 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -140,6 +140,8 @@ 17D5F17124B0BC6700375168 /* SidebarToolbarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */; }; 17D5F17224B0BC6700375168 /* SidebarToolbarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */; }; 17D5F19524B0C1DD00375168 /* SidebarToolbarModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172199F024AB716900A31D04 /* SidebarToolbarModifier.swift */; }; + 17D643B126F8A436008D4C05 /* ArticleThemeDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D643B026F8A436008D4C05 /* ArticleThemeDownloader.swift */; }; + 17D643B226F8A436008D4C05 /* ArticleThemeDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D643B026F8A436008D4C05 /* ArticleThemeDownloader.swift */; }; 17D7586F2679C21800B17787 /* OnePasswordExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D7586E2679C21800B17787 /* OnePasswordExtension.m */; }; 17E0084625941887000C23F0 /* SizeCategories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E0084525941887000C23F0 /* SizeCategories.swift */; }; 17E4DBD624BFC53E00FE462A /* AdvancedPreferencesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E4DBD524BFC53E00FE462A /* AdvancedPreferencesModel.swift */; }; @@ -1608,6 +1610,7 @@ 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedModel.swift; sourceTree = ""; }; 17D3CEE2257C4D2300E74939 /* AddAccountSignUp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountSignUp.swift; sourceTree = ""; }; 17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarToolbarModel.swift; sourceTree = ""; }; + 17D643B026F8A436008D4C05 /* ArticleThemeDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleThemeDownloader.swift; sourceTree = ""; }; 17D7586C2679C21700B17787 /* NetNewsWire-iOS-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NetNewsWire-iOS-Bridging-Header.h"; sourceTree = ""; }; 17D7586D2679C21800B17787 /* OnePasswordExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OnePasswordExtension.h; sourceTree = ""; }; 17D7586E2679C21800B17787 /* OnePasswordExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OnePasswordExtension.m; sourceTree = ""; }; @@ -3461,6 +3464,7 @@ children = ( 849A97871ED9ECEF007D329B /* ArticleTheme.swift */, 849A97881ED9ECEF007D329B /* ArticleThemesManager.swift */, + 17D643B026F8A436008D4C05 /* ArticleThemeDownloader.swift */, 179D280C26F73D83003B2E0A /* ArticleThemePlist.swift */, 17071EEF26F8137400F5E71D /* ArticleTheme+Notifications.swift */, ); @@ -5589,6 +5593,7 @@ 512392BE24E33A3C00F11704 /* RedditSelectAccountTableViewController.swift in Sources */, 515A517B243E90260089E588 /* ExtensionPoint.swift in Sources */, 51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */, + 17D643B226F8A436008D4C05 /* ArticleThemeDownloader.swift in Sources */, 51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */, 51F9F3F723DF6DB200A314FD /* ArticleIconSchemeHandler.swift in Sources */, 512392C524E3451400F11704 /* TwitterEnterDetailTableViewController.swift in Sources */, @@ -5721,6 +5726,7 @@ 8426118A1FCB67AA0086A189 /* WebFeedIconDownloader.swift in Sources */, 84C9FC7B22629E1200D921D6 /* PreferencesControlsBackgroundView.swift in Sources */, 84162A152038C12C00035290 /* MarkCommandValidationStatus.swift in Sources */, + 17D643B126F8A436008D4C05 /* ArticleThemeDownloader.swift in Sources */, 84E95D241FB1087500552D99 /* ArticlePasteboardWriter.swift in Sources */, 849A975B1ED9EB0D007D329B /* ArticleUtilities.swift in Sources */, 849ADEE8235981A0000E1B81 /* NNW3OpenPanelAccessoryViewController.swift in Sources */, diff --git a/Shared/ArticleStyles/ArticleThemeDownloader.swift b/Shared/ArticleStyles/ArticleThemeDownloader.swift new file mode 100644 index 000000000..8ee711cde --- /dev/null +++ b/Shared/ArticleStyles/ArticleThemeDownloader.swift @@ -0,0 +1,70 @@ +// +// ArticleThemeDownloader.swift +// ArticleThemeDownloader +// +// Created by Stuart Breckenridge on 20/09/2021. +// Copyright © 2021 Ranchero Software. All rights reserved. +// + +import Foundation +import Zip + +public struct ArticleThemeDownloader { + + static func handleFile(at location: URL) throws { + #if os(iOS) + createDownloadDirectoryIfRequired() + #endif + let movedFileLocation = try moveTheme(from: location) + let unzippedFileLocation = try unzipFile(at: movedFileLocation) + let renamedFile = try renameFileToThemeName(at: unzippedFileLocation) + NotificationCenter.default.post(name: .didEndDownloadingTheme, object: nil, userInfo: ["url" : renamedFile]) + } + + private static func createDownloadDirectoryIfRequired() { + let downloadDirectory = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first! + try? FileManager.default.createDirectory(at: downloadDirectory, withIntermediateDirectories: true, attributes: nil) + } + + private static func moveTheme(from location: URL) throws -> URL { + #if os(iOS) + var downloadDirectory = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first! + #else + var downloadDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! + #endif + let tmpFileName = UUID().uuidString + ".zip" + downloadDirectory.appendPathComponent("\(tmpFileName)") + try FileManager.default.moveItem(at: location, to: downloadDirectory) + return downloadDirectory + } + + private static func unzipFile(at location: URL) throws -> URL { + var unzippedDir = location.deletingLastPathComponent() + unzippedDir.appendPathComponent("newtheme.nnwtheme") + do { + try Zip.unzipFile(location, destination: unzippedDir, overwrite: true, password: nil, progress: nil, fileOutputHandler: nil) + try FileManager.default.removeItem(at: location) + return unzippedDir + } catch { + try? FileManager.default.removeItem(at: location) + throw error + } + } + + private static func renameFileToThemeName(at location: URL) throws -> URL { + let decoder = PropertyListDecoder() + let plistURL = URL(fileURLWithPath: location.appendingPathComponent("Info.plist").path) + let data = try Data(contentsOf: plistURL) + let plist = try decoder.decode(ArticleThemePlist.self, from: data) + var renamedUnzippedDir = location.deletingLastPathComponent() + renamedUnzippedDir.appendPathComponent(plist.name + ".nnwtheme") + if FileManager.default.fileExists(atPath: renamedUnzippedDir.path) { + try FileManager.default.removeItem(at: renamedUnzippedDir) + } + try FileManager.default.moveItem(at: location, to: renamedUnzippedDir) + return renamedUnzippedDir + } + + + +} diff --git a/iOS/SceneDelegate.swift b/iOS/SceneDelegate.swift index 54fc4dcc3..de24868d5 100644 --- a/iOS/SceneDelegate.swift +++ b/iOS/SceneDelegate.swift @@ -29,6 +29,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(importDownloadedTheme(_:)), name: .didEndDownloadingTheme, object: nil) + if let _ = connectionOptions.urlContexts.first?.url { window?.makeKeyAndVisible() self.scene(scene, openURLContexts: connectionOptions.urlContexts) @@ -184,19 +186,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { NotificationCenter.default.post(name: .didBeginDownloadingTheme, object: nil) } let task = URLSession.shared.downloadTask(with: request) { [weak self] location, response, error in - guard let self = self, let location = location else { return } - self.createDownloadDirectoryIfRequired() + guard let self = self, + let location = location else { return } + do { - let movedFileLocation = try self.moveTheme(from: location) - let unzippedFileLocation = try self.unzipFile(at: movedFileLocation) - let renamedFileLocation = try self.renameFileToThemeName(at: unzippedFileLocation) - DispatchQueue.main.async { - NotificationCenter.default.post(name: .didEndDownloadingTheme, object: nil) - self.coordinator.importTheme(filename: renamedFileLocation.path) - } + try ArticleThemeDownloader.handleFile(at: location) } catch { DispatchQueue.main.async { - NotificationCenter.default.post(name: .didEndDownloadingThemeWithError, object: nil, userInfo: ["error" : error]) self.showAlert(error) } } @@ -213,46 +209,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } - // MARK: - Theme Downloader - private func createDownloadDirectoryIfRequired() { - let downloadDirectory = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first! - try? FileManager.default.createDirectory(at: downloadDirectory, withIntermediateDirectories: true, attributes: nil) - } - private func moveTheme(from location: URL) throws -> URL { - var downloadDirectory = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first! - let tmpFileName = UUID().uuidString + ".zip" - downloadDirectory.appendPathComponent("\(tmpFileName)") - try FileManager.default.moveItem(at: location, to: downloadDirectory) - return downloadDirectory - } - - private func unzipFile(at location: URL) throws -> URL { - var unzippedDir = location.deletingLastPathComponent() - unzippedDir.appendPathComponent("newtheme.nnwtheme") - do { - try Zip.unzipFile(location, destination: unzippedDir, overwrite: true, password: nil, progress: nil, fileOutputHandler: nil) - try FileManager.default.removeItem(at: location) - return unzippedDir - } catch { - try? FileManager.default.removeItem(at: location) - throw error - } - } - - private func renameFileToThemeName(at location: URL) throws -> URL { - let decoder = PropertyListDecoder() - let plistURL = URL(fileURLWithPath: location.appendingPathComponent("Info.plist").path) - let data = try Data(contentsOf: plistURL) - let plist = try decoder.decode(ArticleThemePlist.self, from: data) - var renamedUnzippedDir = location.deletingLastPathComponent() - renamedUnzippedDir.appendPathComponent(plist.name + ".nnwtheme") - if FileManager.default.fileExists(atPath: renamedUnzippedDir.path) { - try FileManager.default.removeItem(at: renamedUnzippedDir) - } - try FileManager.default.moveItem(at: location, to: renamedUnzippedDir) - return renamedUnzippedDir - } private func showAlert(_ error: Error) { let alert = UIAlertController(title: NSLocalizedString("Error", comment: "Error"), @@ -295,4 +252,15 @@ private extension SceneDelegate { } } + @objc func importDownloadedTheme(_ note: Notification) { + guard let userInfo = note.userInfo, + let url = userInfo["url"] as? URL else { + return + } + + DispatchQueue.main.async { + self.coordinator.importTheme(filename: url.path) + } + } + }