@capacitor-plus/ios 8.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Capacitor/Capacitor/AppUUID.swift +51 -0
- package/Capacitor/Capacitor/Array+Capacitor.swift +31 -0
- package/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift +32 -0
- package/Capacitor/Capacitor/CAPBridge.swift +25 -0
- package/Capacitor/Capacitor/CAPBridgeDelegate.swift +6 -0
- package/Capacitor/Capacitor/CAPBridgeProtocol.swift +148 -0
- package/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h +6 -0
- package/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m +5 -0
- package/Capacitor/Capacitor/CAPBridgeViewController.swift +384 -0
- package/Capacitor/Capacitor/CAPBridgedJSTypes.h +18 -0
- package/Capacitor/Capacitor/CAPBridgedJSTypes.m +43 -0
- package/Capacitor/Capacitor/CAPBridgedPlugin+getMethod.swift +13 -0
- package/Capacitor/Capacitor/CAPBridgedPlugin.h +41 -0
- package/Capacitor/Capacitor/CAPFile.swift +14 -0
- package/Capacitor/Capacitor/CAPInstanceConfiguration.h +38 -0
- package/Capacitor/Capacitor/CAPInstanceConfiguration.m +93 -0
- package/Capacitor/Capacitor/CAPInstanceConfiguration.swift +78 -0
- package/Capacitor/Capacitor/CAPInstanceDescriptor.h +167 -0
- package/Capacitor/Capacitor/CAPInstanceDescriptor.m +56 -0
- package/Capacitor/Capacitor/CAPInstanceDescriptor.swift +198 -0
- package/Capacitor/Capacitor/CAPInstancePlugin.swift +10 -0
- package/Capacitor/Capacitor/CAPLog.swift +11 -0
- package/Capacitor/Capacitor/CAPNotifications.swift +69 -0
- package/Capacitor/Capacitor/CAPPlugin+LoadInstance.swift +20 -0
- package/Capacitor/Capacitor/CAPPlugin.h +60 -0
- package/Capacitor/Capacitor/CAPPlugin.m +180 -0
- package/Capacitor/Capacitor/CAPPluginCall.h +25 -0
- package/Capacitor/Capacitor/CAPPluginCall.m +36 -0
- package/Capacitor/Capacitor/CAPPluginCall.swift +97 -0
- package/Capacitor/Capacitor/CAPPluginMethod.h +36 -0
- package/Capacitor/Capacitor/CAPPluginMethod.m +44 -0
- package/Capacitor/Capacitor/CAPPluginMethod.swift +17 -0
- package/Capacitor/Capacitor/Capacitor.h +15 -0
- package/Capacitor/Capacitor/Capacitor.modulemap +8 -0
- package/Capacitor/Capacitor/CapacitorBridge.swift +768 -0
- package/Capacitor/Capacitor/CapacitorExtension.swift +24 -0
- package/Capacitor/Capacitor/Codable/JSValueDecoder.swift +479 -0
- package/Capacitor/Capacitor/Codable/JSValueEncoder.swift +671 -0
- package/Capacitor/Capacitor/Data+Capacitor.swift +22 -0
- package/Capacitor/Capacitor/DocLinks.swift +8 -0
- package/Capacitor/Capacitor/Info.plist +24 -0
- package/Capacitor/Capacitor/JS.swift +122 -0
- package/Capacitor/Capacitor/JSExport.swift +218 -0
- package/Capacitor/Capacitor/JSTypes.swift +254 -0
- package/Capacitor/Capacitor/KeyPath.swift +60 -0
- package/Capacitor/Capacitor/KeyValueStore.swift +303 -0
- package/Capacitor/Capacitor/NotificationHandlerProtocol.swift +6 -0
- package/Capacitor/Capacitor/NotificationRouter.swift +60 -0
- package/Capacitor/Capacitor/PluginCallResult.swift +106 -0
- package/Capacitor/Capacitor/PluginConfig.swift +56 -0
- package/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift +141 -0
- package/Capacitor/Capacitor/Plugins/CapacitorCookies.swift +57 -0
- package/Capacitor/Capacitor/Plugins/CapacitorHttp.swift +57 -0
- package/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift +225 -0
- package/Capacitor/Capacitor/Plugins/Console.swift +16 -0
- package/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift +236 -0
- package/Capacitor/Capacitor/Plugins/SystemBars.swift +133 -0
- package/Capacitor/Capacitor/Plugins/WebView.swift +44 -0
- package/Capacitor/Capacitor/PrivacyInfo.xcprivacy +14 -0
- package/Capacitor/Capacitor/Router.swift +29 -0
- package/Capacitor/Capacitor/TmpViewController.swift +8 -0
- package/Capacitor/Capacitor/UIColor.swift +55 -0
- package/Capacitor/Capacitor/UIStatusBarManager+CAPHandleTapAction.m +37 -0
- package/Capacitor/Capacitor/WKWebView+Capacitor.m +15 -0
- package/Capacitor/Capacitor/WKWebView+Capacitor.swift +80 -0
- package/Capacitor/Capacitor/WebViewAssetHandler.swift +550 -0
- package/Capacitor/Capacitor/WebViewDelegationHandler.swift +361 -0
- package/Capacitor/Capacitor/assets/native-bridge.js +1029 -0
- package/Capacitor.podspec +23 -0
- package/CapacitorCordova/CapacitorCordova/CapacitorCordova.h +25 -0
- package/CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap +6 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/AppDelegate.h +8 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/AppDelegate.m +5 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDV.h +28 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailability.h +109 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailabilityDeprecated.h +21 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegate.h +49 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.h +39 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.m +149 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVConfigParser.h +31 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVConfigParser.m +81 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.h +52 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.m +116 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin+Resources.h +39 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin+Resources.m +38 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.h +76 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.m +136 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginManager.h +25 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginManager.m +77 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.h +82 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.m +216 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVScreenOrientationDelegate.h +33 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVURLProtocol.h +27 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVURLProtocol.m +74 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVViewController.h +30 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVViewController.m +37 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.h +34 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.m +55 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/NSDictionary+CordovaPreferences.h +35 -0
- package/CapacitorCordova/CapacitorCordova/Classes/Public/NSDictionary+CordovaPreferences.m +63 -0
- package/CapacitorCordova/CapacitorCordova/Info.plist +26 -0
- package/CapacitorCordova/CapacitorCordova/PrivacyInfo.xcprivacy +14 -0
- package/CapacitorCordova.podspec +26 -0
- package/LICENSE +21 -0
- package/package.json +33 -0
- package/scripts/pods_helpers.rb +12 -0
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Dispatch
|
|
3
|
+
import WebKit
|
|
4
|
+
import Cordova
|
|
5
|
+
|
|
6
|
+
internal typealias CapacitorPlugin = CAPPlugin & CAPBridgedPlugin
|
|
7
|
+
|
|
8
|
+
struct RegistrationList: Codable {
|
|
9
|
+
let packageClassList: Set<String>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
An internal class adopting a public protocol means that we have a lot of `public` methods
|
|
14
|
+
but that is by design not a mistake. And since the bridge is the center of the whole project
|
|
15
|
+
its size/complexity is unavoidable.
|
|
16
|
+
|
|
17
|
+
Quiet these warnings for the whole file.
|
|
18
|
+
*/
|
|
19
|
+
// swiftlint:disable lower_acl_than_parent
|
|
20
|
+
// swiftlint:disable file_length
|
|
21
|
+
// swiftlint:disable type_body_length
|
|
22
|
+
open class CapacitorBridge: NSObject, CAPBridgeProtocol {
|
|
23
|
+
|
|
24
|
+
// this decision is needed before the bridge is instantiated,
|
|
25
|
+
// so we need a class property to avoid duplication
|
|
26
|
+
public static var isDevEnvironment: Bool {
|
|
27
|
+
#if DEBUG
|
|
28
|
+
return true
|
|
29
|
+
#else
|
|
30
|
+
// this is needed for SPM xcframework Capacitor. Can eventually be removed when the SPM package moves to being source-based.
|
|
31
|
+
if let debugValue = Bundle.main.object(forInfoDictionaryKey: "CAPACITOR_DEBUG") as? String, debugValue == "true" {
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
return false
|
|
35
|
+
#endif
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// MARK: - CAPBridgeProtocol: Properties
|
|
39
|
+
|
|
40
|
+
public var webView: WKWebView? {
|
|
41
|
+
return bridgeDelegate?.bridgedWebView
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public let autoRegisterPlugins: Bool
|
|
45
|
+
|
|
46
|
+
public var notificationRouter: NotificationRouter
|
|
47
|
+
|
|
48
|
+
public var isSimEnvironment: Bool {
|
|
49
|
+
#if targetEnvironment(simulator)
|
|
50
|
+
return true
|
|
51
|
+
#else
|
|
52
|
+
return false
|
|
53
|
+
#endif
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public var isDevEnvironment: Bool {
|
|
57
|
+
return CapacitorBridge.isDevEnvironment
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public var userInterfaceStyle: UIUserInterfaceStyle {
|
|
61
|
+
return viewController?.traitCollection.userInterfaceStyle ?? .unspecified
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public var statusBarVisible: Bool {
|
|
65
|
+
get {
|
|
66
|
+
return !(viewController?.prefersStatusBarHidden ?? true)
|
|
67
|
+
}
|
|
68
|
+
set {
|
|
69
|
+
DispatchQueue.main.async { [weak self] in
|
|
70
|
+
(self?.viewController as? CAPBridgeViewController)?.setStatusBarVisible(newValue)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public var statusBarStyle: UIStatusBarStyle {
|
|
76
|
+
get {
|
|
77
|
+
return viewController?.preferredStatusBarStyle ?? .default
|
|
78
|
+
}
|
|
79
|
+
set {
|
|
80
|
+
DispatchQueue.main.async { [weak self] in
|
|
81
|
+
(self?.viewController as? CAPBridgeViewController)?.setStatusBarStyle(newValue)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public var statusBarAnimation: UIStatusBarAnimation {
|
|
87
|
+
get {
|
|
88
|
+
return (viewController as? CAPBridgeViewController)?.statusBarAnimation ?? .slide
|
|
89
|
+
}
|
|
90
|
+
set {
|
|
91
|
+
DispatchQueue.main.async { [weak self] in
|
|
92
|
+
(self?.viewController as? CAPBridgeViewController)?.setStatusBarAnimation(newValue)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
var tmpWindow: UIWindow?
|
|
98
|
+
static let tmpVCAppeared = Notification(name: Notification.Name(rawValue: "tmpViewControllerAppeared"))
|
|
99
|
+
public static let capacitorSite = "https://capacitorjs.com/"
|
|
100
|
+
public static let fileStartIdentifier = "/_capacitor_file_"
|
|
101
|
+
public static let httpInterceptorStartIdentifier = "/_capacitor_http_interceptor_"
|
|
102
|
+
@available(*, deprecated, message: "`httpsInterceptorStartIdentifier` is no longer required. All proxied requests are handled via `httpInterceptorStartIdentifier` instead")
|
|
103
|
+
public static let httpsInterceptorStartIdentifier = "/_capacitor_https_interceptor_"
|
|
104
|
+
public static let httpInterceptorUrlParam = "u"
|
|
105
|
+
public static let defaultScheme = "capacitor"
|
|
106
|
+
|
|
107
|
+
public private(set) var webViewAssetHandler: WebViewAssetHandler
|
|
108
|
+
public private(set) var webViewDelegationHandler: WebViewDelegationHandler
|
|
109
|
+
public private(set) weak var bridgeDelegate: CAPBridgeDelegate?
|
|
110
|
+
@objc public var viewController: UIViewController? {
|
|
111
|
+
return bridgeDelegate?.bridgedViewController
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
var lastPlugin: CAPPlugin?
|
|
115
|
+
|
|
116
|
+
@objc public var config: InstanceConfiguration
|
|
117
|
+
// Map of all loaded and instantiated plugins by pluginId -> instance
|
|
118
|
+
var plugins = [String: CapacitorPlugin]()
|
|
119
|
+
// Manager for getting Cordova plugins
|
|
120
|
+
var cordovaPluginManager: CDVPluginManager?
|
|
121
|
+
// Calls we are storing to resolve later
|
|
122
|
+
var storedCalls = ConcurrentDictionary<CAPPluginCall>()
|
|
123
|
+
// Whether to inject the Cordova files
|
|
124
|
+
private var injectCordovaFiles = false
|
|
125
|
+
private var cordovaParser: CDVConfigParser?
|
|
126
|
+
private var injectMiscFiles: [String] = []
|
|
127
|
+
private var canInjectJS: Bool = true
|
|
128
|
+
|
|
129
|
+
// Background dispatch queue for plugin calls
|
|
130
|
+
open private(set) var dispatchQueue = DispatchQueue(label: "bridge")
|
|
131
|
+
// Array of block based observers
|
|
132
|
+
var observers: [NSObjectProtocol] = []
|
|
133
|
+
|
|
134
|
+
// MARK: - CAPBridgeProtocol: Deprecated
|
|
135
|
+
|
|
136
|
+
public func getWebView() -> WKWebView? {
|
|
137
|
+
return webView
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public func isSimulator() -> Bool {
|
|
141
|
+
return isSimEnvironment
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public func isDevMode() -> Bool {
|
|
145
|
+
return isDevEnvironment
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public func getStatusBarVisible() -> Bool {
|
|
149
|
+
return statusBarVisible
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@nonobjc public func setStatusBarVisible(_ visible: Bool) {
|
|
153
|
+
statusBarVisible = visible
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public func getStatusBarStyle() -> UIStatusBarStyle {
|
|
157
|
+
return statusBarStyle
|
|
158
|
+
}
|
|
159
|
+
@nonobjc public func setStatusBarStyle(_ style: UIStatusBarStyle) {
|
|
160
|
+
statusBarStyle = style
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public func getUserInterfaceStyle() -> UIUserInterfaceStyle {
|
|
164
|
+
return userInterfaceStyle
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public func getLocalUrl() -> String {
|
|
168
|
+
return config.localURL.absoluteString
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@nonobjc public func setStatusBarAnimation(_ animation: UIStatusBarAnimation) {
|
|
172
|
+
statusBarAnimation = animation
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public func setServerBasePath(_ path: String) {
|
|
176
|
+
let url = URL(fileURLWithPath: path, isDirectory: true)
|
|
177
|
+
guard FileManager.default.fileExists(atPath: url.path) else { return }
|
|
178
|
+
config = config.updatingAppLocation(url)
|
|
179
|
+
webViewAssetHandler.setAssetPath(url.path)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// MARK: - Static Methods
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
Print a hopefully informative error message to the log when something
|
|
186
|
+
particularly dreadful happens.
|
|
187
|
+
*/
|
|
188
|
+
static func fatalError(_ error: Error, _ originalError: Error) {
|
|
189
|
+
CAPLog.print("⚡️ ❌ Capacitor: FATAL ERROR")
|
|
190
|
+
CAPLog.print("⚡️ ❌ Error was: ", originalError.localizedDescription)
|
|
191
|
+
switch error {
|
|
192
|
+
case CapacitorBridgeError.errorExportingCoreJS:
|
|
193
|
+
CAPLog.print("⚡️ ❌ Unable to export required Bridge JavaScript. Bridge will not function.")
|
|
194
|
+
CAPLog.print("⚡️ ❌ You should run \"npx capacitor copy\" to ensure the Bridge JS is added to your project.")
|
|
195
|
+
if let wke = originalError as? WKError {
|
|
196
|
+
CAPLog.print("⚡️ ❌ ", wke.userInfo)
|
|
197
|
+
}
|
|
198
|
+
default:
|
|
199
|
+
CAPLog.print("⚡️ ❌ Unknown error")
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
CAPLog.print("⚡️ ❌ Please verify your installation or file an issue")
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// MARK: - Initialization
|
|
206
|
+
|
|
207
|
+
public init(with configuration: InstanceConfiguration, delegate bridgeDelegate: CAPBridgeDelegate, cordovaConfiguration: CDVConfigParser, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler, autoRegisterPlugins: Bool = true) {
|
|
208
|
+
self.bridgeDelegate = bridgeDelegate
|
|
209
|
+
self.webViewAssetHandler = assetHandler
|
|
210
|
+
self.webViewDelegationHandler = delegationHandler
|
|
211
|
+
self.config = configuration
|
|
212
|
+
self.cordovaParser = cordovaConfiguration
|
|
213
|
+
self.notificationRouter = NotificationRouter()
|
|
214
|
+
self.notificationRouter.handleApplicationNotifications = configuration.handleApplicationNotifications
|
|
215
|
+
self.autoRegisterPlugins = autoRegisterPlugins
|
|
216
|
+
super.init()
|
|
217
|
+
|
|
218
|
+
self.webViewDelegationHandler.bridge = self
|
|
219
|
+
|
|
220
|
+
exportCoreJS(localUrl: configuration.localURL.absoluteString)
|
|
221
|
+
registerPlugins()
|
|
222
|
+
setupCordovaCompatibility()
|
|
223
|
+
exportMiscJS()
|
|
224
|
+
canInjectJS = false
|
|
225
|
+
observers.append(NotificationCenter.default.addObserver(forName: type(of: self).tmpVCAppeared.name, object: .none, queue: .none) { [weak self] _ in
|
|
226
|
+
self?.tmpWindow = nil
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
self.setupWebDebugging(configuration: configuration)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
deinit {
|
|
233
|
+
// the message handler needs to removed to avoid any retain cycles
|
|
234
|
+
webViewDelegationHandler.cleanUp()
|
|
235
|
+
for observer in observers {
|
|
236
|
+
NotificationCenter.default.removeObserver(observer)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// MARK: - Plugins
|
|
241
|
+
/**
|
|
242
|
+
Export core JavaScript to the webview
|
|
243
|
+
*/
|
|
244
|
+
func exportCoreJS(localUrl: String) {
|
|
245
|
+
do {
|
|
246
|
+
try JSExport.exportCapacitorGlobalJS(userContentController: webViewDelegationHandler.contentController,
|
|
247
|
+
isDebug: isDevEnvironment,
|
|
248
|
+
loggingEnabled: config.loggingEnabled,
|
|
249
|
+
localUrl: localUrl)
|
|
250
|
+
try JSExport.exportBridgeJS(userContentController: webViewDelegationHandler.contentController)
|
|
251
|
+
} catch {
|
|
252
|
+
type(of: self).fatalError(error, error)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
Export misc JavaScript to the webview
|
|
258
|
+
*/
|
|
259
|
+
func exportMiscJS() {
|
|
260
|
+
JSExport.exportMiscFileJS(paths: injectMiscFiles, userContentController: webViewDelegationHandler.contentController)
|
|
261
|
+
injectMiscFiles.removeAll()
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
Set up our Cordova compat by loading all known Cordova plugins and injecting their JS.
|
|
266
|
+
*/
|
|
267
|
+
func setupCordovaCompatibility() {
|
|
268
|
+
if injectCordovaFiles {
|
|
269
|
+
exportCordovaJS()
|
|
270
|
+
registerCordovaPlugins()
|
|
271
|
+
} else {
|
|
272
|
+
observers.append(NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in
|
|
273
|
+
self?.triggerDocumentJSEvent(eventName: "resume")
|
|
274
|
+
})
|
|
275
|
+
observers.append(NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in
|
|
276
|
+
self?.triggerDocumentJSEvent(eventName: "pause")
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
Export the core Cordova JS runtime
|
|
283
|
+
*/
|
|
284
|
+
func exportCordovaJS() {
|
|
285
|
+
do {
|
|
286
|
+
try JSExport.exportCordovaJS(userContentController: webViewDelegationHandler.contentController)
|
|
287
|
+
} catch {
|
|
288
|
+
type(of: self).fatalError(error, error)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
Reset the state of the bridge between navigations to avoid
|
|
294
|
+
sending data back to the page from a previous page.
|
|
295
|
+
*/
|
|
296
|
+
func reset() {
|
|
297
|
+
storedCalls.withLock { $0.removeAll() }
|
|
298
|
+
removeAllPluginListeners()
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
Register all plugins that have been declared
|
|
303
|
+
*/
|
|
304
|
+
func registerPlugins() {
|
|
305
|
+
var pluginList: [AnyClass] = [CAPHttpPlugin.self, CAPConsolePlugin.self, CAPWebViewPlugin.self, CAPCookiesPlugin.self, CAPSystemBarsPlugin.self]
|
|
306
|
+
|
|
307
|
+
if autoRegisterPlugins {
|
|
308
|
+
do {
|
|
309
|
+
if let pluginJSON = Bundle.main.url(forResource: "capacitor.config", withExtension: "json") {
|
|
310
|
+
let pluginData = try Data(contentsOf: pluginJSON)
|
|
311
|
+
let registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData)
|
|
312
|
+
|
|
313
|
+
for plugin in registrationList.packageClassList {
|
|
314
|
+
if let pluginClass = NSClassFromString(plugin) {
|
|
315
|
+
if pluginClass == CDVPlugin.self {
|
|
316
|
+
injectCordovaFiles = true
|
|
317
|
+
} else {
|
|
318
|
+
pluginList.append(pluginClass)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
} catch {
|
|
324
|
+
CAPLog.print("Error registering plugins: \(error)")
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
for plugin in pluginList {
|
|
329
|
+
if plugin is CAPInstancePlugin.Type { continue }
|
|
330
|
+
if let capPlugin = plugin as? CapacitorPlugin.Type {
|
|
331
|
+
registerPlugin(capPlugin)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
public func registerPluginType(_ pluginType: CAPPlugin.Type) {
|
|
337
|
+
if autoRegisterPlugins { return }
|
|
338
|
+
if pluginType is CAPInstancePlugin.Type {
|
|
339
|
+
Swift.fatalError("""
|
|
340
|
+
|
|
341
|
+
⚡️ ❌ Cannot register class \(pluginType): CAPInstancePlugin through registerPluginType(_:).
|
|
342
|
+
⚡️ ❌ Use `registerPluginInstance(_:)` to register subclasses of CAPInstancePlugin.
|
|
343
|
+
""")
|
|
344
|
+
}
|
|
345
|
+
guard let bridgedType = pluginType as? CapacitorPlugin.Type else { return }
|
|
346
|
+
registerPlugin(bridgedType)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
public func registerPluginInstance(_ pluginInstance: CAPPlugin) {
|
|
350
|
+
guard let pluginInstance = pluginInstance as? CapacitorPlugin else {
|
|
351
|
+
CAPLog.print("""
|
|
352
|
+
|
|
353
|
+
⚡️ Plugin \(pluginInstance.classForCoder) must conform to CAPBridgedPlugin.
|
|
354
|
+
⚡️ Not loading plugin \(pluginInstance.classForCoder)
|
|
355
|
+
""")
|
|
356
|
+
return
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if plugins[pluginInstance.jsName] != nil {
|
|
360
|
+
CAPLog.print("⚡️ Overriding existing registered plugin \(pluginInstance.classForCoder)")
|
|
361
|
+
}
|
|
362
|
+
plugins[pluginInstance.jsName] = pluginInstance
|
|
363
|
+
pluginInstance.load(on: self)
|
|
364
|
+
|
|
365
|
+
JSExport.exportJS(for: pluginInstance, in: webViewDelegationHandler.contentController)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
Register a single plugin.
|
|
370
|
+
*/
|
|
371
|
+
func registerPlugin(_ pluginType: CapacitorPlugin.Type) {
|
|
372
|
+
if let plugin = loadPlugin(type: pluginType) {
|
|
373
|
+
JSExport.exportJS(for: plugin, in: webViewDelegationHandler.contentController)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
func loadPlugin(type: CAPPlugin.Type) -> CapacitorPlugin? {
|
|
378
|
+
guard let plugin = type.init() as? CapacitorPlugin else {
|
|
379
|
+
CAPLog.print("⚡️ Unable to load plugin \(type.classForCoder()). No such module found.")
|
|
380
|
+
return nil
|
|
381
|
+
}
|
|
382
|
+
plugin.load(on: self)
|
|
383
|
+
plugins[plugin.jsName] = plugin
|
|
384
|
+
return plugin
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// MARK: - CAPBridgeProtocol: Plugin Access
|
|
388
|
+
|
|
389
|
+
@objc public func plugin(withName: String) -> CAPPlugin? {
|
|
390
|
+
return self.plugins[withName]
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// MARK: - CAPBridgeProtocol: Call Management
|
|
394
|
+
|
|
395
|
+
@objc public func saveCall(_ call: CAPPluginCall) {
|
|
396
|
+
storedCalls[call.callbackId] = call
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
@objc public func savedCall(withID: String) -> CAPPluginCall? {
|
|
400
|
+
return storedCalls[withID]
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
@objc public func releaseCall(_ call: CAPPluginCall) {
|
|
404
|
+
releaseCall(withID: call.callbackId)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
@objc public func releaseCall(withID: String) {
|
|
408
|
+
_ = storedCalls.withLock { $0.removeValue(forKey: withID) }
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// MARK: - Deprecated Versions
|
|
412
|
+
|
|
413
|
+
@objc public func getSavedCall(_ callbackId: String) -> CAPPluginCall? {
|
|
414
|
+
return savedCall(withID: callbackId)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
@objc public func releaseCall(callbackId: String) {
|
|
418
|
+
releaseCall(withID: callbackId)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// MARK: - Internal
|
|
422
|
+
|
|
423
|
+
func getDispatchQueue() -> DispatchQueue {
|
|
424
|
+
return self.dispatchQueue
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
func registerCordovaPlugins() {
|
|
428
|
+
guard let cordovaParser = cordovaParser else {
|
|
429
|
+
return
|
|
430
|
+
}
|
|
431
|
+
cordovaPluginManager = CDVPluginManager.init(parser: cordovaParser, viewController: self.viewController, webView: self.getWebView())
|
|
432
|
+
if cordovaParser.startupPluginNames.count > 0 {
|
|
433
|
+
for pluginName in cordovaParser.startupPluginNames {
|
|
434
|
+
_ = cordovaPluginManager?.getCommandInstance(pluginName as? String)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
do {
|
|
438
|
+
try JSExport.exportCordovaPluginsJS(userContentController: webViewDelegationHandler.contentController)
|
|
439
|
+
} catch {
|
|
440
|
+
type(of: self).fatalError(error, error)
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
func reload() {
|
|
445
|
+
self.getWebView()?.reload()
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
func docLink(_ url: String) -> String {
|
|
449
|
+
return "\(type(of: self).capacitorSite)docs/\(url)"
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
private func setupWebDebugging(configuration: InstanceConfiguration) {
|
|
453
|
+
let isWebDebuggable = configuration.isWebDebuggable
|
|
454
|
+
if isWebDebuggable, #unavailable(iOS 16.4) {
|
|
455
|
+
CAPLog.print("⚡️ Warning: isWebDebuggable only functions as intended on iOS 16.4 and above.")
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if #available(iOS 16.4, *) {
|
|
459
|
+
self.webView?.isInspectable = isWebDebuggable
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
Handle a call from JavaScript. First, find the corresponding plugin, construct a selector,
|
|
465
|
+
and perform that selector on the plugin instance.
|
|
466
|
+
|
|
467
|
+
Quiet the length warning because we don't want to refactor the function at this time.
|
|
468
|
+
*/
|
|
469
|
+
// swiftlint:disable:next function_body_length
|
|
470
|
+
func handleJSCall(call: JSCall) {
|
|
471
|
+
let load = {
|
|
472
|
+
NSClassFromString(call.pluginId)
|
|
473
|
+
.flatMap { $0 as? CAPPlugin.Type }
|
|
474
|
+
.flatMap(self.loadPlugin(type:))
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
guard let plugin = plugins[call.pluginId] ?? load() else {
|
|
478
|
+
CAPLog.print("⚡️ Error loading plugin \(call.pluginId) for call. Check that the pluginId is correct")
|
|
479
|
+
return
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
let selector: Selector
|
|
483
|
+
if call.method == "addListener" || call.method == "removeListener" || call.method == "removeAllListeners" {
|
|
484
|
+
selector = NSSelectorFromString(call.method + ":")
|
|
485
|
+
} else {
|
|
486
|
+
guard let method = plugin.getMethod(named: call.method) else {
|
|
487
|
+
CAPLog.print("⚡️ Error calling method \(call.method) on plugin \(call.pluginId): No method found.")
|
|
488
|
+
CAPLog.print("⚡️ Ensure plugin method exists and uses @objc in its declaration, and has been defined")
|
|
489
|
+
return
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
selector = method.selector
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if !plugin.responds(to: selector) {
|
|
496
|
+
// we don't want to break up string literals
|
|
497
|
+
// swiftlint:disable line_length
|
|
498
|
+
CAPLog.print("⚡️ Error: Plugin \(plugin.getId()) does not respond to method call \"\(call.method)\" using selector \"\(selector)\".")
|
|
499
|
+
CAPLog.print("⚡️ Ensure plugin method exists, uses @objc in its declaration, and arguments match selector without callbacks in CAP_PLUGIN_METHOD.")
|
|
500
|
+
CAPLog.print("⚡️ Learn more: \(docLink(DocLinks.CAPPluginMethodSelector.rawValue))")
|
|
501
|
+
// swiftlint:enable line_length
|
|
502
|
+
return
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Create a plugin call object and handle the success/error callbacks
|
|
506
|
+
dispatchQueue.async { [weak self] in
|
|
507
|
+
// let startTime = CFAbsoluteTimeGetCurrent()
|
|
508
|
+
|
|
509
|
+
let pluginCall = CAPPluginCall(callbackId: call.callbackId, methodName: call.method,
|
|
510
|
+
options: JSTypes.coerceDictionaryToJSObject(call.options,
|
|
511
|
+
formattingDatesAsStrings: plugin.shouldStringifyDatesInCalls) ?? [:],
|
|
512
|
+
success: {(result: CAPPluginCallResult?, pluginCall: CAPPluginCall?) in
|
|
513
|
+
if let result = result {
|
|
514
|
+
self?.toJs(result: JSResult(call: call, callResult: result), save: pluginCall?.keepAlive ?? false)
|
|
515
|
+
} else {
|
|
516
|
+
self?.toJs(result: JSResult(call: call, result: .dictionary([:])), save: pluginCall?.keepAlive ?? false)
|
|
517
|
+
}
|
|
518
|
+
}, error: {(error: CAPPluginCallError?) in
|
|
519
|
+
if let error = error {
|
|
520
|
+
self?.toJsError(error: JSResultError(call: call, callError: error))
|
|
521
|
+
} else {
|
|
522
|
+
self?.toJsError(error: JSResultError(call: call,
|
|
523
|
+
errorMessage: "",
|
|
524
|
+
errorDescription: "",
|
|
525
|
+
errorCode: nil,
|
|
526
|
+
result: .dictionary([:])))
|
|
527
|
+
}
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
if let pluginCall = pluginCall {
|
|
531
|
+
plugin.perform(selector, with: pluginCall)
|
|
532
|
+
if pluginCall.keepAlive {
|
|
533
|
+
self?.saveCall(pluginCall)
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
|
|
538
|
+
// CAPLog.print("Native call took", timeElapsed)
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
Handle a Cordova call from JavaScript. First, find the corresponding plugin,
|
|
544
|
+
construct a selector, and perform that selector on the plugin instance.
|
|
545
|
+
*/
|
|
546
|
+
func handleCordovaJSCall(call: JSCall) {
|
|
547
|
+
// Create a selector to send to the plugin
|
|
548
|
+
|
|
549
|
+
if let plugin = self.cordovaPluginManager?.getCommandInstance(call.pluginId.lowercased()) {
|
|
550
|
+
let selector = NSSelectorFromString("\(call.method):")
|
|
551
|
+
if !plugin.responds(to: selector) {
|
|
552
|
+
CAPLog.print("Error: Plugin \(plugin.className ?? "") does not respond to method call \(selector).")
|
|
553
|
+
CAPLog.print("Ensure plugin method exists and uses @objc in its declaration")
|
|
554
|
+
return
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
let arguments: [Any] = call.options["options"] as? [Any] ?? []
|
|
558
|
+
let pluginCall = CDVInvokedUrlCommand(arguments: arguments,
|
|
559
|
+
callbackId: call.callbackId,
|
|
560
|
+
className: plugin.className,
|
|
561
|
+
methodName: call.method)
|
|
562
|
+
plugin.perform(selector, with: pluginCall)
|
|
563
|
+
|
|
564
|
+
} else {
|
|
565
|
+
CAPLog.print("Error: Cordova Plugin mapping not found")
|
|
566
|
+
return
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
func removeAllPluginListeners() {
|
|
571
|
+
for plugin in plugins.values {
|
|
572
|
+
plugin.perform(#selector(CAPPlugin.removeAllListeners(_:)), with: nil)
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
Send a successful result to the JavaScript layer.
|
|
578
|
+
*/
|
|
579
|
+
func toJs(result: JSResultProtocol, save: Bool) {
|
|
580
|
+
let resultJson = result.jsonPayload()
|
|
581
|
+
CAPLog.print("⚡️ TO JS", resultJson.prefix(256))
|
|
582
|
+
|
|
583
|
+
DispatchQueue.main.async {
|
|
584
|
+
self.webView?.evaluateJavaScript("""
|
|
585
|
+
window.Capacitor.fromNative({
|
|
586
|
+
callbackId: '\(result.callbackID)',
|
|
587
|
+
pluginId: '\(result.pluginID)',
|
|
588
|
+
methodName: '\(result.methodName)',
|
|
589
|
+
save: \(save),
|
|
590
|
+
success: true,
|
|
591
|
+
data: \(resultJson)
|
|
592
|
+
})
|
|
593
|
+
""") { (_, error) in
|
|
594
|
+
if let error = error {
|
|
595
|
+
CAPLog.print(error)
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
Send an error result to the JavaScript layer.
|
|
603
|
+
*/
|
|
604
|
+
func toJsError(error: JSResultProtocol) {
|
|
605
|
+
DispatchQueue.main.async {
|
|
606
|
+
self.webView?.evaluateJavaScript("window.Capacitor.fromNative({ callbackId: '\(error.callbackID)', pluginId: '\(error.pluginID)', methodName: '\(error.methodName)', success: false, error: \(error.jsonPayload())})") { (_, error) in
|
|
607
|
+
if let error = error {
|
|
608
|
+
CAPLog.print(error)
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// MARK: - CAPBridgeProtocol: JavaScript Handling
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
Inject JavaScript from an external file before the WebView loads.
|
|
618
|
+
|
|
619
|
+
`path` is relative to the public folder
|
|
620
|
+
*/
|
|
621
|
+
public func injectScriptBeforeLoad(path: String) {
|
|
622
|
+
if canInjectJS {
|
|
623
|
+
injectMiscFiles.append(path)
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
Eval JS for a specific plugin.
|
|
629
|
+
|
|
630
|
+
`js` is a short name but needs to be preserved for backwards compatibility.
|
|
631
|
+
*/
|
|
632
|
+
// swiftlint:disable:next identifier_name
|
|
633
|
+
@objc public func evalWithPlugin(_ plugin: CAPPlugin, js: String) {
|
|
634
|
+
let wrappedJs = """
|
|
635
|
+
window.Capacitor.withPlugin('\(plugin.getId())', function(plugin) {
|
|
636
|
+
if(!plugin) { console.error('Unable to execute JS in plugin, no such plugin found for id \(plugin.getId())'); }
|
|
637
|
+
\(js)
|
|
638
|
+
});
|
|
639
|
+
"""
|
|
640
|
+
|
|
641
|
+
DispatchQueue.main.async {
|
|
642
|
+
self.getWebView()?.evaluateJavaScript(wrappedJs, completionHandler: { (_, error) in
|
|
643
|
+
if let error = error {
|
|
644
|
+
CAPLog.print("⚡️ JS Eval error", error.localizedDescription)
|
|
645
|
+
}
|
|
646
|
+
})
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
Eval JS in the web view
|
|
652
|
+
|
|
653
|
+
`js` is a short name but needs to be preserved for backwards compatibility.
|
|
654
|
+
*/
|
|
655
|
+
// swiftlint:disable:next identifier_name
|
|
656
|
+
@objc public func eval(js: String) {
|
|
657
|
+
DispatchQueue.main.async {
|
|
658
|
+
self.getWebView()?.evaluateJavaScript(js, completionHandler: { (_, error) in
|
|
659
|
+
if let error = error {
|
|
660
|
+
CAPLog.print("⚡️ JS Eval error", error.localizedDescription)
|
|
661
|
+
}
|
|
662
|
+
})
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
@objc public func triggerJSEvent(eventName: String, target: String) {
|
|
667
|
+
self.eval(js: "window.Capacitor.triggerEvent('\(eventName)', '\(target)')")
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
@objc public func triggerJSEvent(eventName: String, target: String, data: String) {
|
|
671
|
+
self.eval(js: "window.Capacitor.triggerEvent('\(eventName)', '\(target)', \(data))")
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
@objc public func triggerWindowJSEvent(eventName: String) {
|
|
675
|
+
self.triggerJSEvent(eventName: eventName, target: "window")
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
@objc public func triggerWindowJSEvent(eventName: String, data: String) {
|
|
679
|
+
self.triggerJSEvent(eventName: eventName, target: "window", data: data)
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
@objc public func triggerDocumentJSEvent(eventName: String) {
|
|
683
|
+
self.triggerJSEvent(eventName: eventName, target: "document")
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
@objc public func triggerDocumentJSEvent(eventName: String, data: String) {
|
|
687
|
+
self.triggerJSEvent(eventName: eventName, target: "document", data: data)
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
public func logToJs(_ message: String, _ level: String = "log") {
|
|
691
|
+
DispatchQueue.main.async {
|
|
692
|
+
self.getWebView()?.evaluateJavaScript("window.Capacitor.logJs('\(message)', '\(level)')") { (result, error) in
|
|
693
|
+
if error != nil, let result = result {
|
|
694
|
+
CAPLog.print(result)
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// MARK: - CAPBridgeProtocol: Paths, Files, Assets
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
Translate a URL from the web view into a file URL for native iOS.
|
|
704
|
+
|
|
705
|
+
The web view may be handling several different types of URLs:
|
|
706
|
+
- res:// (shortcut scheme to web assets)
|
|
707
|
+
- file:// (fully qualified URL to file on the local device)
|
|
708
|
+
- base64:// (to be implemented)
|
|
709
|
+
- [web view scheme]:// (already converted once to load in the web view, to be implemented)
|
|
710
|
+
*/
|
|
711
|
+
public func localURL(fromWebURL webURL: URL?) -> URL? {
|
|
712
|
+
guard let inputURL = webURL else {
|
|
713
|
+
return nil
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
let url: URL
|
|
717
|
+
|
|
718
|
+
switch inputURL.scheme {
|
|
719
|
+
case "res":
|
|
720
|
+
url = config.appLocation.appendingPathComponent(inputURL.path)
|
|
721
|
+
case "file":
|
|
722
|
+
url = inputURL
|
|
723
|
+
default:
|
|
724
|
+
return nil
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return url
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
Translate a file URL for native iOS into a URL to load in the web view.
|
|
732
|
+
*/
|
|
733
|
+
public func portablePath(fromLocalURL localURL: URL?) -> URL? {
|
|
734
|
+
guard let inputURL = localURL else {
|
|
735
|
+
return nil
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return self.config.localURL.appendingPathComponent(CapacitorBridge.fileStartIdentifier).appendingPathComponent(inputURL.path)
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// MARK: - CAPBridgeProtocol: View Presentation
|
|
742
|
+
|
|
743
|
+
@objc open func showAlertWith(title: String, message: String, buttonTitle: String) {
|
|
744
|
+
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
|
|
745
|
+
alert.addAction(UIAlertAction(title: buttonTitle, style: UIAlertAction.Style.default, handler: nil))
|
|
746
|
+
self.viewController?.present(alert, animated: true, completion: nil)
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
@objc open func presentVC(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
|
750
|
+
if viewControllerToPresent.modalPresentationStyle == .popover {
|
|
751
|
+
self.viewController?.present(viewControllerToPresent, animated: flag, completion: completion)
|
|
752
|
+
} else {
|
|
753
|
+
self.tmpWindow = UIWindow.init(frame: UIScreen.main.bounds)
|
|
754
|
+
self.tmpWindow?.rootViewController = TmpViewController.init()
|
|
755
|
+
self.tmpWindow?.makeKeyAndVisible()
|
|
756
|
+
self.tmpWindow?.rootViewController?.present(viewControllerToPresent, animated: flag, completion: completion)
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
@objc open func dismissVC(animated flag: Bool, completion: (() -> Void)? = nil) {
|
|
761
|
+
if self.tmpWindow == nil {
|
|
762
|
+
self.viewController?.dismiss(animated: flag, completion: completion)
|
|
763
|
+
} else {
|
|
764
|
+
self.tmpWindow?.rootViewController?.dismiss(animated: flag, completion: completion)
|
|
765
|
+
self.tmpWindow = nil
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|