mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-26 17:47:40 +00:00
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled
download .kmp and unzip in temp folder then decide whether to replace existing package
170 lines
5.7 KiB
Swift
170 lines
5.7 KiB
Swift
/*
|
|
* Keyman is copyright (C) SIL Global. MIT License.
|
|
*
|
|
* Created by Shawn Schantz on 2026-06-16
|
|
*
|
|
* Webview to search for Keyman keyboards
|
|
*/
|
|
|
|
import Foundation
|
|
|
|
import SwiftUI
|
|
import WebKit
|
|
import KeymanSettings
|
|
|
|
struct KeyboardSearchView: NSViewRepresentable {
|
|
@EnvironmentObject var settings: SettingsContainer
|
|
|
|
// note that the EnvironmentObject is not available within init (if we were to implement that)
|
|
// it is injected just before makeNSView and updateNSView are called
|
|
|
|
let searchURL = URL(string: "https://keyman.com/go/macos/14.0/download-keyboards/?version=19.0.284")!
|
|
|
|
|
|
// Creates the Coordinator to handle WebKit delegate methods
|
|
func makeCoordinator() -> Coordinator {
|
|
Coordinator()
|
|
}
|
|
|
|
// Creates the underlying NSView (WKWebView) for macOS
|
|
func makeNSView(context: Context) -> WKWebView {
|
|
print("makeNSView called")
|
|
let webView = WKWebView()
|
|
|
|
// assign the coordinator as the navigation delegate
|
|
webView.navigationDelegate = context.coordinator
|
|
|
|
let request = URLRequest(url: searchURL)
|
|
webView.load(request)
|
|
return webView
|
|
}
|
|
|
|
/**
|
|
* Updates the view when the state changes.
|
|
* This is a safe place to pass the SettingsContainer to the Coordinator
|
|
* as the environment has been loaded by now.
|
|
*/
|
|
|
|
func updateNSView(_ nsView: WKWebView, context: Context) {
|
|
if context.coordinator.settings == nil {
|
|
context.coordinator.settings = self.settings
|
|
print("updateNSView, settings intialized for coordinator")
|
|
}
|
|
}
|
|
|
|
class Coordinator: NSObject, WKNavigationDelegate, WKDownloadDelegate {
|
|
var downloadFileUrl: URL? = nil
|
|
var settings: SettingsContainer?
|
|
|
|
func webView(_ webView: WKWebView,
|
|
decidePolicyFor navigationAction: WKNavigationAction,
|
|
preferences: WKWebpagePreferences,
|
|
decisionHandler: @escaping @MainActor (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) {
|
|
|
|
print("deciding navigation based on action")
|
|
|
|
if let url = navigationAction.request.url {
|
|
print("webView navigationAction.request.url: \(url)")
|
|
}
|
|
|
|
// Trust HTML download attribute if present
|
|
if navigationAction.shouldPerformDownload {
|
|
print("webView called decisionHandler for download")
|
|
decisionHandler(.download, preferences)
|
|
return
|
|
}
|
|
|
|
// check if URL ends with a target file extension
|
|
if let url = navigationAction.request.url {
|
|
if url.pathExtension.lowercased() == KeymanPaths.keymanPackageFileExtension {
|
|
decisionHandler(.download, preferences)
|
|
print("webView found .kmp, called decisionHandler for download")
|
|
return
|
|
}
|
|
}
|
|
|
|
decisionHandler(.allow, preferences)
|
|
}
|
|
|
|
/**
|
|
* decide whether the navigation should be allowed, canceled or result in a download
|
|
*/
|
|
func webView(_ webView: WKWebView,
|
|
decidePolicyFor navigationResponse: WKNavigationResponse,
|
|
decisionHandler: @escaping @MainActor (WKNavigationResponsePolicy) -> Void) {
|
|
print("deciding navigation based on response")
|
|
|
|
if navigationResponse.canShowMIMEType {
|
|
decisionHandler(.allow)
|
|
} else {
|
|
guard let keymanSettings = self.settings else {
|
|
print("webView decidePolicyFor:decisionHandler: no settings")
|
|
decisionHandler(.cancel)
|
|
return
|
|
}
|
|
|
|
// if a download is already in progress then stop another from starting
|
|
if keymanSettings.isDownloadInProgress() {
|
|
print("download already in progress, download canceled")
|
|
decisionHandler(.cancel)
|
|
} else {
|
|
decisionHandler(.download)
|
|
}
|
|
}
|
|
}
|
|
|
|
func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) {
|
|
print("webView navigationAction:didBecome called")
|
|
download.delegate = self // Assign delegate for file saving
|
|
}
|
|
|
|
func webView(_ webView: WKWebView,
|
|
navigationAction: WKNavigationAction,
|
|
didCommit download: WKDownload) {
|
|
print("webView navigationAction:didCommit called")
|
|
download.delegate = self
|
|
}
|
|
|
|
func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String, completionHandler: @escaping @MainActor @Sendable (URL?) -> Void) {
|
|
print("download initiated")
|
|
|
|
guard let keymanSettings = self.settings else {
|
|
print("tried to access settings before they were intialized in updateNSView")
|
|
return
|
|
}
|
|
|
|
// notify settings that a keyboard download is beginning and get the URL to
|
|
// the temporary folder where it should be downloaded
|
|
|
|
downloadFileUrl = keymanSettings.preparePackageDownload(kmpFileName: suggestedFilename)
|
|
if let downloadFileUrl {
|
|
completionHandler(downloadFileUrl)
|
|
} else {
|
|
print("could not find Keyman packages directory")
|
|
}
|
|
}
|
|
|
|
func downloadDidFinish(_ download: WKDownload) {
|
|
if let downloadFileUrl {
|
|
print("Download of \(downloadFileUrl.path()) was successful.")
|
|
if let settings {
|
|
settings.packageDownloadComplete(kmpFileUrl: downloadFileUrl)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MAC-CONFIG-TODO: remove package if it already exists
|
|
|
|
func download(_ download: WKDownload, didFailWithError error: Error, resumeData: Data?) {
|
|
print("Download failed with error: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
|
|
// The web process crashed. Reload the webview safely here.
|
|
print("WebKit process terminated unexpectedly: reloading content...")
|
|
webView.reload()
|
|
}
|
|
}
|
|
|
|
|