refactors downloader code for macOS & iOS

More consistent code across platforms.
This commit is contained in:
Stuart Breckenridge 2021-09-20 19:34:25 +08:00
parent eb8f27b457
commit a1b01384d3
5 changed files with 106 additions and 79 deletions

View File

@ -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

View File

@ -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)
}

View File

@ -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 = "<group>"; };
17D3CEE2257C4D2300E74939 /* AddAccountSignUp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountSignUp.swift; sourceTree = "<group>"; };
17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarToolbarModel.swift; sourceTree = "<group>"; };
17D643B026F8A436008D4C05 /* ArticleThemeDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleThemeDownloader.swift; sourceTree = "<group>"; };
17D7586C2679C21700B17787 /* NetNewsWire-iOS-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NetNewsWire-iOS-Bridging-Header.h"; sourceTree = "<group>"; };
17D7586D2679C21800B17787 /* OnePasswordExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OnePasswordExtension.h; sourceTree = "<group>"; };
17D7586E2679C21800B17787 /* OnePasswordExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OnePasswordExtension.m; sourceTree = "<group>"; };
@ -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 */,

View File

@ -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
}
}

View File

@ -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)
}
}
}