From 180fef2d3584b4822193dece22cb75eb104a462e Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 20 May 2024 15:48:15 -0400 Subject: [PATCH 01/30] Start of cordova removal --- .../CAPBridgeViewController+CDVScreenOrientationDelegate.h | 6 ------ .../CAPBridgeViewController+CDVScreenOrientationDelegate.m | 5 ----- .../CAPBridgeViewController+CDVScreenOrientationDelegate.h | 6 ++++++ .../CAPBridgeViewController+CDVScreenOrientationDelegate.m | 5 +++++ 4 files changed, 11 insertions(+), 11 deletions(-) delete mode 100644 ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h delete mode 100644 ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m create mode 100644 ios/CapacitorCordova/CapacitorCordova/Classes/Public/CAPBridgeViewController+CDVScreenOrientationDelegate.h create mode 100644 ios/CapacitorCordova/CapacitorCordova/Classes/Public/CAPBridgeViewController+CDVScreenOrientationDelegate.m diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h b/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h deleted file mode 100644 index aeda727d5..000000000 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import - -@interface CAPBridgeViewController (CDVScreenOrientationDelegate) - -@end - diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m b/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m deleted file mode 100644 index 94c169a0c..000000000 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m +++ /dev/null @@ -1,5 +0,0 @@ -#import "CAPBridgeViewController+CDVScreenOrientationDelegate.h" - -@implementation CAPBridgeViewController (CDVScreenOrientationDelegate) - -@end diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CAPBridgeViewController+CDVScreenOrientationDelegate.h b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CAPBridgeViewController+CDVScreenOrientationDelegate.h new file mode 100644 index 000000000..ba82a3062 --- /dev/null +++ b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CAPBridgeViewController+CDVScreenOrientationDelegate.h @@ -0,0 +1,6 @@ +//#import +// +//@interface CAPBridgeViewController (CDVScreenOrientationDelegate) +// +//@end + diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CAPBridgeViewController+CDVScreenOrientationDelegate.m b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CAPBridgeViewController+CDVScreenOrientationDelegate.m new file mode 100644 index 000000000..690a665b1 --- /dev/null +++ b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CAPBridgeViewController+CDVScreenOrientationDelegate.m @@ -0,0 +1,5 @@ +//#import "CAPBridgeViewController+CDVScreenOrientationDelegate.h" +// +//@implementation CAPBridgeViewController (CDVScreenOrientationDelegate) +// +//@end From bfb66bee258e5a822dcd546259d646cbacdc35b5 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 20 May 2024 15:48:59 -0400 Subject: [PATCH 02/30] Add files --- ios/Capacitor.podspec | 1 - .../CAPApplicationDelegateProxy.swift | 2 +- .../Capacitor/CAPBridgeViewController.swift | 2 - .../Capacitor/CAPInstanceDescriptor.h | 4 +- .../Capacitor/CAPInstanceDescriptor.m | 2 +- .../Capacitor/CAPInstanceDescriptor.swift | 33 ++--- ios/Capacitor/Capacitor/CapacitorBridge.swift | 130 +++++++++--------- .../Capacitor/WebViewDelegationHandler.swift | 29 ++-- ios/CapacitorCordova.podspec | 2 +- 9 files changed, 105 insertions(+), 100 deletions(-) diff --git a/ios/Capacitor.podspec b/ios/Capacitor.podspec index 9283df34d..20324bf6a 100644 --- a/ios/Capacitor.podspec +++ b/ios/Capacitor.podspec @@ -18,6 +18,5 @@ Pod::Spec.new do |s| s.source_files = "#{prefix}Capacitor/Capacitor/**/*.{swift,h,m}" s.module_map = "#{prefix}Capacitor/Capacitor/Capacitor.modulemap" s.resources = ["#{prefix}Capacitor/Capacitor/assets/native-bridge.js", "#{prefix}Capacitor/Capacitor/PrivacyInfo.xcprivacy"] - s.dependency 'CapacitorCordova' s.swift_version = '5.1' end diff --git a/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift b/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift index 7f510bc5a..50027babc 100644 --- a/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift +++ b/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift @@ -11,7 +11,7 @@ public class ApplicationDelegateProxy: NSObject, UIApplicationDelegate { "url": url, "options": options ]) - NotificationCenter.default.post(name: NSNotification.Name.CDVPluginHandleOpenURL, object: url) + //NotificationCenter.default.post(name: NSNotification.Name.CDVPluginHandleOpenURL, object: url) lastURL = url return true } diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index 460af412c..ad71cbec2 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -1,6 +1,5 @@ import UIKit import WebKit -import Cordova @objc open class CAPBridgeViewController: UIViewController { private var capacitorBridge: CapacitorBridge? @@ -59,7 +58,6 @@ import Cordova // create the bridge capacitorBridge = CapacitorBridge(with: configuration, delegate: self, - cordovaConfiguration: configDescriptor.cordovaConfiguration, assetHandler: assetHandler, delegationHandler: delegationHandler) capacitorDidLoad() diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h index f477ef55b..3ca447e6f 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h @@ -2,7 +2,7 @@ #define CAPInstanceDescriptor_h @import UIKit; -@import Cordova; + typedef NS_ENUM(NSInteger, CAPInstanceType) { CAPInstanceTypeFixed NS_SWIFT_NAME(fixed), @@ -137,7 +137,7 @@ NS_SWIFT_NAME(InstanceDescriptor) /** @brief The parser used to load the cofiguration for Cordova plugins. */ -@property (nonatomic, copy, nonnull) CDVConfigParser *cordovaConfiguration; +@property (nonatomic, copy, nonnull) NSObject *cordovaConfiguration; /** @brief Warnings generated during initialization. */ diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m index 2d7a4fba9..ac574dda3 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m @@ -45,7 +45,7 @@ - (void)_setDefaultsWithAppLocation:(NSURL*)location { _contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; _appLocation = location; _limitsNavigationsToAppBoundDomains = FALSE; - _cordovaConfiguration = [[CDVConfigParser alloc] init]; + // _cordovaConfiguration = [[CDVConfigParser alloc] init]; _warnings = 0; if (location == nil) { _warnings |= CAPInstanceWarningMissingAppDir; diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift index a7eff5f62..177d26a30 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift @@ -53,21 +53,21 @@ internal extension InstanceDescriptor { } // parse the cordova configuration - var configParser: XMLParser? - if let cordovaURL = cordovaURL, - FileManager.default.fileExists(atPath: cordovaURL.path, isDirectory: &isDirectory), - isDirectory.boolValue == false { - configParser = XMLParser(contentsOf: cordovaURL) - } else { - warnings.update(with: .missingCordovaFile) - // we don't want to break up string literals - // swiftlint:disable:next line_length - if let cordovaXML = "".data(using: .utf8) { - configParser = XMLParser(data: cordovaXML) - } - } - configParser?.delegate = cordovaConfiguration - configParser?.parse() +// var configParser: XMLParser? +// if let cordovaURL = cordovaURL, +// FileManager.default.fileExists(atPath: cordovaURL.path, isDirectory: &isDirectory), +// isDirectory.boolValue == false { +// configParser = XMLParser(contentsOf: cordovaURL) +// } else { +// warnings.update(with: .missingCordovaFile) +// // we don't want to break up string literals +// // swiftlint:disable:next line_length +// if let cordovaXML = "".data(using: .utf8) { +// configParser = XMLParser(data: cordovaXML) +// } +// } +// configParser?.delegate = cordovaConfiguration +// configParser?.parse() // extract our configuration values if let config = config { @@ -160,7 +160,8 @@ internal extension InstanceDescriptor { extension InstanceDescriptor { @objc public var cordovaDeployDisabled: Bool { - return (cordovaConfiguration.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false + return true + //return (cordovaConfiguration.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false } @objc public func normalize() { diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index 9caa21850..67b24df9f 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -1,7 +1,6 @@ import Foundation import Dispatch import WebKit -import Cordova internal typealias CapacitorPlugin = CAPPlugin & CAPBridgedPlugin @@ -117,12 +116,12 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { // Map of all loaded and instantiated plugins by pluginId -> instance var plugins = [String: CapacitorPlugin]() // Manager for getting Cordova plugins - var cordovaPluginManager: CDVPluginManager? + //var cordovaPluginManager: CDVPluginManager? // Calls we are storing to resolve later var storedCalls = ConcurrentDictionary() // Whether to inject the Cordova files private var injectCordovaFiles = false - private var cordovaParser: CDVConfigParser? + //private var cordovaParser: CDVConfigParser? private var injectMiscFiles: [String] = [] private var canInjectJS: Bool = true @@ -204,12 +203,19 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { // MARK: - Initialization - public init(with configuration: InstanceConfiguration, delegate bridgeDelegate: CAPBridgeDelegate, cordovaConfiguration: CDVConfigParser, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler, autoRegisterPlugins: Bool = true) { + + + @available(*, deprecated, renamed: "init", message: "Use different init") + public convenience init(with configuration: InstanceConfiguration, delegate bridgeDelegate: CAPBridgeDelegate, cordovaConfiguration: Any, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler, autoRegisterPlugins: Bool = true) { + self.init(with: configuration, delegate: bridgeDelegate, assetHandler: assetHandler, delegationHandler: delegationHandler, autoRegisterPlugins: autoRegisterPlugins) + } + + public init(with configuration: InstanceConfiguration, delegate bridgeDelegate: CAPBridgeDelegate, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler, autoRegisterPlugins: Bool = true) { + self.bridgeDelegate = bridgeDelegate self.webViewAssetHandler = assetHandler self.webViewDelegationHandler = delegationHandler self.config = configuration - self.cordovaParser = cordovaConfiguration self.notificationRouter = NotificationRouter() self.notificationRouter.handleApplicationNotifications = configuration.handleApplicationNotifications self.autoRegisterPlugins = autoRegisterPlugins @@ -219,7 +225,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { exportCoreJS(localUrl: configuration.localURL.absoluteString) registerPlugins() - setupCordovaCompatibility() + // setupCordovaCompatibility() exportMiscJS() canInjectJS = false observers.append(NotificationCenter.default.addObserver(forName: type(of: self).tmpVCAppeared.name, object: .none, queue: .none) { [weak self] _ in @@ -264,19 +270,19 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { /** Set up our Cordova compat by loading all known Cordova plugins and injecting their JS. */ - func setupCordovaCompatibility() { - if injectCordovaFiles { - exportCordovaJS() - registerCordovaPlugins() - } else { - observers.append(NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in - self?.triggerDocumentJSEvent(eventName: "resume") - }) - observers.append(NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in - self?.triggerDocumentJSEvent(eventName: "pause") - }) - } - } +// func setupCordovaCompatibility() { +// if injectCordovaFiles { +// exportCordovaJS() +// // registerCordovaPlugins() +// } else { +// observers.append(NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in +// self?.triggerDocumentJSEvent(eventName: "resume") +// }) +// observers.append(NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in +// self?.triggerDocumentJSEvent(eventName: "pause") +// }) +// } +// } /** Export the core Cordova JS runtime @@ -312,11 +318,11 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { for plugin in registrationList.packageClassList { if let pluginClass = NSClassFromString(plugin) { - if pluginClass == CDVPlugin.self { - injectCordovaFiles = true - } else { +// if pluginClass == CDVPlugin.self { +// injectCordovaFiles = true +// } else { pluginList.append(pluginClass) - } +// } } } } @@ -424,22 +430,22 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { return self.dispatchQueue } - func registerCordovaPlugins() { - guard let cordovaParser = cordovaParser else { - return - } - cordovaPluginManager = CDVPluginManager.init(parser: cordovaParser, viewController: self.viewController, webView: self.getWebView()) - if cordovaParser.startupPluginNames.count > 0 { - for pluginName in cordovaParser.startupPluginNames { - _ = cordovaPluginManager?.getCommandInstance(pluginName as? String) - } - } - do { - try JSExport.exportCordovaPluginsJS(userContentController: webViewDelegationHandler.contentController) - } catch { - type(of: self).fatalError(error, error) - } - } +// func registerCordovaPlugins() { +// guard let cordovaParser = cordovaParser else { +// return +// } +// cordovaPluginManager = CDVPluginManager.init(parser: cordovaParser, viewController: self.viewController, webView: self.getWebView()) +// if cordovaParser.startupPluginNames.count > 0 { +// for pluginName in cordovaParser.startupPluginNames { +// _ = cordovaPluginManager?.getCommandInstance(pluginName as? String) +// } +// } +// do { +// try JSExport.exportCordovaPluginsJS(userContentController: webViewDelegationHandler.contentController) +// } catch { +// type(of: self).fatalError(error, error) +// } +// } func reload() { self.getWebView()?.reload() @@ -543,29 +549,29 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { Handle a Cordova call from JavaScript. First, find the corresponding plugin, construct a selector, and perform that selector on the plugin instance. */ - func handleCordovaJSCall(call: JSCall) { - // Create a selector to send to the plugin - - if let plugin = self.cordovaPluginManager?.getCommandInstance(call.pluginId.lowercased()) { - let selector = NSSelectorFromString("\(call.method):") - if !plugin.responds(to: selector) { - CAPLog.print("Error: Plugin \(plugin.className ?? "") does not respond to method call \(selector).") - CAPLog.print("Ensure plugin method exists and uses @objc in its declaration") - return - } - - let arguments: [Any] = call.options["options"] as? [Any] ?? [] - let pluginCall = CDVInvokedUrlCommand(arguments: arguments, - callbackId: call.callbackId, - className: plugin.className, - methodName: call.method) - plugin.perform(selector, with: pluginCall) - - } else { - CAPLog.print("Error: Cordova Plugin mapping not found") - return - } - } +// func handleCordovaJSCall(call: JSCall) { +// // Create a selector to send to the plugin +// +// if let plugin = self.cordovaPluginManager?.getCommandInstance(call.pluginId.lowercased()) { +// let selector = NSSelectorFromString("\(call.method):") +// if !plugin.responds(to: selector) { +// CAPLog.print("Error: Plugin \(plugin.className ?? "") does not respond to method call \(selector).") +// CAPLog.print("Ensure plugin method exists and uses @objc in its declaration") +// return +// } +// +// let arguments: [Any] = call.options["options"] as? [Any] ?? [] +// let pluginCall = CDVInvokedUrlCommand(arguments: arguments, +// callbackId: call.callbackId, +// className: plugin.className, +// methodName: call.method) +// plugin.perform(selector, with: pluginCall) +// +// } else { +// CAPLog.print("Error: Cordova Plugin mapping not found") +// return +// } +// } func removeAllPluginListeners() { for plugin in plugins.values { diff --git a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift index d0870ad1e..9b3f2eccb 100644 --- a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift @@ -180,26 +180,27 @@ open class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDelegat let pluginId = dict["pluginId"] as? String ?? "" let method = dict["methodName"] as? String ?? "" let callbackId = dict["callbackId"] as? String ?? "" - + let options = dict["options"] as? [String: Any] ?? [:] - + if pluginId != "Console" { CAPLog.print("⚡️ To Native -> ", pluginId, method, callbackId) } - + bridge.handleJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId)) - } else if type == "cordova" { - let pluginId = dict["service"] as? String ?? "" - let method = dict["action"] as? String ?? "" - let callbackId = dict["callbackId"] as? String ?? "" - - let args = dict["actionArgs"] as? Array ?? [] - let options = ["options": args] - - CAPLog.print("To Native Cordova -> ", pluginId, method, callbackId, options) - - bridge.handleCordovaJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId)) } +// } else if type == "cordova" { +// let pluginId = dict["service"] as? String ?? "" +// let method = dict["action"] as? String ?? "" +// let callbackId = dict["callbackId"] as? String ?? "" +// +// let args = dict["actionArgs"] as? Array ?? [] +// let options = ["options": args] +// +// CAPLog.print("To Native Cordova -> ", pluginId, method, callbackId, options) +// +// //bridge.handleCordovaJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId)) +// } } } diff --git a/ios/CapacitorCordova.podspec b/ios/CapacitorCordova.podspec index eea7e77b8..6d10067aa 100644 --- a/ios/CapacitorCordova.podspec +++ b/ios/CapacitorCordova.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| s.authors = { 'Ionic Team' => 'hi@ionicframework.com' } s.source = { git: 'https://github.com/ionic-team/capacitor', tag: s.version.to_s } s.platform = :ios, 15.0 - s.source_files = "#{prefix}CapacitorCordova/CapacitorCordova/**/*.{h,m}" + s.source_files = "#{prefix}CapacitorCordova/CapacitorCordova/**/*.{h,m,swift}" s.public_header_files = "#{prefix}CapacitorCordova/CapacitorCordova/Classes/Public/*.h", "#{prefix}CapacitorCordova/CapacitorCordova/CapacitorCordova.h" s.module_map = "#{prefix}CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap" From 1b369d32c8faf65506f0d980a74575b2de19949d Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Mon, 20 May 2024 16:09:55 -0500 Subject: [PATCH 03/30] Continue cordova removal --- .../Capacitor/CAPBridgeProtocol.swift | 3 + .../Capacitor/CAPInstanceDescriptor.swift | 2 +- ios/Capacitor/Capacitor/CapacitorBridge.swift | 59 +++++---- ios/Capacitor/Capacitor/JS.swift | 10 +- ios/Capacitor/Capacitor/JSExport.swift | 76 ++++++------ .../Capacitor/WebViewDelegationHandler.swift | 2 + ios/CapacitorCordova.podspec | 1 + .../Classes/Public/Plugin.swift | 117 ++++++++++++++++++ 8 files changed, 202 insertions(+), 68 deletions(-) create mode 100644 ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift diff --git a/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift b/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift index f86b914f8..d5b96eb44 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift @@ -79,6 +79,9 @@ import WebKit func registerPluginType(_ pluginType: CAPPlugin.Type) func registerPluginInstance(_ pluginInstance: CAPPlugin) + // MARK: - Interceptors + func registerCallInterceptor(_ name: String, handler: @escaping ([String: Any]) -> Void) + // MARK: - View Presentation func showAlertWith(title: String, message: String, buttonTitle: String) func presentVC(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift index 177d26a30..615401426 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift @@ -160,7 +160,7 @@ internal extension InstanceDescriptor { extension InstanceDescriptor { @objc public var cordovaDeployDisabled: Bool { - return true + return false //return (cordovaConfiguration.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false } diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index 67b24df9f..95de7ef5f 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -120,13 +120,14 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { // Calls we are storing to resolve later var storedCalls = ConcurrentDictionary() // Whether to inject the Cordova files - private var injectCordovaFiles = false + private var cordovaIsPresent = false //private var cordovaParser: CDVConfigParser? private var injectMiscFiles: [String] = [] private var canInjectJS: Bool = true // Background dispatch queue for plugin calls open private(set) var dispatchQueue = DispatchQueue(label: "bridge") + internal private(set) var callInterceptors: [String: ([String: Any]) -> Void] = [:] // Array of block based observers var observers: [NSObjectProtocol] = [] @@ -226,6 +227,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { exportCoreJS(localUrl: configuration.localURL.absoluteString) registerPlugins() // setupCordovaCompatibility() + setupListeners() exportMiscJS() canInjectJS = false observers.append(NotificationCenter.default.addObserver(forName: type(of: self).tmpVCAppeared.name, object: .none, queue: .none) { [weak self] _ in @@ -267,6 +269,20 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { injectMiscFiles.removeAll() } + // TODO: Change this description + /// Setup listeners if Cordova is not present + func setupListeners() { + if !cordovaIsPresent { + observers.append( + NotificationCenter.default.addObserver( + forName: UIApplication.willEnterForegroundNotification, + object: nil, + queue: .main + ) { [weak self] _ in + self?.triggerDocumentJSEvent(eventName: "resume") + } + ) + /** Set up our Cordova compat by loading all known Cordova plugins and injecting their JS. */ @@ -275,23 +291,15 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { // exportCordovaJS() // // registerCordovaPlugins() // } else { -// observers.append(NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in -// self?.triggerDocumentJSEvent(eventName: "resume") -// }) -// observers.append(NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in -// self?.triggerDocumentJSEvent(eventName: "pause") -// }) -// } -// } - - /** - Export the core Cordova JS runtime - */ - func exportCordovaJS() { - do { - try JSExport.exportCordovaJS(userContentController: webViewDelegationHandler.contentController) - } catch { - type(of: self).fatalError(error, error) + observers.append( + NotificationCenter.default.addObserver( + forName: UIApplication.didEnterBackgroundNotification, + object: nil, + queue: .main + ) { [weak self] _ in + self?.triggerDocumentJSEvent(eventName: "pause") + } + ) } } @@ -317,12 +325,11 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { let registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData) for plugin in registrationList.packageClassList { - if let pluginClass = NSClassFromString(plugin) { -// if pluginClass == CDVPlugin.self { -// injectCordovaFiles = true -// } else { - pluginList.append(pluginClass) -// } + if let pluginClass = NSClassFromString(plugin), pluginClass == CAPPlugin.self { + pluginList.append(pluginClass) + if plugin == "CordovaPlugin" { + cordovaIsPresent = true + } } } } @@ -398,6 +405,10 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { // MARK: - CAPBridgeProtocol: Call Management + public func registerCallInterceptor(_ name: String, handler: @escaping ([String: Any]) -> Void) { + callInterceptors[name] = handler + } + @objc public func saveCall(_ call: CAPPluginCall) { storedCalls[call.callbackId] = call } diff --git a/ios/Capacitor/Capacitor/JS.swift b/ios/Capacitor/Capacitor/JS.swift index 46972c48a..b1198b178 100644 --- a/ios/Capacitor/Capacitor/JS.swift +++ b/ios/Capacitor/Capacitor/JS.swift @@ -14,11 +14,11 @@ public typealias JSResultBody = [String: Any] /** * A call originating from JavaScript land */ -internal struct JSCall { - let options: [String: Any] - let pluginId: String - let method: String - let callbackId: String +public struct JSCall { + public let options: [String: Any] + public let pluginId: String + public let method: String + public let callbackId: String } internal protocol JSResultProtocol { diff --git a/ios/Capacitor/Capacitor/JSExport.swift b/ios/Capacitor/Capacitor/JSExport.swift index f6934c17a..c5e895e40 100644 --- a/ios/Capacitor/Capacitor/JSExport.swift +++ b/ios/Capacitor/Capacitor/JSExport.swift @@ -35,23 +35,23 @@ internal class JSExport { } } - static func exportCordovaJS(userContentController: WKUserContentController) throws { - guard let cordovaUrl = Bundle.main.url(forResource: "public/cordova", withExtension: "js") else { - CAPLog.print("ERROR: Required cordova.js file not found. Cordova plugins will not function!") - throw CapacitorBridgeError.errorExportingCoreJS - } - guard let cordovaPluginsUrl = Bundle.main.url(forResource: "public/cordova_plugins", withExtension: "js") else { - CAPLog.print("ERROR: Required cordova_plugins.js file not found. Cordova plugins will not function!") - throw CapacitorBridgeError.errorExportingCoreJS - } - do { - try self.injectFile(fileURL: cordovaUrl, userContentController: userContentController) - try self.injectFile(fileURL: cordovaPluginsUrl, userContentController: userContentController) - } catch { - CAPLog.print("ERROR: Unable to read required cordova files. Cordova plugins will not function!") - throw CapacitorBridgeError.errorExportingCoreJS - } - } +// static func exportCordovaJS(userContentController: WKUserContentController) throws { +// guard let cordovaUrl = Bundle.main.url(forResource: "public/cordova", withExtension: "js") else { +// CAPLog.print("ERROR: Required cordova.js file not found. Cordova plugins will not function!") +// throw CapacitorBridgeError.errorExportingCoreJS +// } +// guard let cordovaPluginsUrl = Bundle.main.url(forResource: "public/cordova_plugins", withExtension: "js") else { +// CAPLog.print("ERROR: Required cordova_plugins.js file not found. Cordova plugins will not function!") +// throw CapacitorBridgeError.errorExportingCoreJS +// } +// do { +// try self.injectFile(fileURL: cordovaUrl, userContentController: userContentController) +// try self.injectFile(fileURL: cordovaPluginsUrl, userContentController: userContentController) +// } catch { +// CAPLog.print("ERROR: Unable to read required cordova files. Cordova plugins will not function!") +// throw CapacitorBridgeError.errorExportingCoreJS +// } +// } static func exportMiscFileJS(paths: [String], userContentController: WKUserContentController) { for path in paths { @@ -184,27 +184,27 @@ internal class JSExport { return lines.joined(separator: "\n") } - static func exportCordovaPluginsJS(userContentController: WKUserContentController) throws { - if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) { - self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) - } - } - - static func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) { - let fileManager = FileManager.default - do { - let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: []) - for fileURL in fileURLs { - if fileURL.hasDirectoryPath { - injectFilesForFolder(folder: fileURL, userContentController: userContentController) - } else { - try self.injectFile(fileURL: fileURL, userContentController: userContentController) - } - } - } catch { - CAPLog.print("Error while enumerating files") - } - } +// static func exportCordovaPluginsJS(userContentController: WKUserContentController) throws { +// if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) { +// self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) +// } +// } + +// static func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) { +// let fileManager = FileManager.default +// do { +// let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: []) +// for fileURL in fileURLs { +// if fileURL.hasDirectoryPath { +// injectFilesForFolder(folder: fileURL, userContentController: userContentController) +// } else { +// try self.injectFile(fileURL: fileURL, userContentController: userContentController) +// } +// } +// } catch { +// CAPLog.print("Error while enumerating files") +// } +// } static func injectFile(fileURL: URL, userContentController: WKUserContentController) throws { do { diff --git a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift index 9b3f2eccb..608be37d7 100644 --- a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift @@ -188,6 +188,8 @@ open class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDelegat } bridge.handleJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId)) + } else if let handler = bridge.callInterceptors[type] { + handler(dict) } // } else if type == "cordova" { // let pluginId = dict["service"] as? String ?? "" diff --git a/ios/CapacitorCordova.podspec b/ios/CapacitorCordova.podspec index 6d10067aa..bf147ef44 100644 --- a/ios/CapacitorCordova.podspec +++ b/ios/CapacitorCordova.podspec @@ -22,5 +22,6 @@ Pod::Spec.new do |s| s.module_map = "#{prefix}CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap" s.resources = ["#{prefix}CapacitorCordova/CapacitorCordova/PrivacyInfo.xcprivacy"] s.requires_arc = true + s.dependency 'Capacitor', s.version.to_s s.framework = 'WebKit' end diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift new file mode 100644 index 000000000..a1ef8c34b --- /dev/null +++ b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift @@ -0,0 +1,117 @@ +import Capacitor + +public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { + public let jsName = "__CordovaPlugin" + public let pluginMethods: [CAPPluginMethod] = [] + public var identifier: String { jsName } + + public override func load() { + injectJavascript() + configureRuntime() + } + + func configureRuntime() { + let parser = CDVConfigParser() + guard let pluginManager = CDVPluginManager( + parser: parser, + viewController: bridge?.viewController, + webView: bridge?.webView + ) else { return } + + + for plugin in parser.startupPluginNames.compactMap({ $0 as? String }) { + _ = pluginManager.getCommandInstance(plugin) + } + + guard let bridge, let webView = bridge.webView else { return } + + exportCordovaPluginsJS(userContentController: webView.configuration.userContentController) + + bridge.registerCallInterceptor("cordova") { [pluginManager] dict in + let pluginId = dict["service"] as? String ?? "" + let method = dict["action"] as? String ?? "" + let callbackId = dict["callbackId"] as? String ?? "" + + let args = dict["actionArgs"] as? Array ?? [] + let options = ["options": args] + + CAPLog.print("To Native Cordova -> ", pluginId, method, callbackId, options) + + if let plugin = pluginManager.getCommandInstance(pluginId.lowercased()) { + let selector = NSSelectorFromString("\(method):") + if !plugin.responds(to: selector) { + CAPLog.print("Error: Plugin \(plugin.className ?? "") does not respond to method call \(selector).") + CAPLog.print("Ensure plugin method exists and uses @objc in its declaration") + return + } + + let arguments = options["options"] ?? [] + let pluginCall = CDVInvokedUrlCommand( + arguments: arguments, + callbackId: callbackId, + className: plugin.className, + methodName: method + ) + + plugin.perform(selector, with: pluginCall) + + } else { + CAPLog.print("Error: Cordova Plugin mapping not found") + return + } + } + + if (parser.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false { + // TODO: Ensure that the previously persisted base path will be loaded +// bridge.usePersistedBasePath() + } + } + + + func injectJavascript() { + guard let cordovaUrl = Bundle.main.url(forResource: "public/cordova", withExtension: "js") else { + fatalError("ERROR: Required cordova.js file not found. Cordova plugins will not function!") + } + + guard let cordovaPluginsUrl = Bundle.main.url(forResource: "public/cordova_plugins", withExtension: "js") else { + fatalError("ERROR: Required cordova_plugins.js file not found. Cordova plugins will not function!") + } + + guard let webView = bridge?.webView else { return } + + injectFile(fileURL: cordovaUrl, userContentController: webView.configuration.userContentController) + injectFile(fileURL: cordovaPluginsUrl, userContentController: webView.configuration.userContentController) + } + + func exportCordovaPluginsJS(userContentController: WKUserContentController) { + if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) { + self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) + } + } + + func injectFile(fileURL: URL, userContentController: WKUserContentController) { + do { + let data = try String(contentsOf: fileURL, encoding: .utf8) + let userScript = WKUserScript(source: data, injectionTime: .atDocumentStart, forMainFrameOnly: true) + userContentController.addUserScript(userScript) + } catch { + fatalError("Unable to inject js file") + } + } + + func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) { + let fileManager = FileManager.default + do { + let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: []) + for fileURL in fileURLs { + if fileURL.hasDirectoryPath { + injectFilesForFolder(folder: fileURL, userContentController: userContentController) + } else { + injectFile(fileURL: fileURL, userContentController: userContentController) + } + } + } catch { + CAPLog.print("Error while enumerating files") + } + } +} From 8f0f21a1c812af2053f3aecd16df0ffd69e53d12 Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Wed, 22 May 2024 09:56:24 -0500 Subject: [PATCH 04/30] Allow CordovaPlugin to be initialized and used --- ios/Capacitor/Capacitor/CapacitorBridge.swift | 2 +- .../CapacitorCordova/Classes/Public/Plugin.swift | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index 95de7ef5f..95b9e1205 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -325,7 +325,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { let registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData) for plugin in registrationList.packageClassList { - if let pluginClass = NSClassFromString(plugin), pluginClass == CAPPlugin.self { + if let pluginClass = NSClassFromString(plugin), pluginClass is CAPPlugin.Type { pluginList.append(pluginClass) if plugin == "CordovaPlugin" { cordovaIsPresent = true diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift index a1ef8c34b..4fbaa357d 100644 --- a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift +++ b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift @@ -1,5 +1,6 @@ import Capacitor +@objc(CordovaPlugin) public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { public let jsName = "__CordovaPlugin" public let pluginMethods: [CAPPluginMethod] = [] @@ -85,7 +86,7 @@ public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { func exportCordovaPluginsJS(userContentController: WKUserContentController) { if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) { - self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) + injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) } } From fc8b3265dde206a0d3b8f6d5578a80122cf4d12f Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 22 May 2024 12:47:18 -0400 Subject: [PATCH 05/30] Cordova plugins working, first pass --- ios/Capacitor/Capacitor/CapacitorBridge.swift | 6 +++-- .../Classes/Public/Plugin.swift | 23 +++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index 95b9e1205..c301a3d28 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -5,7 +5,7 @@ import WebKit internal typealias CapacitorPlugin = CAPPlugin & CAPBridgedPlugin struct RegistrationList: Codable { - let packageClassList: Set + var packageClassList: Set } /** @@ -322,7 +322,9 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { do { if let pluginJSON = Bundle.main.url(forResource: "capacitor.config", withExtension: "json") { let pluginData = try Data(contentsOf: pluginJSON) - let registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData) + var registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData) + #warning("Don't hardcode this") + registrationList.packageClassList.insert("CordovaPlugin") for plugin in registrationList.packageClassList { if let pluginClass = NSClassFromString(plugin), pluginClass is CAPPlugin.Type { diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift index 4fbaa357d..fa19d54cf 100644 --- a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift +++ b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift @@ -12,20 +12,25 @@ public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { } func configureRuntime() { - let parser = CDVConfigParser() + guard let configURL = Bundle.main.url(forResource: "config", withExtension: "xml") else { fatalError() } + guard let bridge, let webView = bridge.webView else { return } + + let cordovaConfigParser = CDVConfigParser() + + let xmlParser = XMLParser(contentsOf: configURL) + xmlParser?.delegate = cordovaConfigParser + xmlParser?.parse() + guard let pluginManager = CDVPluginManager( - parser: parser, - viewController: bridge?.viewController, - webView: bridge?.webView + parser: cordovaConfigParser, + viewController: bridge.viewController, + webView: bridge.webView ) else { return } - - for plugin in parser.startupPluginNames.compactMap({ $0 as? String }) { + for plugin in cordovaConfigParser.startupPluginNames.compactMap({ $0 as? String }) { _ = pluginManager.getCommandInstance(plugin) } - guard let bridge, let webView = bridge.webView else { return } - exportCordovaPluginsJS(userContentController: webView.configuration.userContentController) bridge.registerCallInterceptor("cordova") { [pluginManager] dict in @@ -62,7 +67,7 @@ public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { } } - if (parser.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false { + if (cordovaConfigParser.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false { // TODO: Ensure that the previously persisted base path will be loaded // bridge.usePersistedBasePath() } From 5b2f576b0c51327653bc2f7c8a692675461144c4 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 22 May 2024 13:03:32 -0400 Subject: [PATCH 06/30] Fixed server base path --- .../Capacitor/CAPBridgeViewController.swift | 15 +++++++++++---- .../Capacitor/CAPInstanceDescriptor.swift | 17 ----------------- ios/Capacitor/Capacitor/CapacitorBridge.swift | 17 ----------------- .../Classes/Public/Plugin.swift | 11 +++++++---- 4 files changed, 18 insertions(+), 42 deletions(-) diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index ad71cbec2..1971512e7 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -61,6 +61,7 @@ import WebKit assetHandler: assetHandler, delegationHandler: delegationHandler) capacitorDidLoad() + updateAppLocationIfNeeded() if configDescriptor.instanceType == .fixed { updateBinaryVersion() @@ -93,18 +94,24 @@ import WebKit - Note: This is called early in the View Controller's lifecycle. Not all properties will be set at invocation. */ open func instanceDescriptor() -> InstanceDescriptor { - let descriptor = InstanceDescriptor.init() - if !isNewBinary && !descriptor.cordovaDeployDisabled { + return InstanceDescriptor() + } + + func updateAppLocationIfNeeded() { + let cordovaPlugin = bridge?.plugin(withName: "CordovaPlugin") + let cordovaDeployDisabled = cordovaPlugin?.perform(Selector(("cordovaDeployDisabled:"))) as? Bool ?? false + + if !isNewBinary && !cordovaDeployDisabled { if let persistedPath = KeyValueStore.standard["serverBasePath", as: String.self], !persistedPath.isEmpty { if let libPath = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first { - descriptor.appLocation = URL(fileURLWithPath: libPath, isDirectory: true) + let serverBasePath = URL(fileURLWithPath: libPath, isDirectory: true) .appendingPathComponent("NoCloud") .appendingPathComponent("ionic_built_snapshots") .appendingPathComponent(URL(fileURLWithPath: persistedPath, isDirectory: true).lastPathComponent) + setServerBasePath(path: serverBasePath.path) } } } - return descriptor } open func router() -> Router { diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift index 615401426..fa86a1f11 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift @@ -52,23 +52,6 @@ internal extension InstanceDescriptor { warnings.update(with: .missingFile) } - // parse the cordova configuration -// var configParser: XMLParser? -// if let cordovaURL = cordovaURL, -// FileManager.default.fileExists(atPath: cordovaURL.path, isDirectory: &isDirectory), -// isDirectory.boolValue == false { -// configParser = XMLParser(contentsOf: cordovaURL) -// } else { -// warnings.update(with: .missingCordovaFile) -// // we don't want to break up string literals -// // swiftlint:disable:next line_length -// if let cordovaXML = "".data(using: .utf8) { -// configParser = XMLParser(data: cordovaXML) -// } -// } -// configParser?.delegate = cordovaConfiguration -// configParser?.parse() - // extract our configuration values if let config = config { // to be removed diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index c301a3d28..5ad24a4d3 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -443,23 +443,6 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { return self.dispatchQueue } -// func registerCordovaPlugins() { -// guard let cordovaParser = cordovaParser else { -// return -// } -// cordovaPluginManager = CDVPluginManager.init(parser: cordovaParser, viewController: self.viewController, webView: self.getWebView()) -// if cordovaParser.startupPluginNames.count > 0 { -// for pluginName in cordovaParser.startupPluginNames { -// _ = cordovaPluginManager?.getCommandInstance(pluginName as? String) -// } -// } -// do { -// try JSExport.exportCordovaPluginsJS(userContentController: webViewDelegationHandler.contentController) -// } catch { -// type(of: self).fatalError(error, error) -// } -// } - func reload() { self.getWebView()?.reload() } diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift index fa19d54cf..6274f3ef8 100644 --- a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift +++ b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift @@ -6,6 +6,8 @@ public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { public let pluginMethods: [CAPPluginMethod] = [] public var identifier: String { jsName } + private var _cordovaDeployDisabled = false + public override func load() { injectJavascript() configureRuntime() @@ -67,12 +69,13 @@ public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { } } - if (cordovaConfigParser.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false { - // TODO: Ensure that the previously persisted base path will be loaded -// bridge.usePersistedBasePath() - } + _cordovaDeployDisabled = (cordovaConfigParser.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false } + @objc + func cordovaDeployDisabled() -> Bool { + return _cordovaDeployDisabled + } func injectJavascript() { guard let cordovaUrl = Bundle.main.url(forResource: "public/cordova", withExtension: "js") else { From b6b4e50b33d4c5bc186cda9e73ac5554cf59a4bd Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 22 May 2024 13:12:46 -0400 Subject: [PATCH 07/30] Update warning text --- ios/Capacitor/Capacitor/CapacitorBridge.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index 5ad24a4d3..b37e30a22 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -323,7 +323,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { if let pluginJSON = Bundle.main.url(forResource: "capacitor.config", withExtension: "json") { let pluginData = try Data(contentsOf: pluginJSON) var registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData) - #warning("Don't hardcode this") + #warning("Don't hardcode this, CLI will handle this eventually") registrationList.packageClassList.insert("CordovaPlugin") for plugin in registrationList.packageClassList { From 2373486e1d6a06e9867db2f90946a5ae462642bd Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 22 May 2024 13:35:40 -0400 Subject: [PATCH 08/30] Fix cordovaDeployDisabled selector nonsense --- ios/Capacitor/Capacitor/CAPBridgeViewController.swift | 7 ++++--- .../CapacitorCordova/Classes/Public/Plugin.swift | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index 1971512e7..5bb2bae94 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -97,9 +97,10 @@ import WebKit return InstanceDescriptor() } - func updateAppLocationIfNeeded() { - let cordovaPlugin = bridge?.plugin(withName: "CordovaPlugin") - let cordovaDeployDisabled = cordovaPlugin?.perform(Selector(("cordovaDeployDisabled:"))) as? Bool ?? false + /// This function must be called after plugins are loaded or it will have no effect. + open func updateAppLocationIfNeeded() { + let cordovaPlugin = bridge?.plugin(withName: "__CordovaPlugin") + let cordovaDeployDisabled = cordovaPlugin?.perform(Selector(("cordovaDeployDisabled"))).takeUnretainedValue() as? Bool ?? false if !isNewBinary && !cordovaDeployDisabled { if let persistedPath = KeyValueStore.standard["serverBasePath", as: String.self], !persistedPath.isEmpty { diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift index 6274f3ef8..b45b3c461 100644 --- a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift +++ b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift @@ -73,8 +73,8 @@ public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { } @objc - func cordovaDeployDisabled() -> Bool { - return _cordovaDeployDisabled + func cordovaDeployDisabled() -> NSNumber { + return _cordovaDeployDisabled as NSNumber } func injectJavascript() { From 286f953fee1cc9a1538da4d6dc7c6bf9fd94c492 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 22 May 2024 14:52:47 -0400 Subject: [PATCH 09/30] Fix Capacitor Project --- ios/Capacitor/Capacitor.xcodeproj/project.pbxproj | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index 843ee1e10..1acc16aa5 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -9,8 +9,6 @@ /* Begin PBXBuildFile section */ 0F83E885285A332E006C43CB /* AppUUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F83E884285A332D006C43CB /* AppUUID.swift */; }; 0F8F33B327DA980A003F49D6 /* PluginConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F33B127DA980A003F49D6 /* PluginConfig.swift */; }; - 2F81F5C926FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */; }; - 2F81F5CA26FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */; }; 373A69C1255C9360000A6F44 /* NotificationHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */; }; 373A69F2255C95D0000A6F44 /* NotificationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */; }; 501CBAA71FC0A723009B0D4D /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 501CBAA61FC0A723009B0D4D /* WebKit.framework */; }; @@ -157,8 +155,6 @@ /* Begin PBXFileReference section */ 0F83E884285A332D006C43CB /* AppUUID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUUID.swift; sourceTree = ""; }; 0F8F33B127DA980A003F49D6 /* PluginConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PluginConfig.swift; sourceTree = ""; }; - 2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CAPBridgeViewController+CDVScreenOrientationDelegate.h"; sourceTree = ""; }; - 2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CAPBridgeViewController+CDVScreenOrientationDelegate.m"; sourceTree = ""; }; 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandlerProtocol.swift; sourceTree = ""; }; 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRouter.swift; sourceTree = ""; }; 501CBAA61FC0A723009B0D4D /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; @@ -401,8 +397,6 @@ 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */, 62D43AEF2581817500673C24 /* WKWebView+Capacitor.swift */, 62D43B642582A13D00673C24 /* WKWebView+Capacitor.m */, - 2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */, - 2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */, 62959AE92524DA7700A3D7F1 /* UIColor.swift */, 62959AFE2524DA7700A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m */, 62959B122524DA7700A3D7F1 /* Info.plist */, @@ -512,7 +506,6 @@ 623D6909254C6FDF002D01D1 /* CAPInstanceDescriptor.h in Headers */, 62959B192524DA7800A3D7F1 /* CAPBridgedPlugin.h in Headers */, 621ECCB82542045900D3D615 /* CAPBridgedJSTypes.h in Headers */, - 2F81F5C926FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -725,7 +718,6 @@ 62959B1D2524DA7800A3D7F1 /* UIColor.swift in Sources */, 62959B332524DA7800A3D7F1 /* CAPPlugin.m in Sources */, 62959B1C2524DA7800A3D7F1 /* CAPPluginMethod.m in Sources */, - 2F81F5CA26FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m in Sources */, 62ADC0CA25CB678000E914DE /* PluginCallResult.swift in Sources */, 62959B472524DA7800A3D7F1 /* CAPNotifications.swift in Sources */, 62D43B652582A13D00673C24 /* WKWebView+Capacitor.m in Sources */, From 9eaedba103a15482fc99f8dfda157304a3aa98bd Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Thu, 23 May 2024 16:16:48 -0400 Subject: [PATCH 10/30] Move CLI functions to prepare for cordova changes --- cli/src/ios/update.ts | 308 ++------------------------- cli/src/util/cordova-ios.ts | 412 ++++++++++++++++++++++++++++++++++++ 2 files changed, 425 insertions(+), 295 deletions(-) create mode 100644 cli/src/util/cordova-ios.ts diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index e5aaea6c4..e95361ead 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -9,8 +9,6 @@ import { fatal } from '../errors'; import { logger } from '../log'; import { PluginType, - getAllElements, - getFilePath, getPlatformElement, getPluginType, getPlugins, @@ -18,12 +16,17 @@ import { } from '../plugin'; import type { Plugin } from '../plugin'; import { copy as copyTask } from '../tasks/copy'; +import { + generateCordovaPodspecs, + copyPluginsNativeFiles, + removePluginsNativeFiles, + cordovaPodfileLines, +} from '../util/cordova-ios'; import { convertToUnixPath } from '../util/fs'; import { generateIOSPackageJSON } from '../util/iosplugin'; import { resolveNode } from '../util/node'; import { checkPackageManager, generatePackageFile, checkPluginsForPackageSwift } from '../util/spm'; import { runCommand, isInstalled } from '../util/subprocess'; -import { extractTemplate } from '../util/template'; import { getIOSPlugins, getMajoriOSVersion } from './common'; @@ -44,6 +47,9 @@ async function updatePluginFiles(config: Config, plugins: Plugin[], deployment: await removePluginsNativeFiles(config); const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova); if (cordovaPlugins.length > 0) { + // TODO: all the new logic should probably go here + // with an else that removes cordova plugins + await copyPluginsNativeFiles(config, cordovaPlugins); } if (!(await pathExists(await config.ios.webDirAbs))) { @@ -185,297 +191,19 @@ async function generatePodFile(config: Config, plugins: Plugin[]): Promise getPluginType(p, platform) === PluginType.Cordova); - cordovaPlugins.map(async (p) => { - const podspecs = getPlatformElement(p, platform, 'podspec'); - podspecs.map((podspec: any) => { - podspec.pods.map((pPods: any) => { - pPods.pod.map((pod: any) => { - if (pod.$.git) { - let gitRef = ''; - if (pod.$.tag) { - gitRef = `, :tag => '${pod.$.tag}'`; - } else if (pod.$.branch) { - gitRef = `, :branch => '${pod.$.branch}'`; - } else if (pod.$.commit) { - gitRef = `, :commit => '${pod.$.commit}'`; - } - pods.push(` pod '${pod.$.name}', :git => '${pod.$.git}'${gitRef}\n`); - } - }); - }); - }); - }); - const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p)); - const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el)); - if (noStaticPlugins.length > 0) { - pods.push(` pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'\n`); - } + const cordovaPodlines = cordovaPodfileLines(config, plugins); + pods.concat(cordovaPodlines); if (staticPlugins.length > 0) { pods.push(` pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'\n`); } const resourcesPlugins = cordovaPlugins.filter(filterResources); if (resourcesPlugins.length > 0) { pods.push(` pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'\n`); - } + return ` pod 'Capacitor', :path => '${relativeCapacitoriOSPath}' pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}' -${pods.join('').trimRight()}`; -} - -function getFrameworkName(framework: any) { - if (isFramework(framework)) { - if (framework.$.custom && framework.$.custom === 'true') { - return framework.$.src; - } - return framework.$.src.substr(0, framework.$.src.indexOf('.')); - } - return framework.$.src.substr(0, framework.$.src.indexOf('.')).replace('lib', ''); -} - -function isFramework(framework: any) { - return framework.$.src.split('.').pop().includes('framework'); -} - -async function generateCordovaPodspecs(cordovaPlugins: Plugin[], config: Config) { - const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p)); - const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el)); - generateCordovaPodspec(noStaticPlugins, config, false); - generateCordovaPodspec(staticPlugins, config, true); -} - -async function generateCordovaPodspec(cordovaPlugins: Plugin[], config: Config, isStatic: boolean) { - const weakFrameworks: string[] = []; - const linkedFrameworks: string[] = []; - const customFrameworks: string[] = []; - const systemLibraries: string[] = []; - const sourceFrameworks: string[] = []; - const frameworkDeps: string[] = []; - const compilerFlags: string[] = []; - let prefsArray: any[] = []; - let name = 'CordovaPlugins'; - let sourcesFolderName = 'sources'; - if (isStatic) { - name += 'Static'; - frameworkDeps.push('s.static_framework = true'); - sourcesFolderName += 'static'; - } - cordovaPlugins.map((plugin: any) => { - const frameworks = getPlatformElement(plugin, platform, 'framework'); - frameworks.map((framework: any) => { - if (!framework.$.type) { - const name = getFrameworkName(framework); - if (isFramework(framework)) { - if (framework.$.weak && framework.$.weak === 'true') { - if (!weakFrameworks.includes(name)) { - weakFrameworks.push(name); - } - } else if (framework.$.custom && framework.$.custom === 'true') { - const frameworktPath = join(sourcesFolderName, plugin.name, name); - if (!customFrameworks.includes(frameworktPath)) { - customFrameworks.push(frameworktPath); - } - } else { - if (!linkedFrameworks.includes(name)) { - linkedFrameworks.push(name); - } - } - } else { - if (!systemLibraries.includes(name)) { - systemLibraries.push(name); - } - } - } else if (framework.$.type && framework.$.type === 'podspec') { - let depString = `s.dependency '${framework.$.src}'`; - if (framework.$.spec && framework.$.spec !== '') { - depString += `, '${framework.$.spec}'`; - } - if (!frameworkDeps.includes(depString)) { - frameworkDeps.push(depString); - } - } - }); - prefsArray = prefsArray.concat(getAllElements(plugin, platform, 'preference')); - const podspecs = getPlatformElement(plugin, platform, 'podspec'); - podspecs.map((podspec: any) => { - podspec.pods.map((pods: any) => { - pods.pod.map((pod: any) => { - let depString = `s.dependency '${pod.$.name}'`; - if (pod.$.spec && pod.$.spec !== '') { - depString += `, '${pod.$.spec}'`; - } - if (!frameworkDeps.includes(depString)) { - frameworkDeps.push(depString); - } - }); - }); - }); - const sourceFiles = getPlatformElement(plugin, platform, 'source-file'); - sourceFiles.map((sourceFile: any) => { - if (sourceFile.$.framework && sourceFile.$.framework === 'true') { - let fileName = sourceFile.$.src.split('/').pop(); - if (!fileName.startsWith('lib')) { - fileName = 'lib' + fileName; - } - const frameworktPath = join(sourcesFolderName, plugin.name, fileName); - if (!sourceFrameworks.includes(frameworktPath)) { - sourceFrameworks.push(frameworktPath); - } - } else if (sourceFile.$['compiler-flags']) { - const cFlag = sourceFile.$['compiler-flags']; - if (!compilerFlags.includes(cFlag)) { - compilerFlags.push(cFlag); - } - } - }); - }); - const onlySystemLibraries = systemLibraries.filter((library) => removeNoSystem(library, sourceFrameworks)); - if (weakFrameworks.length > 0) { - frameworkDeps.push(`s.weak_frameworks = '${weakFrameworks.join(`', '`)}'`); - } - if (linkedFrameworks.length > 0) { - frameworkDeps.push(`s.frameworks = '${linkedFrameworks.join(`', '`)}'`); - } - if (onlySystemLibraries.length > 0) { - frameworkDeps.push(`s.libraries = '${onlySystemLibraries.join(`', '`)}'`); - } - if (customFrameworks.length > 0) { - frameworkDeps.push(`s.vendored_frameworks = '${customFrameworks.join(`', '`)}'`); - frameworkDeps.push( - `s.exclude_files = 'sources/**/*.framework/Headers/*.h', 'sources/**/*.framework/PrivateHeaders/*.h'`, - ); - } - if (sourceFrameworks.length > 0) { - frameworkDeps.push(`s.vendored_libraries = '${sourceFrameworks.join(`', '`)}'`); - } - if (compilerFlags.length > 0) { - frameworkDeps.push(`s.compiler_flags = '${compilerFlags.join(' ')}'`); - } - const arcPlugins = cordovaPlugins.filter(filterARCFiles); - if (arcPlugins.length > 0) { - frameworkDeps.push(`s.subspec 'noarc' do |sna| - sna.requires_arc = false - sna.source_files = 'noarc/**/*.{swift,h,m,c,cc,mm,cpp}' - end`); - } - let frameworksString = frameworkDeps.join('\n '); - frameworksString = await replaceFrameworkVariables(config, prefsArray, frameworksString); - const content = ` - Pod::Spec.new do |s| - s.name = '${name}' - s.version = '${config.cli.package.version}' - s.summary = 'Autogenerated spec' - s.license = 'Unknown' - s.homepage = 'https://example.com' - s.authors = { 'Capacitor Generator' => 'hi@example.com' } - s.source = { :git => 'https://github.com/ionic-team/does-not-exist.git', :tag => '${config.cli.package.version}' } - s.source_files = '${sourcesFolderName}/**/*.{swift,h,m,c,cc,mm,cpp}' - s.ios.deployment_target = '${config.ios.minVersion}' - s.xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1 WK_WEB_VIEW_ONLY=1' } - s.dependency 'CapacitorCordova'${getLinkerFlags(config)} - s.swift_version = '5.1' - ${frameworksString} - end`; - await writeFile(join(config.ios.cordovaPluginsDirAbs, `${name}.podspec`), content); -} - -function getLinkerFlags(config: Config) { - if (config.app.extConfig.ios?.cordovaLinkerFlags) { - return `\n s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '${config.app.extConfig.ios.cordovaLinkerFlags.join( - ' ', - )}' }`; - } - return ''; -} - -async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) { - for (const p of cordovaPlugins) { - const sourceFiles = getPlatformElement(p, platform, 'source-file'); - const headerFiles = getPlatformElement(p, platform, 'header-file'); - const codeFiles = sourceFiles.concat(headerFiles); - const frameworks = getPlatformElement(p, platform, 'framework'); - let sourcesFolderName = 'sources'; - if (needsStaticPod(p)) { - sourcesFolderName += 'static'; - } - const sourcesFolder = join(config.ios.cordovaPluginsDirAbs, sourcesFolderName, p.name); - for (const codeFile of codeFiles) { - let fileName = codeFile.$.src.split('/').pop(); - const fileExt = codeFile.$.src.split('.').pop(); - if (fileExt === 'a' && !fileName.startsWith('lib')) { - fileName = 'lib' + fileName; - } - let destFolder = sourcesFolderName; - if (codeFile.$['compiler-flags'] && codeFile.$['compiler-flags'] === '-fno-objc-arc') { - destFolder = 'noarc'; - } - const filePath = getFilePath(config, p, codeFile.$.src); - const fileDest = join(config.ios.cordovaPluginsDirAbs, destFolder, p.name, fileName); - await copy(filePath, fileDest); - if (!codeFile.$.framework) { - let fileContent = await readFile(fileDest, { encoding: 'utf-8' }); - if (fileExt === 'swift') { - fileContent = 'import Cordova\n' + fileContent; - await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); - } else { - if (fileContent.includes('@import Firebase;')) { - fileContent = fileContent.replace('@import Firebase;', '#import '); - await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); - } - if ( - fileContent.includes('[NSBundle bundleForClass:[self class]]') || - fileContent.includes('[NSBundle bundleForClass:[CDVCapture class]]') - ) { - fileContent = fileContent.replace('[NSBundle bundleForClass:[self class]]', '[NSBundle mainBundle]'); - fileContent = fileContent.replace('[NSBundle bundleForClass:[CDVCapture class]]', '[NSBundle mainBundle]'); - await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); - } - if (fileContent.includes('[self.webView superview]') || fileContent.includes('self.webView.superview')) { - fileContent = fileContent.replace(/\[self.webView superview\]/g, 'self.viewController.view'); - fileContent = fileContent.replace(/self.webView.superview/g, 'self.viewController.view'); - await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); - } - } - } - } - const resourceFiles = getPlatformElement(p, platform, 'resource-file'); - for (const resourceFile of resourceFiles) { - const fileName = resourceFile.$.src.split('/').pop(); - await copy( - getFilePath(config, p, resourceFile.$.src), - join(config.ios.cordovaPluginsDirAbs, 'resources', fileName), - ); - } - for (const framework of frameworks) { - if (framework.$.custom && framework.$.custom === 'true') { - await copy(getFilePath(config, p, framework.$.src), join(sourcesFolder, framework.$.src)); - } - } - } -} - -async function removePluginsNativeFiles(config: Config) { - await remove(config.ios.cordovaPluginsDirAbs); - await extractTemplate(config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, config.ios.cordovaPluginsDirAbs); -} - -function filterResources(plugin: Plugin) { - const resources = getPlatformElement(plugin, platform, 'resource-file'); - return resources.length > 0; -} - -function filterARCFiles(plugin: Plugin) { - const sources = getPlatformElement(plugin, platform, 'source-file'); - const sourcesARC = sources.filter( - (sourceFile: any) => sourceFile.$['compiler-flags'] && sourceFile.$['compiler-flags'] === '-fno-objc-arc', - ); - return sourcesARC.length > 0; -} - -function removeNoSystem(library: string, sourceFrameworks: string[]) { - const libraries = sourceFrameworks.filter((framework) => framework.includes(library)); - return libraries.length === 0; +${pods.join('').trimEnd()}`; } async function getPluginsTask(config: Config) { @@ -485,13 +213,3 @@ async function getPluginsTask(config: Config) { return iosPlugins; }); } - -async function replaceFrameworkVariables(config: Config, prefsArray: any[], frameworkString: string) { - prefsArray.map((preference: any) => { - frameworkString = frameworkString.replace( - new RegExp(('$' + preference.$.name).replace('$', '\\$&'), 'g'), - preference.$.default, - ); - }); - return frameworkString; -} diff --git a/cli/src/util/cordova-ios.ts b/cli/src/util/cordova-ios.ts new file mode 100644 index 000000000..d3e71b9f6 --- /dev/null +++ b/cli/src/util/cordova-ios.ts @@ -0,0 +1,412 @@ +import { copy, readFile, writeFile, remove } from '@ionic/utils-fs'; +import { join } from 'path'; + +import { needsStaticPod } from '../cordova'; +import type { Config } from '../definitions'; +import { + PluginType, + getPlatformElement, + getPluginType, + getPlugins, + printPlugins, + getAllElements, + getFilePath, +} from '../plugin'; +import type { Plugin } from '../plugin'; +import { extractTemplate } from '../util/template'; + +const platform = 'ios'; + +export async function generateCordovaPodspecs( + cordovaPlugins: Plugin[], + config: Config, +): Promise { + const staticPlugins = cordovaPlugins.filter(p => needsStaticPod(p, config)); + const noStaticPlugins = cordovaPlugins.filter( + el => !staticPlugins.includes(el), + ); + generateCordovaPodspec(noStaticPlugins, config, false); + generateCordovaPodspec(staticPlugins, config, true); +} + +export async function generateCordovaPodspec( + cordovaPlugins: Plugin[], + config: Config, + isStatic: boolean, +): Promise { + const weakFrameworks: string[] = []; + const linkedFrameworks: string[] = []; + const customFrameworks: string[] = []; + const systemLibraries: string[] = []; + const sourceFrameworks: string[] = []; + const frameworkDeps: string[] = []; + const compilerFlags: string[] = []; + let prefsArray: any[] = []; + let name = 'CordovaPlugins'; + let sourcesFolderName = 'sources'; + if (isStatic) { + name += 'Static'; + frameworkDeps.push('s.static_framework = true'); + sourcesFolderName += 'static'; + } + cordovaPlugins.map((plugin: any) => { + const frameworks = getPlatformElement(plugin, platform, 'framework'); + frameworks.map((framework: any) => { + if (!framework.$.type) { + const name = getFrameworkName(framework); + if (isFramework(framework)) { + if (framework.$.weak && framework.$.weak === 'true') { + if (!weakFrameworks.includes(name)) { + weakFrameworks.push(name); + } + } else if (framework.$.custom && framework.$.custom === 'true') { + const frameworktPath = join(sourcesFolderName, plugin.name, name); + if (!customFrameworks.includes(frameworktPath)) { + customFrameworks.push(frameworktPath); + } + } else { + if (!linkedFrameworks.includes(name)) { + linkedFrameworks.push(name); + } + } + } else { + if (!systemLibraries.includes(name)) { + systemLibraries.push(name); + } + } + } else if (framework.$.type && framework.$.type === 'podspec') { + let depString = `s.dependency '${framework.$.src}'`; + if (framework.$.spec && framework.$.spec !== '') { + depString += `, '${framework.$.spec}'`; + } + if (!frameworkDeps.includes(depString)) { + frameworkDeps.push(depString); + } + } + }); + prefsArray = prefsArray.concat( + getAllElements(plugin, platform, 'preference'), + ); + const podspecs = getPlatformElement(plugin, platform, 'podspec'); + podspecs.map((podspec: any) => { + podspec.pods.map((pods: any) => { + pods.pod.map((pod: any) => { + let depString = `s.dependency '${pod.$.name}'`; + if (pod.$.spec && pod.$.spec !== '') { + depString += `, '${pod.$.spec}'`; + } + if (!frameworkDeps.includes(depString)) { + frameworkDeps.push(depString); + } + }); + }); + }); + const sourceFiles = getPlatformElement(plugin, platform, 'source-file'); + sourceFiles.map((sourceFile: any) => { + if (sourceFile.$.framework && sourceFile.$.framework === 'true') { + let fileName = sourceFile.$.src.split('/').pop(); + if (!fileName.startsWith('lib')) { + fileName = 'lib' + fileName; + } + const frameworktPath = join(sourcesFolderName, plugin.name, fileName); + if (!sourceFrameworks.includes(frameworktPath)) { + sourceFrameworks.push(frameworktPath); + } + } else if (sourceFile.$['compiler-flags']) { + const cFlag = sourceFile.$['compiler-flags']; + if (!compilerFlags.includes(cFlag)) { + compilerFlags.push(cFlag); + } + } + }); + }); + const onlySystemLibraries = systemLibraries.filter(library => + removeNoSystem(library, sourceFrameworks), + ); + if (weakFrameworks.length > 0) { + frameworkDeps.push(`s.weak_frameworks = '${weakFrameworks.join(`', '`)}'`); + } + if (linkedFrameworks.length > 0) { + frameworkDeps.push(`s.frameworks = '${linkedFrameworks.join(`', '`)}'`); + } + if (onlySystemLibraries.length > 0) { + frameworkDeps.push(`s.libraries = '${onlySystemLibraries.join(`', '`)}'`); + } + if (customFrameworks.length > 0) { + frameworkDeps.push( + `s.vendored_frameworks = '${customFrameworks.join(`', '`)}'`, + ); + frameworkDeps.push( + `s.exclude_files = 'sources/**/*.framework/Headers/*.h', 'sources/**/*.framework/PrivateHeaders/*.h'`, + ); + } + if (sourceFrameworks.length > 0) { + frameworkDeps.push( + `s.vendored_libraries = '${sourceFrameworks.join(`', '`)}'`, + ); + } + if (compilerFlags.length > 0) { + frameworkDeps.push(`s.compiler_flags = '${compilerFlags.join(' ')}'`); + } + const arcPlugins = cordovaPlugins.filter(filterARCFiles); + if (arcPlugins.length > 0) { + frameworkDeps.push(`s.subspec 'noarc' do |sna| + sna.requires_arc = false + sna.source_files = 'noarc/**/*.{swift,h,m,c,cc,mm,cpp}' + end`); + } + let frameworksString = frameworkDeps.join('\n '); + frameworksString = await replaceFrameworkVariables( + config, + prefsArray, + frameworksString, + ); + const content = ` + Pod::Spec.new do |s| + s.name = '${name}' + s.version = '${config.cli.package.version}' + s.summary = 'Autogenerated spec' + s.license = 'Unknown' + s.homepage = 'https://example.com' + s.authors = { 'Capacitor Generator' => 'hi@example.com' } + s.source = { :git => 'https://github.com/ionic-team/does-not-exist.git', :tag => '${ + config.cli.package.version + }' } + s.source_files = '${sourcesFolderName}/**/*.{swift,h,m,c,cc,mm,cpp}' + s.ios.deployment_target = '${config.ios.minVersion}' + s.xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1 WK_WEB_VIEW_ONLY=1' } + s.dependency 'CapacitorCordova'${getLinkerFlags(config)} + s.swift_version = '5.1' + ${frameworksString} + end`; + await writeFile( + join(config.ios.cordovaPluginsDirAbs, `${name}.podspec`), + content, + ); +} + +function getLinkerFlags(config: Config) { + if (config.app.extConfig.ios?.cordovaLinkerFlags) { + return `\n s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '${config.app.extConfig.ios.cordovaLinkerFlags.join( + ' ', + )}' }`; + } + return ''; +} + +export async function copyPluginsNativeFiles( + config: Config, + cordovaPlugins: Plugin[], +): Promise { + for (const p of cordovaPlugins) { + const sourceFiles = getPlatformElement(p, platform, 'source-file'); + const headerFiles = getPlatformElement(p, platform, 'header-file'); + const codeFiles = sourceFiles.concat(headerFiles); + const frameworks = getPlatformElement(p, platform, 'framework'); + let sourcesFolderName = 'sources'; + if (needsStaticPod(p, config)) { + sourcesFolderName += 'static'; + } + const sourcesFolder = join( + config.ios.cordovaPluginsDirAbs, + sourcesFolderName, + p.name, + ); + for (const codeFile of codeFiles) { + let fileName = codeFile.$.src.split('/').pop(); + const fileExt = codeFile.$.src.split('.').pop(); + if (fileExt === 'a' && !fileName.startsWith('lib')) { + fileName = 'lib' + fileName; + } + let destFolder = sourcesFolderName; + if ( + codeFile.$['compiler-flags'] && + codeFile.$['compiler-flags'] === '-fno-objc-arc' + ) { + destFolder = 'noarc'; + } + const filePath = getFilePath(config, p, codeFile.$.src); + const fileDest = join( + config.ios.cordovaPluginsDirAbs, + destFolder, + p.name, + fileName, + ); + await copy(filePath, fileDest); + if (!codeFile.$.framework) { + let fileContent = await readFile(fileDest, { encoding: 'utf-8' }); + if (fileExt === 'swift') { + fileContent = 'import Cordova\n' + fileContent; + await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); + } else { + if (fileContent.includes('@import Firebase;')) { + fileContent = fileContent.replace( + '@import Firebase;', + '#import ', + ); + await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); + } + if ( + fileContent.includes('[NSBundle bundleForClass:[self class]]') || + fileContent.includes('[NSBundle bundleForClass:[CDVCapture class]]') + ) { + fileContent = fileContent.replace( + '[NSBundle bundleForClass:[self class]]', + '[NSBundle mainBundle]', + ); + fileContent = fileContent.replace( + '[NSBundle bundleForClass:[CDVCapture class]]', + '[NSBundle mainBundle]', + ); + await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); + } + if ( + fileContent.includes('[self.webView superview]') || + fileContent.includes('self.webView.superview') + ) { + fileContent = fileContent.replace( + /\[self.webView superview\]/g, + 'self.viewController.view', + ); + fileContent = fileContent.replace( + /self.webView.superview/g, + 'self.viewController.view', + ); + await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); + } + } + } + } + const resourceFiles = getPlatformElement(p, platform, 'resource-file'); + for (const resourceFile of resourceFiles) { + const fileName = resourceFile.$.src.split('/').pop(); + await copy( + getFilePath(config, p, resourceFile.$.src), + join(config.ios.cordovaPluginsDirAbs, 'resources', fileName), + ); + } + for (const framework of frameworks) { + if (framework.$.custom && framework.$.custom === 'true') { + await copy( + getFilePath(config, p, framework.$.src), + join(sourcesFolder, framework.$.src), + ); + } + } + } +} + +export async function removePluginsNativeFiles(config: Config): Promise { + await remove(config.ios.cordovaPluginsDirAbs); + await extractTemplate( + config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, + config.ios.cordovaPluginsDirAbs, + ); +} + +export function filterARCFiles(plugin: Plugin): boolean { + const sources = getPlatformElement(plugin, platform, 'source-file'); + const sourcesARC = sources.filter( + (sourceFile: any) => + sourceFile.$['compiler-flags'] && + sourceFile.$['compiler-flags'] === '-fno-objc-arc', + ); + return sourcesARC.length > 0; +} + +function removeNoSystem(library: string, sourceFrameworks: string[]): boolean { + const libraries = sourceFrameworks.filter(framework => + framework.includes(library), + ); + return libraries.length === 0; +} + +async function replaceFrameworkVariables( + config: Config, + prefsArray: any[], + frameworkString: string, +): Promise { + prefsArray.map((preference: any) => { + frameworkString = frameworkString.replace( + new RegExp(('$' + preference.$.name).replace('$', '\\$&'), 'g'), + preference.$.default, + ); + }); + return frameworkString; +} + +function getFrameworkName(framework: any): string { + if (isFramework(framework)) { + if (framework.$.custom && framework.$.custom === 'true') { + return framework.$.src; + } + return framework.$.src.substr(0, framework.$.src.indexOf('.')); + } + return framework.$.src + .substr(0, framework.$.src.indexOf('.')) + .replace('lib', ''); +} + +function isFramework(framework: any) { + return framework.$.src.split('.').pop().includes('framework'); +} + +export function cordovaPodfileLines( + config: Config, + plugins: Plugin[], +): string[] { + const pods: string[] = []; + + const cordovaPlugins = plugins.filter( + p => getPluginType(p, platform) === PluginType.Cordova, + ); + cordovaPlugins.map(async p => { + const podspecs = getPlatformElement(p, platform, 'podspec'); + podspecs.map((podspec: any) => { + podspec.pods.map((pPods: any) => { + pPods.pod.map((pod: any) => { + if (pod.$.git) { + let gitRef = ''; + if (pod.$.tag) { + gitRef = `, :tag => '${pod.$.tag}'`; + } else if (pod.$.branch) { + gitRef = `, :branch => '${pod.$.branch}'`; + } else if (pod.$.commit) { + gitRef = `, :commit => '${pod.$.commit}'`; + } + pods.push( + ` pod '${pod.$.name}', :git => '${pod.$.git}'${gitRef}\n`, + ); + } + }); + }); + }); + }); + const staticPlugins = cordovaPlugins.filter(p => needsStaticPod(p, config)); + const noStaticPlugins = cordovaPlugins.filter( + el => !staticPlugins.includes(el), + ); + if (noStaticPlugins.length > 0) { + pods.push( + ` pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'\n`, + ); + } + if (staticPlugins.length > 0) { + pods.push( + ` pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'\n`, + ); + } + const resourcesPlugins = cordovaPlugins.filter(filterResources); + if (resourcesPlugins.length > 0) { + pods.push( + ` pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'\n`, + ); + } + + return pods; +} + +function filterResources(plugin: Plugin) { + const resources = getPlatformElement(plugin, platform, 'resource-file'); + return resources.length > 0; +} From b5d262b5131b9e9663a2e2cd8cea22d3ed983f99 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 16 Dec 2024 09:51:36 -0500 Subject: [PATCH 11/30] fix: syntax errors from merge, update cordova cli --- cli/src/ios/update.ts | 22 ++--- cli/src/util/cordova-ios.ts | 170 ++++++++---------------------------- 2 files changed, 43 insertions(+), 149 deletions(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index e95361ead..2b2fe1891 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -1,4 +1,4 @@ -import { copy, remove, pathExists, readFile, realpath, writeFile } from 'fs-extra'; +import { pathExists, readFile, realpath, writeFile } from 'fs-extra'; import { basename, dirname, join, relative } from 'path'; import c from '../colors'; @@ -7,13 +7,7 @@ import { checkPluginDependencies, handleCordovaPluginsJS, logCordovaManualSteps, import type { Config } from '../definitions'; import { fatal } from '../errors'; import { logger } from '../log'; -import { - PluginType, - getPlatformElement, - getPluginType, - getPlugins, - printPlugins, -} from '../plugin'; +import { PluginType, getPluginType, getPlugins, printPlugins } from '../plugin'; import type { Plugin } from '../plugin'; import { copy as copyTask } from '../tasks/copy'; import { @@ -193,17 +187,11 @@ async function generatePodFile(config: Config, plugins: Plugin[]): Promise 0) { - pods.push(` pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'\n`); - } - const resourcesPlugins = cordovaPlugins.filter(filterResources); - if (resourcesPlugins.length > 0) { - pods.push(` pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'\n`); return ` - pod 'Capacitor', :path => '${relativeCapacitoriOSPath}' - pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}' -${pods.join('').trimEnd()}`; + pod 'Capacitor', :path => '${relativeCapacitoriOSPath}' + pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}' + ${pods.join('').trimEnd()}`; } async function getPluginsTask(config: Config) { diff --git a/cli/src/util/cordova-ios.ts b/cli/src/util/cordova-ios.ts index d3e71b9f6..863b83ea8 100644 --- a/cli/src/util/cordova-ios.ts +++ b/cli/src/util/cordova-ios.ts @@ -1,30 +1,17 @@ -import { copy, readFile, writeFile, remove } from '@ionic/utils-fs'; +import { copy, readFile, writeFile, remove } from 'fs-extra'; import { join } from 'path'; import { needsStaticPod } from '../cordova'; import type { Config } from '../definitions'; -import { - PluginType, - getPlatformElement, - getPluginType, - getPlugins, - printPlugins, - getAllElements, - getFilePath, -} from '../plugin'; +import { PluginType, getPlatformElement, getPluginType, getAllElements, getFilePath } from '../plugin'; import type { Plugin } from '../plugin'; import { extractTemplate } from '../util/template'; const platform = 'ios'; -export async function generateCordovaPodspecs( - cordovaPlugins: Plugin[], - config: Config, -): Promise { - const staticPlugins = cordovaPlugins.filter(p => needsStaticPod(p, config)); - const noStaticPlugins = cordovaPlugins.filter( - el => !staticPlugins.includes(el), - ); +export async function generateCordovaPodspecs(cordovaPlugins: Plugin[], config: Config): Promise { + const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p)); + const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el)); generateCordovaPodspec(noStaticPlugins, config, false); generateCordovaPodspec(staticPlugins, config, true); } @@ -84,9 +71,7 @@ export async function generateCordovaPodspec( } } }); - prefsArray = prefsArray.concat( - getAllElements(plugin, platform, 'preference'), - ); + prefsArray = prefsArray.concat(getAllElements(plugin, platform, 'preference')); const podspecs = getPlatformElement(plugin, platform, 'podspec'); podspecs.map((podspec: any) => { podspec.pods.map((pods: any) => { @@ -120,9 +105,7 @@ export async function generateCordovaPodspec( } }); }); - const onlySystemLibraries = systemLibraries.filter(library => - removeNoSystem(library, sourceFrameworks), - ); + const onlySystemLibraries = systemLibraries.filter((library) => removeNoSystem(library, sourceFrameworks)); if (weakFrameworks.length > 0) { frameworkDeps.push(`s.weak_frameworks = '${weakFrameworks.join(`', '`)}'`); } @@ -133,17 +116,13 @@ export async function generateCordovaPodspec( frameworkDeps.push(`s.libraries = '${onlySystemLibraries.join(`', '`)}'`); } if (customFrameworks.length > 0) { - frameworkDeps.push( - `s.vendored_frameworks = '${customFrameworks.join(`', '`)}'`, - ); + frameworkDeps.push(`s.vendored_frameworks = '${customFrameworks.join(`', '`)}'`); frameworkDeps.push( `s.exclude_files = 'sources/**/*.framework/Headers/*.h', 'sources/**/*.framework/PrivateHeaders/*.h'`, ); } if (sourceFrameworks.length > 0) { - frameworkDeps.push( - `s.vendored_libraries = '${sourceFrameworks.join(`', '`)}'`, - ); + frameworkDeps.push(`s.vendored_libraries = '${sourceFrameworks.join(`', '`)}'`); } if (compilerFlags.length > 0) { frameworkDeps.push(`s.compiler_flags = '${compilerFlags.join(' ')}'`); @@ -156,11 +135,7 @@ export async function generateCordovaPodspec( end`); } let frameworksString = frameworkDeps.join('\n '); - frameworksString = await replaceFrameworkVariables( - config, - prefsArray, - frameworksString, - ); + frameworksString = await replaceFrameworkVariables(config, prefsArray, frameworksString); const content = ` Pod::Spec.new do |s| s.name = '${name}' @@ -169,9 +144,7 @@ export async function generateCordovaPodspec( s.license = 'Unknown' s.homepage = 'https://example.com' s.authors = { 'Capacitor Generator' => 'hi@example.com' } - s.source = { :git => 'https://github.com/ionic-team/does-not-exist.git', :tag => '${ - config.cli.package.version - }' } + s.source = { :git => 'https://github.com/ionic-team/does-not-exist.git', :tag => '${config.cli.package.version}' } s.source_files = '${sourcesFolderName}/**/*.{swift,h,m,c,cc,mm,cpp}' s.ios.deployment_target = '${config.ios.minVersion}' s.xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1 WK_WEB_VIEW_ONLY=1' } @@ -179,10 +152,7 @@ export async function generateCordovaPodspec( s.swift_version = '5.1' ${frameworksString} end`; - await writeFile( - join(config.ios.cordovaPluginsDirAbs, `${name}.podspec`), - content, - ); + await writeFile(join(config.ios.cordovaPluginsDirAbs, `${name}.podspec`), content); } function getLinkerFlags(config: Config) { @@ -194,24 +164,17 @@ function getLinkerFlags(config: Config) { return ''; } -export async function copyPluginsNativeFiles( - config: Config, - cordovaPlugins: Plugin[], -): Promise { +export async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]): Promise { for (const p of cordovaPlugins) { const sourceFiles = getPlatformElement(p, platform, 'source-file'); const headerFiles = getPlatformElement(p, platform, 'header-file'); const codeFiles = sourceFiles.concat(headerFiles); const frameworks = getPlatformElement(p, platform, 'framework'); let sourcesFolderName = 'sources'; - if (needsStaticPod(p, config)) { + if (needsStaticPod(p)) { sourcesFolderName += 'static'; } - const sourcesFolder = join( - config.ios.cordovaPluginsDirAbs, - sourcesFolderName, - p.name, - ); + const sourcesFolder = join(config.ios.cordovaPluginsDirAbs, sourcesFolderName, p.name); for (const codeFile of codeFiles) { let fileName = codeFile.$.src.split('/').pop(); const fileExt = codeFile.$.src.split('.').pop(); @@ -219,19 +182,11 @@ export async function copyPluginsNativeFiles( fileName = 'lib' + fileName; } let destFolder = sourcesFolderName; - if ( - codeFile.$['compiler-flags'] && - codeFile.$['compiler-flags'] === '-fno-objc-arc' - ) { + if (codeFile.$['compiler-flags'] && codeFile.$['compiler-flags'] === '-fno-objc-arc') { destFolder = 'noarc'; } const filePath = getFilePath(config, p, codeFile.$.src); - const fileDest = join( - config.ios.cordovaPluginsDirAbs, - destFolder, - p.name, - fileName, - ); + const fileDest = join(config.ios.cordovaPluginsDirAbs, destFolder, p.name, fileName); await copy(filePath, fileDest); if (!codeFile.$.framework) { let fileContent = await readFile(fileDest, { encoding: 'utf-8' }); @@ -240,38 +195,20 @@ export async function copyPluginsNativeFiles( await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); } else { if (fileContent.includes('@import Firebase;')) { - fileContent = fileContent.replace( - '@import Firebase;', - '#import ', - ); + fileContent = fileContent.replace('@import Firebase;', '#import '); await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); } if ( fileContent.includes('[NSBundle bundleForClass:[self class]]') || fileContent.includes('[NSBundle bundleForClass:[CDVCapture class]]') ) { - fileContent = fileContent.replace( - '[NSBundle bundleForClass:[self class]]', - '[NSBundle mainBundle]', - ); - fileContent = fileContent.replace( - '[NSBundle bundleForClass:[CDVCapture class]]', - '[NSBundle mainBundle]', - ); + fileContent = fileContent.replace('[NSBundle bundleForClass:[self class]]', '[NSBundle mainBundle]'); + fileContent = fileContent.replace('[NSBundle bundleForClass:[CDVCapture class]]', '[NSBundle mainBundle]'); await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); } - if ( - fileContent.includes('[self.webView superview]') || - fileContent.includes('self.webView.superview') - ) { - fileContent = fileContent.replace( - /\[self.webView superview\]/g, - 'self.viewController.view', - ); - fileContent = fileContent.replace( - /self.webView.superview/g, - 'self.viewController.view', - ); + if (fileContent.includes('[self.webView superview]') || fileContent.includes('self.webView.superview')) { + fileContent = fileContent.replace(/\[self.webView superview\]/g, 'self.viewController.view'); + fileContent = fileContent.replace(/self.webView.superview/g, 'self.viewController.view'); await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); } } @@ -287,10 +224,7 @@ export async function copyPluginsNativeFiles( } for (const framework of frameworks) { if (framework.$.custom && framework.$.custom === 'true') { - await copy( - getFilePath(config, p, framework.$.src), - join(sourcesFolder, framework.$.src), - ); + await copy(getFilePath(config, p, framework.$.src), join(sourcesFolder, framework.$.src)); } } } @@ -298,34 +232,23 @@ export async function copyPluginsNativeFiles( export async function removePluginsNativeFiles(config: Config): Promise { await remove(config.ios.cordovaPluginsDirAbs); - await extractTemplate( - config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, - config.ios.cordovaPluginsDirAbs, - ); + await extractTemplate(config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, config.ios.cordovaPluginsDirAbs); } export function filterARCFiles(plugin: Plugin): boolean { const sources = getPlatformElement(plugin, platform, 'source-file'); const sourcesARC = sources.filter( - (sourceFile: any) => - sourceFile.$['compiler-flags'] && - sourceFile.$['compiler-flags'] === '-fno-objc-arc', + (sourceFile: any) => sourceFile.$['compiler-flags'] && sourceFile.$['compiler-flags'] === '-fno-objc-arc', ); return sourcesARC.length > 0; } function removeNoSystem(library: string, sourceFrameworks: string[]): boolean { - const libraries = sourceFrameworks.filter(framework => - framework.includes(library), - ); + const libraries = sourceFrameworks.filter((framework) => framework.includes(library)); return libraries.length === 0; } -async function replaceFrameworkVariables( - config: Config, - prefsArray: any[], - frameworkString: string, -): Promise { +async function replaceFrameworkVariables(config: Config, prefsArray: any[], frameworkString: string): Promise { prefsArray.map((preference: any) => { frameworkString = frameworkString.replace( new RegExp(('$' + preference.$.name).replace('$', '\\$&'), 'g'), @@ -342,25 +265,18 @@ function getFrameworkName(framework: any): string { } return framework.$.src.substr(0, framework.$.src.indexOf('.')); } - return framework.$.src - .substr(0, framework.$.src.indexOf('.')) - .replace('lib', ''); + return framework.$.src.substr(0, framework.$.src.indexOf('.')).replace('lib', ''); } function isFramework(framework: any) { return framework.$.src.split('.').pop().includes('framework'); } -export function cordovaPodfileLines( - config: Config, - plugins: Plugin[], -): string[] { +export function cordovaPodfileLines(config: Config, plugins: Plugin[]): string[] { const pods: string[] = []; - const cordovaPlugins = plugins.filter( - p => getPluginType(p, platform) === PluginType.Cordova, - ); - cordovaPlugins.map(async p => { + const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova); + cordovaPlugins.map(async (p) => { const podspecs = getPlatformElement(p, platform, 'podspec'); podspecs.map((podspec: any) => { podspec.pods.map((pPods: any) => { @@ -374,33 +290,23 @@ export function cordovaPodfileLines( } else if (pod.$.commit) { gitRef = `, :commit => '${pod.$.commit}'`; } - pods.push( - ` pod '${pod.$.name}', :git => '${pod.$.git}'${gitRef}\n`, - ); + pods.push(` pod '${pod.$.name}', :git => '${pod.$.git}'${gitRef}\n`); } }); }); }); }); - const staticPlugins = cordovaPlugins.filter(p => needsStaticPod(p, config)); - const noStaticPlugins = cordovaPlugins.filter( - el => !staticPlugins.includes(el), - ); + const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p)); + const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el)); if (noStaticPlugins.length > 0) { - pods.push( - ` pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'\n`, - ); + pods.push(` pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'\n`); } if (staticPlugins.length > 0) { - pods.push( - ` pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'\n`, - ); + pods.push(` pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'\n`); } const resourcesPlugins = cordovaPlugins.filter(filterResources); if (resourcesPlugins.length > 0) { - pods.push( - ` pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'\n`, - ); + pods.push(` pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'\n`); } return pods; From b454063f58c12df373705d8b49aa273df3659f52 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 16 Dec 2024 10:44:57 -0500 Subject: [PATCH 12/30] fix: problem with podfile regex --- cli/src/ios/update.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 2b2fe1891..4c0d99256 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -23,6 +23,7 @@ import { checkPackageManager, generatePackageFile, checkPluginsForPackageSwift } import { runCommand, isInstalled } from '../util/subprocess'; import { getIOSPlugins, getMajoriOSVersion } from './common'; +import { replace } from 'tar'; const platform = 'ios'; @@ -125,7 +126,7 @@ async function updatePodfile(config: Config, plugins: Plugin[], deployment: bool let podfileContent = await readFile(podfilePath, { encoding: 'utf-8' }); podfileContent = podfileContent.replace(/(def capacitor_pods)[\s\S]+?(\nend)/, `$1${dependenciesContent}$2`); podfileContent = podfileContent.replace( - /(require_relative)[\s\S]+?(@capacitor\/ios\/scripts\/pods_helpers')/, + /(require_relative)[\s\S]+?(ios\/scripts\/pods_helpers')/, `require_relative '${relativeCapacitoriOSPath}/scripts/pods_helpers'`, ); await writeFile(podfilePath, podfileContent, { encoding: 'utf-8' }); From fca79e6322eed51214c1819fad3ec3db1b29794e Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 16 Dec 2024 11:24:15 -0500 Subject: [PATCH 13/30] Removed commented out stuff --- .../CAPApplicationDelegateProxy.swift | 1 - .../Capacitor/CAPInstanceDescriptor.swift | 1 - ios/Capacitor/Capacitor/CapacitorBridge.swift | 48 +++++++------- ios/Capacitor/Capacitor/JSExport.swift | 63 +++++++------------ .../Capacitor/WebViewDelegationHandler.swift | 18 +----- .../Classes/Public/Plugin.swift | 4 +- 6 files changed, 51 insertions(+), 84 deletions(-) diff --git a/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift b/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift index 50027babc..d687953fe 100644 --- a/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift +++ b/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift @@ -11,7 +11,6 @@ public class ApplicationDelegateProxy: NSObject, UIApplicationDelegate { "url": url, "options": options ]) - //NotificationCenter.default.post(name: NSNotification.Name.CDVPluginHandleOpenURL, object: url) lastURL = url return true } diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift index fa86a1f11..495de0634 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift @@ -144,7 +144,6 @@ internal extension InstanceDescriptor { extension InstanceDescriptor { @objc public var cordovaDeployDisabled: Bool { return false - //return (cordovaConfiguration.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false } @objc public func normalize() { diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index b37e30a22..4ee330d98 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -115,8 +115,6 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { @objc public var config: InstanceConfiguration // Map of all loaded and instantiated plugins by pluginId -> instance var plugins = [String: CapacitorPlugin]() - // Manager for getting Cordova plugins - //var cordovaPluginManager: CDVPluginManager? // Calls we are storing to resolve later var storedCalls = ConcurrentDictionary() // Whether to inject the Cordova files @@ -204,8 +202,6 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { // MARK: - Initialization - - @available(*, deprecated, renamed: "init", message: "Use different init") public convenience init(with configuration: InstanceConfiguration, delegate bridgeDelegate: CAPBridgeDelegate, cordovaConfiguration: Any, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler, autoRegisterPlugins: Bool = true) { self.init(with: configuration, delegate: bridgeDelegate, assetHandler: assetHandler, delegationHandler: delegationHandler, autoRegisterPlugins: autoRegisterPlugins) @@ -545,28 +541,28 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { Handle a Cordova call from JavaScript. First, find the corresponding plugin, construct a selector, and perform that selector on the plugin instance. */ -// func handleCordovaJSCall(call: JSCall) { -// // Create a selector to send to the plugin -// -// if let plugin = self.cordovaPluginManager?.getCommandInstance(call.pluginId.lowercased()) { -// let selector = NSSelectorFromString("\(call.method):") -// if !plugin.responds(to: selector) { -// CAPLog.print("Error: Plugin \(plugin.className ?? "") does not respond to method call \(selector).") -// CAPLog.print("Ensure plugin method exists and uses @objc in its declaration") -// return -// } -// -// let arguments: [Any] = call.options["options"] as? [Any] ?? [] -// let pluginCall = CDVInvokedUrlCommand(arguments: arguments, -// callbackId: call.callbackId, -// className: plugin.className, -// methodName: call.method) -// plugin.perform(selector, with: pluginCall) -// -// } else { -// CAPLog.print("Error: Cordova Plugin mapping not found") -// return -// } + // func handleCordovaJSCall(call: JSCall) { + // // Create a selector to send to the plugin + // + // if let plugin = self.cordovaPluginManager?.getCommandInstance(call.pluginId.lowercased()) { + // let selector = NSSelectorFromString("\(call.method):") + // if !plugin.responds(to: selector) { + // CAPLog.print("Error: Plugin \(plugin.className ?? "") does not respond to method call \(selector).") + // CAPLog.print("Ensure plugin method exists and uses @objc in its declaration") + // return + // } + // + // let arguments: [Any] = call.options["options"] as? [Any] ?? [] + // let pluginCall = CDVInvokedUrlCommand(arguments: arguments, + // callbackId: call.callbackId, + // className: plugin.className, + // methodName: call.method) + // plugin.perform(selector, with: pluginCall) + // + // } else { + // CAPLog.print("Error: Cordova Plugin mapping not found") + // return + // } // } func removeAllPluginListeners() { diff --git a/ios/Capacitor/Capacitor/JSExport.swift b/ios/Capacitor/Capacitor/JSExport.swift index c5e895e40..d2fd3eeaf 100644 --- a/ios/Capacitor/Capacitor/JSExport.swift +++ b/ios/Capacitor/Capacitor/JSExport.swift @@ -35,24 +35,6 @@ internal class JSExport { } } -// static func exportCordovaJS(userContentController: WKUserContentController) throws { -// guard let cordovaUrl = Bundle.main.url(forResource: "public/cordova", withExtension: "js") else { -// CAPLog.print("ERROR: Required cordova.js file not found. Cordova plugins will not function!") -// throw CapacitorBridgeError.errorExportingCoreJS -// } -// guard let cordovaPluginsUrl = Bundle.main.url(forResource: "public/cordova_plugins", withExtension: "js") else { -// CAPLog.print("ERROR: Required cordova_plugins.js file not found. Cordova plugins will not function!") -// throw CapacitorBridgeError.errorExportingCoreJS -// } -// do { -// try self.injectFile(fileURL: cordovaUrl, userContentController: userContentController) -// try self.injectFile(fileURL: cordovaPluginsUrl, userContentController: userContentController) -// } catch { -// CAPLog.print("ERROR: Unable to read required cordova files. Cordova plugins will not function!") -// throw CapacitorBridgeError.errorExportingCoreJS -// } -// } - static func exportMiscFileJS(paths: [String], userContentController: WKUserContentController) { for path in paths { if let miscJSFilePath = Bundle.main.url(forResource: "public/\(path.replacingOccurrences(of: ".js", with: ""))", withExtension: "js") { @@ -67,6 +49,9 @@ internal class JSExport { } } + + + /** Export the JS required to implement the given plugin. */ @@ -184,27 +169,27 @@ internal class JSExport { return lines.joined(separator: "\n") } -// static func exportCordovaPluginsJS(userContentController: WKUserContentController) throws { -// if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) { -// self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) -// } -// } - -// static func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) { -// let fileManager = FileManager.default -// do { -// let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: []) -// for fileURL in fileURLs { -// if fileURL.hasDirectoryPath { -// injectFilesForFolder(folder: fileURL, userContentController: userContentController) -// } else { -// try self.injectFile(fileURL: fileURL, userContentController: userContentController) -// } -// } -// } catch { -// CAPLog.print("Error while enumerating files") -// } -// } + // static func exportCordovaPluginsJS(userContentController: WKUserContentController) throws { + // if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) { + // self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) + // } + // } + + // static func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) { + // let fileManager = FileManager.default + // do { + // let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: []) + // for fileURL in fileURLs { + // if fileURL.hasDirectoryPath { + // injectFilesForFolder(folder: fileURL, userContentController: userContentController) + // } else { + // try self.injectFile(fileURL: fileURL, userContentController: userContentController) + // } + // } + // } catch { + // CAPLog.print("Error while enumerating files") + // } + // } static func injectFile(fileURL: URL, userContentController: WKUserContentController) throws { do { diff --git a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift index 608be37d7..75da5016f 100644 --- a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift @@ -180,29 +180,17 @@ open class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDelegat let pluginId = dict["pluginId"] as? String ?? "" let method = dict["methodName"] as? String ?? "" let callbackId = dict["callbackId"] as? String ?? "" - + let options = dict["options"] as? [String: Any] ?? [:] - + if pluginId != "Console" { CAPLog.print("⚡️ To Native -> ", pluginId, method, callbackId) } - + bridge.handleJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId)) } else if let handler = bridge.callInterceptors[type] { handler(dict) } -// } else if type == "cordova" { -// let pluginId = dict["service"] as? String ?? "" -// let method = dict["action"] as? String ?? "" -// let callbackId = dict["callbackId"] as? String ?? "" -// -// let args = dict["actionArgs"] as? Array ?? [] -// let options = ["options": args] -// -// CAPLog.print("To Native Cordova -> ", pluginId, method, callbackId, options) -// -// //bridge.handleCordovaJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId)) -// } } } diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift index b45b3c461..d58c4ca33 100644 --- a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift +++ b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift @@ -8,7 +8,7 @@ public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { private var _cordovaDeployDisabled = false - public override func load() { + override public func load() { injectJavascript() configureRuntime() } @@ -68,7 +68,7 @@ public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { return } } - + _cordovaDeployDisabled = (cordovaConfigParser.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false } From 3818f6fb1f504479ac431466585de3d236eda72c Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 16 Dec 2024 11:25:43 -0500 Subject: [PATCH 14/30] npm run fmt --- cli/src/ios/update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 4c0d99256..9be9098ae 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -1,5 +1,6 @@ import { pathExists, readFile, realpath, writeFile } from 'fs-extra'; import { basename, dirname, join, relative } from 'path'; +import { replace } from 'tar'; import c from '../colors'; import { checkPlatformVersions, getCapacitorPackageVersion, runTask } from '../common'; @@ -23,7 +24,6 @@ import { checkPackageManager, generatePackageFile, checkPluginsForPackageSwift } import { runCommand, isInstalled } from '../util/subprocess'; import { getIOSPlugins, getMajoriOSVersion } from './common'; -import { replace } from 'tar'; const platform = 'ios'; From cd993e87cbfb6448263069838c33238d71f47c2d Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Tue, 17 Dec 2024 10:58:19 -0500 Subject: [PATCH 15/30] More code cleanup --- ios/Capacitor/Capacitor/JSExport.swift | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/ios/Capacitor/Capacitor/JSExport.swift b/ios/Capacitor/Capacitor/JSExport.swift index d2fd3eeaf..aa3e2cbc1 100644 --- a/ios/Capacitor/Capacitor/JSExport.swift +++ b/ios/Capacitor/Capacitor/JSExport.swift @@ -169,28 +169,6 @@ internal class JSExport { return lines.joined(separator: "\n") } - // static func exportCordovaPluginsJS(userContentController: WKUserContentController) throws { - // if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) { - // self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) - // } - // } - - // static func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) { - // let fileManager = FileManager.default - // do { - // let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: []) - // for fileURL in fileURLs { - // if fileURL.hasDirectoryPath { - // injectFilesForFolder(folder: fileURL, userContentController: userContentController) - // } else { - // try self.injectFile(fileURL: fileURL, userContentController: userContentController) - // } - // } - // } catch { - // CAPLog.print("Error while enumerating files") - // } - // } - static func injectFile(fileURL: URL, userContentController: WKUserContentController) throws { do { let data = try String(contentsOf: fileURL, encoding: .utf8) From a77bc8c71c0ffc095acfa892906640b41722a225 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Tue, 17 Dec 2024 11:36:58 -0500 Subject: [PATCH 16/30] fix test error looking for cordova --- ios/Capacitor/CapacitorTests/ConfigurationTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/ios/Capacitor/CapacitorTests/ConfigurationTests.swift b/ios/Capacitor/CapacitorTests/ConfigurationTests.swift index 778bfd688..177f211e7 100644 --- a/ios/Capacitor/CapacitorTests/ConfigurationTests.swift +++ b/ios/Capacitor/CapacitorTests/ConfigurationTests.swift @@ -33,7 +33,6 @@ class ConfigurationTests: XCTestCase { let descriptor = InstanceDescriptor.init() XCTAssertTrue(descriptor.warnings.contains(.missingAppDir)) XCTAssertTrue(descriptor.warnings.contains(.missingFile)) - XCTAssertTrue(descriptor.warnings.contains(.missingCordovaFile)) } func testMissingAppDetection() throws { From 4888f82bc7d9bf1f406bde73fcaaffefefbd58d8 Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Fri, 24 May 2024 14:41:24 -0500 Subject: [PATCH 17/30] Starting android cordova removal (cherry picked from commit c84b1347e1014f0dd5cda254c5a3810ccc74edf3) --- .../main/java/com/getcapacitor/Bridge.java | 43 ++++---- .../java/com/getcapacitor/BridgeActivity.java | 3 +- .../java/com/getcapacitor/MessageHandler.java | 104 +++++++++++------- .../{PluginManager.java => PluginLoader.java} | 5 +- .../getcapacitor/cordova/CordovaPlugin.java | 8 ++ .../cordova/MockCordovaInterfaceImpl.java | 2 +- .../cordova/MockCordovaWebViewImpl.java | 4 +- 7 files changed, 104 insertions(+), 65 deletions(-) rename android/capacitor/src/main/java/com/getcapacitor/{PluginManager.java => PluginLoader.java} (95%) create mode 100644 android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 3b135910c..436632cdb 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -53,13 +53,14 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.cordova.ConfigXmlParser; -import org.apache.cordova.CordovaPreferences; -import org.apache.cordova.CordovaWebView; -import org.apache.cordova.PluginEntry; -import org.apache.cordova.PluginManager; +//import org.apache.cordova.ConfigXmlParser; +//import org.apache.cordova.CordovaPreferences; +//import org.apache.cordova.CordovaWebView; +//import org.apache.cordova.PluginEntry; +//import org.apache.cordova.PluginManager; import org.json.JSONException; /** @@ -125,9 +126,9 @@ public class Bridge { private Boolean canInjectJS = true; // A reference to the main WebView for the app private final WebView webView; - public final MockCordovaInterfaceImpl cordovaInterface; - private CordovaWebView cordovaWebView; - private CordovaPreferences preferences; +// public final MockCordovaInterfaceImpl cordovaInterface; +// private CordovaWebView cordovaWebView; +// private CordovaPreferences preferences; private BridgeWebViewClient webViewClient; private App app; @@ -147,6 +148,8 @@ public class Bridge { // A map of Plugin Id's to PluginHandle's private Map plugins = new HashMap<>(); + private Map interceptors = new HashMap<>(); + // Stored plugin calls that we're keeping around to call again someday private Map savedCalls = new HashMap<>(); @@ -181,12 +184,12 @@ public Bridge( AppCompatActivity context, WebView webView, List> initialPlugins, - MockCordovaInterfaceImpl cordovaInterface, - PluginManager pluginManager, - CordovaPreferences preferences, + Object cordovaInterface, + Object pluginManager, + Object preferences, CapConfig config ) { - this(context, null, null, webView, initialPlugins, new ArrayList<>(), cordovaInterface, pluginManager, preferences, config); + this(context, (ServerPath) null, (Fragment) null, webView, initialPlugins, new ArrayList<>(), config); } private Bridge( @@ -196,9 +199,6 @@ private Bridge( WebView webView, List> initialPlugins, List pluginInstances, - MockCordovaInterfaceImpl cordovaInterface, - PluginManager pluginManager, - CordovaPreferences preferences, CapConfig config ) { this.app = new App(); @@ -209,8 +209,6 @@ private Bridge( this.webViewClient = new BridgeWebViewClient(this); this.initialPlugins = initialPlugins; this.pluginInstances = pluginInstances; - this.cordovaInterface = cordovaInterface; - this.preferences = preferences; // Start our plugin execution threads and handlers handlerThread.start(); @@ -218,11 +216,10 @@ private Bridge( this.config = config != null ? config : CapConfig.loadDefault(getActivity()); Logger.init(this.config); - // Initialize web view and message handler for it this.initWebView(); this.setAllowedOriginRules(); - this.msgHandler = new MessageHandler(this, webView, pluginManager); + this.msgHandler = new MessageHandler(this, webView); // Grab any intent info that our app was launched with Intent intent = context.getIntent(); @@ -567,6 +564,14 @@ public CapConfig getConfig() { return this.config; } + public MessageHandler.Interceptor getCallInterceptor(String type) { + return this.interceptors.get(type); + } + + public void registerInterceptor(String type, MessageHandler.Interceptor interceptor) { + this.interceptors.put(type, interceptor); + } + public void reset() { savedCalls = new HashMap<>(); for (PluginHandle handle : this.plugins.values()) { diff --git a/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java b/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java index c3779a548..c62a339e8 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java +++ b/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java @@ -31,7 +31,8 @@ protected void onCreate(Bundle savedInstanceState) { return; } - PluginManager loader = new PluginManager(getAssets()); + // TODO: Undo this + PluginLoader loader = new PluginLoader(getAssets()); try { bridgeBuilder.addPlugins(loader.loadPluginClasses()); diff --git a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java index dc91c9b01..cd8c68368 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java +++ b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java @@ -5,23 +5,30 @@ import androidx.webkit.JavaScriptReplyProxy; import androidx.webkit.WebViewCompat; import androidx.webkit.WebViewFeature; -import org.apache.cordova.PluginManager; + +import java.util.function.Function; +//import org.apache.cordova.PluginManager; /** * MessageHandler handles messages from the WebView, dispatching them * to plugins. */ public class MessageHandler { + @FunctionalInterface + public interface Interceptor { + void intercept(JSObject object); + } + private Bridge bridge; private WebView webView; - private PluginManager cordovaPluginManager; +// private PluginManager cordovaPluginManager; private JavaScriptReplyProxy javaScriptReplyProxy; - public MessageHandler(Bridge bridge, WebView webView, PluginManager cordovaPluginManager) { + + public MessageHandler(Bridge bridge, WebView webView) { this.bridge = bridge; this.webView = webView; - this.cordovaPluginManager = cordovaPluginManager; if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER) && !bridge.getConfig().isUsingLegacyBridge()) { WebViewCompat.WebMessageListener capListener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> { @@ -42,6 +49,11 @@ public MessageHandler(Bridge bridge, WebView webView, PluginManager cordovaPlugi } } + @Deprecated + public MessageHandler(Bridge bridge, WebView webView, Object cordovaPluginManager) { + this(bridge, webView); + } + /** * The main message handler that will be called from JavaScript * to send a message to the native bridge. @@ -55,44 +67,56 @@ public void postMessage(String jsonStr) { String type = postData.getString("type"); - boolean typeIsNotNull = type != null; - boolean isCordovaPlugin = typeIsNotNull && type.equals("cordova"); - boolean isJavaScriptError = typeIsNotNull && type.equals("js.error"); - - String callbackId = postData.getString("callbackId"); - - if (isCordovaPlugin) { - String service = postData.getString("service"); - String action = postData.getString("action"); - String actionArgs = postData.getString("actionArgs"); - - Logger.verbose( - Logger.tags("Plugin"), - "To native (Cordova plugin): callbackId: " + - callbackId + - ", service: " + - service + - ", action: " + - action + - ", actionArgs: " + - actionArgs - ); - - this.callCordovaPluginMethod(callbackId, service, action, actionArgs); - } else if (isJavaScriptError) { - Logger.error("JavaScript Error: " + jsonStr); - } else { - String pluginId = postData.getString("pluginId"); - String methodName = postData.getString("methodName"); - JSObject methodData = postData.getJSObject("options", new JSObject()); - - Logger.verbose( - Logger.tags("Plugin"), - "To native (Capacitor plugin): callbackId: " + callbackId + ", pluginId: " + pluginId + ", methodName: " + methodName - ); +// boolean typeIsNotNull = type != null; + if (type == null) + return; - this.callPluginMethod(callbackId, pluginId, methodName, methodData); + Interceptor interceptor = bridge.getCallInterceptor(type); + if (interceptor != null) { + interceptor.intercept(postData); } + + +// boolean isCapacitorPlugin = typeIsNotNull && type.equals("message"); +// boolean isCordovaPlugin = typeIsNotNull && type.equals("cordova"); +// boolean isJavaScriptError = typeIsNotNull && type.equals("js.error"); +// +// String callbackId = postData.getString("callbackId"); +// +// if (isCordovaPlugin) { +// String pluginId = postData.getString("pluginId"); +// String methodName = postData.getString("methodName"); +// JSObject methodData = postData.getJSObject("options", new JSObject()); +// +// Logger.verbose( +// Logger.tags("Plugin"), +// "To native (Capacitor plugin): callbackId: " + callbackId + ", pluginId: " + pluginId + ", methodName: " + methodName +// ); +// +// this.callPluginMethod(callbackId, pluginId, methodName, methodData); +// // start cordova +// String service = postData.getString("service"); +// String action = postData.getString("action"); +// String actionArgs = postData.getString("actionArgs"); +// +// Logger.verbose( +// Logger.tags("Plugin"), +// "To native (Cordova plugin): callbackId: " + +// callbackId + +// ", service: " + +// service + +// ", action: " + +// action + +// ", actionArgs: " + +// actionArgs +// ); +// +// this.callCordovaPluginMethod(callbackId, service, action, actionArgs); +// } else if (isJavaScriptError) { +// Logger.error("JavaScript Error: " + jsonStr); +// } else if (typeIsNotNull && bridge.getCallInterceptor(type) != null) { +// bridge.getCallInterceptor(type).intercept(postData); +// } } catch (Exception ex) { Logger.error("Post message error:", ex); } diff --git a/android/capacitor/src/main/java/com/getcapacitor/PluginManager.java b/android/capacitor/src/main/java/com/getcapacitor/PluginLoader.java similarity index 95% rename from android/capacitor/src/main/java/com/getcapacitor/PluginManager.java rename to android/capacitor/src/main/java/com/getcapacitor/PluginLoader.java index 540bc9122..93825799a 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/PluginManager.java +++ b/android/capacitor/src/main/java/com/getcapacitor/PluginLoader.java @@ -10,11 +10,12 @@ import org.json.JSONException; import org.json.JSONObject; -public class PluginManager { +// TODO: Undo this rename +public class PluginLoader { private final AssetManager assetManager; - public PluginManager(AssetManager assetManager) { + public PluginLoader(AssetManager assetManager) { this.assetManager = assetManager; } diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java new file mode 100644 index 000000000..d50a7da02 --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java @@ -0,0 +1,8 @@ +package com.getcapacitor.cordova; + +import com.getcapacitor.annotation.CapacitorPlugin; + +@CapacitorPlugin +public class CordovaPlugin { + +} diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java index 7e8358da5..11116bf13 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java @@ -7,7 +7,7 @@ import org.apache.cordova.CordovaPlugin; import org.json.JSONException; -public class MockCordovaInterfaceImpl extends CordovaInterfaceImpl { +class MockCordovaInterfaceImpl extends CordovaInterfaceImpl { public MockCordovaInterfaceImpl(AppCompatActivity activity) { super(activity, Executors.newCachedThreadPool()); diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java index 1115429d7..72136c496 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java @@ -20,7 +20,7 @@ import org.apache.cordova.PluginManager; import org.apache.cordova.PluginResult; -public class MockCordovaWebViewImpl implements CordovaWebView { +class MockCordovaWebViewImpl implements CordovaWebView { private Context context; private PluginManager pluginManager; @@ -32,7 +32,7 @@ public class MockCordovaWebViewImpl implements CordovaWebView { private WebView webView; private boolean hasPausedEver; - public MockCordovaWebViewImpl(Context context) { + MockCordovaWebViewImpl(Context context) { this.context = context; } From 1e0fe97cbe354be28b2e8330459f1232926bdae0 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Tue, 28 May 2024 12:07:54 -0400 Subject: [PATCH 18/30] Removing Cordova part 1 (cherry picked from commit 9b4a2755653c93a900ec2a52b10e89ac852d59d6) --- .../main/java/com/getcapacitor/Bridge.java | 18 +---- .../java/com/getcapacitor/MessageHandler.java | 12 ++-- .../getcapacitor/cordova/CordovaPlugin.java | 67 ++++++++++++++++++- 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 436632cdb..a1b231280 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -35,8 +35,6 @@ import com.getcapacitor.android.R; import com.getcapacitor.annotation.CapacitorPlugin; import com.getcapacitor.annotation.Permission; -import com.getcapacitor.cordova.MockCordovaInterfaceImpl; -import com.getcapacitor.cordova.MockCordovaWebViewImpl; import com.getcapacitor.util.HostMask; import com.getcapacitor.util.InternalUtils; import com.getcapacitor.util.PermissionHelper; @@ -1582,22 +1580,8 @@ public Builder setServerPath(ServerPath serverPath) { public Bridge create() { // Cordova initialization - ConfigXmlParser parser = new ConfigXmlParser(); - parser.parse(activity.getApplicationContext()); - CordovaPreferences preferences = parser.getPreferences(); - preferences.setPreferencesBundle(activity.getIntent().getExtras()); - List pluginEntries = parser.getPluginEntries(); - - MockCordovaInterfaceImpl cordovaInterface = new MockCordovaInterfaceImpl(activity); - if (instanceState != null) { - cordovaInterface.restoreInstanceState(instanceState); - } - WebView webView = this.fragment != null ? fragment.getView().findViewById(R.id.webview) : activity.findViewById(R.id.webview); - MockCordovaWebViewImpl mockWebView = new MockCordovaWebViewImpl(activity.getApplicationContext()); - mockWebView.init(cordovaInterface, pluginEntries, preferences, webView); - PluginManager pluginManager = mockWebView.getPluginManager(); - cordovaInterface.onCordovaInit(pluginManager); + // Bridge initialization Bridge bridge = new Bridge( diff --git a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java index cd8c68368..991c7a187 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java +++ b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java @@ -173,9 +173,11 @@ private void callPluginMethod(String callbackId, String pluginId, String methodN bridge.callPluginMethod(pluginId, methodName, call); } - private void callCordovaPluginMethod(String callbackId, String service, String action, String actionArgs) { - bridge.execute(() -> { - cordovaPluginManager.exec(service, action, callbackId, actionArgs); - }); - } +// private void callCordovaPluginMethod(String callbackId, String service, String action, String actionArgs) { +// bridge.execute( +// () -> { +// cordovaPluginManager.exec(service, action, callbackId, actionArgs); +// } +// ); +// } } diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java index d50a7da02..0971bbd28 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java @@ -1,8 +1,73 @@ package com.getcapacitor.cordova; +import android.os.Bundle; +import android.webkit.WebView; + +import com.getcapacitor.Logger; +import com.getcapacitor.Plugin; +import com.getcapacitor.android.R; import com.getcapacitor.annotation.CapacitorPlugin; +import org.apache.cordova.ConfigXmlParser; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPreferences; +import org.apache.cordova.PluginEntry; +import org.apache.cordova.PluginManager; + +import java.util.List; + @CapacitorPlugin -public class CordovaPlugin { +public class CordovaPlugin extends Plugin { + private MockCordovaInterfaceImpl cordovaInterface; + + @Override + public void load() { + ConfigXmlParser parser = new ConfigXmlParser(); + parser.parse(getActivity().getApplicationContext()); + CordovaPreferences preferences = parser.getPreferences(); + preferences.setPreferencesBundle(getActivity().getIntent().getExtras()); + List pluginEntries = parser.getPluginEntries(); + + cordovaInterface = new MockCordovaInterfaceImpl(getActivity()); + + MockCordovaWebViewImpl mockWebView = new MockCordovaWebViewImpl(getActivity().getApplicationContext()); + mockWebView.init(cordovaInterface, pluginEntries, preferences, bridge.getWebView()); + PluginManager pluginManager = mockWebView.getPluginManager(); + cordovaInterface.onCordovaInit(pluginManager); + + + bridge.registerInterceptor("cordova", (postData) -> { + String callbackId = postData.getString("callbackId"); + + String service = postData.getString("service"); + String action = postData.getString("action"); + String actionArgs = postData.getString("actionArgs"); + + Logger.verbose( + Logger.tags("Plugin"), + "To native (Cordova plugin): callbackId: " + + callbackId + + ", service: " + + service + + ", action: " + + action + + ", actionArgs: " + + actionArgs + ); + + bridge.execute( + () -> { + pluginManager.exec(service, action, callbackId, actionArgs); + } + ); + }); + } + // TODO: How to we ensure this gets called? Should we punt and use reflection? + @Override + protected void restoreState(Bundle state) { + if (state != null) { + cordovaInterface.restoreInstanceState(state); + } + } } From 2d52269a7487c6d9fe006db8774059f2227fb670 Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Tue, 28 May 2024 16:33:05 -0500 Subject: [PATCH 19/30] Cordova removal part 3 (cherry picked from commit 37d46ab4e5d550a9a8f94402978e5567c26753f8) --- .../main/java/com/getcapacitor/Bridge.java | 85 +++++++++++-------- .../getcapacitor/cordova/CordovaPlugin.java | 75 +++++++++++++++- 2 files changed, 123 insertions(+), 37 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index a1b231280..c718d1459 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -124,6 +124,7 @@ public class Bridge { private Boolean canInjectJS = true; // A reference to the main WebView for the app private final WebView webView; + /// RUH ROH // public final MockCordovaInterfaceImpl cordovaInterface; // private CordovaWebView cordovaWebView; // private CordovaPreferences preferences; @@ -469,9 +470,9 @@ public boolean isDevMode() { return (getActivity().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; } - protected void setCordovaWebView(CordovaWebView cordovaWebView) { - this.cordovaWebView = cordovaWebView; - } +// protected void setCordovaWebView(CordovaWebView cordovaWebView) { +// this.cordovaWebView = cordovaWebView; +// } /** * Get the Context for the App @@ -1134,11 +1135,14 @@ boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] if (plugin == null) { boolean permissionHandled = false; Logger.debug("Unable to find a Capacitor plugin to handle permission requestCode, trying Cordova plugins " + requestCode); - try { - permissionHandled = cordovaInterface.handlePermissionResult(requestCode, permissions, grantResults); - } catch (JSONException e) { - Logger.debug("Error on Cordova plugin permissions request " + e.getMessage()); + PluginHandle cordovaHandle = getPlugin("__CordovaPlugin"); + + if (cordovaHandle != null) { + Plugin cordovaPlugin = cordovaHandle.getInstance(); + cordovaPlugin.handleRequestPermissionsResult(requestCode, permissions, grantResults); + permissionHandled = cordovaPlugin.hasDefinedRequiredPermissions(); } + return permissionHandled; } @@ -1273,7 +1277,14 @@ boolean onActivityResult(int requestCode, int resultCode, Intent data) { if (plugin == null || plugin.getInstance() == null) { Logger.debug("Unable to find a Capacitor plugin to handle requestCode, trying Cordova plugins " + requestCode); - return cordovaInterface.onActivityResult(requestCode, resultCode, data); + PluginHandle cordovaHandle = getPlugin("__CordovaPlugin"); + if (cordovaHandle != null) { + Plugin cordovaPlugin = cordovaHandle.getInstance(); + cordovaPlugin.handleOnActivityResult(requestCode, resultCode, data); + // This is our disgusting way of returning the boolean out of the cordova interface + return cordovaPlugin.hasRequiredPermissions(); + } +// return cordovaInterface.onActivityResult(requestCode, resultCode, data); } // deprecated, to be removed @@ -1303,10 +1314,10 @@ public void onNewIntent(Intent intent) { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnNewIntent(intent); } - - if (cordovaWebView != null) { - cordovaWebView.onNewIntent(intent); - } +// +// if (cordovaWebView != null) { +// cordovaWebView.onNewIntent(intent); +// } } /** @@ -1336,9 +1347,9 @@ public void onStart() { plugin.getInstance().handleOnStart(); } - if (cordovaWebView != null) { - cordovaWebView.handleStart(); - } +// if (cordovaWebView != null) { +// cordovaWebView.handleStart(); +// } } /** @@ -1348,10 +1359,10 @@ public void onResume() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnResume(); } - - if (cordovaWebView != null) { - cordovaWebView.handleResume(this.shouldKeepRunning()); - } +// +// if (cordovaWebView != null) { +// cordovaWebView.handleResume(this.shouldKeepRunning()); +// } } /** @@ -1362,10 +1373,10 @@ public void onPause() { plugin.getInstance().handleOnPause(); } - if (cordovaWebView != null) { - boolean keepRunning = this.shouldKeepRunning() || cordovaInterface.getActivityResultCallback() != null; - cordovaWebView.handlePause(keepRunning); - } +// if (cordovaWebView != null) { +// boolean keepRunning = this.shouldKeepRunning() || cordovaInterface.getActivityResultCallback() != null; +// cordovaWebView.handlePause(keepRunning); +// } } /** @@ -1375,10 +1386,10 @@ public void onStop() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnStop(); } - - if (cordovaWebView != null) { - cordovaWebView.handleStop(); - } +// +// if (cordovaWebView != null) { +// cordovaWebView.handleStop(); +// } } /** @@ -1390,10 +1401,10 @@ public void onDestroy() { } handlerThread.quitSafely(); - - if (cordovaWebView != null) { - cordovaWebView.handleDestroy(); - } +// +// if (cordovaWebView != null) { +// cordovaWebView.handleDestroy(); +// } } /** @@ -1591,9 +1602,9 @@ public Bridge create() { webView, plugins, pluginInstances, - cordovaInterface, - pluginManager, - preferences, +// cordovaInterface, +// pluginManager, +// preferences, config ); @@ -1602,11 +1613,15 @@ public Bridge create() { capacitorWebView.edgeToEdgeHandler(bridge); } - bridge.setCordovaWebView(mockWebView); +// bridge.setCordovaWebView(mockWebView); bridge.setWebViewListeners(webViewListeners); bridge.setRouteProcessor(routeProcessor); if (instanceState != null) { + PluginHandle maybeCordova = bridge.getPlugin("__CordovaPlugin"); + if (maybeCordova != null) { + maybeCordova.getInstance().restoreState(instanceState); + } bridge.restoreInstanceState(instanceState); } diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java index 0971bbd28..375916c2f 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java @@ -1,5 +1,6 @@ package com.getcapacitor.cordova; +import android.content.Intent; import android.os.Bundle; import android.webkit.WebView; @@ -11,20 +12,27 @@ import org.apache.cordova.ConfigXmlParser; import org.apache.cordova.CordovaInterface; import org.apache.cordova.CordovaPreferences; +import org.apache.cordova.CordovaWebView; import org.apache.cordova.PluginEntry; import org.apache.cordova.PluginManager; +import org.json.JSONException; import java.util.List; -@CapacitorPlugin +@CapacitorPlugin(name = "__CordovaPlugin") public class CordovaPlugin extends Plugin { private MockCordovaInterfaceImpl cordovaInterface; + private CordovaWebView webView; + private CordovaPreferences preferences; + + private boolean pluginHadActivityResult = false; + private boolean pluginHadPermissionResult = false; @Override public void load() { ConfigXmlParser parser = new ConfigXmlParser(); parser.parse(getActivity().getApplicationContext()); - CordovaPreferences preferences = parser.getPreferences(); + preferences = parser.getPreferences(); preferences.setPreferencesBundle(getActivity().getIntent().getExtras()); List pluginEntries = parser.getPluginEntries(); @@ -32,6 +40,7 @@ public void load() { MockCordovaWebViewImpl mockWebView = new MockCordovaWebViewImpl(getActivity().getApplicationContext()); mockWebView.init(cordovaInterface, pluginEntries, preferences, bridge.getWebView()); + webView = mockWebView; PluginManager pluginManager = mockWebView.getPluginManager(); cordovaInterface.onCordovaInit(pluginManager); @@ -70,4 +79,66 @@ protected void restoreState(Bundle state) { cordovaInterface.restoreInstanceState(state); } } + + + @Override + protected void handleRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + try { + pluginHadPermissionResult = cordovaInterface.handlePermissionResult(requestCode, permissions, grantResults); + } catch (JSONException e) { + Logger.debug("Error on Cordova plugin permissions request " + e.getMessage()); + } + } + + @Override + public boolean hasDefinedRequiredPermissions() { + boolean currentPermissionResult = pluginHadPermissionResult; + pluginHadPermissionResult = false; + return currentPermissionResult; + } + + @Override + protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) { + pluginHadActivityResult = cordovaInterface.onActivityResult(requestCode, resultCode, data); + } + + @Override + public boolean hasRequiredPermissions() { + boolean currentActivityResult = pluginHadActivityResult; + pluginHadActivityResult = false; + return currentActivityResult; + } + + @Override + protected void handleOnNewIntent(Intent intent) { + webView.onNewIntent(intent); + } + + @Override + protected void handleOnStart() { + webView.handleStart(); + } + + @Override + protected void handleOnResume() { + webView.handleResume(bridge.shouldKeepRunning()); + } + + @Override + protected void handleOnPause() { + boolean keepRunning = bridge.shouldKeepRunning() || cordovaInterface.getActivityResultCallback() != null; + webView.handlePause(keepRunning); + } + + @Override + protected void handleOnStop() { + webView.handleStop(); + } + + @Override + protected void handleOnDestroy() { + webView.handleDestroy(); + } + + } From 0efb4349c2730375816ee5b624f5a06097712cc9 Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Tue, 28 May 2024 16:43:29 -0500 Subject: [PATCH 20/30] Cordova removal part 4 (cherry picked from commit 2973a19caa09af5c3bc4a4de6329404fa559d16d) --- .../main/java/com/getcapacitor/Bridge.java | 22 +++++++++++++++++-- .../getcapacitor/cordova/CordovaPlugin.java | 13 +++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index c718d1459..46b16c790 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -446,12 +446,30 @@ private boolean isNewBinary() { return false; } + private Plugin cordova() { + PluginHandle handle = getPlugin("__CordovaHandle"); + if (handle != null) { + return handle.getInstance(); + } + return null; + } + public boolean isDeployDisabled() { - return preferences.getBoolean("DisableDeploy", false); + Plugin cordova = this.cordova(); + if (cordova != null) { + return cordova.hasPermission("DisableDeploy"); + } else { + return false; + } } public boolean shouldKeepRunning() { - return preferences.getBoolean("KeepRunning", true); + Plugin cordova = this.cordova(); + if (cordova != null) { + return cordova.hasPermission("KeepRunning"); + } else { + return false; + } } public void handleAppUrlLoadError(Exception ex) { diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java index 375916c2f..010130fdb 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java @@ -90,6 +90,19 @@ protected void handleRequestPermissionsResult(int requestCode, String[] permissi } } + @Override + public boolean hasPermission(String permission) { + if (permission.equals("DisableDeploy")) { + return preferences.getBoolean(permission, false); + } + + if (permission.equals("KeepRunning")) { + return preferences.getBoolean(permission, true); + } + + return false; + } + @Override public boolean hasDefinedRequiredPermissions() { boolean currentPermissionResult = pluginHadPermissionResult; From cf939780dd136b1365666066009936f71b9c2749 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 29 May 2024 16:15:24 -0400 Subject: [PATCH 21/30] Fixed interceptors maybe (cherry picked from commit d558c468e66c1bacca6bb956b86913d10f48e8ca) --- .../main/java/com/getcapacitor/Bridge.java | 24 ++++++++++ .../java/com/getcapacitor/MessageHandler.java | 44 +------------------ 2 files changed, 25 insertions(+), 43 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 46b16c790..5efa5b70f 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -1643,6 +1643,30 @@ public Bridge create() { bridge.restoreInstanceState(instanceState); } + bridge.registerInterceptor("message", (postData) -> { + try { + String callbackId = postData.getString("callbackId"); + String pluginId = postData.getString("pluginId"); + String methodName = postData.getString("methodName"); + JSObject methodData = postData.getJSObject("options", new JSObject()); + + Logger.verbose( + Logger.tags("Plugin"), + "To native (Capacitor plugin): callbackId: " + callbackId + ", pluginId: " + pluginId + ", methodName: " + methodName + ); + + PluginCall call = new PluginCall(bridge.msgHandler, pluginId, callbackId, methodName, methodData); + bridge.callPluginMethod(pluginId, methodName, call); + + } catch(JSONException e) { + Logger.error(e.getMessage()); + } + }); + + bridge.registerInterceptor("js.error", (postData) -> { + Logger.error("JavaScript Error: " + postData.toString()); + }); + return bridge; } } diff --git a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java index 991c7a187..7a758e338 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java +++ b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java @@ -69,54 +69,12 @@ public void postMessage(String jsonStr) { // boolean typeIsNotNull = type != null; if (type == null) - return; + type = "message"; Interceptor interceptor = bridge.getCallInterceptor(type); if (interceptor != null) { interceptor.intercept(postData); } - - -// boolean isCapacitorPlugin = typeIsNotNull && type.equals("message"); -// boolean isCordovaPlugin = typeIsNotNull && type.equals("cordova"); -// boolean isJavaScriptError = typeIsNotNull && type.equals("js.error"); -// -// String callbackId = postData.getString("callbackId"); -// -// if (isCordovaPlugin) { -// String pluginId = postData.getString("pluginId"); -// String methodName = postData.getString("methodName"); -// JSObject methodData = postData.getJSObject("options", new JSObject()); -// -// Logger.verbose( -// Logger.tags("Plugin"), -// "To native (Capacitor plugin): callbackId: " + callbackId + ", pluginId: " + pluginId + ", methodName: " + methodName -// ); -// -// this.callPluginMethod(callbackId, pluginId, methodName, methodData); -// // start cordova -// String service = postData.getString("service"); -// String action = postData.getString("action"); -// String actionArgs = postData.getString("actionArgs"); -// -// Logger.verbose( -// Logger.tags("Plugin"), -// "To native (Cordova plugin): callbackId: " + -// callbackId + -// ", service: " + -// service + -// ", action: " + -// action + -// ", actionArgs: " + -// actionArgs -// ); -// -// this.callCordovaPluginMethod(callbackId, service, action, actionArgs); -// } else if (isJavaScriptError) { -// Logger.error("JavaScript Error: " + jsonStr); -// } else if (typeIsNotNull && bridge.getCallInterceptor(type) != null) { -// bridge.getCallInterceptor(type).intercept(postData); -// } } catch (Exception ex) { Logger.error("Post message error:", ex); } From 67ed25d104efcac7f39b6a48ce5ffefe728c7eea Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 16 Dec 2024 12:15:11 -0500 Subject: [PATCH 22/30] Cleanup for PR --- .../src/main/java/com/getcapacitor/Bridge.java | 13 ------------- .../main/java/com/getcapacitor/MessageHandler.java | 10 ---------- 2 files changed, 23 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 5efa5b70f..0320b0c06 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -54,11 +54,6 @@ import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -//import org.apache.cordova.ConfigXmlParser; -//import org.apache.cordova.CordovaPreferences; -//import org.apache.cordova.CordovaWebView; -//import org.apache.cordova.PluginEntry; -//import org.apache.cordova.PluginManager; import org.json.JSONException; /** @@ -124,10 +119,6 @@ public class Bridge { private Boolean canInjectJS = true; // A reference to the main WebView for the app private final WebView webView; - /// RUH ROH -// public final MockCordovaInterfaceImpl cordovaInterface; -// private CordovaWebView cordovaWebView; -// private CordovaPreferences preferences; private BridgeWebViewClient webViewClient; private App app; @@ -488,10 +479,6 @@ public boolean isDevMode() { return (getActivity().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; } -// protected void setCordovaWebView(CordovaWebView cordovaWebView) { -// this.cordovaWebView = cordovaWebView; -// } - /** * Get the Context for the App * @return diff --git a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java index 7a758e338..c96b33ca0 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java +++ b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java @@ -22,7 +22,6 @@ public interface Interceptor { private Bridge bridge; private WebView webView; -// private PluginManager cordovaPluginManager; private JavaScriptReplyProxy javaScriptReplyProxy; @@ -67,7 +66,6 @@ public void postMessage(String jsonStr) { String type = postData.getString("type"); -// boolean typeIsNotNull = type != null; if (type == null) type = "message"; @@ -130,12 +128,4 @@ private void callPluginMethod(String callbackId, String pluginId, String methodN PluginCall call = new PluginCall(this, pluginId, callbackId, methodName, methodData); bridge.callPluginMethod(pluginId, methodName, call); } - -// private void callCordovaPluginMethod(String callbackId, String service, String action, String actionArgs) { -// bridge.execute( -// () -> { -// cordovaPluginManager.exec(service, action, callbackId, actionArgs); -// } -// ); -// } } From a75f1cd25a88ea0cf9b626e9b2dd15993dfea7d9 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Tue, 17 Dec 2024 07:52:54 -0500 Subject: [PATCH 23/30] fmt --- .../main/java/com/getcapacitor/Bridge.java | 75 ++++++++++--------- .../java/com/getcapacitor/MessageHandler.java | 8 +- .../getcapacitor/cordova/CordovaPlugin.java | 42 +++++------ cli/src/ios/update.ts | 1 - 4 files changed, 58 insertions(+), 68 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 0320b0c06..9d3da1302 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -1289,7 +1289,7 @@ boolean onActivityResult(int requestCode, int resultCode, Intent data) { // This is our disgusting way of returning the boolean out of the cordova interface return cordovaPlugin.hasRequiredPermissions(); } -// return cordovaInterface.onActivityResult(requestCode, resultCode, data); + // return cordovaInterface.onActivityResult(requestCode, resultCode, data); } // deprecated, to be removed @@ -1319,10 +1319,10 @@ public void onNewIntent(Intent intent) { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnNewIntent(intent); } -// -// if (cordovaWebView != null) { -// cordovaWebView.onNewIntent(intent); -// } + // + // if (cordovaWebView != null) { + // cordovaWebView.onNewIntent(intent); + // } } /** @@ -1351,10 +1351,9 @@ public void onStart() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnStart(); } - -// if (cordovaWebView != null) { -// cordovaWebView.handleStart(); -// } + // if (cordovaWebView != null) { + // cordovaWebView.handleStart(); + // } } /** @@ -1364,10 +1363,10 @@ public void onResume() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnResume(); } -// -// if (cordovaWebView != null) { -// cordovaWebView.handleResume(this.shouldKeepRunning()); -// } + // + // if (cordovaWebView != null) { + // cordovaWebView.handleResume(this.shouldKeepRunning()); + // } } /** @@ -1377,11 +1376,10 @@ public void onPause() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnPause(); } - -// if (cordovaWebView != null) { -// boolean keepRunning = this.shouldKeepRunning() || cordovaInterface.getActivityResultCallback() != null; -// cordovaWebView.handlePause(keepRunning); -// } + // if (cordovaWebView != null) { + // boolean keepRunning = this.shouldKeepRunning() || cordovaInterface.getActivityResultCallback() != null; + // cordovaWebView.handlePause(keepRunning); + // } } /** @@ -1391,10 +1389,10 @@ public void onStop() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnStop(); } -// -// if (cordovaWebView != null) { -// cordovaWebView.handleStop(); -// } + // + // if (cordovaWebView != null) { + // cordovaWebView.handleStop(); + // } } /** @@ -1406,10 +1404,10 @@ public void onDestroy() { } handlerThread.quitSafely(); -// -// if (cordovaWebView != null) { -// cordovaWebView.handleDestroy(); -// } + // + // if (cordovaWebView != null) { + // cordovaWebView.handleDestroy(); + // } } /** @@ -1598,7 +1596,6 @@ public Bridge create() { // Cordova initialization WebView webView = this.fragment != null ? fragment.getView().findViewById(R.id.webview) : activity.findViewById(R.id.webview); - // Bridge initialization Bridge bridge = new Bridge( activity, @@ -1607,9 +1604,9 @@ public Bridge create() { webView, plugins, pluginInstances, -// cordovaInterface, -// pluginManager, -// preferences, + // cordovaInterface, + // pluginManager, + // preferences, config ); @@ -1618,7 +1615,7 @@ public Bridge create() { capacitorWebView.edgeToEdgeHandler(bridge); } -// bridge.setCordovaWebView(mockWebView); + // bridge.setCordovaWebView(mockWebView); bridge.setWebViewListeners(webViewListeners); bridge.setRouteProcessor(routeProcessor); @@ -1630,7 +1627,7 @@ public Bridge create() { bridge.restoreInstanceState(instanceState); } - bridge.registerInterceptor("message", (postData) -> { + bridge.registerInterceptor("message", postData -> { try { String callbackId = postData.getString("callbackId"); String pluginId = postData.getString("pluginId"); @@ -1638,19 +1635,23 @@ public Bridge create() { JSObject methodData = postData.getJSObject("options", new JSObject()); Logger.verbose( - Logger.tags("Plugin"), - "To native (Capacitor plugin): callbackId: " + callbackId + ", pluginId: " + pluginId + ", methodName: " + methodName + Logger.tags("Plugin"), + "To native (Capacitor plugin): callbackId: " + + callbackId + + ", pluginId: " + + pluginId + + ", methodName: " + + methodName ); PluginCall call = new PluginCall(bridge.msgHandler, pluginId, callbackId, methodName, methodData); bridge.callPluginMethod(pluginId, methodName, call); - - } catch(JSONException e) { + } catch (JSONException e) { Logger.error(e.getMessage()); } }); - bridge.registerInterceptor("js.error", (postData) -> { + bridge.registerInterceptor("js.error", postData -> { Logger.error("JavaScript Error: " + postData.toString()); }); diff --git a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java index c96b33ca0..1e2d9763f 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java +++ b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java @@ -5,8 +5,8 @@ import androidx.webkit.JavaScriptReplyProxy; import androidx.webkit.WebViewCompat; import androidx.webkit.WebViewFeature; - import java.util.function.Function; + //import org.apache.cordova.PluginManager; /** @@ -14,17 +14,16 @@ * to plugins. */ public class MessageHandler { + @FunctionalInterface public interface Interceptor { void intercept(JSObject object); } - private Bridge bridge; private WebView webView; private JavaScriptReplyProxy javaScriptReplyProxy; - public MessageHandler(Bridge bridge, WebView webView) { this.bridge = bridge; this.webView = webView; @@ -66,8 +65,7 @@ public void postMessage(String jsonStr) { String type = postData.getString("type"); - if (type == null) - type = "message"; + if (type == null) type = "message"; Interceptor interceptor = bridge.getCallInterceptor(type); if (interceptor != null) { diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java index 010130fdb..1eed6efd4 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java @@ -3,12 +3,11 @@ import android.content.Intent; import android.os.Bundle; import android.webkit.WebView; - import com.getcapacitor.Logger; import com.getcapacitor.Plugin; import com.getcapacitor.android.R; import com.getcapacitor.annotation.CapacitorPlugin; - +import java.util.List; import org.apache.cordova.ConfigXmlParser; import org.apache.cordova.CordovaInterface; import org.apache.cordova.CordovaPreferences; @@ -17,10 +16,9 @@ import org.apache.cordova.PluginManager; import org.json.JSONException; -import java.util.List; - @CapacitorPlugin(name = "__CordovaPlugin") public class CordovaPlugin extends Plugin { + private MockCordovaInterfaceImpl cordovaInterface; private CordovaWebView webView; private CordovaPreferences preferences; @@ -44,8 +42,7 @@ public void load() { PluginManager pluginManager = mockWebView.getPluginManager(); cordovaInterface.onCordovaInit(pluginManager); - - bridge.registerInterceptor("cordova", (postData) -> { + bridge.registerInterceptor("cordova", postData -> { String callbackId = postData.getString("callbackId"); String service = postData.getString("service"); @@ -53,22 +50,20 @@ public void load() { String actionArgs = postData.getString("actionArgs"); Logger.verbose( - Logger.tags("Plugin"), - "To native (Cordova plugin): callbackId: " + - callbackId + - ", service: " + - service + - ", action: " + - action + - ", actionArgs: " + - actionArgs + Logger.tags("Plugin"), + "To native (Cordova plugin): callbackId: " + + callbackId + + ", service: " + + service + + ", action: " + + action + + ", actionArgs: " + + actionArgs ); - bridge.execute( - () -> { - pluginManager.exec(service, action, callbackId, actionArgs); - } - ); + bridge.execute(() -> { + pluginManager.exec(service, action, callbackId, actionArgs); + }); }); } @@ -80,7 +75,6 @@ protected void restoreState(Bundle state) { } } - @Override protected void handleRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { try { @@ -134,7 +128,7 @@ protected void handleOnStart() { @Override protected void handleOnResume() { - webView.handleResume(bridge.shouldKeepRunning()); + webView.handleResume(bridge.shouldKeepRunning()); } @Override @@ -150,8 +144,6 @@ protected void handleOnStop() { @Override protected void handleOnDestroy() { - webView.handleDestroy(); + webView.handleDestroy(); } - - } diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 9be9098ae..c06e401e9 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -1,6 +1,5 @@ import { pathExists, readFile, realpath, writeFile } from 'fs-extra'; import { basename, dirname, join, relative } from 'path'; -import { replace } from 'tar'; import c from '../colors'; import { checkPlatformVersions, getCapacitorPackageVersion, runTask } from '../common'; From 71154fcbd874153de69dccf3d3efc2b178fd9fa3 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 16 Dec 2024 18:43:56 +0100 Subject: [PATCH 24/30] chore(cli): migrate command updates (#7795) --- cli/src/tasks/migrate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 4b76836e6..0af11b611 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -1,6 +1,6 @@ import { writeFileSync, readFileSync, existsSync } from 'fs-extra'; import { join } from 'path'; -import { rimraf } from 'rimraf'; +import rimraf from 'rimraf'; import { coerce, gte, lt } from 'semver'; import { getAndroidPlugins } from '../android/common'; From dbd6f7297c47b6ae86901b5fccf040e9f38f9c0c Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Tue, 17 Dec 2024 15:07:38 -0500 Subject: [PATCH 25/30] More code cleanup --- .../main/java/com/getcapacitor/Bridge.java | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 9d3da1302..439401a7f 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -1319,10 +1319,6 @@ public void onNewIntent(Intent intent) { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnNewIntent(intent); } - // - // if (cordovaWebView != null) { - // cordovaWebView.onNewIntent(intent); - // } } /** @@ -1351,9 +1347,6 @@ public void onStart() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnStart(); } - // if (cordovaWebView != null) { - // cordovaWebView.handleStart(); - // } } /** @@ -1363,10 +1356,6 @@ public void onResume() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnResume(); } - // - // if (cordovaWebView != null) { - // cordovaWebView.handleResume(this.shouldKeepRunning()); - // } } /** @@ -1376,10 +1365,6 @@ public void onPause() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnPause(); } - // if (cordovaWebView != null) { - // boolean keepRunning = this.shouldKeepRunning() || cordovaInterface.getActivityResultCallback() != null; - // cordovaWebView.handlePause(keepRunning); - // } } /** @@ -1389,10 +1374,6 @@ public void onStop() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnStop(); } - // - // if (cordovaWebView != null) { - // cordovaWebView.handleStop(); - // } } /** @@ -1404,10 +1385,6 @@ public void onDestroy() { } handlerThread.quitSafely(); - // - // if (cordovaWebView != null) { - // cordovaWebView.handleDestroy(); - // } } /** From 6109926d206d22e165106673bfa1f4835f769ab6 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Tue, 17 Dec 2024 12:40:35 -0500 Subject: [PATCH 26/30] update ios/update to remove cordova if not needed (cherry picked from commit d538c0a1a2d7b8b0acf5cba21b95470109386686) --- cli/src/ios/update.ts | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index c06e401e9..041510373 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -40,15 +40,17 @@ export async function updateIOS(config: Config, deployment: boolean): Promise getPluginType(p, platform) === PluginType.Cordova); - if (cordovaPlugins.length > 0) { - // TODO: all the new logic should probably go here - // with an else that removes cordova plugins + const enableCordova = cordovaPlugins.length > 0 + + if (enableCordova) { await copyPluginsNativeFiles(config, cordovaPlugins); } + if (!(await pathExists(await config.ios.webDirAbs))) { await copyTask(config, platform); } + await handleCordovaPluginsJS(cordovaPlugins, config, platform); await checkPluginDependencies(plugins, platform, config.app.extConfig.cordova?.failOnUninstalledPlugins); if ((await checkPackageManager(config)) === 'SPM') { @@ -57,13 +59,16 @@ async function updatePluginFiles(config: Config, plugins: Plugin[], deployment: const validSPMPackages = await checkPluginsForPackageSwift(config, plugins); await generatePackageFile(config, validSPMPackages.concat(cordovaPlugins)); - } else { + if (enableCordova) { await generateCordovaPodspecs(cordovaPlugins, config); +} await installCocoaPodsPlugins(config, plugins, deployment); - } + + if (enableCordova) { await logCordovaManualSteps(cordovaPlugins, config, platform); +} - const incompatibleCordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Incompatible); + await installCocoaPodsPlugins(config, plugins, deployment, enableCordova); printPlugins(incompatibleCordovaPlugins, platform, 'incompatible'); } @@ -76,7 +81,7 @@ async function generateCordovaPackageFiles(cordovaPlugins: Plugin[], config: Con async function generateCordovaPackageFile(p: Plugin, config: Config) { const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); const iosVersion = getMajoriOSVersion(config); - const headerFiles = getPlatformElement(p, platform, 'header-file'); + await logCordovaManualSteps(cordovaPlugins, config, platform); let headersText = ''; if (headerFiles.length > 0) { headersText = `, @@ -112,14 +117,14 @@ let package = Package( await writeFile(join(config.ios.cordovaPluginsDirAbs, 'sources', p.name, 'Package.swift'), content); } -export async function installCocoaPodsPlugins(config: Config, plugins: Plugin[], deployment: boolean): Promise { +export async function installCocoaPodsPlugins(config: Config, plugins: Plugin[], deployment: boolean, enableCordova: boolean): Promise { await runTask(`Updating iOS native dependencies with ${c.input(`${await config.ios.podPath} install`)}`, () => { - return updatePodfile(config, plugins, deployment); + return updatePodfile(config, plugins, deployment, enableCordova); }); } -async function updatePodfile(config: Config, plugins: Plugin[], deployment: boolean): Promise { - const dependenciesContent = await generatePodFile(config, plugins); +async function updatePodfile(config: Config, plugins: Plugin[], deployment: boolean, enableCordova: boolean): Promise { + const dependenciesContent = await generatePodFile(config, plugins, enableCordova); const relativeCapacitoriOSPath = await getRelativeCapacitoriOSPath(config); const podfilePath = join(config.ios.nativeProjectDirAbs, 'Podfile'); let podfileContent = await readFile(podfilePath, { encoding: 'utf-8' }); @@ -170,7 +175,7 @@ async function getRelativeCapacitoriOSPath(config: Config) { return convertToUnixPath(relative(config.ios.nativeProjectDirAbs, await realpath(dirname(capacitoriOSPath)))); } -async function generatePodFile(config: Config, plugins: Plugin[]): Promise { +async function generatePodFile(config: Config, plugins: Plugin[], enableCordova: boolean): Promise { const relativeCapacitoriOSPath = await getRelativeCapacitoriOSPath(config); const capacitorPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Core); @@ -188,10 +193,16 @@ async function generatePodFile(config: Config, plugins: Plugin[]): Promise '${relativeCapacitoriOSPath}' - pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}' - ${pods.join('').trimEnd()}`; + let podfileString = '\n' + podfileString += ` pod 'Capacitor', :path => '${relativeCapacitoriOSPath}'\n` + + if (enableCordova) { + podfileString += ` pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}'\n` + } + + podfileString += pods.join('').trimEnd() + + return podfileString } async function getPluginsTask(config: Config) { From ca5d95bc2f0b87318d2bbb56ea1825882c355436 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Tue, 17 Dec 2024 13:06:00 -0500 Subject: [PATCH 27/30] npm run fmt (cherry picked from commit 805be0e3f3c8a8a56969a5b43d8d8ce1a19aab92) --- cli/src/ios/update.ts | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 041510373..646ef6ecb 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -41,7 +41,7 @@ async function updatePluginFiles(config: Config, plugins: Plugin[], deployment: await removePluginsNativeFiles(config); const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova); - const enableCordova = cordovaPlugins.length > 0 + const enableCordova = cordovaPlugins.length > 0; if (enableCordova) { await copyPluginsNativeFiles(config, cordovaPlugins); @@ -117,13 +117,23 @@ let package = Package( await writeFile(join(config.ios.cordovaPluginsDirAbs, 'sources', p.name, 'Package.swift'), content); } -export async function installCocoaPodsPlugins(config: Config, plugins: Plugin[], deployment: boolean, enableCordova: boolean): Promise { +export async function installCocoaPodsPlugins( + config: Config, + plugins: Plugin[], + deployment: boolean, + enableCordova: boolean, +): Promise { await runTask(`Updating iOS native dependencies with ${c.input(`${await config.ios.podPath} install`)}`, () => { return updatePodfile(config, plugins, deployment, enableCordova); }); } -async function updatePodfile(config: Config, plugins: Plugin[], deployment: boolean, enableCordova: boolean): Promise { +async function updatePodfile( + config: Config, + plugins: Plugin[], + deployment: boolean, + enableCordova: boolean, +): Promise { const dependenciesContent = await generatePodFile(config, plugins, enableCordova); const relativeCapacitoriOSPath = await getRelativeCapacitoriOSPath(config); const podfilePath = join(config.ios.nativeProjectDirAbs, 'Podfile'); @@ -193,16 +203,16 @@ async function generatePodFile(config: Config, plugins: Plugin[], enableCordova: const cordovaPodlines = cordovaPodfileLines(config, plugins); pods.concat(cordovaPodlines); - let podfileString = '\n' - podfileString += ` pod 'Capacitor', :path => '${relativeCapacitoriOSPath}'\n` + let podfileString = '\n'; + podfileString += ` pod 'Capacitor', :path => '${relativeCapacitoriOSPath}'\n`; if (enableCordova) { - podfileString += ` pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}'\n` + podfileString += ` pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}'\n`; } - podfileString += pods.join('').trimEnd() + podfileString += pods.join('').trimEnd(); - return podfileString + return podfileString; } async function getPluginsTask(config: Config) { From d10926550e710b8501af7dceae70c9d2dc171f9a Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 5 Nov 2025 10:38:34 -0500 Subject: [PATCH 28/30] npm run fmt --- .../main/java/com/getcapacitor/Bridge.java | 14 ++++++------ .../getcapacitor/cordova/CordovaPlugin.java | 16 +++++++------- cli/src/ios/update.ts | 20 ++++++++--------- ios/Capacitor/Capacitor/CapacitorBridge.swift | 22 +++++++++---------- ios/Capacitor/Capacitor/JSExport.swift | 3 --- 5 files changed, 36 insertions(+), 39 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 439401a7f..67692f8fa 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -1604,7 +1604,7 @@ public Bridge create() { bridge.restoreInstanceState(instanceState); } - bridge.registerInterceptor("message", postData -> { + bridge.registerInterceptor("message", (postData) -> { try { String callbackId = postData.getString("callbackId"); String pluginId = postData.getString("pluginId"); @@ -1614,11 +1614,11 @@ public Bridge create() { Logger.verbose( Logger.tags("Plugin"), "To native (Capacitor plugin): callbackId: " + - callbackId + - ", pluginId: " + - pluginId + - ", methodName: " + - methodName + callbackId + + ", pluginId: " + + pluginId + + ", methodName: " + + methodName ); PluginCall call = new PluginCall(bridge.msgHandler, pluginId, callbackId, methodName, methodData); @@ -1628,7 +1628,7 @@ public Bridge create() { } }); - bridge.registerInterceptor("js.error", postData -> { + bridge.registerInterceptor("js.error", (postData) -> { Logger.error("JavaScript Error: " + postData.toString()); }); diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java index 1eed6efd4..985e14b1e 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java @@ -42,7 +42,7 @@ public void load() { PluginManager pluginManager = mockWebView.getPluginManager(); cordovaInterface.onCordovaInit(pluginManager); - bridge.registerInterceptor("cordova", postData -> { + bridge.registerInterceptor("cordova", (postData) -> { String callbackId = postData.getString("callbackId"); String service = postData.getString("service"); @@ -52,13 +52,13 @@ public void load() { Logger.verbose( Logger.tags("Plugin"), "To native (Cordova plugin): callbackId: " + - callbackId + - ", service: " + - service + - ", action: " + - action + - ", actionArgs: " + - actionArgs + callbackId + + ", service: " + + service + + ", action: " + + action + + ", actionArgs: " + + actionArgs ); bridge.execute(() -> { diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 646ef6ecb..3b1be6f68 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -59,17 +59,17 @@ async function updatePluginFiles(config: Config, plugins: Plugin[], deployment: const validSPMPackages = await checkPluginsForPackageSwift(config, plugins); await generatePackageFile(config, validSPMPackages.concat(cordovaPlugins)); - if (enableCordova) { - await generateCordovaPodspecs(cordovaPlugins, config); -} - await installCocoaPodsPlugins(config, plugins, deployment); - if (enableCordova) { - await logCordovaManualSteps(cordovaPlugins, config, platform); -} + if (enableCordova) { + await generateCordovaPodspecs(cordovaPlugins, config); + } + + await installCocoaPodsPlugins(config, plugins, deployment, enableCordova); - await installCocoaPodsPlugins(config, plugins, deployment, enableCordova); - printPlugins(incompatibleCordovaPlugins, platform, 'incompatible'); + if (enableCordova) { + await logCordovaManualSteps(cordovaPlugins, config, platform); + } + } } async function generateCordovaPackageFiles(cordovaPlugins: Plugin[], config: Config) { @@ -81,7 +81,7 @@ async function generateCordovaPackageFiles(cordovaPlugins: Plugin[], config: Con async function generateCordovaPackageFile(p: Plugin, config: Config) { const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); const iosVersion = getMajoriOSVersion(config); - await logCordovaManualSteps(cordovaPlugins, config, platform); + await logCordovaManualSteps(cordovaPlugins, config, platform); let headersText = ''; if (headerFiles.length > 0) { headersText = `, diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index 4ee330d98..99601d3ef 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -119,7 +119,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { var storedCalls = ConcurrentDictionary() // Whether to inject the Cordova files private var cordovaIsPresent = false - //private var cordovaParser: CDVConfigParser? + // private var cordovaParser: CDVConfigParser? private var injectMiscFiles: [String] = [] private var canInjectJS: Bool = true @@ -222,7 +222,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { exportCoreJS(localUrl: configuration.localURL.absoluteString) registerPlugins() - // setupCordovaCompatibility() + // setupCordovaCompatibility() setupListeners() exportMiscJS() canInjectJS = false @@ -279,14 +279,14 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { } ) - /** - Set up our Cordova compat by loading all known Cordova plugins and injecting their JS. - */ -// func setupCordovaCompatibility() { -// if injectCordovaFiles { -// exportCordovaJS() -// // registerCordovaPlugins() -// } else { + /** + Set up our Cordova compat by loading all known Cordova plugins and injecting their JS. + */ + // func setupCordovaCompatibility() { + // if injectCordovaFiles { + // exportCordovaJS() + // // registerCordovaPlugins() + // } else { observers.append( NotificationCenter.default.addObserver( forName: UIApplication.didEnterBackgroundNotification, @@ -563,7 +563,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { // CAPLog.print("Error: Cordova Plugin mapping not found") // return // } -// } + // } func removeAllPluginListeners() { for plugin in plugins.values { diff --git a/ios/Capacitor/Capacitor/JSExport.swift b/ios/Capacitor/Capacitor/JSExport.swift index aa3e2cbc1..c51da733d 100644 --- a/ios/Capacitor/Capacitor/JSExport.swift +++ b/ios/Capacitor/Capacitor/JSExport.swift @@ -49,9 +49,6 @@ internal class JSExport { } } - - - /** Export the JS required to implement the given plugin. */ From d7d17cf81806cc426cd96e3aa66f32e956da8d1a Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 5 Nov 2025 10:43:01 -0500 Subject: [PATCH 29/30] Remove some code that snuck back in --- ios/Capacitor/Capacitor/CapacitorBridge.swift | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index 99601d3ef..09737a3a1 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -265,8 +265,6 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { injectMiscFiles.removeAll() } - // TODO: Change this description - /// Setup listeners if Cordova is not present func setupListeners() { if !cordovaIsPresent { observers.append( @@ -279,14 +277,6 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { } ) - /** - Set up our Cordova compat by loading all known Cordova plugins and injecting their JS. - */ - // func setupCordovaCompatibility() { - // if injectCordovaFiles { - // exportCordovaJS() - // // registerCordovaPlugins() - // } else { observers.append( NotificationCenter.default.addObserver( forName: UIApplication.didEnterBackgroundNotification, @@ -537,34 +527,6 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { } } - /** - Handle a Cordova call from JavaScript. First, find the corresponding plugin, - construct a selector, and perform that selector on the plugin instance. - */ - // func handleCordovaJSCall(call: JSCall) { - // // Create a selector to send to the plugin - // - // if let plugin = self.cordovaPluginManager?.getCommandInstance(call.pluginId.lowercased()) { - // let selector = NSSelectorFromString("\(call.method):") - // if !plugin.responds(to: selector) { - // CAPLog.print("Error: Plugin \(plugin.className ?? "") does not respond to method call \(selector).") - // CAPLog.print("Ensure plugin method exists and uses @objc in its declaration") - // return - // } - // - // let arguments: [Any] = call.options["options"] as? [Any] ?? [] - // let pluginCall = CDVInvokedUrlCommand(arguments: arguments, - // callbackId: call.callbackId, - // className: plugin.className, - // methodName: call.method) - // plugin.perform(selector, with: pluginCall) - // - // } else { - // CAPLog.print("Error: Cordova Plugin mapping not found") - // return - // } - // } - func removeAllPluginListeners() { for plugin in plugins.values { plugin.perform(#selector(CAPPlugin.removeAllListeners(_:)), with: nil) From 7f13e9807e35417be4edd7ac415c43deeb798b1b Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 5 Nov 2025 10:47:15 -0500 Subject: [PATCH 30/30] More code cleanup --- android/capacitor/src/main/java/com/getcapacitor/Bridge.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 67692f8fa..531e9f28d 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -1286,10 +1286,9 @@ boolean onActivityResult(int requestCode, int resultCode, Intent data) { if (cordovaHandle != null) { Plugin cordovaPlugin = cordovaHandle.getInstance(); cordovaPlugin.handleOnActivityResult(requestCode, resultCode, data); - // This is our disgusting way of returning the boolean out of the cordova interface + // This is a bit hacky but required to return the boolean out of the cordova interface return cordovaPlugin.hasRequiredPermissions(); } - // return cordovaInterface.onActivityResult(requestCode, resultCode, data); } // deprecated, to be removed