mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-07-18 16:57:19 +02:00
22a17980ff
* feat: update to tauri beta.24 * remove .tauri * pnpm build
135 lines
3.9 KiB
Swift
135 lines
3.9 KiB
Swift
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
import Foundation
|
|
import SwiftRs
|
|
import UIKit
|
|
import WebKit
|
|
import os.log
|
|
|
|
class PluginHandle {
|
|
var instance: Plugin
|
|
var loaded = false
|
|
|
|
init(plugin: Plugin) {
|
|
instance = plugin
|
|
}
|
|
}
|
|
|
|
public class PluginManager {
|
|
static let shared: PluginManager = PluginManager()
|
|
public var viewController: UIViewController?
|
|
var plugins: [String: PluginHandle] = [:]
|
|
var ipcDispatchQueue = DispatchQueue(label: "ipc")
|
|
public var isSimEnvironment: Bool {
|
|
#if targetEnvironment(simulator)
|
|
return true
|
|
#else
|
|
return false
|
|
#endif
|
|
}
|
|
|
|
public func assetUrl(fromLocalURL url: URL?) -> URL? {
|
|
guard let inputURL = url else {
|
|
return nil
|
|
}
|
|
|
|
return URL(string: "asset://localhost")!.appendingPathComponent(inputURL.path)
|
|
}
|
|
|
|
func onWebviewCreated(_ webview: WKWebView) {
|
|
for (_, handle) in plugins {
|
|
if !handle.loaded {
|
|
handle.instance.load(webview: webview)
|
|
}
|
|
}
|
|
}
|
|
|
|
func load<P: Plugin>(name: String, plugin: P, config: String, webview: WKWebView?) {
|
|
plugin.setConfig(config)
|
|
let handle = PluginHandle(plugin: plugin)
|
|
if let webview = webview {
|
|
handle.instance.load(webview: webview)
|
|
handle.loaded = true
|
|
}
|
|
plugins[name] = handle
|
|
}
|
|
|
|
func invoke(name: String, invoke: Invoke) {
|
|
if let plugin = plugins[name] {
|
|
ipcDispatchQueue.async {
|
|
let selectorWithThrows = Selector(("\(invoke.command):error:"))
|
|
if plugin.instance.responds(to: selectorWithThrows) {
|
|
var error: NSError? = nil
|
|
withUnsafeMutablePointer(to: &error) {
|
|
let methodIMP: IMP! = plugin.instance.method(for: selectorWithThrows)
|
|
unsafeBitCast(
|
|
methodIMP, to: (@convention(c) (Any?, Selector, Invoke, OpaquePointer) -> Void).self)(
|
|
plugin.instance, selectorWithThrows, invoke, OpaquePointer($0))
|
|
}
|
|
if let error = error {
|
|
invoke.reject("\(error)")
|
|
// TODO: app crashes without this leak
|
|
let _ = Unmanaged.passRetained(error)
|
|
}
|
|
} else {
|
|
let selector = Selector(("\(invoke.command):"))
|
|
if plugin.instance.responds(to: selector) {
|
|
plugin.instance.perform(selector, with: invoke)
|
|
} else {
|
|
invoke.reject("No command \(invoke.command) found for plugin \(name)")
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
invoke.reject("Plugin \(name) not initialized")
|
|
}
|
|
}
|
|
}
|
|
|
|
extension PluginManager: NSCopying {
|
|
public func copy(with zone: NSZone? = nil) -> Any {
|
|
return self
|
|
}
|
|
}
|
|
|
|
@_cdecl("register_plugin")
|
|
func registerPlugin(name: SRString, plugin: NSObject, config: SRString, webview: WKWebView?) {
|
|
PluginManager.shared.load(
|
|
name: name.toString(),
|
|
plugin: plugin as! Plugin,
|
|
config: config.toString(),
|
|
webview: webview
|
|
)
|
|
}
|
|
|
|
@_cdecl("on_webview_created")
|
|
func onWebviewCreated(webview: WKWebView, viewController: UIViewController) {
|
|
PluginManager.shared.viewController = viewController
|
|
PluginManager.shared.onWebviewCreated(webview)
|
|
}
|
|
|
|
@_cdecl("run_plugin_command")
|
|
func runCommand(
|
|
id: Int,
|
|
name: SRString,
|
|
command: SRString,
|
|
data: SRString,
|
|
callback: @escaping @convention(c) (Int, Bool, UnsafePointer<CChar>) -> Void,
|
|
sendChannelData: @escaping @convention(c) (UInt64, UnsafePointer<CChar>) -> Void
|
|
) {
|
|
let callbackId: UInt64 = 0
|
|
let errorId: UInt64 = 1
|
|
let invoke = Invoke(
|
|
command: command.toString(), callback: callbackId, error: errorId,
|
|
sendResponse: { (fn: UInt64, payload: String?) -> Void in
|
|
let success = fn == callbackId
|
|
callback(id, success, payload ?? "null")
|
|
},
|
|
sendChannelData: { (id: UInt64, payload: String) -> Void in
|
|
sendChannelData(id, payload)
|
|
}, data: data.toString())
|
|
PluginManager.shared.invoke(name: name.toString(), invoke: invoke)
|
|
}
|