mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-28 18:47:41 +00:00
feat(mac): used shared webview for help and readme
Some checks are pending
Keyman Build Summary / Summarize build status checks (push) Waiting to run
Some checks are pending
Keyman Build Summary / Summarize build status checks (push) Waiting to run
added default and min size for config view
This commit is contained in:
parent
f194a15c7b
commit
52d41e7932
8 changed files with 267 additions and 118 deletions
|
|
@ -33,23 +33,6 @@
|
|||
D88F03DD2F50ED5100C02A31 /* ConfigUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ConfigUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
375D21122FF2F21800FCD24A /* Exceptions for "Config" folder in "Config" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
ConfigTests/ConfigTests.swift,
|
||||
);
|
||||
target = D88F03C52F50ED5000C02A31 /* Config */;
|
||||
};
|
||||
375D21132FF2F21800FCD24A /* Exceptions for "Config" folder in "ConfigTests" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
ConfigTests/ConfigTests.swift,
|
||||
);
|
||||
target = D88F03D22F50ED5100C02A31 /* ConfigTests */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
D87D6F492FAF95400083A95E /* Installation */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
|
|
@ -58,10 +41,6 @@
|
|||
};
|
||||
D88F03C82F50ED5000C02A31 /* Config */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
375D21122FF2F21800FCD24A /* Exceptions for "Config" folder in "Config" target */,
|
||||
375D21132FF2F21800FCD24A /* Exceptions for "Config" folder in "ConfigTests" target */,
|
||||
);
|
||||
path = Config;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ struct ConfigApp: App {
|
|||
var body: some Scene {
|
||||
Window("Configuration", id: "main-config") {
|
||||
MainConfigView()
|
||||
.frame(
|
||||
minWidth: 600, maxWidth: 800,
|
||||
minHeight: 400, maxHeight: .infinity
|
||||
)
|
||||
.environmentObject(settings)
|
||||
.task {
|
||||
if !installation.getHasDisplayedInstallationComplete() {
|
||||
|
|
@ -27,6 +31,10 @@ struct ConfigApp: App {
|
|||
.onReceive(NotificationCenter.default.publisher(for: .installationRepairStarted)) { notification in openWindow(id: "install")
|
||||
}
|
||||
}
|
||||
// the size of the window when first opened
|
||||
// .defaultSize(width: 1024, height: 768)
|
||||
.defaultSize(width: 800, height: 600)
|
||||
.windowResizability(.contentSize)
|
||||
Window("Installation", id: "install") {
|
||||
MainInstallView()
|
||||
.environmentObject(installation)
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*
|
||||
* Created by Gabriel Schantz on 2026-08-03
|
||||
*
|
||||
* Webview used to show help for Keyman keyboards
|
||||
*/
|
||||
import Foundation
|
||||
|
||||
import SwiftUI
|
||||
import WebKit
|
||||
import KeymanSettings
|
||||
|
||||
public struct HelpView: NSViewRepresentable {
|
||||
let helpFileURL: URL
|
||||
|
||||
// create the AppKit view instance
|
||||
public func makeNSView(context: Context) -> WKWebView {
|
||||
let webView = WKWebView()
|
||||
return webView
|
||||
}
|
||||
|
||||
// update the view when SwiftUI state changes
|
||||
public func updateNSView(_ nsView: WKWebView, context: Context) {
|
||||
let request = URLRequest(url: helpFileURL)
|
||||
|
||||
// only load the request if it's not already loading/loaded to prevent infinite loops
|
||||
if nsView.url != helpFileURL {
|
||||
if let helpUrl = request.url {
|
||||
nsView.loadFileURL(helpUrl, allowingReadAccessTo: helpUrl.deletingLastPathComponent())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ struct MainConfigView: View {
|
|||
@State private var packageSelectedForHelpUrl: URL? = nil
|
||||
|
||||
// for drag and drop package installation
|
||||
@State private var dropError: DropKmpError?
|
||||
@State private var packageInstallHelper: PackageInstallHelper? = nil
|
||||
@State private var isShowingDropKmpAlert = false
|
||||
@State private var alertMessage = ""
|
||||
@State private var isHovering = false
|
||||
|
|
@ -77,23 +77,37 @@ struct MainConfigView: View {
|
|||
return false // the drop failed
|
||||
}
|
||||
do {
|
||||
try settings.processDroppedKmpFile(at: droppedFileUrl)
|
||||
packageInstallHelper = try settings.initiateKmpFileInstallation(at: droppedFileUrl)
|
||||
return true // the drop was successful
|
||||
} catch {
|
||||
self.alertMessage = error.localizedDescription
|
||||
self.isShowingDropKmpAlert = true
|
||||
return false
|
||||
|
||||
}
|
||||
} isTargeted: { hovering in
|
||||
isHovering = hovering
|
||||
}
|
||||
// alert triggers automatically when $dropError becomes non-nil
|
||||
// alert triggers automatically when $isShowingDropKmpAlert is true
|
||||
.alert("Package Installation Failed", isPresented: $isShowingDropKmpAlert) {
|
||||
Button("OK", role: .cancel) { }
|
||||
} message: {
|
||||
Text(alertMessage)
|
||||
}
|
||||
.sheet(item: $packageInstallHelper) { helper in
|
||||
PackageInstallView(installHelper: helper) { accepted in
|
||||
if accepted {
|
||||
print("Processing validated package: \(helper.packageName ?? "unknown package")")
|
||||
do {
|
||||
try helper.install()
|
||||
} catch {
|
||||
print("failed to install package: \(helper.packageName ?? "unknown package") with error: \(error.localizedDescription)")
|
||||
}
|
||||
} else {
|
||||
settings.userCanceledPackageInstallation()
|
||||
}
|
||||
packageInstallHelper = nil
|
||||
}
|
||||
}
|
||||
|
||||
// the Spacer pushes the contents of the VStack to the top of the VStack
|
||||
Spacer()
|
||||
|
|
@ -103,7 +117,7 @@ struct MainConfigView: View {
|
|||
.tag(0)
|
||||
|
||||
if let url = packageSelectedForHelpUrl {
|
||||
HelpView(helpFileURL: url)
|
||||
PackageContentWebView(packageFileUrl: url)
|
||||
.padding()
|
||||
.tabItem { Text("Help") }
|
||||
.tag(1)
|
||||
|
|
|
|||
72
mac/Config/Config/PackageContentWebView.swift
Normal file
72
mac/Config/Config/PackageContentWebView.swift
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*
|
||||
* Created by Gabriel Schantz on 2026-08-03
|
||||
*
|
||||
* Webview used to display html content from within the Keyman package
|
||||
* Any http links clicked are opened in a browser window
|
||||
*/
|
||||
import Foundation
|
||||
|
||||
import SwiftUI
|
||||
import WebKit
|
||||
import KeymanSettings
|
||||
|
||||
public struct PackageContentWebView: NSViewRepresentable {
|
||||
let packageFileUrl: URL
|
||||
|
||||
// create the AppKit view instance
|
||||
public func makeNSView(context: Context) -> WKWebView {
|
||||
let webView = WKWebView()
|
||||
|
||||
// Connect the delegate to catch link clicks
|
||||
webView.navigationDelegate = context.coordinator
|
||||
|
||||
return webView
|
||||
}
|
||||
|
||||
// update the view when SwiftUI state changes
|
||||
public func updateNSView(_ nsView: WKWebView, context: Context) {
|
||||
let request = URLRequest(url: packageFileUrl)
|
||||
|
||||
// only load the request if it's not already loading/loaded to prevent infinite loops
|
||||
if nsView.url != packageFileUrl {
|
||||
if let fileUrl = request.url {
|
||||
nsView.loadFileURL(fileUrl, allowingReadAccessTo: fileUrl.deletingLastPathComponent())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Coordinator acts as the WKNavigationDelegate
|
||||
*/
|
||||
public func makeCoordinator() -> Coordinator {
|
||||
Coordinator()
|
||||
}
|
||||
|
||||
/**
|
||||
* If a url links to the web rather than locally, open it in the default browser
|
||||
*/
|
||||
@MainActor
|
||||
public class Coordinator: NSObject, WKNavigationDelegate {
|
||||
public func webView(_ webView: WKWebView,
|
||||
decidePolicyFor navigationAction: WKNavigationAction,
|
||||
decisionHandler: @escaping @MainActor @Sendable (WKNavigationActionPolicy) -> Void) {
|
||||
|
||||
// check whether the user clicked a link
|
||||
if navigationAction.navigationType == .linkActivated,
|
||||
let url = navigationAction.request.url {
|
||||
|
||||
// if it is an external link, intercept it and open it a browser window
|
||||
if url.scheme == "http" || url.scheme == "https" {
|
||||
NSWorkspace.shared.open(url) // opens default macOS browser
|
||||
decisionHandler(.cancel) // blocks the webview from loading it
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// allow local navigation
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
}
|
||||
}
|
||||
48
mac/Config/Config/PackageInstallView.swift
Normal file
48
mac/Config/Config/PackageInstallView.swift
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*
|
||||
* Created by Shawn Schantz on 2026-06-16
|
||||
*
|
||||
* View presented as modal sheet in response to initiating a package installation.
|
||||
* Displays readme.htm contents and allows user to proceed with install or cancel.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import KeymanSettings
|
||||
|
||||
struct PackageInstallView: View {
|
||||
let installHelper: PackageInstallHelper
|
||||
let completion: (Bool) -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 20) {
|
||||
Text("Install Package?")
|
||||
.font(.headline)
|
||||
|
||||
Text("Ready to install: \(installHelper.packageName ?? "unknown package")")
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
if let readmeFileUrl = installHelper.packageToInstall?.readmeFileUrl {
|
||||
PackageContentWebView(packageFileUrl: readmeFileUrl)
|
||||
.padding()
|
||||
} else {
|
||||
Text("Read me not available.")
|
||||
.font(.title)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Button("Cancel") {
|
||||
completion(false)
|
||||
}
|
||||
.keyboardShortcut(.cancelAction)
|
||||
|
||||
Button("Accept & Install") {
|
||||
completion(true)
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.frame(width: 540, height: 400)
|
||||
}
|
||||
}
|
||||
|
|
@ -89,8 +89,8 @@ public class SettingsContainer : ObservableObject {
|
|||
@Published public private(set) var singleKeyboardPackages: [KeymanPackage]
|
||||
@Published public private(set) var multiKeyboardPackages: [KeymanPackage]
|
||||
|
||||
// when a new package is downloaded, it is tracked here
|
||||
public private(set) var packageDownload: PackageInstallHelper? = nil
|
||||
// when a new package is being installed, it is tracked here
|
||||
public private(set) var packageInstall: PackageInstallHelper? = nil
|
||||
|
||||
fileprivate let packageRepository: PackageRepo
|
||||
fileprivate let defaultsRepository: DefaultsRepo
|
||||
|
|
@ -176,7 +176,7 @@ public class SettingsContainer : ObservableObject {
|
|||
@objc func newPackageInstalled(_ notification: Notification) {
|
||||
print("newPackageInstalled notification received")
|
||||
self.addInstalledPackage()
|
||||
self.packageDownload = nil
|
||||
self.packageInstall = nil
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -185,7 +185,7 @@ public class SettingsContainer : ObservableObject {
|
|||
@objc func existingPackageReplaced(_ notification: Notification) {
|
||||
print("existingPackageReplaced notification received")
|
||||
self.replaceInstalledPackage()
|
||||
self.packageDownload = nil
|
||||
self.packageInstall = nil
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -213,11 +213,11 @@ public class SettingsContainer : ObservableObject {
|
|||
* Called when user approves the downgrade of package
|
||||
*/
|
||||
public func userConfirmedPackageDowngrade() {
|
||||
if let download = self.packageDownload {
|
||||
if let install = self.packageInstall {
|
||||
do {
|
||||
try download.replaceExistingPackageWithNewPackage()
|
||||
try install.replaceExistingPackageWithNewPackage()
|
||||
} catch {
|
||||
print("unable to downgrade package: \(download.packageToInstall?.packageName ?? "unknown")")
|
||||
print("unable to downgrade package: \(install.packageToInstall?.packageName ?? "unknown")")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -226,14 +226,26 @@ public class SettingsContainer : ObservableObject {
|
|||
* Called when user chooses to cancel downgrade of package
|
||||
*/
|
||||
public func userCanceledPackageDowngrade() {
|
||||
if let download = self.packageDownload {
|
||||
if let install = self.packageInstall {
|
||||
print("user cancelled package downgrade")
|
||||
download.cleanupFailedInstallation()
|
||||
install.cleanupFailedInstallation()
|
||||
}
|
||||
|
||||
self.packageDownload = nil
|
||||
self.packageInstall = nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when user chooses to cancel downgrade of package
|
||||
*/
|
||||
public func userCanceledPackageInstallation() {
|
||||
if let install = self.packageInstall {
|
||||
print("user cancelled package installation")
|
||||
install.cleanupFailedInstallation()
|
||||
}
|
||||
|
||||
self.packageInstall = nil
|
||||
}
|
||||
|
||||
/**
|
||||
* for debugging: prints UserDefaults values
|
||||
*/
|
||||
|
|
@ -447,7 +459,7 @@ public class SettingsContainer : ObservableObject {
|
|||
*/
|
||||
public func isDownloadInProgress() -> Bool {
|
||||
// MAC-CONFIG-TODO: add logic, this does not actually prevent downloads when hard-coded to true
|
||||
return self.packageDownload != nil
|
||||
return self.packageInstall != nil
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -461,7 +473,7 @@ public class SettingsContainer : ObservableObject {
|
|||
|
||||
let packageDownload = PackageInstallHelper(filename: kmpFileName, packageName: packageName, packageRepo: self.packageRepository, installedPackages: self.installedPackages, isDownload: true)
|
||||
|
||||
self.packageDownload = packageDownload
|
||||
self.packageInstall = packageDownload
|
||||
return packageDownload.temporaryKmpFileLocation
|
||||
}
|
||||
|
||||
|
|
@ -472,14 +484,14 @@ public class SettingsContainer : ObservableObject {
|
|||
public func packageDownloadComplete(kmpFileUrl: URL) throws {
|
||||
print ("packageDownloadComplete \(kmpFileUrl)")
|
||||
|
||||
try self.packageDownload?.packageDownloadComplete(for: kmpFileUrl)
|
||||
try self.packageInstall?.packageDownloadComplete(for: kmpFileUrl)
|
||||
}
|
||||
|
||||
/**
|
||||
* The package is approved for installation, so add it to the package list and update the UserDefaults for enabled keyboards
|
||||
*/
|
||||
func addInstalledPackage() {
|
||||
if let package = self.packageDownload?.packageToInstall {
|
||||
if let package = self.packageInstall?.packageToInstall {
|
||||
self.installedPackages.append(package)
|
||||
self.addEnabledKeyboards(for: package)
|
||||
}
|
||||
|
|
@ -490,7 +502,7 @@ public class SettingsContainer : ObservableObject {
|
|||
* Also update the UserDefaults for enabled keyboards because the new package is enabled by default, and the existing may be disabled
|
||||
*/
|
||||
func replaceInstalledPackage() {
|
||||
if let package = self.packageDownload?.packageToInstall {
|
||||
if let package = self.packageInstall?.packageToInstall {
|
||||
if let index = self.installedPackages.firstIndex(where: { $0.packageName == package.packageName }) {
|
||||
self.installedPackages[index] = package
|
||||
self.addEnabledKeyboards(for: package)
|
||||
|
|
@ -516,27 +528,54 @@ public class SettingsContainer : ObservableObject {
|
|||
// try self.installDroppedKmpFile(from: fileLocation, to: destinationURL)
|
||||
|
||||
let droppedFilename = fileLocation.lastPathComponent
|
||||
if let packageDownload = self.preparePackageDrop(kmpFileName: droppedFilename) {
|
||||
self.packageDownload = packageDownload
|
||||
if let packageDownload = self.preparePackageDrop(kmpFilename: droppedFilename) {
|
||||
self.packageInstall = packageDownload
|
||||
do {
|
||||
try packageDownload.prepareToInstall(for: fileLocation)
|
||||
} catch {
|
||||
// clear failed download
|
||||
self.packageDownload = nil
|
||||
self.packageInstall = nil
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to install a package from a KMP file. Called when file is dropped on the Configuration view
|
||||
*/
|
||||
public func initiateKmpFileInstallation(at fileLocation: URL) throws -> PackageInstallHelper? {
|
||||
guard !self.isDownloadInProgress() else {
|
||||
throw InstallPackageError.downloadInProgress
|
||||
}
|
||||
|
||||
// validate the URL of the KMP file
|
||||
try self.validateDroppedFile(from: fileLocation)
|
||||
|
||||
let kmpFilename = fileLocation.lastPathComponent
|
||||
|
||||
if let helper = self.preparePackageDrop(kmpFilename: kmpFilename) {
|
||||
self.packageInstall = helper
|
||||
do {
|
||||
try helper.prepareToInstall(for: fileLocation)
|
||||
} catch {
|
||||
// clear failed download
|
||||
self.packageInstall = nil
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return self.packageInstall
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PackageInstallHelper instance to manage the state of the package being downloaded with the specified name.
|
||||
* Returns a URL to the temporary location where the package is to be downloaded as a .kmp file.
|
||||
*/
|
||||
public func preparePackageDrop(kmpFileName: String) -> PackageInstallHelper? {
|
||||
public func preparePackageDrop(kmpFilename: String) -> PackageInstallHelper? {
|
||||
// package name is filename minus .kmp extension
|
||||
let packageName = kmpFileName.replacingOccurrences(of: kmpFileExtension, with: "")
|
||||
let packageName = kmpFilename.replacingOccurrences(of: kmpFileExtension, with: "")
|
||||
|
||||
return PackageInstallHelper(filename: kmpFileName, packageName: packageName, packageRepo: self.packageRepository, installedPackages: self.installedPackages, isDownload: false)
|
||||
return PackageInstallHelper(filename: kmpFilename, packageName: packageName, packageRepo: self.packageRepository, installedPackages: self.installedPackages, isDownload: false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -584,29 +623,29 @@ public class SettingsContainer : ObservableObject {
|
|||
/**
|
||||
* Install the package from the dropped kmp file at the specified location
|
||||
*/
|
||||
func installDroppedKmpFile(from droppedFileUrl: URL, to installPackageLocation: URL) throws {
|
||||
var newPackage: KeymanPackage? = nil
|
||||
|
||||
try self.packageRepository.unzipKmpFile(at: droppedFileUrl, to: installPackageLocation)
|
||||
|
||||
do {
|
||||
// load the unzipped package and get a reference to it
|
||||
newPackage = try self.packageRepository.loadSinglePackage(packageUrl: installPackageLocation)
|
||||
}
|
||||
catch {
|
||||
// the package could not be loaded, so remove it from the installation directory
|
||||
self.removeFailedInstallation(at: installPackageLocation)
|
||||
|
||||
// re-throw error to notify user of reason installation failed
|
||||
throw error
|
||||
}
|
||||
|
||||
if let installedPackage = newPackage {
|
||||
// add the newly installed package to the array and enable its keyboards
|
||||
self.installedPackages.append(installedPackage)
|
||||
self.addEnabledKeyboards(for: installedPackage)
|
||||
}
|
||||
}
|
||||
// func installDroppedKmpFile(from droppedFileUrl: URL, to installPackageLocation: URL) throws {
|
||||
// var newPackage: KeymanPackage? = nil
|
||||
//
|
||||
// try self.packageRepository.unzipKmpFile(at: droppedFileUrl, to: installPackageLocation)
|
||||
//
|
||||
// do {
|
||||
// // load the unzipped package and get a reference to it
|
||||
// newPackage = try self.packageRepository.loadSinglePackage(packageUrl: installPackageLocation)
|
||||
// }
|
||||
// catch {
|
||||
// // the package could not be loaded, so remove it from the installation directory
|
||||
// self.removeFailedInstallation(at: installPackageLocation)
|
||||
//
|
||||
// // re-throw error to notify user of reason installation failed
|
||||
// throw error
|
||||
// }
|
||||
//
|
||||
// if let installedPackage = newPackage {
|
||||
// // add the newly installed package to the array and enable its keyboards
|
||||
// self.installedPackages.append(installedPackage)
|
||||
// self.addEnabledKeyboards(for: installedPackage)
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Clean up after a failed drag and drop installation
|
||||
|
|
|
|||
|
|
@ -12,15 +12,22 @@
|
|||
import Foundation
|
||||
|
||||
@MainActor // run on the main actor as it is called from SettingsContainer
|
||||
public class PackageInstallHelper {
|
||||
public class PackageInstallHelper: Identifiable {
|
||||
public let id = UUID()
|
||||
// public var installationError: LocalizedError?
|
||||
// public var errorMessage: String?
|
||||
let temporaryKmpFileLocation: URL
|
||||
let temporaryPackageLocation: URL
|
||||
let installPackageLocation: URL
|
||||
let installedPackages: [KeymanPackage] // needed to check for existing package after download
|
||||
let isDownload: Bool // if not download, then the package was opened from disk or dropped
|
||||
var packageToInstall: KeymanPackage? // the newly downloaded package
|
||||
public private(set) var packageToInstall: KeymanPackage? // the newly downloaded package
|
||||
var packageToReplace: KeymanPackage? // the package to replace, if it exists
|
||||
|
||||
public var packageName: String? {
|
||||
return packageToInstall?.packageName
|
||||
}
|
||||
|
||||
fileprivate let packageRepository: PackageRepo
|
||||
|
||||
public init(filename: String, packageName: String, packageRepo: PackageRepo, installedPackages: [KeymanPackage], isDownload: Bool) {
|
||||
|
|
@ -48,14 +55,13 @@ public class PackageInstallHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Indicates that a package is ready to be unzipped and installed
|
||||
* Indicates that a package is ready to be unzipped and loaded
|
||||
*/
|
||||
public func prepareToInstall(for kmpFileUrl: URL) throws {
|
||||
print ("prepareToInstall \(kmpFileUrl)")
|
||||
|
||||
do {
|
||||
try self.unzipDownloadedPackage(for: kmpFileUrl)
|
||||
try self.handleNewPackage()
|
||||
try self.unzipPackage(for: kmpFileUrl)
|
||||
} catch {
|
||||
self.cleanupFailedInstallation()
|
||||
print ("package installation failed with error '\(error)' for \(kmpFileUrl)")
|
||||
|
|
@ -63,10 +69,25 @@ public class PackageInstallHelper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that a package is ready to be unzipped and installed
|
||||
*/
|
||||
public func install() throws {
|
||||
print ("install \(self.packageToInstall?.packageName ?? "unknown package")")
|
||||
|
||||
do {
|
||||
try self.handleNewPackage()
|
||||
} catch {
|
||||
self.cleanupFailedInstallation()
|
||||
print ("package installation failed with error '\(self.packageToInstall?.packageName ?? "unknown package")")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unzip and load the downloaded package
|
||||
*/
|
||||
func unzipDownloadedPackage(for kmpFileUrl: URL) throws {
|
||||
func unzipPackage(for kmpFileUrl: URL) throws {
|
||||
try self.packageRepository.unzipKmpFile(at: kmpFileUrl, to: self.temporaryPackageLocation)
|
||||
|
||||
// load the unzipped package from the temporary location and save a reference to it
|
||||
|
|
@ -145,14 +166,16 @@ public class PackageInstallHelper {
|
|||
* Clean up the downloaded .kmp file and package folder
|
||||
*/
|
||||
func cleanupFailedInstallation() {
|
||||
print("cleanupFailedInstallation of: \(self.temporaryPackageLocation.lastPathComponent)")
|
||||
do {
|
||||
try self.deleteDownloadedKmpFile()
|
||||
} catch {
|
||||
print("cleanupFailedInstallation did not delete downloaded .kmp file: \(self.temporaryKmpFileLocation.lastPathComponent)")
|
||||
// we only have a .kmp file in the temp directory for downloads
|
||||
if (self.isDownload) {
|
||||
do {
|
||||
try self.deleteDownloadedKmpFile()
|
||||
} catch {
|
||||
print("cleanupFailedInstallation did not delete downloaded .kmp file: \(self.temporaryKmpFileLocation.lastPathComponent)")
|
||||
}
|
||||
}
|
||||
do {
|
||||
try self.deleteDownloadedPackage()
|
||||
try self.deleteUnzippedPackage()
|
||||
} catch {
|
||||
print("cleanupFailedInstallation did not delete downloaded package: \(self.temporaryPackageLocation.lastPathComponent)")
|
||||
}
|
||||
|
|
@ -185,9 +208,9 @@ public class PackageInstallHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Delete the downloaded package from the temp directory
|
||||
* Delete the unzipped package in the temp directory
|
||||
*/
|
||||
func deleteDownloadedPackage() throws {
|
||||
func deleteUnzippedPackage() throws {
|
||||
try FileManager.default.removeItem(at: self.temporaryPackageLocation)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue