From 9d874c9b33c84731c5a27ee7592bca9468a09c29 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 20 May 2024 15:48:15 -0400 Subject: [PATCH 01/16] 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 d6606aba0329b82edfd5cb038937e3bcd4e4bb66 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 20 May 2024 15:48:59 -0400 Subject: [PATCH 02/16] 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 6bd7593bc..afa48a62e 100644 --- a/ios/Capacitor.podspec +++ b/ios/Capacitor.podspec @@ -19,6 +19,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 93ab96c39..688a7fa08 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? @@ -47,7 +46,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 75b990b72..05911695d 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 { @@ -152,7 +152,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 ae8064428..86b218d16 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 @@ -113,12 +112,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? // Background dispatch queue for plugin calls open private(set) var dispatchQueue = DispatchQueue(label: "bridge") @@ -198,12 +197,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 @@ -213,7 +219,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { exportCoreJS(localUrl: configuration.localURL.absoluteString) registerPlugins() - setupCordovaCompatibility() + // setupCordovaCompatibility() observers.append(NotificationCenter.default.addObserver(forName: type(of: self).tmpVCAppeared.name, object: .none, queue: .none) { [weak self] _ in self?.tmpWindow = nil }) @@ -248,19 +254,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 @@ -295,11 +301,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) - } +// } } } } @@ -407,22 +413,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() @@ -526,29 +532,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 +// } +// } /** Send a successful result to the JavaScript layer. diff --git a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift index bf542c003..9d9d48d7a 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 d02a8882e..0a4af035c 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, 14.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 73b816399455adb88e28eca1c83ac708c410d8b9 Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Mon, 20 May 2024 16:09:55 -0500 Subject: [PATCH 03/16] Continue cordova removal --- .../Capacitor/CAPBridgeProtocol.swift | 3 + .../Capacitor/CAPInstanceDescriptor.swift | 2 +- ios/Capacitor/Capacitor/CapacitorBridge.swift | 68 +++++----- 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(+), 77 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 306756e2b..167917df8 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift @@ -77,6 +77,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 05911695d..fe5fb5979 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift @@ -152,7 +152,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 86b218d16..1da9a704b 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -116,11 +116,12 @@ 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? // 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] = [] @@ -219,7 +220,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { exportCoreJS(localUrl: configuration.localURL.absoluteString) registerPlugins() - // setupCordovaCompatibility() + setupListeners() observers.append(NotificationCenter.default.addObserver(forName: type(of: self).tmpVCAppeared.name, object: .none, queue: .none) { [weak self] _ in self?.tmpWindow = nil }) @@ -251,31 +252,29 @@ 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") -// }) -// } -// } - - /** - Export the core Cordova JS runtime - */ - func exportCordovaJS() { - do { - try JSExport.exportCordovaJS(userContentController: webViewDelegationHandler.contentController) - } catch { - type(of: self).fatalError(error, error) + // 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") + } + ) + + observers.append( + NotificationCenter.default.addObserver( + forName: UIApplication.didEnterBackgroundNotification, + object: nil, + queue: .main + ) { [weak self] _ in + self?.triggerDocumentJSEvent(eventName: "pause") + } + ) } } @@ -300,12 +299,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 + } } } } @@ -381,6 +379,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 79c03ed2b..59899cafd 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 +// } +// } /** Export the JS required to implement the given plugin. @@ -170,27 +170,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 9d9d48d7a..183812d3c 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 0a4af035c..c071ff5af 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 ab4e554cfe85334faafdf1af4bd4fc8dc9dbca71 Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Wed, 22 May 2024 09:56:24 -0500 Subject: [PATCH 04/16] 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 1da9a704b..bde9b9dd0 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -299,7 +299,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 9b214f97bf809423fbd138a427d51a12b1f6ad9a Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 22 May 2024 12:47:18 -0400 Subject: [PATCH 05/16] 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 bde9b9dd0..8d42db5a3 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 } /** @@ -296,7 +296,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 52d97e9a2ed60240d361cfa795a23e44c276dec9 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 22 May 2024 13:03:32 -0400 Subject: [PATCH 06/16] 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 688a7fa08..088764405 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -49,6 +49,7 @@ import WebKit assetHandler: assetHandler, delegationHandler: delegationHandler) capacitorDidLoad() + updateAppLocationIfNeeded() if configDescriptor.instanceType == .fixed { updateBinaryVersion() @@ -82,18 +83,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 fe5fb5979..259e0b7d8 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 8d42db5a3..5b1123dcc 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -417,23 +417,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 6168b3bdfcfed426e12077fa15522db27e8b150a Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 22 May 2024 13:12:46 -0400 Subject: [PATCH 07/16] 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 5b1123dcc..5d1e45880 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -297,7 +297,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 57a6cba85e95379bf701255674da87fcb91b68dd Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 22 May 2024 13:35:40 -0400 Subject: [PATCH 08/16] 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 088764405..421766cb6 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -86,9 +86,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 148e58bca093fa4011c8e45cd3ff7e0cdcd34447 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 22 May 2024 14:52:47 -0400 Subject: [PATCH 09/16] 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 0da3a3336..a2904e5e5 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 */; }; @@ -158,8 +156,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; }; @@ -403,8 +399,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 */, @@ -515,7 +509,6 @@ 623D6909254C6FDF002D01D1 /* CAPInstanceDescriptor.h in Headers */, 62959B192524DA7800A3D7F1 /* CAPBridgedPlugin.h in Headers */, 621ECCB82542045900D3D615 /* CAPBridgedJSTypes.h in Headers */, - 2F81F5C926FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -728,7 +721,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 e79681b8a1dfe3020fd7749543db75abd1c9747d Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Thu, 23 May 2024 16:16:48 -0400 Subject: [PATCH 10/16] 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 83724ebe9..74e4bc616 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 } from '../util/spm'; import { runCommand, isInstalled } from '../util/subprocess'; -import { extractTemplate } from '../util/template'; import { getIOSPlugins } from './common'; @@ -49,6 +52,9 @@ async function updateIOSCocoaPods(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))) { @@ -138,297 +144,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) { @@ -438,13 +166,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 f8f593e596b2fa7451b83af02b1429baa457397c Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 16 Dec 2024 09:51:36 -0500 Subject: [PATCH 11/16] fix: syntax errors from merge, update cordova cli --- cli/src/ios/update.ts | 24 ++--- cli/src/util/cordova-ios.ts | 170 ++++++++---------------------------- 2 files changed, 44 insertions(+), 150 deletions(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 74e4bc616..b7acd262c 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -1,19 +1,13 @@ -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'; import { checkPlatformVersions, runTask } from '../common'; -import { checkPluginDependencies, handleCordovaPluginsJS, logCordovaManualSteps, needsStaticPod } from '../cordova'; +import { checkPluginDependencies, handleCordovaPluginsJS, logCordovaManualSteps } from '../cordova'; 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 { @@ -146,17 +140,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 36c4f5463f2ac9a6e7945ce74a4b444ce784935f Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 16 Dec 2024 10:44:57 -0500 Subject: [PATCH 12/16] 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 b7acd262c..fbf2b33fc 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -23,6 +23,7 @@ import { checkPackageManager, generatePackageFile } from '../util/spm'; import { runCommand, isInstalled } from '../util/subprocess'; import { getIOSPlugins } from './common'; +import { replace } from 'tar'; const platform = 'ios'; @@ -78,7 +79,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 4dc70308331454eafdd5f41cd845201eea90925c Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 16 Dec 2024 11:24:15 -0500 Subject: [PATCH 13/16] Removed commented out stuff --- .../CAPApplicationDelegateProxy.swift | 1 - .../Capacitor/CAPInstanceDescriptor.swift | 1 - ios/Capacitor/Capacitor/CapacitorBridge.swift | 51 +++++++--------- ios/Capacitor/Capacitor/JSExport.swift | 60 +++++++------------ .../Capacitor/WebViewDelegationHandler.swift | 18 +----- .../Classes/Public/Plugin.swift | 4 +- 6 files changed, 49 insertions(+), 86 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 259e0b7d8..df3c25ff2 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift @@ -136,7 +136,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 5d1e45880..4d3b44d20 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -111,13 +111,10 @@ 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 private var cordovaIsPresent = false - //private var cordovaParser: CDVConfigParser? // Background dispatch queue for plugin calls open private(set) var dispatchQueue = DispatchQueue(label: "bridge") @@ -198,8 +195,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) @@ -519,29 +514,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 + // } + // } /** Send a successful result to the JavaScript layer. diff --git a/ios/Capacitor/Capacitor/JSExport.swift b/ios/Capacitor/Capacitor/JSExport.swift index 59899cafd..10034fb10 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 -// } -// } - /** Export the JS required to implement the given plugin. */ @@ -170,27 +152,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 183812d3c..049d421bf 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 fa28deed261b55683063ed0bed8af0df3bf1e3ba Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 16 Dec 2024 11:25:43 -0500 Subject: [PATCH 14/16] 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 fbf2b33fc..16155d8ac 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, runTask } from '../common'; @@ -23,7 +24,6 @@ import { checkPackageManager, generatePackageFile } from '../util/spm'; import { runCommand, isInstalled } from '../util/subprocess'; import { getIOSPlugins } from './common'; -import { replace } from 'tar'; const platform = 'ios'; From a782f5b9d64dac806bd0211bef5fdd6fe9255abb Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Tue, 17 Dec 2024 10:58:19 -0500 Subject: [PATCH 15/16] 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 10034fb10..29ea548fd 100644 --- a/ios/Capacitor/Capacitor/JSExport.swift +++ b/ios/Capacitor/Capacitor/JSExport.swift @@ -152,28 +152,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 de147dfabdd35581ddbee7dfa61818f4a0cc47c1 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Tue, 17 Dec 2024 11:36:58 -0500 Subject: [PATCH 16/16] 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 {