From 3d8c595114b1a4fc86522ef9de083e8117db46b1 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 10 Aug 2026 10:35:58 -0500 Subject: [PATCH 01/42] starting on turning capacitor ios into a swift package --- .gitignore | 1 + ios/Package.swift | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 ios/Package.swift diff --git a/.gitignore b/.gitignore index 0968d10c62..703a749612 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ Pods/ Podfile.lock Build/* build/ +.build Index/ .*.sw* android-template.iml diff --git a/ios/Package.swift b/ios/Package.swift new file mode 100644 index 0000000000..6cfe0d97f1 --- /dev/null +++ b/ios/Package.swift @@ -0,0 +1,29 @@ +// swift-tools-version: 6.3 +import PackageDescription + +let package = Package( + name: "Capacitor", + platforms: [.iOS(.v16)], + products: [ + .library( + name:"Capacitor", + targets: ["Capacitor"] + ), + .library( + name: "CapacitorCordova", + targets: ["CapacitorCordova"] + ) + ], + dependencies: [], + targets: [ + .target( + name: "Capacitor", + path: "Capacitor/Capacitor" + ), + .target( + name: "CapacitorCordova", + path: "CapacitorCordova/CapacitorCordova" + ), + ], + swiftLanguageModes: [.v6] +) From 1c748f1ea2bfbf0a39622d751171bee26a4be98b Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 10 Aug 2026 12:14:04 -0500 Subject: [PATCH 02/42] Starting on converting Objective C files to Swift --- ios/Capacitor/Capacitor/CAPBridgedJSTypes.h | 19 -- ios/Capacitor/Capacitor/CAPBridgedJSTypes.m | 51 ----- ios/Capacitor/Capacitor/CAPBridgedPlugin.h | 41 ---- .../Capacitor/CAPInstanceConfiguration.m | 93 --------- .../Capacitor/CAPInstanceDescriptor.m | 55 ----- ios/Capacitor/Capacitor/CAPPlugin.m | 176 ---------------- ios/Capacitor/Capacitor/CAPPluginCall.m | 36 ---- ios/Capacitor/Capacitor/CAPPluginCall.swift | 84 -------- ios/Capacitor/Capacitor/CAPPluginMethod.m | 44 ---- ios/Capacitor/Capacitor/CAPPluginMethod.swift | 17 -- .../UIStatusBarManager+CAPHandleTapAction.m | 37 ---- ios/Capacitor/Capacitor/WKWebView+Capacitor.m | 15 -- ios/Package.swift | 38 +++- .../Capacitor/AppUUID.swift | 0 .../Capacitor/Array+Capacitor.swift | 0 ...ridgedJSValueContainerImplementation.swift | 54 +++++ .../CAPApplicationDelegateProxy.swift | 0 .../Capacitor/CAPBridgeDelegate.swift | 0 .../Capacitor/CAPBridgeProtocol.swift | 0 .../Capacitor/CAPBridgeViewController.swift | 6 + .../CAPBridgedPlugin+getMethod.swift | 0 ios/Sources/Capacitor/CAPBridgedPlugin.swift | 21 ++ .../Capacitor/CAPInstanceConfiguration.swift | 24 ++- .../Capacitor/CAPInstanceDescriptor.swift | 29 +++ .../Capacitor/CAPInstancePlugin.swift | 0 .../Capacitor/CAPLog.swift | 0 .../Capacitor/CAPNotifications.swift | 0 .../Capacitor/CAPPlugin+LoadInstance.swift | 0 ios/Sources/Capacitor/CAPPlugin.swift | 197 ++++++++++++++++++ ios/Sources/Capacitor/CAPPluginCall.swift | 179 ++++++++++++++++ ios/Sources/Capacitor/CAPPluginMethod.swift | 66 ++++++ .../Capacitor/CAPSceneDelegateProxy.swift | 0 .../Capacitor/Capacitor-Bridging-Header.h | 19 ++ .../Capacitor/CapacitorBridge.swift | 0 .../Capacitor/CapacitorExtension.swift | 0 .../Capacitor/Codable/JSValueDecoder.swift | 0 .../Capacitor/Codable/JSValueEncoder.swift | 0 .../Capacitor/Data+Capacitor.swift | 2 + .../Capacitor/DocLinks.swift | 0 .../Capacitor/InstanceConfiguration.swift | 115 ++++++++++ .../Capacitor/InstanceDescriptor.swift | 95 +++++++++ ios/{Capacitor => Sources}/Capacitor/JS.swift | 0 .../Capacitor/JSExport.swift | 2 + .../Capacitor/JSTypes.swift | 0 .../Capacitor/KeyPath.swift | 0 .../Capacitor/KeyValueStore.swift | 0 .../NotificationHandlerProtocol.swift | 1 + .../Capacitor/NotificationRouter.swift | 1 + .../Capacitor/PluginCallResult.swift | 0 .../Capacitor/PluginConfig.swift | 0 .../Plugins/CapacitorCookieManager.swift | 0 .../Capacitor/Plugins/CapacitorCookies.swift | 0 .../Capacitor/Plugins/CapacitorHttp.swift | 0 .../Plugins/CapacitorUrlRequest.swift | 0 .../Capacitor/Plugins/Console.swift | 0 .../Plugins/HttpRequestHandler.swift | 0 .../Capacitor/Plugins/SystemBars.swift | 0 .../Capacitor/Plugins/WebView.swift | 0 .../Capacitor/Router.swift | 0 .../Capacitor/UIColor.swift | 0 ...IStatusBarManager+CAPHandleTapAction.swift | 53 +++++ .../Capacitor/WKWebView+Capacitor.swift | 11 +- .../Capacitor/WebViewAssetHandler.swift | 1 + .../Capacitor/WebViewDelegationHandler.swift | 0 .../CapacitorC}/Capacitor.modulemap | 0 .../CapacitorCordova}/AppDelegate.m | 0 .../CDVCommandDelegateImpl.m | 0 .../CapacitorCordova}/CDVConfigParser.m | 0 .../CapacitorCordova}/CDVInvokedUrlCommand.m | 0 .../CapacitorCordova}/CDVPlugin+Resources.m | 0 .../CapacitorCordova}/CDVPlugin.m | 0 .../CapacitorCordova}/CDVPluginManager.m | 0 .../CapacitorCordova}/CDVPluginResult.m | 0 .../CapacitorCordova}/CDVURLProtocol.m | 0 .../CapacitorCordova}/CDVViewController.m | 0 .../CDVWebViewProcessPoolFactory.m | 0 .../CapacitorCordova.modulemap | 0 .../NSDictionary+CordovaPreferences.m | 0 .../CapacitorCordova/include}/AppDelegate.h | 0 .../CapacitorCordova/include}/CDV.h | 0 .../include}/CDVAvailability.h | 0 .../include}/CDVAvailabilityDeprecated.h | 0 .../include}/CDVCommandDelegate.h | 0 .../include}/CDVCommandDelegateImpl.h | 0 .../include}/CDVConfigParser.h | 0 .../include}/CDVInvokedUrlCommand.h | 0 .../include}/CDVPlugin+Resources.h | 0 .../CapacitorCordova/include}/CDVPlugin.h | 0 .../include}/CDVPluginManager.h | 0 .../include}/CDVPluginResult.h | 0 .../include}/CDVScreenOrientationDelegate.h | 0 .../include}/CDVURLProtocol.h | 0 .../include}/CDVViewController.h | 0 .../include}/CDVWebViewProcessPoolFactory.h | 0 .../NSDictionary+CordovaPreferences.h | 0 .../include}/CAPInstanceConfiguration.h | 0 .../include}/CAPInstanceDescriptor.h | 0 .../CapacitorObjC/include}/CAPPlugin.h | 0 .../CapacitorObjC/include}/CAPPluginCall.h | 0 .../CapacitorObjC/include}/CAPPluginMethod.h | 0 .../CapacitorObjC/include}/Capacitor.h | 0 101 files changed, 900 insertions(+), 682 deletions(-) delete mode 100644 ios/Capacitor/Capacitor/CAPBridgedJSTypes.h delete mode 100644 ios/Capacitor/Capacitor/CAPBridgedJSTypes.m delete mode 100644 ios/Capacitor/Capacitor/CAPBridgedPlugin.h delete mode 100644 ios/Capacitor/Capacitor/CAPInstanceConfiguration.m delete mode 100644 ios/Capacitor/Capacitor/CAPInstanceDescriptor.m delete mode 100644 ios/Capacitor/Capacitor/CAPPlugin.m delete mode 100644 ios/Capacitor/Capacitor/CAPPluginCall.m delete mode 100644 ios/Capacitor/Capacitor/CAPPluginCall.swift delete mode 100644 ios/Capacitor/Capacitor/CAPPluginMethod.m delete mode 100644 ios/Capacitor/Capacitor/CAPPluginMethod.swift delete mode 100644 ios/Capacitor/Capacitor/UIStatusBarManager+CAPHandleTapAction.m delete mode 100644 ios/Capacitor/Capacitor/WKWebView+Capacitor.m rename ios/{Capacitor => Sources}/Capacitor/AppUUID.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Array+Capacitor.swift (100%) create mode 100644 ios/Sources/Capacitor/BridgedJSValueContainerImplementation.swift rename ios/{Capacitor => Sources}/Capacitor/CAPApplicationDelegateProxy.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/CAPBridgeDelegate.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/CAPBridgeProtocol.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/CAPBridgeViewController.swift (98%) rename ios/{Capacitor => Sources}/Capacitor/CAPBridgedPlugin+getMethod.swift (100%) create mode 100644 ios/Sources/Capacitor/CAPBridgedPlugin.swift rename ios/{Capacitor => Sources}/Capacitor/CAPInstanceConfiguration.swift (87%) rename ios/{Capacitor => Sources}/Capacitor/CAPInstanceDescriptor.swift (91%) rename ios/{Capacitor => Sources}/Capacitor/CAPInstancePlugin.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/CAPLog.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/CAPNotifications.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/CAPPlugin+LoadInstance.swift (100%) create mode 100644 ios/Sources/Capacitor/CAPPlugin.swift create mode 100644 ios/Sources/Capacitor/CAPPluginCall.swift create mode 100644 ios/Sources/Capacitor/CAPPluginMethod.swift rename ios/{Capacitor => Sources}/Capacitor/CAPSceneDelegateProxy.swift (100%) create mode 100644 ios/Sources/Capacitor/Capacitor-Bridging-Header.h rename ios/{Capacitor => Sources}/Capacitor/CapacitorBridge.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/CapacitorExtension.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Codable/JSValueDecoder.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Codable/JSValueEncoder.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Data+Capacitor.swift (97%) rename ios/{Capacitor => Sources}/Capacitor/DocLinks.swift (100%) create mode 100644 ios/Sources/Capacitor/InstanceConfiguration.swift create mode 100644 ios/Sources/Capacitor/InstanceDescriptor.swift rename ios/{Capacitor => Sources}/Capacitor/JS.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/JSExport.swift (99%) rename ios/{Capacitor => Sources}/Capacitor/JSTypes.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/KeyPath.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/KeyValueStore.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/NotificationHandlerProtocol.swift (90%) rename ios/{Capacitor => Sources}/Capacitor/NotificationRouter.swift (99%) rename ios/{Capacitor => Sources}/Capacitor/PluginCallResult.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/PluginConfig.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Plugins/CapacitorCookieManager.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Plugins/CapacitorCookies.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Plugins/CapacitorHttp.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Plugins/CapacitorUrlRequest.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Plugins/Console.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Plugins/HttpRequestHandler.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Plugins/SystemBars.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Plugins/WebView.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/Router.swift (100%) rename ios/{Capacitor => Sources}/Capacitor/UIColor.swift (100%) create mode 100644 ios/Sources/Capacitor/UIStatusBarManager+CAPHandleTapAction.swift rename ios/{Capacitor => Sources}/Capacitor/WKWebView+Capacitor.swift (90%) rename ios/{Capacitor => Sources}/Capacitor/WebViewAssetHandler.swift (99%) rename ios/{Capacitor => Sources}/Capacitor/WebViewDelegationHandler.swift (100%) rename ios/{Capacitor/Capacitor => Sources/CapacitorC}/Capacitor.modulemap (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/AppDelegate.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/CDVCommandDelegateImpl.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/CDVConfigParser.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/CDVInvokedUrlCommand.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/CDVPlugin+Resources.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/CDVPlugin.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/CDVPluginManager.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/CDVPluginResult.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/CDVURLProtocol.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/CDVViewController.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/CDVWebViewProcessPoolFactory.m (100%) rename ios/{CapacitorCordova => Sources}/CapacitorCordova/CapacitorCordova.modulemap (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/NSDictionary+CordovaPreferences.m (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/AppDelegate.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDV.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVAvailability.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVAvailabilityDeprecated.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVCommandDelegate.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVCommandDelegateImpl.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVConfigParser.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVInvokedUrlCommand.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVPlugin+Resources.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVPlugin.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVPluginManager.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVPluginResult.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVScreenOrientationDelegate.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVURLProtocol.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVViewController.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/CDVWebViewProcessPoolFactory.h (100%) rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova/include}/NSDictionary+CordovaPreferences.h (100%) rename ios/{Capacitor/Capacitor => Sources/CapacitorObjC/include}/CAPInstanceConfiguration.h (100%) rename ios/{Capacitor/Capacitor => Sources/CapacitorObjC/include}/CAPInstanceDescriptor.h (100%) rename ios/{Capacitor/Capacitor => Sources/CapacitorObjC/include}/CAPPlugin.h (100%) rename ios/{Capacitor/Capacitor => Sources/CapacitorObjC/include}/CAPPluginCall.h (100%) rename ios/{Capacitor/Capacitor => Sources/CapacitorObjC/include}/CAPPluginMethod.h (100%) rename ios/{Capacitor/Capacitor => Sources/CapacitorObjC/include}/Capacitor.h (100%) diff --git a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h b/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h deleted file mode 100644 index 67f8a9501d..0000000000 --- a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h +++ /dev/null @@ -1,19 +0,0 @@ -// Convenience methods for bridging to/from JavaScript types. Deliberately hidden from -// Swift by omission (to avoid collisions with Swift protocols), use -// `#import ` if working in Objective-C. - -#import -#import - -@protocol BridgedJSValueContainerImplementation -@required -- (NSString * _Nullable)getString:(NSString * _Nonnull)key defaultValue:(NSString * _Nullable)defaultValue; -- (NSDate * _Nullable)getDate:(NSString * _Nonnull)key defaultValue:(NSDate * _Nullable)defaultValue; -- (NSDictionary * _Nullable)getObject:(NSString * _Nonnull)key defaultValue:(NSDictionary * _Nullable)defaultValue; -- (NSArray * _Nullable)getArray:(NSString * _Nonnull)key defaultValue:(NSArray * _Nullable)defaultValue; -- (NSNumber * _Nullable)getNumber:(NSString * _Nonnull)key defaultValue:(NSNumber * _Nullable)defaultValue; -- (BOOL)getBool:(NSString * _Nonnull)key defaultValue:(BOOL)defaultValue; -@end - -@interface CAPPluginCall (BridgedJSProtocol) -@end diff --git a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m b/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m deleted file mode 100644 index c8ec07da7f..0000000000 --- a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m +++ /dev/null @@ -1,51 +0,0 @@ -#import -#import "CAPBridgedJSTypes.h" - -@implementation CAPPluginCall (BridgedJSProtocol) -- (NSString * _Nullable)getString:(NSString * _Nonnull)key defaultValue:(NSString * _Nullable)defaultValue { - id value = [[self dictionaryRepresentation] objectForKey:key]; - if (value != nil && [value isKindOfClass:[NSString class]]) { - return value; - } - return defaultValue; -} - -- (NSDate * _Nullable)getDate:(NSString * _Nonnull)key defaultValue:(NSDate * _Nullable)defaultValue { - id value = [[self dictionaryRepresentation] objectForKey:key]; - if (value != nil && [value isKindOfClass:[NSDate class]]) { - return value; - } - else if (value != nil && [value isKindOfClass:[NSString class]]) { - return [[[self class] jsDateFormatter] dateFromString:value]; - } - return defaultValue; -} - -- (NSDictionary * _Nullable)getObject:(NSString * _Nonnull)key defaultValue:(NSDictionary * _Nullable)defaultValue { - id value = [[self dictionaryRepresentation] objectForKey:key]; - if (value != nil && [value isKindOfClass:[NSDictionary class]]) { - return value; - } - return defaultValue; -} - -- (NSArray * _Nullable)getArray:(NSString * _Nonnull)key defaultValue:(NSArray * _Nullable)defaultValue; { - id value = [[self dictionaryRepresentation] objectForKey:key]; - if (value != nil && [value isKindOfClass:[NSArray class]]) { - return value; - } - return defaultValue; -} - -- (NSNumber * _Nullable)getNumber:(NSString * _Nonnull)key defaultValue:(NSNumber * _Nullable)defaultValue { - id value = [[self dictionaryRepresentation] objectForKey:key]; - if (value != nil && [value isKindOfClass:[NSNumber class]]) { - return value; - } - return defaultValue; -} - -- (BOOL)getBool:(NSString * _Nonnull)key defaultValue:(BOOL)defaultValue { - return [[self getNumber:key defaultValue:[NSNumber numberWithBool:defaultValue]] boolValue]; -} -@end diff --git a/ios/Capacitor/Capacitor/CAPBridgedPlugin.h b/ios/Capacitor/Capacitor/CAPBridgedPlugin.h deleted file mode 100644 index a6f6324de1..0000000000 --- a/ios/Capacitor/Capacitor/CAPBridgedPlugin.h +++ /dev/null @@ -1,41 +0,0 @@ -#import "CAPPluginMethod.h" - -#if defined(__cplusplus) -#define CAP_EXTERN extern "C" __attribute__((visibility("default"))) -#else -#define CAP_EXTERN extern __attribute__((visibility("default"))) -#endif - -#define CAPPluginReturnNone @"none" -#define CAPPluginReturnCallback @"callback" -#define CAPPluginReturnPromise @"promise" - -@class CAPPluginCall; -@class CAPPlugin; - -@protocol CAPBridgedPlugin -@property (nonnull, readonly) NSString *identifier; -@property (nonnull, readonly) NSString *jsName; -@property (nonnull, readonly) NSArray *pluginMethods; -@end - -#define CAP_PLUGIN_CONFIG(plugin_id, js_name) \ -- (NSString *)identifier { return @#plugin_id; } \ -- (NSString *)jsName { return @js_name; } -#define CAP_PLUGIN_METHOD(method_name, method_return_type) \ -[methods addObject:[[CAPPluginMethod alloc] initWithName:@#method_name returnType:method_return_type]] - -#define CAP_PLUGIN(objc_name, js_name, methods_body) \ -@interface objc_name : NSObject \ -@end \ -@interface objc_name (CAPPluginCategory) \ -@end \ -@implementation objc_name (CAPPluginCategory) \ -- (NSArray *)pluginMethods { \ - NSMutableArray *methods = [NSMutableArray new]; \ - methods_body \ - return methods; \ -} \ -CAP_PLUGIN_CONFIG(objc_name, js_name) \ -@end - diff --git a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m deleted file mode 100644 index d6b7785e34..0000000000 --- a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m +++ /dev/null @@ -1,93 +0,0 @@ -#import "CAPInstanceConfiguration.h" -#import - -@interface CAPInstanceConfiguration (Internal) -- (instancetype)initWithConfiguration:(CAPInstanceConfiguration*)configuration andLocation:(NSURL*)location; -@end - - -@implementation CAPInstanceConfiguration - -- (instancetype)initWithDescriptor:(CAPInstanceDescriptor *)descriptor isDebug:(BOOL)debug { - if (self = [super init]) { - // first, give the descriptor a chance to make itself internally consistent - [descriptor normalize]; - // now copy the simple properties - _appendedUserAgentString = descriptor.appendedUserAgentString; - _overridenUserAgentString = descriptor.overridenUserAgentString; - _backgroundColor = descriptor.backgroundColor; - _allowedNavigationHostnames = descriptor.allowedNavigationHostnames; - switch (descriptor.loggingBehavior) { - case CAPInstanceLoggingBehaviorProduction: - _loggingEnabled = true; - break; - case CAPInstanceLoggingBehaviorDebug: - _loggingEnabled = debug; - break; - default: - _loggingEnabled = false; - break; - } - _scrollingEnabled = descriptor.scrollingEnabled; - _zoomingEnabled = descriptor.zoomingEnabled; - _allowLinkPreviews = descriptor.allowLinkPreviews; - _handleApplicationNotifications = descriptor.handleApplicationNotifications; - _contentInsetAdjustmentBehavior = descriptor.contentInsetAdjustmentBehavior; - _appLocation = descriptor.appLocation; - _appStartPath = descriptor.appStartPath; - _limitsNavigationsToAppBoundDomains = descriptor.limitsNavigationsToAppBoundDomains; - _preferredContentMode = descriptor.preferredContentMode; - _pluginConfigurations = descriptor.pluginConfigurations; - _isWebDebuggable = descriptor.isWebDebuggable; - _hasInitialFocus = descriptor.hasInitialFocus; - _legacyConfig = descriptor.legacyConfig; - // construct the necessary URLs - _localURL = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@://%@", descriptor.urlScheme, descriptor.urlHostname]]; - if (descriptor.serverURL != nil) { - _serverURL = [[NSURL alloc] initWithString:(descriptor.serverURL)]; - } - else { - _serverURL = _localURL; - } - _errorPath = descriptor.errorPath; - // extract the one value we care about from the cordova configuration - _cordovaDeployDisabled = [descriptor cordovaDeployDisabled]; - } - return self; -} - -- (instancetype)initWithConfiguration:(CAPInstanceConfiguration*)configuration andLocation:(NSURL*)location { - if (self = [super init]) { - _appendedUserAgentString = [[configuration appendedUserAgentString] copy]; - _overridenUserAgentString = [[configuration overridenUserAgentString] copy]; - _backgroundColor = configuration.backgroundColor; - _allowedNavigationHostnames = [[configuration allowedNavigationHostnames] copy]; - _localURL = [[configuration localURL] copy]; - _serverURL = [[configuration serverURL] copy]; - _errorPath = [[configuration errorPath] copy]; - _pluginConfigurations = [[configuration pluginConfigurations] copy]; - _loggingEnabled = configuration.loggingEnabled; - _scrollingEnabled = configuration.scrollingEnabled; - _zoomingEnabled = configuration.zoomingEnabled; - _allowLinkPreviews = configuration.allowLinkPreviews; - _handleApplicationNotifications = configuration.handleApplicationNotifications; - _isWebDebuggable = configuration.isWebDebuggable; - _hasInitialFocus = configuration.hasInitialFocus; - _cordovaDeployDisabled = configuration.cordovaDeployDisabled; - _contentInsetAdjustmentBehavior = configuration.contentInsetAdjustmentBehavior; - // we don't care about internal usage of deprecated APIs and the framework should build cleanly -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - _legacyConfig = [[configuration legacyConfig] copy]; -#pragma clang diagnostic pop - _appStartPath = configuration.appStartPath; - _appLocation = [location copy]; - } - return self; -} - -- (instancetype)updatingAppLocation:(NSURL*)location { - return [[CAPInstanceConfiguration alloc] initWithConfiguration:self andLocation:location]; -} - -@end diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m deleted file mode 100644 index acdbc08caf..0000000000 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m +++ /dev/null @@ -1,55 +0,0 @@ -#import "CAPInstanceDescriptor.h" -#import - -// Swift extensions marked as @objc and internal are available to the Obj-C runtime but are not available at compile time. -// so we need this declaration to avoid compiler complaints -@interface CAPInstanceDescriptor (InternalSwiftExtension) -- (void)_parseConfigurationAt:(NSURL *)configURL cordovaConfiguration:(NSURL *)cordovaURL; -@end - -NSString* const CAPInstanceDescriptorDefaultScheme = @"capacitor"; -NSString* const CAPInstanceDescriptorDefaultHostname = @"localhost"; - -@implementation CAPInstanceDescriptor -- (instancetype)initAsDefault { - if (self = [super init]) { - _instanceType = CAPInstanceTypeFixed; - [self _setDefaultsWithAppLocation:[[NSBundle mainBundle] URLForResource:@"public" withExtension:nil]]; - [self _parseConfigurationAt:[[NSBundle mainBundle] URLForResource:@"capacitor.config" withExtension:@"json"] cordovaConfiguration:[[NSBundle mainBundle] URLForResource:@"config" withExtension:@"xml"]]; - } - return self; -} - -- (instancetype)initAtLocation:(NSURL*)appURL configuration:(NSURL*)configURL cordovaConfiguration:(NSURL*)cordovaURL { - if (self = [super init]) { - _instanceType = CAPInstanceTypeVariable; - [self _setDefaultsWithAppLocation:appURL]; - [self _parseConfigurationAt:configURL cordovaConfiguration:cordovaURL]; - } - return self; -} - -- (void)_setDefaultsWithAppLocation:(NSURL*)location { - _allowedNavigationHostnames = @[]; - _urlScheme = CAPInstanceDescriptorDefaultScheme; - _urlHostname = CAPInstanceDescriptorDefaultHostname; - _pluginConfigurations = @{}; - _legacyConfig = @{}; - _loggingBehavior = CAPInstanceLoggingBehaviorDebug; - _scrollingEnabled = YES; - _zoomingEnabled = NO; - _allowLinkPreviews = YES; - _handleApplicationNotifications = YES; - _isWebDebuggable = NO; - _hasInitialFocus = YES; - _contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; - _appLocation = location; - _limitsNavigationsToAppBoundDomains = FALSE; - _warnings = 0; - if (location == nil) { - _warnings |= CAPInstanceWarningMissingAppDir; - // location is nil so assume it was supposed to be the default - _appLocation = [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:@"public"]; - } -} -@end diff --git a/ios/Capacitor/Capacitor/CAPPlugin.m b/ios/Capacitor/Capacitor/CAPPlugin.m deleted file mode 100644 index 92bf24e7dd..0000000000 --- a/ios/Capacitor/Capacitor/CAPPlugin.m +++ /dev/null @@ -1,176 +0,0 @@ -#import "CAPPlugin.h" -#import "CAPBridgedJSTypes.h" -#import -#import - -@implementation CAPPlugin - --(instancetype) initWithBridge:(id)bridge pluginId:(NSString *)pluginId pluginName:(NSString *)pluginName { - self.bridge = bridge; - self.webView = bridge.webView; - self.pluginId = pluginId; - self.pluginName = pluginName; - self.eventListeners = [[NSMutableDictionary alloc] init]; - self.retainedEventArguments = [[NSMutableDictionary alloc] init]; - self.shouldStringifyDatesInCalls = true; - return self; -} - --(NSString *) getId { - return self.pluginName; -} - -- (BOOL)getBool:(CAPPluginCall *)call field:(NSString *)field defaultValue:(BOOL)defaultValue { - NSNumber* value = [call getNumber:field defaultValue:[NSNumber numberWithBool:defaultValue]]; - return [value boolValue]; -} - -- (NSString *) getString:(CAPPluginCall *)call field:(NSString *)field defaultValue:(NSString *)defaultValue { - return [call getString:field defaultValue:defaultValue]; -} - --(PluginConfig*)getConfig { - return [self.bridge.config getPluginConfig:self.pluginName]; -} - --(void)load {} - -- (void)addEventListener:(NSString *)eventName listener:(CAPPluginCall *)listener { - NSMutableArray *listenersForEvent = [self.eventListeners objectForKey:eventName]; - if(listenersForEvent == nil || [listenersForEvent count] == 0) { - listenersForEvent = [[NSMutableArray alloc] initWithObjects:listener, nil]; - [self.eventListeners setValue:listenersForEvent forKey:eventName]; - - [self sendRetainedArgumentsForEvent:eventName]; - } else { - [listenersForEvent addObject:listener]; - } -} - -- (void)sendRetainedArgumentsForEvent:(NSString *)eventName { - // copy retained args and null source to prevent potential race conditions - NSMutableArray *retained = [self.retainedEventArguments objectForKey:eventName]; - if (retained == nil) { - return; - } - - [self.retainedEventArguments removeObjectForKey:eventName]; - - for(id data in retained) { - [self notifyListeners:eventName data:data]; - } -} - -- (void)removeEventListener:(NSString *)eventName listener:(CAPPluginCall *)listener { - NSMutableArray *listenersForEvent = [self.eventListeners objectForKey:eventName]; - if(!listenersForEvent) { return; } - NSUInteger listenerIndex = [listenersForEvent indexOfObject:listener]; - if(listenerIndex == NSNotFound) { - return; - } - [listenersForEvent removeObjectAtIndex:listenerIndex]; -} - -- (void)notifyListeners:(NSString *)eventName data:(NSDictionary *)data { - [self notifyListeners:eventName data:data retainUntilConsumed:NO]; -} - -- (void)notifyListeners:(NSString *)eventName data:(NSDictionary *)data retainUntilConsumed:(BOOL)retain { - NSArray *listenersForEvent = [self.eventListeners objectForKey:eventName]; - if(listenersForEvent == nil || [listenersForEvent count] == 0) { - if (retain == YES) { - - if ([self.retainedEventArguments objectForKey:eventName] == nil) { - [self.retainedEventArguments setObject:[[NSMutableArray alloc] init] forKey:eventName]; - } - - [[self.retainedEventArguments objectForKey:eventName] addObject:data]; - } - return; - } - - for (int i=0; i < listenersForEvent.count; i++) { - CAPPluginCall *call = listenersForEvent[i]; - if (call != nil) { - CAPPluginCallResult *result = [[CAPPluginCallResult alloc] init:data]; - call.successHandler(result, call); - } - } -} - -- (void)addListener:(CAPPluginCall *)call { - NSString *eventName = [call.options objectForKey:@"eventName"]; - [call setKeepAlive:TRUE]; - [self addEventListener:eventName listener:call]; -} - -- (void)removeListener:(CAPPluginCall *)call { - NSString *eventName = [call.options objectForKey:@"eventName"]; - NSString *callbackId = [call.options objectForKey:@"callbackId"]; - CAPPluginCall *storedCall = [self.bridge savedCallWithID:callbackId]; - [self removeEventListener:eventName listener:storedCall]; - [self.bridge releaseCallWithID:callbackId]; -} - -- (void)removeAllListeners:(CAPPluginCall *)call { - [self.eventListeners removeAllObjects]; - [call resolve]; -} - -- (NSArray*)getListeners:(NSString *)eventName { - NSArray* listeners = [self.eventListeners objectForKey:eventName]; - return listeners; -} - -- (BOOL)hasListeners:(NSString *)eventName { - NSArray* listeners = [self.eventListeners objectForKey:eventName]; - - if (listeners == nil) { - return false; - } - return [listeners count] > 0; -} - -- (void)checkPermissions:(CAPPluginCall *)call { - [call resolve]; -} - -- (void)requestPermissions:(CAPPluginCall *)call { - [call resolve]; -} - -/** - * Configure popover sourceRect, sourceView and permittedArrowDirections to show it centered - */ --(void)setCenteredPopover:(UIViewController *) vc { - if (self.bridge.viewController != nil) { - vc.popoverPresentationController.sourceRect = CGRectMake(self.bridge.viewController.view.center.x, self.bridge.viewController.view.center.y, 0, 0); - vc.popoverPresentationController.sourceView = self.bridge.viewController.view; - vc.popoverPresentationController.permittedArrowDirections = 0; - } -} - --(void)setCenteredPopover:(UIViewController* _Nonnull) vc size:(CGSize) size { - if (self.bridge.viewController != nil) { - vc.popoverPresentationController.sourceRect = CGRectMake(self.bridge.viewController.view.center.x, self.bridge.viewController.view.center.y, 0, 0); - vc.preferredContentSize = size; - vc.popoverPresentationController.sourceView = self.bridge.viewController.view; - vc.popoverPresentationController.permittedArrowDirections = 0; - } -} - --(BOOL)supportsPopover { - return YES; -} - -- (NSNumber*)shouldOverrideLoad:(WKNavigationAction*)navigationAction { - return nil; -} - -- (BOOL)handleWKWebViewURLAuthenticationChallenge:(NSURLAuthenticationChallenge* _Nonnull)challenge completionHandler:(void (^_Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { - return NO; -} - - -@end - diff --git a/ios/Capacitor/Capacitor/CAPPluginCall.m b/ios/Capacitor/Capacitor/CAPPluginCall.m deleted file mode 100644 index fc99de3835..0000000000 --- a/ios/Capacitor/Capacitor/CAPPluginCall.m +++ /dev/null @@ -1,36 +0,0 @@ -#import -#import "CAPPluginCall.h" - -@implementation CAPPluginCall - -- (instancetype)initWithCallbackId:(NSString *)callbackId options:(NSDictionary *)options success:(CAPPluginCallSuccessHandler) success error:(CAPPluginCallErrorHandler) error __deprecated { - self.callbackId = callbackId; - self.methodName = @""; - self.options = options; - self.successHandler = success; - self.errorHandler = error; - return self; -} - -- (instancetype)initWithCallbackId:(NSString *)callbackId methodName:(NSString *)methodName options:(NSDictionary *)options success:(CAPPluginCallSuccessHandler) success error:(CAPPluginCallErrorHandler) error { - self.callbackId = callbackId; - self.methodName = methodName; - self.options = options; - self.successHandler = success; - self.errorHandler = error; - return self; -} - -- (BOOL)isSaved { - return self.keepAlive; -} - -- (void)setIsSaved:(BOOL)saved { - self.keepAlive = saved; -} - -- (void)save { - self.keepAlive = true; -} - -@end diff --git a/ios/Capacitor/Capacitor/CAPPluginCall.swift b/ios/Capacitor/Capacitor/CAPPluginCall.swift deleted file mode 100644 index 24d16a9e40..0000000000 --- a/ios/Capacitor/Capacitor/CAPPluginCall.swift +++ /dev/null @@ -1,84 +0,0 @@ -import Foundation - -/** - * Swift niceties for CAPPluginCall - */ - -extension CAPPluginCall: JSValueContainer { - public var jsObjectRepresentation: JSObject { - return options as? JSObject ?? [:] - } -} - -@objc extension CAPPluginCall: BridgedJSValueContainer { - public var dictionaryRepresentation: NSDictionary { - return options as NSDictionary - } - - public static var jsDateFormatter: ISO8601DateFormatter = { - return ISO8601DateFormatter() - }() -} - -@objc public extension CAPPluginCall { - func resolve() { - successHandler(CAPPluginCallResult(nil), self) - } - - func resolve(_ data: PluginCallResultData = [:]) { - successHandler(CAPPluginCallResult(data), self) - } - - func reject(_ message: String, _ code: String? = nil, _ error: Error? = nil, _ data: PluginCallResultData? = nil) { - errorHandler(CAPPluginCallError(message: message, code: code, error: error, data: data)) - } - - func unimplemented() { - unimplemented("not implemented") - } - - func unimplemented(_ message: String) { - errorHandler(CAPPluginCallError(message: message, code: "UNIMPLEMENTED", error: nil, data: [:])) - } - - func unavailable() { - unavailable("not available") - } - - func unavailable(_ message: String) { - errorHandler(CAPPluginCallError(message: message, code: "UNAVAILABLE", error: nil, data: [:])) - } -} - -// MARK: Codable Support -public extension CAPPluginCall { - /// Encodes the given value to a ``JSObject`` and resolves the call. If an error is thrown during encoding, ``reject(_:_:_:_:)`` is called. - /// - Parameters: - /// - data: The value to encode - /// - encoder: The encoder to use. Defaults to `JSValueEncoder()` - /// - messageForRejectionFromError: A closure that takes the error thrown from ``JSValueEncoder/encodeJSObject(_:)`` - /// and returns a string to be provided to ``reject(_:_:_:_:)``. Defaults to a function that returns "Failed encoding response". - func resolve( - with data: T, - encoder: JSValueEncoder = JSValueEncoder(), - messageForRejectionFromError: (Error) -> String = { _ in "Failed encoding response" } - ) { - do { - let encoded = try encoder.encodeJSObject(data) - resolve(encoded) - } catch { - let message = messageForRejectionFromError(error) - reject(message, nil, error) - } - } - - /// Decodes the options to the given type. - /// - Parameters: - /// - type: The type to decode to. - /// - decoder: The decoder to use. Defaults to `JSValueDecoder()`. - /// - Throws: If the options cannot be decoded. - /// - Returns: The decoded value. - func decode(_ type: T.Type, decoder: JSValueDecoder = JSValueDecoder()) throws -> T { - try decoder.decode(type, from: options as? JSObject ?? [:]) - } -} diff --git a/ios/Capacitor/Capacitor/CAPPluginMethod.m b/ios/Capacitor/Capacitor/CAPPluginMethod.m deleted file mode 100644 index d765540e92..0000000000 --- a/ios/Capacitor/Capacitor/CAPPluginMethod.m +++ /dev/null @@ -1,44 +0,0 @@ -#import -#import "CAPPluginMethod.h" - -typedef void(^CAPCallback)(id _arg, NSInteger index); - -@implementation CAPPluginMethodArgument - -- (instancetype)initWithName:(NSString *)name nullability:(CAPPluginMethodArgumentNullability)nullability type:(NSString *)type { - self.name = name; - self.nullability = nullability; - return self; -} - -@end - -@implementation CAPPluginMethod { - // NSInvocation's retainArguments doesn't work with our arguments - // so we have to retain args manually - NSMutableArray *_manualRetainArgs; - // Retain invocation instance - NSInvocation *_invocation; - NSMutableArray *_methodArgumentCallbacks; - CAPPluginCall *_call; - SEL _selector; -} - --(instancetype)initWithName:(NSString *)name returnType:(CAPPluginReturnType *)returnType { - self.name = name; - self.selector = NSSelectorFromString([name stringByAppendingString:@":"]); - self.returnType = returnType; - return self; -} - --(instancetype)initWithSelector:(SEL) selector returnType:(CAPPluginReturnType *)returnType { - // need to drop the : from the selector string - NSString* rawSelString = NSStringFromSelector(selector); - self.name = [rawSelString substringToIndex:[rawSelString length] - 1]; - self.selector = selector; - self.returnType = returnType; - return self; -} - -@end - diff --git a/ios/Capacitor/Capacitor/CAPPluginMethod.swift b/ios/Capacitor/Capacitor/CAPPluginMethod.swift deleted file mode 100644 index 0df641ad33..0000000000 --- a/ios/Capacitor/Capacitor/CAPPluginMethod.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// CAPPluginMethod.swift -// Capacitor -// -// Created by Steven Sherry on 4/18/24. -// Copyright © 2024 Drifty Co. All rights reserved. -// - -extension CAPPluginMethod { - public enum ReturnType: String { - case promise, callback, none - } - - public convenience init(_ selector: Selector, returnType: ReturnType = .promise) { - self.init(selector: selector, returnType: returnType.rawValue) - } -} diff --git a/ios/Capacitor/Capacitor/UIStatusBarManager+CAPHandleTapAction.m b/ios/Capacitor/Capacitor/UIStatusBarManager+CAPHandleTapAction.m deleted file mode 100644 index 44272a33ba..0000000000 --- a/ios/Capacitor/Capacitor/UIStatusBarManager+CAPHandleTapAction.m +++ /dev/null @@ -1,37 +0,0 @@ -#import -#import -#import - -@implementation UIStatusBarManager (CAPHandleTapAction) - -+ (void)load { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - Class class = [self class]; - SEL originalSelector = NSSelectorFromString(@"handleTapAction:"); - SEL swizzledSelector = @selector(nofity_handleTapAction:); - - Method originalMethod = class_getInstanceMethod(class, originalSelector); - Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); - - BOOL didAddMethod = class_addMethod(class, - originalSelector, - method_getImplementation(swizzledMethod), - method_getTypeEncoding(swizzledMethod)); - if (didAddMethod) { - class_replaceMethod(class, - swizzledSelector, - method_getImplementation(originalMethod), - method_getTypeEncoding(originalMethod)); - } else { - method_exchangeImplementations(originalMethod, swizzledMethod); - } - }); -} - --(void)nofity_handleTapAction:(id)arg1 { - [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:NSNotification.capacitorStatusBarTapped object:nil]]; - [self nofity_handleTapAction:arg1]; -} - -@end diff --git a/ios/Capacitor/Capacitor/WKWebView+Capacitor.m b/ios/Capacitor/Capacitor/WKWebView+Capacitor.m deleted file mode 100644 index b861f0637d..0000000000 --- a/ios/Capacitor/Capacitor/WKWebView+Capacitor.m +++ /dev/null @@ -1,15 +0,0 @@ -#import -#import - -// Swift extensions marked as @objc and internal are available to the runtime but won't be found at compile time -// so we need this declaration to avoid compiler complaints. -@interface WKWebView (InternalSwiftExtension) -+ (void)_swizzleKeyboardMethods; -@end - -// +load is the safest place to swizzle methods but that won't work from a swift extension so we need this wrapper. -@implementation WKWebView (CapacitorAutoFocus) -+ (void)load { - [self _swizzleKeyboardMethods]; -} -@end diff --git a/ios/Package.swift b/ios/Package.swift index 6cfe0d97f1..b353fc28e5 100644 --- a/ios/Package.swift +++ b/ios/Package.swift @@ -6,24 +6,50 @@ let package = Package( platforms: [.iOS(.v16)], products: [ .library( - name:"Capacitor", - targets: ["Capacitor"] + name: "Capacitor", + targets: ["Capacitor", "CapacitorObjC"] ), .library( name: "CapacitorCordova", targets: ["CapacitorCordova"] ) ], - dependencies: [], targets: [ + // Pure ObjC core utilities (no dependencies) + .target( + name: "CapacitorC", + publicHeadersPath: "include", + cSettings: [ + .define("_FORTIFY_SOURCE", to: "2") + ] + ), + + // Pure Swift public API (depends on CapacitorC) .target( name: "Capacitor", - path: "Capacitor/Capacitor" + dependencies: ["CapacitorC"], + publicHeadersPath: "include" + ), + + // Objective-C bridge layer (depends on Capacitor to import Swift headers) + .target( + name: "CapacitorObjC", + dependencies: ["Capacitor"], + publicHeadersPath: "include", + cSettings: [ + .define("_FORTIFY_SOURCE", to: "2") + ] ), + + // Cordova legacy ObjC target .target( name: "CapacitorCordova", - path: "CapacitorCordova/CapacitorCordova" - ), + publicHeadersPath: "include", + cSettings: [ + .headerSearchPath("include"), + .define("_FORTIFY_SOURCE", to: "2") + ] + ) ], swiftLanguageModes: [.v6] ) diff --git a/ios/Capacitor/Capacitor/AppUUID.swift b/ios/Sources/Capacitor/AppUUID.swift similarity index 100% rename from ios/Capacitor/Capacitor/AppUUID.swift rename to ios/Sources/Capacitor/AppUUID.swift diff --git a/ios/Capacitor/Capacitor/Array+Capacitor.swift b/ios/Sources/Capacitor/Array+Capacitor.swift similarity index 100% rename from ios/Capacitor/Capacitor/Array+Capacitor.swift rename to ios/Sources/Capacitor/Array+Capacitor.swift diff --git a/ios/Sources/Capacitor/BridgedJSValueContainerImplementation.swift b/ios/Sources/Capacitor/BridgedJSValueContainerImplementation.swift new file mode 100644 index 0000000000..12deade7e3 --- /dev/null +++ b/ios/Sources/Capacitor/BridgedJSValueContainerImplementation.swift @@ -0,0 +1,54 @@ +// +// BridgedJSValueContainerImplementation.swift +// Capacitor +// +// Copyright © 2024 Drifty Co. All rights reserved. +// + +import Foundation + +/// Protocol for accessing JavaScript values with type safety. +/// Provides convenience accessors for extracting and converting JavaScript types. +@objc public protocol BridgedJSValueContainerImplementation: NSObjectProtocol { + /// Extract a string value from the container + /// - Parameters: + /// - key: The key to retrieve + /// - defaultValue: Value to return if key is missing or not a string + /// - Returns: The string value or defaultValue + @objc func getString(_ key: String, defaultValue: String?) -> String? + + /// Extract a date value from the container, with ISO8601 string parsing + /// - Parameters: + /// - key: The key to retrieve + /// - defaultValue: Value to return if key is missing or not a date + /// - Returns: The date value or defaultValue + @objc func getDate(_ key: String, defaultValue: Date?) -> Date? + + /// Extract a dictionary (object) value from the container + /// - Parameters: + /// - key: The key to retrieve + /// - defaultValue: Value to return if key is missing or not a dictionary + /// - Returns: The dictionary value or defaultValue + @objc func getObject(_ key: String, defaultValue: [String: Any]?) -> [String: Any]? + + /// Extract an array value from the container + /// - Parameters: + /// - key: The key to retrieve + /// - defaultValue: Value to return if key is missing or not an array + /// - Returns: The array value or defaultValue + @objc func getArray(_ key: String, defaultValue: [Any]?) -> [Any]? + + /// Extract a number value from the container + /// - Parameters: + /// - key: The key to retrieve + /// - defaultValue: Value to return if key is missing or not a number + /// - Returns: The number value or defaultValue + @objc func getNumber(_ key: String, defaultValue: NSNumber?) -> NSNumber? + + /// Extract a boolean value from the container + /// - Parameters: + /// - key: The key to retrieve + /// - defaultValue: Value to return if key is missing or not a number + /// - Returns: The boolean value or defaultValue + @objc func getBool(_ key: String, defaultValue: Bool) -> Bool +} diff --git a/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift b/ios/Sources/Capacitor/CAPApplicationDelegateProxy.swift similarity index 100% rename from ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift rename to ios/Sources/Capacitor/CAPApplicationDelegateProxy.swift diff --git a/ios/Capacitor/Capacitor/CAPBridgeDelegate.swift b/ios/Sources/Capacitor/CAPBridgeDelegate.swift similarity index 100% rename from ios/Capacitor/Capacitor/CAPBridgeDelegate.swift rename to ios/Sources/Capacitor/CAPBridgeDelegate.swift diff --git a/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift b/ios/Sources/Capacitor/CAPBridgeProtocol.swift similarity index 100% rename from ios/Capacitor/Capacitor/CAPBridgeProtocol.swift rename to ios/Sources/Capacitor/CAPBridgeProtocol.swift diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Sources/Capacitor/CAPBridgeViewController.swift similarity index 98% rename from ios/Capacitor/Capacitor/CAPBridgeViewController.swift rename to ios/Sources/Capacitor/CAPBridgeViewController.swift index 33f17be66c..2c4503d9f4 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Sources/Capacitor/CAPBridgeViewController.swift @@ -27,6 +27,12 @@ import WebKit }() override public final func loadView() { + // Set up status bar tap handling + UIStatusBarManager.ensureSwizzling() + + // Set up keyboard interaction handling + WKWebView.ensureKeyboardSwizzling() + // load the configuration and set the logging flag let configDescriptor = instanceDescriptor() let configuration = InstanceConfiguration(with: configDescriptor, isDebug: CapacitorBridge.isDevEnvironment) diff --git a/ios/Capacitor/Capacitor/CAPBridgedPlugin+getMethod.swift b/ios/Sources/Capacitor/CAPBridgedPlugin+getMethod.swift similarity index 100% rename from ios/Capacitor/Capacitor/CAPBridgedPlugin+getMethod.swift rename to ios/Sources/Capacitor/CAPBridgedPlugin+getMethod.swift diff --git a/ios/Sources/Capacitor/CAPBridgedPlugin.swift b/ios/Sources/Capacitor/CAPBridgedPlugin.swift new file mode 100644 index 0000000000..b8c59d2f44 --- /dev/null +++ b/ios/Sources/Capacitor/CAPBridgedPlugin.swift @@ -0,0 +1,21 @@ +// +// CAPBridgedPlugin.swift +// Capacitor +// +// Copyright © 2024 Drifty Co. All rights reserved. +// + +import Foundation + +/// Protocol for plugins that are bridged to JavaScript. +/// Plugins conforming to this protocol expose their structure and methods for registration with Capacitor. +@objc public protocol CAPBridgedPlugin: NSObjectProtocol { + /// Unique identifier for the plugin within the bridge (typically the package name or plugin id) + @objc var identifier: String { get } + + /// Name exposed to JavaScript (the plugin's JavaScript class name) + @objc var jsName: String { get } + + /// Array of plugin methods available to JavaScript + @objc var pluginMethods: [CAPPluginMethod] { get } +} diff --git a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.swift b/ios/Sources/Capacitor/CAPInstanceConfiguration.swift similarity index 87% rename from ios/Capacitor/Capacitor/CAPInstanceConfiguration.swift rename to ios/Sources/Capacitor/CAPInstanceConfiguration.swift index 7b1aff6316..82a117b440 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.swift +++ b/ios/Sources/Capacitor/CAPInstanceConfiguration.swift @@ -1,5 +1,14 @@ +// +// CAPInstanceConfiguration.swift +// Capacitor +// +// Copyright © 2024 Drifty Co. All rights reserved. +// + import Foundation +// MARK: - Computed Properties + extension InstanceConfiguration { @objc public var appStartFileURL: URL { if let path = appStartPath { @@ -19,17 +28,24 @@ extension InstanceConfiguration { guard let errorPath = errorPath else { return nil } - return localURL.appendingPathComponent(errorPath) } +} + +// MARK: - Plugin Configuration +extension InstanceConfiguration { @objc public func getPluginConfig(_ pluginId: String) -> PluginConfig { if let cfg = (pluginConfigurations as? JSObject)?[keyPath: KeyPath("\(pluginId)")] as? JSObject { return PluginConfig(config: cfg) } return PluginConfig(config: JSObject()) } +} + +// MARK: - Navigation +extension InstanceConfiguration { @objc public func shouldAllowNavigation(to host: String) -> Bool { for hostname in allowedNavigationHostnames { if doesHost(host, match: hostname) { @@ -39,25 +55,19 @@ extension InstanceConfiguration { return false } - // MARK: - Private - private func doesHost(_ host: String, match pattern: String) -> Bool { - // bail early in the simple case if pattern == "*" { return true } - // break apart the pieces var hostComponents = host.lowercased().split(separator: ".") var patternComponents = pattern.lowercased().split(separator: ".") guard hostComponents.count == patternComponents.count else { return false } - // remove any wildcard segments for wildcard in patternComponents.enumerated().reversed().filter({ $0.element == "*" }) { hostComponents.remove(at: wildcard.offset) patternComponents.remove(at: wildcard.offset) } - // match with what's left return hostComponents == patternComponents } } diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift b/ios/Sources/Capacitor/CAPInstanceDescriptor.swift similarity index 91% rename from ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift rename to ios/Sources/Capacitor/CAPInstanceDescriptor.swift index 495de0634d..36a39dbbef 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift +++ b/ios/Sources/Capacitor/CAPInstanceDescriptor.swift @@ -1,5 +1,34 @@ +// +// CAPInstanceDescriptor.swift +// Capacitor +// +// Copyright © 2024 Drifty Co. All rights reserved. +// + import Foundation +@objc public enum InstanceType: Int { + case fixed = 0 + case variable = 1 +} + +public struct InstanceWarning: OptionSet, Sendable { + public let rawValue: UInt + public init(rawValue: UInt) { self.rawValue = rawValue } + + public static let missingAppDir = InstanceWarning(rawValue: 1 << 0) + public static let missingFile = InstanceWarning(rawValue: 1 << 1) + public static let invalidFile = InstanceWarning(rawValue: 1 << 2) + public static let missingCordovaFile = InstanceWarning(rawValue: 1 << 3) + public static let invalidCordovaFile = InstanceWarning(rawValue: 1 << 4) +} + +@objc public enum InstanceLoggingBehavior: UInt { + case none = 1 + case debug = 2 + case production = 4 +} + public enum InstanceDescriptorDefaults { public static let scheme = "capacitor" public static let hostname = "localhost" diff --git a/ios/Capacitor/Capacitor/CAPInstancePlugin.swift b/ios/Sources/Capacitor/CAPInstancePlugin.swift similarity index 100% rename from ios/Capacitor/Capacitor/CAPInstancePlugin.swift rename to ios/Sources/Capacitor/CAPInstancePlugin.swift diff --git a/ios/Capacitor/Capacitor/CAPLog.swift b/ios/Sources/Capacitor/CAPLog.swift similarity index 100% rename from ios/Capacitor/Capacitor/CAPLog.swift rename to ios/Sources/Capacitor/CAPLog.swift diff --git a/ios/Capacitor/Capacitor/CAPNotifications.swift b/ios/Sources/Capacitor/CAPNotifications.swift similarity index 100% rename from ios/Capacitor/Capacitor/CAPNotifications.swift rename to ios/Sources/Capacitor/CAPNotifications.swift diff --git a/ios/Capacitor/Capacitor/CAPPlugin+LoadInstance.swift b/ios/Sources/Capacitor/CAPPlugin+LoadInstance.swift similarity index 100% rename from ios/Capacitor/Capacitor/CAPPlugin+LoadInstance.swift rename to ios/Sources/Capacitor/CAPPlugin+LoadInstance.swift diff --git a/ios/Sources/Capacitor/CAPPlugin.swift b/ios/Sources/Capacitor/CAPPlugin.swift new file mode 100644 index 0000000000..7e982560e5 --- /dev/null +++ b/ios/Sources/Capacitor/CAPPlugin.swift @@ -0,0 +1,197 @@ +import Foundation +import WebKit +import UIKit + +@objc open class CAPPlugin: NSObject { + @objc public weak var webView: WKWebView? + @objc public weak var bridge: CAPBridgeProtocol? + @objc public var pluginId: String = "" + @objc public var pluginName: String = "" + @objc public var eventListeners: NSMutableDictionary = [:] + @objc public var retainedEventArguments: NSMutableDictionary = [:] + @objc public var shouldStringifyDatesInCalls = true + + @objc public init(bridge: CAPBridgeProtocol, pluginId: String, pluginName: String) { + super.init() + self.bridge = bridge + self.webView = bridge.webView + self.pluginId = pluginId + self.pluginName = pluginName + self.eventListeners = NSMutableDictionary() + self.retainedEventArguments = NSMutableDictionary() + self.shouldStringifyDatesInCalls = true + } + + @objc public func getId() -> String { + return pluginName + } + + @objc public func getBool(_ call: CAPPluginCall, field: String, defaultValue: Bool) -> Bool { + let value = call.getNumber(field, defaultValue: NSNumber(value: defaultValue)) + return value?.boolValue ?? defaultValue + } + + @objc public func getString(_ call: CAPPluginCall, field: String, defaultValue: String) -> String { + return call.getString(field, defaultValue: defaultValue) ?? defaultValue + } + + @objc public func getConfig() -> PluginConfig? { + guard let bridge = bridge else { return nil } + return bridge.config.getPluginConfig(pluginName) + } + + @objc open func load() { + } + + @objc public func addEventListener(_ eventName: String, listener: CAPPluginCall) { + var listenersForEvent = eventListeners.object(forKey: eventName) as? NSMutableArray + + if listenersForEvent == nil || listenersForEvent?.count == 0 { + listenersForEvent = NSMutableArray(object: listener) + eventListeners.setValue(listenersForEvent, forKey: eventName) + sendRetainedArguments(forEvent: eventName) + } else { + listenersForEvent?.add(listener) + } + } + + @objc public func removeEventListener(_ eventName: String, listener: CAPPluginCall) { + guard let listenersForEvent = eventListeners.object(forKey: eventName) as? NSMutableArray else { + return + } + + let listenerIndex = listenersForEvent.index(of: listener) + guard listenerIndex != NSNotFound else { + return + } + + listenersForEvent.removeObject(at: listenerIndex) + } + + @objc public func notifyListeners(_ eventName: String, data: [String: Any]?) { + notifyListeners(eventName, data: data, retainUntilConsumed: false) + } + + @objc public func notifyListeners(_ eventName: String, data: [String: Any]?, retainUntilConsumed: Bool) { + guard let listenersForEvent = eventListeners.object(forKey: eventName) as? [CAPPluginCall] else { + if retainUntilConsumed { + if retainedEventArguments.object(forKey: eventName) == nil { + retainedEventArguments.setObject(NSMutableArray(), forKey: eventName) + } + (retainedEventArguments.object(forKey: eventName) as? NSMutableArray)?.add(data ?? [:]) + } + return + } + + for call in listenersForEvent { + let result = CAPPluginCallResult(data ?? [:]) + call.successHandler(result, call) + } + } + + @objc public func addListener(_ call: CAPPluginCall) { + guard let eventName = call.options["eventName"] as? String else { + return + } + call.keepAlive = true + addEventListener(eventName, listener: call) + } + + @objc public func removeListener(_ call: CAPPluginCall) { + guard let eventName = call.options["eventName"] as? String, + let callbackId = call.options["callbackId"] as? String else { + return + } + + guard let storedCall = bridge?.savedCall(withID: callbackId) else { + return + } + + removeEventListener(eventName, listener: storedCall) + bridge?.releaseCall(withID: callbackId) + } + + @objc public func removeAllListeners(_ call: CAPPluginCall) { + eventListeners.removeAllObjects() + call.resolve() + } + + @objc public func getListeners(_ eventName: String) -> [CAPPluginCall]? { + return eventListeners.object(forKey: eventName) as? [CAPPluginCall] + } + + @objc public func hasListeners(_ eventName: String) -> Bool { + guard let listeners = eventListeners.object(forKey: eventName) as? NSArray else { + return false + } + return listeners.count > 0 + } + + @objc public func checkPermissions(_ call: CAPPluginCall) { + call.resolve() + } + + @objc public func requestPermissions(_ call: CAPPluginCall) { + call.resolve() + } + + @objc public func setCenteredPopover(_ vc: UIViewController) { + guard let viewController = bridge?.viewController else { + return + } + + let popover = vc.popoverPresentationController + popover?.sourceRect = CGRect( + x: viewController.view.center.x, + y: viewController.view.center.y, + width: 0, + height: 0 + ) + popover?.sourceView = viewController.view + popover?.permittedArrowDirections = [] + } + + @objc public func setCenteredPopover(_ vc: UIViewController, size: CGSize) { + guard let viewController = bridge?.viewController else { + return + } + + vc.preferredContentSize = size + let popover = vc.popoverPresentationController + popover?.sourceRect = CGRect( + x: viewController.view.center.x, + y: viewController.view.center.y, + width: 0, + height: 0 + ) + popover?.sourceView = viewController.view + popover?.permittedArrowDirections = [] + } + + @objc public func supportsPopover() -> Bool { + return true + } + + @objc public func shouldOverrideLoad(_ navigationAction: WKNavigationAction) -> NSNumber? { + return nil + } + + @objc public func handleWKWebViewURLAuthenticationChallenge( + _ challenge: NSURLAuthenticationChallenge, + completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void + ) -> Bool { + return false + } + + private func sendRetainedArguments(forEvent eventName: String) { + guard let retained = retainedEventArguments.object(forKey: eventName) as? NSMutableArray else { + return + } + + retainedEventArguments.removeObject(forKey: eventName) + + for data in retained { + notifyListeners(eventName, data: data as? [String: Any]) + } + } +} diff --git a/ios/Sources/Capacitor/CAPPluginCall.swift b/ios/Sources/Capacitor/CAPPluginCall.swift new file mode 100644 index 0000000000..c997215b91 --- /dev/null +++ b/ios/Sources/Capacitor/CAPPluginCall.swift @@ -0,0 +1,179 @@ +import Foundation + +public typealias CAPPluginCallSuccessHandler = (CAPPluginCallResult, CAPPluginCall) -> Void +public typealias CAPPluginCallErrorHandler = (CAPPluginCallError) -> Void + +@objc open class CAPPluginCall: NSObject { + // MARK: - Properties + + @objc public var callbackId: String + @objc public var methodName: String + @objc public var options: [String: Any] + @objc public var successHandler: CAPPluginCallSuccessHandler + @objc public var errorHandler: CAPPluginCallErrorHandler + @objc public var keepAlive: Bool = false + + // MARK: - Initialization + + @objc public init( + callbackId: String, + methodName: String, + options: [String: Any], + success: @escaping CAPPluginCallSuccessHandler, + error: @escaping CAPPluginCallErrorHandler + ) { + self.callbackId = callbackId + self.methodName = methodName + self.options = options + self.successHandler = success + self.errorHandler = error + super.init() + } + + @objc public convenience init( + callbackId: String, + options: [String: Any], + success: @escaping CAPPluginCallSuccessHandler, + error: @escaping CAPPluginCallErrorHandler + ) { + self.init( + callbackId: callbackId, + methodName: "", + options: options, + success: success, + error: error + ) + } + + // MARK: - Accessors + + @objc public func getString(_ key: String, defaultValue: String? = nil) -> String? { + (options[key] as? String) ?? defaultValue + } + + @objc public func getNumber(_ key: String, defaultValue: NSNumber? = nil) -> NSNumber? { + if let number = options[key] as? NSNumber { + return number + } + if let int = options[key] as? Int { + return NSNumber(value: int) + } + if let double = options[key] as? Double { + return NSNumber(value: double) + } + return defaultValue + } + + @objc public func getBool(_ key: String, defaultValue: Bool) -> Bool { + guard let number = getNumber(key) else { return defaultValue } + return number.boolValue + } + + @objc public func getObject(_ key: String) -> [String: Any]? { + options[key] as? [String: Any] + } + + @objc public func getArray(_ key: String) -> [Any]? { + options[key] as? [Any] + } + + @objc public func getDate(_ key: String, defaultValue: Date? = nil) -> Date? { + guard let value = options[key] else { + return defaultValue + } + + if let date = value as? Date { + return date + } + + if let dateString = value as? String { + return BridgedJSValueContainer.jsDateFormatter.date(from: dateString) + } + + return defaultValue + } + + // MARK: - Deprecated + + @objc public var isSaved: Bool { + get { keepAlive } + set { keepAlive = newValue } + } + + @objc public func save() { + keepAlive = true + } +} + + +// MARK: - JSValue Representation + +extension CAPPluginCall: JSValueContainer { + public var jsObjectRepresentation: JSObject { + options as? JSObject ?? [:] + } +} + +@objc extension CAPPluginCall: BridgedJSValueContainer { + public var dictionaryRepresentation: NSDictionary { + options as NSDictionary + } + + public static var jsDateFormatter: ISO8601DateFormatter { + ISO8601DateFormatter() + } +} + +// MARK: - Result Handling + +@objc public extension CAPPluginCall { + func resolve() { + successHandler(CAPPluginCallResult(nil), self) + } + + func resolve(_ data: PluginCallResultData = [:]) { + successHandler(CAPPluginCallResult(data), self) + } + + func reject(_ message: String, _ code: String? = nil, _ error: Error? = nil, _ data: PluginCallResultData? = nil) { + errorHandler(CAPPluginCallError(message: message, code: code, error: error, data: data)) + } + + func unimplemented() { + unimplemented("not implemented") + } + + func unimplemented(_ message: String) { + errorHandler(CAPPluginCallError(message: message, code: "UNIMPLEMENTED", error: nil, data: [:])) + } + + func unavailable() { + unavailable("not available") + } + + func unavailable(_ message: String) { + errorHandler(CAPPluginCallError(message: message, code: "UNAVAILABLE", error: nil, data: [:])) + } +} + +// MARK: - Codable Support + +public extension CAPPluginCall { + func resolve( + with data: T, + encoder: JSValueEncoder = JSValueEncoder(), + messageForRejectionFromError: (Error) -> String = { _ in "Failed encoding response" } + ) { + do { + let encoded = try encoder.encodeJSObject(data) + resolve(encoded) + } catch { + let message = messageForRejectionFromError(error) + reject(message, nil, error) + } + } + + func decode(_ type: T.Type, decoder: JSValueDecoder = JSValueDecoder()) throws -> T { + try decoder.decode(type, from: options as? JSObject ?? [:]) + } +} diff --git a/ios/Sources/Capacitor/CAPPluginMethod.swift b/ios/Sources/Capacitor/CAPPluginMethod.swift new file mode 100644 index 0000000000..2cf78243f5 --- /dev/null +++ b/ios/Sources/Capacitor/CAPPluginMethod.swift @@ -0,0 +1,66 @@ +// +// CAPPluginMethod.swift +// Capacitor +// +// Created by Steven Sherry on 4/18/24. +// Copyright © 2024 Drifty Co. All rights reserved. +// + +import Foundation + +public typealias CAPPluginReturnType = String + +public let CAPPluginReturnNone: CAPPluginReturnType = "none" +public let CAPPluginReturnCallback: CAPPluginReturnType = "callback" +public let CAPPluginReturnPromise: CAPPluginReturnType = "promise" + +@objc public enum CAPPluginMethodArgumentNullability: Int { + case notNullable = 0 + case nullable = 1 +} + +@objc open class CAPPluginMethodArgument: NSObject { + @objc public var name: String + @objc public var nullability: CAPPluginMethodArgumentNullability + @objc public var type: String + + @objc public init(name: String, nullability: CAPPluginMethodArgumentNullability, type: String) { + self.name = name + self.nullability = nullability + self.type = type + super.init() + } +} + +@objc open class CAPPluginMethod: NSObject { + @objc public var selector: Selector + @objc public var name: String + @objc public var returnType: CAPPluginReturnType + + @objc public init(name: String, returnType: CAPPluginReturnType) { + self.name = name + self.selector = Selector(name + ":") + self.returnType = returnType + super.init() + } + + @objc public init(selector: Selector, returnType: CAPPluginReturnType) { + let selectorString = NSStringFromSelector(selector) + self.name = String(selectorString.dropLast()) + self.selector = selector + self.returnType = returnType + super.init() + } +} + +// MARK: - Convenience Helpers + +extension CAPPluginMethod { + public enum ReturnType: String { + case promise, callback, none + } + + public convenience init(_ selector: Selector, returnType: ReturnType = .promise) { + self.init(selector: selector, returnType: returnType.rawValue) + } +} diff --git a/ios/Capacitor/Capacitor/CAPSceneDelegateProxy.swift b/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift similarity index 100% rename from ios/Capacitor/Capacitor/CAPSceneDelegateProxy.swift rename to ios/Sources/Capacitor/CAPSceneDelegateProxy.swift diff --git a/ios/Sources/Capacitor/Capacitor-Bridging-Header.h b/ios/Sources/Capacitor/Capacitor-Bridging-Header.h new file mode 100644 index 0000000000..d70adea269 --- /dev/null +++ b/ios/Sources/Capacitor/Capacitor-Bridging-Header.h @@ -0,0 +1,19 @@ +// +// Capacitor-Bridging-Header.h +// Capacitor +// +// Bridging header for Swift code to access Objective-C types from CapacitorC target +// + +#ifndef Capacitor_Bridging_Header_h +#define Capacitor_Bridging_Header_h + +#import +#import +#import +#import + +#import +#import + +#endif /* Capacitor_Bridging_Header_h */ diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Sources/Capacitor/CapacitorBridge.swift similarity index 100% rename from ios/Capacitor/Capacitor/CapacitorBridge.swift rename to ios/Sources/Capacitor/CapacitorBridge.swift diff --git a/ios/Capacitor/Capacitor/CapacitorExtension.swift b/ios/Sources/Capacitor/CapacitorExtension.swift similarity index 100% rename from ios/Capacitor/Capacitor/CapacitorExtension.swift rename to ios/Sources/Capacitor/CapacitorExtension.swift diff --git a/ios/Capacitor/Capacitor/Codable/JSValueDecoder.swift b/ios/Sources/Capacitor/Codable/JSValueDecoder.swift similarity index 100% rename from ios/Capacitor/Capacitor/Codable/JSValueDecoder.swift rename to ios/Sources/Capacitor/Codable/JSValueDecoder.swift diff --git a/ios/Capacitor/Capacitor/Codable/JSValueEncoder.swift b/ios/Sources/Capacitor/Codable/JSValueEncoder.swift similarity index 100% rename from ios/Capacitor/Capacitor/Codable/JSValueEncoder.swift rename to ios/Sources/Capacitor/Codable/JSValueEncoder.swift diff --git a/ios/Capacitor/Capacitor/Data+Capacitor.swift b/ios/Sources/Capacitor/Data+Capacitor.swift similarity index 97% rename from ios/Capacitor/Capacitor/Data+Capacitor.swift rename to ios/Sources/Capacitor/Data+Capacitor.swift index 1105c581ad..d58c073d52 100644 --- a/ios/Capacitor/Capacitor/Data+Capacitor.swift +++ b/ios/Sources/Capacitor/Data+Capacitor.swift @@ -1,3 +1,5 @@ +import Foundation + extension Data: CapacitorExtension {} public extension CapacitorExtensionTypeWrapper where T == Data { diff --git a/ios/Capacitor/Capacitor/DocLinks.swift b/ios/Sources/Capacitor/DocLinks.swift similarity index 100% rename from ios/Capacitor/Capacitor/DocLinks.swift rename to ios/Sources/Capacitor/DocLinks.swift diff --git a/ios/Sources/Capacitor/InstanceConfiguration.swift b/ios/Sources/Capacitor/InstanceConfiguration.swift new file mode 100644 index 0000000000..e54de9e864 --- /dev/null +++ b/ios/Sources/Capacitor/InstanceConfiguration.swift @@ -0,0 +1,115 @@ +// +// InstanceConfiguration.swift +// Capacitor +// +// Copyright © 2024 Drifty Co. All rights reserved. +// + +import Foundation +import UIKit + +@objc(CAPInstanceConfiguration) +open class InstanceConfiguration: NSObject { + // MARK: - Properties + + @objc public let appendedUserAgentString: String? + @objc public let overridenUserAgentString: String? + @objc public let backgroundColor: UIColor? + @objc public let allowedNavigationHostnames: [String] + @objc public let localURL: URL + @objc public let serverURL: URL + @objc public let errorPath: String? + @objc public let pluginConfigurations: [String: Any] + @objc public let loggingEnabled: Bool + @objc public let scrollingEnabled: Bool + @objc public let zoomingEnabled: Bool + @objc public let allowLinkPreviews: Bool + @objc public let handleApplicationNotifications: Bool + @objc public let isWebDebuggable: Bool + @objc public let hasInitialFocus: Bool + @objc public let cordovaDeployDisabled: Bool + @objc public let contentInsetAdjustmentBehavior: UIScrollView.ContentInsetAdjustmentBehavior + @objc public let appLocation: URL + @objc public let appStartPath: String? + @objc public let limitsNavigationsToAppBoundDomains: Bool + @objc public let preferredContentMode: String? + @objc public let legacyConfig: [String: Any] + + // MARK: - Initialization + + @objc public init(with descriptor: InstanceDescriptor, isDebug: Bool) { + descriptor.normalize() + + self.appendedUserAgentString = descriptor.appendedUserAgentString + self.overridenUserAgentString = descriptor.overridenUserAgentString + self.backgroundColor = descriptor.backgroundColor + self.allowedNavigationHostnames = descriptor.allowedNavigationHostnames + self.scrollingEnabled = descriptor.scrollingEnabled + self.zoomingEnabled = descriptor.zoomingEnabled + self.allowLinkPreviews = descriptor.allowLinkPreviews + self.handleApplicationNotifications = descriptor.handleApplicationNotifications + self.contentInsetAdjustmentBehavior = descriptor.contentInsetAdjustmentBehavior + self.appLocation = descriptor.appLocation + self.appStartPath = descriptor.appStartPath + self.limitsNavigationsToAppBoundDomains = descriptor.limitsNavigationsToAppBoundDomains + self.preferredContentMode = descriptor.preferredContentMode + self.pluginConfigurations = descriptor.pluginConfigurations + self.isWebDebuggable = descriptor.isWebDebuggable + self.hasInitialFocus = descriptor.hasInitialFocus + self.legacyConfig = descriptor.legacyConfig + self.errorPath = descriptor.errorPath + self.cordovaDeployDisabled = descriptor.cordovaDeployDisabled + + switch descriptor.loggingBehavior { + case .production: + self.loggingEnabled = true + case .debug: + self.loggingEnabled = isDebug + case .none: + self.loggingEnabled = false + @unknown default: + self.loggingEnabled = false + } + + self.localURL = URL(string: "\(descriptor.urlScheme)://\(descriptor.urlHostname)")! + + if let serverURLString = descriptor.serverURL { + self.serverURL = URL(string: serverURLString) ?? self.localURL + } else { + self.serverURL = self.localURL + } + + super.init() + } + + @objc public init(with configuration: InstanceConfiguration, andLocation location: URL) { + self.appendedUserAgentString = configuration.appendedUserAgentString + self.overridenUserAgentString = configuration.overridenUserAgentString + self.backgroundColor = configuration.backgroundColor + self.allowedNavigationHostnames = configuration.allowedNavigationHostnames + self.localURL = configuration.localURL + self.serverURL = configuration.serverURL + self.errorPath = configuration.errorPath + self.pluginConfigurations = configuration.pluginConfigurations + self.loggingEnabled = configuration.loggingEnabled + self.scrollingEnabled = configuration.scrollingEnabled + self.zoomingEnabled = configuration.zoomingEnabled + self.allowLinkPreviews = configuration.allowLinkPreviews + self.handleApplicationNotifications = configuration.handleApplicationNotifications + self.isWebDebuggable = configuration.isWebDebuggable + self.hasInitialFocus = configuration.hasInitialFocus + self.cordovaDeployDisabled = configuration.cordovaDeployDisabled + self.contentInsetAdjustmentBehavior = configuration.contentInsetAdjustmentBehavior + self.legacyConfig = configuration.legacyConfig + self.appStartPath = configuration.appStartPath + self.appLocation = location + self.limitsNavigationsToAppBoundDomains = configuration.limitsNavigationsToAppBoundDomains + self.preferredContentMode = configuration.preferredContentMode + + super.init() + } + + @objc public func updatingAppLocation(_ location: URL) -> InstanceConfiguration { + InstanceConfiguration(with: self, andLocation: location) + } +} diff --git a/ios/Sources/Capacitor/InstanceDescriptor.swift b/ios/Sources/Capacitor/InstanceDescriptor.swift new file mode 100644 index 0000000000..89013fade4 --- /dev/null +++ b/ios/Sources/Capacitor/InstanceDescriptor.swift @@ -0,0 +1,95 @@ +// +// InstanceDescriptor.swift +// Capacitor +// +// Copyright © 2024 Drifty Co. All rights reserved. +// + +import Foundation +import UIKit +import WebKit + +@objc(CAPInstanceDescriptor) +open class InstanceDescriptor: NSObject { + // MARK: - Properties + + @objc public var appendedUserAgentString: String? + @objc public var overridenUserAgentString: String? + @objc public var backgroundColor: UIColor? + @objc public var allowedNavigationHostnames: [String] = [] + @objc public var urlScheme: String = InstanceDescriptorDefaults.scheme + @objc public var urlHostname: String = InstanceDescriptorDefaults.hostname + @objc public var serverURL: String? + @objc public var errorPath: String? + @objc public var pluginConfigurations: [String: Any] = [:] + @objc public var loggingBehavior: InstanceLoggingBehavior = .debug + @objc public var scrollingEnabled: Bool = true + @objc public var zoomingEnabled: Bool = false + @objc public var allowLinkPreviews: Bool = true + @objc public var handleApplicationNotifications: Bool = true + @objc public var isWebDebuggable: Bool = false + @objc public var hasInitialFocus: Bool = true + @objc public var contentInsetAdjustmentBehavior: UIScrollView.ContentInsetAdjustmentBehavior = .never + @objc public var appLocation: URL + @objc public var appStartPath: String? + @objc public var limitsNavigationsToAppBoundDomains: Bool = false + @objc public var preferredContentMode: String? + @objc public var cordovaConfiguration: NSObject + public var warnings: InstanceWarning = [] + public let instanceType: InstanceType + @objc public var legacyConfig: [String: Any] = [:] + + // MARK: - Initialization + + @objc public override init() { + self.instanceType = .fixed + let publicURL = Bundle.main.url(forResource: "public", withExtension: nil) + self.appLocation = publicURL ?? Bundle.main.resourceURL ?? URL(fileURLWithPath: "/") + self.cordovaConfiguration = NSObject() + + super.init() + + setDefaults(withAppLocation: publicURL) + _parseConfiguration( + at: Bundle.main.url(forResource: "capacitor.config", withExtension: "json"), + cordovaConfiguration: Bundle.main.url(forResource: "config", withExtension: "xml") + ) + } + + @objc public init(at appURL: URL, configuration configURL: URL?, cordovaConfiguration cordovaURL: URL?) { + self.instanceType = .variable + self.appLocation = appURL + self.cordovaConfiguration = NSObject() + + super.init() + + setDefaults(withAppLocation: appURL) + _parseConfiguration(at: configURL, cordovaConfiguration: cordovaURL) + } + + // MARK: - Private + + private func setDefaults(withAppLocation location: URL?) { + allowedNavigationHostnames = [] + urlScheme = InstanceDescriptorDefaults.scheme + urlHostname = InstanceDescriptorDefaults.hostname + pluginConfigurations = [:] + legacyConfig = [:] + loggingBehavior = .debug + scrollingEnabled = true + zoomingEnabled = false + allowLinkPreviews = true + handleApplicationNotifications = true + isWebDebuggable = false + hasInitialFocus = true + contentInsetAdjustmentBehavior = .never + limitsNavigationsToAppBoundDomains = false + + if let location = location { + appLocation = location + } else { + warnings.insert(.missingAppDir) + appLocation = Bundle.main.resourceURL?.appendingPathComponent("public") ?? URL(fileURLWithPath: "/") + } + } +} diff --git a/ios/Capacitor/Capacitor/JS.swift b/ios/Sources/Capacitor/JS.swift similarity index 100% rename from ios/Capacitor/Capacitor/JS.swift rename to ios/Sources/Capacitor/JS.swift diff --git a/ios/Capacitor/Capacitor/JSExport.swift b/ios/Sources/Capacitor/JSExport.swift similarity index 99% rename from ios/Capacitor/Capacitor/JSExport.swift rename to ios/Sources/Capacitor/JSExport.swift index c51da733df..35fc2a74b6 100644 --- a/ios/Capacitor/Capacitor/JSExport.swift +++ b/ios/Sources/Capacitor/JSExport.swift @@ -1,3 +1,5 @@ +import WebKit + internal struct PluginHeaderMethod: Codable { let name: String let rtype: String? diff --git a/ios/Capacitor/Capacitor/JSTypes.swift b/ios/Sources/Capacitor/JSTypes.swift similarity index 100% rename from ios/Capacitor/Capacitor/JSTypes.swift rename to ios/Sources/Capacitor/JSTypes.swift diff --git a/ios/Capacitor/Capacitor/KeyPath.swift b/ios/Sources/Capacitor/KeyPath.swift similarity index 100% rename from ios/Capacitor/Capacitor/KeyPath.swift rename to ios/Sources/Capacitor/KeyPath.swift diff --git a/ios/Capacitor/Capacitor/KeyValueStore.swift b/ios/Sources/Capacitor/KeyValueStore.swift similarity index 100% rename from ios/Capacitor/Capacitor/KeyValueStore.swift rename to ios/Sources/Capacitor/KeyValueStore.swift diff --git a/ios/Capacitor/Capacitor/NotificationHandlerProtocol.swift b/ios/Sources/Capacitor/NotificationHandlerProtocol.swift similarity index 90% rename from ios/Capacitor/Capacitor/NotificationHandlerProtocol.swift rename to ios/Sources/Capacitor/NotificationHandlerProtocol.swift index 2b559bd811..3274da0260 100644 --- a/ios/Capacitor/Capacitor/NotificationHandlerProtocol.swift +++ b/ios/Sources/Capacitor/NotificationHandlerProtocol.swift @@ -1,4 +1,5 @@ import Foundation +import UserNotifications @objc(CAPNotificationHandlerProtocol) public protocol NotificationHandlerProtocol { func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions diff --git a/ios/Capacitor/Capacitor/NotificationRouter.swift b/ios/Sources/Capacitor/NotificationRouter.swift similarity index 99% rename from ios/Capacitor/Capacitor/NotificationRouter.swift rename to ios/Sources/Capacitor/NotificationRouter.swift index 99fcbb2550..7a29babee3 100644 --- a/ios/Capacitor/Capacitor/NotificationRouter.swift +++ b/ios/Sources/Capacitor/NotificationRouter.swift @@ -1,4 +1,5 @@ import Foundation +import UserNotifications @objc(CAPNotificationRouter) public class NotificationRouter: NSObject, UNUserNotificationCenterDelegate { var handleApplicationNotifications: Bool { diff --git a/ios/Capacitor/Capacitor/PluginCallResult.swift b/ios/Sources/Capacitor/PluginCallResult.swift similarity index 100% rename from ios/Capacitor/Capacitor/PluginCallResult.swift rename to ios/Sources/Capacitor/PluginCallResult.swift diff --git a/ios/Capacitor/Capacitor/PluginConfig.swift b/ios/Sources/Capacitor/PluginConfig.swift similarity index 100% rename from ios/Capacitor/Capacitor/PluginConfig.swift rename to ios/Sources/Capacitor/PluginConfig.swift diff --git a/ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift b/ios/Sources/Capacitor/Plugins/CapacitorCookieManager.swift similarity index 100% rename from ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift rename to ios/Sources/Capacitor/Plugins/CapacitorCookieManager.swift diff --git a/ios/Capacitor/Capacitor/Plugins/CapacitorCookies.swift b/ios/Sources/Capacitor/Plugins/CapacitorCookies.swift similarity index 100% rename from ios/Capacitor/Capacitor/Plugins/CapacitorCookies.swift rename to ios/Sources/Capacitor/Plugins/CapacitorCookies.swift diff --git a/ios/Capacitor/Capacitor/Plugins/CapacitorHttp.swift b/ios/Sources/Capacitor/Plugins/CapacitorHttp.swift similarity index 100% rename from ios/Capacitor/Capacitor/Plugins/CapacitorHttp.swift rename to ios/Sources/Capacitor/Plugins/CapacitorHttp.swift diff --git a/ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift b/ios/Sources/Capacitor/Plugins/CapacitorUrlRequest.swift similarity index 100% rename from ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift rename to ios/Sources/Capacitor/Plugins/CapacitorUrlRequest.swift diff --git a/ios/Capacitor/Capacitor/Plugins/Console.swift b/ios/Sources/Capacitor/Plugins/Console.swift similarity index 100% rename from ios/Capacitor/Capacitor/Plugins/Console.swift rename to ios/Sources/Capacitor/Plugins/Console.swift diff --git a/ios/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift b/ios/Sources/Capacitor/Plugins/HttpRequestHandler.swift similarity index 100% rename from ios/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift rename to ios/Sources/Capacitor/Plugins/HttpRequestHandler.swift diff --git a/ios/Capacitor/Capacitor/Plugins/SystemBars.swift b/ios/Sources/Capacitor/Plugins/SystemBars.swift similarity index 100% rename from ios/Capacitor/Capacitor/Plugins/SystemBars.swift rename to ios/Sources/Capacitor/Plugins/SystemBars.swift diff --git a/ios/Capacitor/Capacitor/Plugins/WebView.swift b/ios/Sources/Capacitor/Plugins/WebView.swift similarity index 100% rename from ios/Capacitor/Capacitor/Plugins/WebView.swift rename to ios/Sources/Capacitor/Plugins/WebView.swift diff --git a/ios/Capacitor/Capacitor/Router.swift b/ios/Sources/Capacitor/Router.swift similarity index 100% rename from ios/Capacitor/Capacitor/Router.swift rename to ios/Sources/Capacitor/Router.swift diff --git a/ios/Capacitor/Capacitor/UIColor.swift b/ios/Sources/Capacitor/UIColor.swift similarity index 100% rename from ios/Capacitor/Capacitor/UIColor.swift rename to ios/Sources/Capacitor/UIColor.swift diff --git a/ios/Sources/Capacitor/UIStatusBarManager+CAPHandleTapAction.swift b/ios/Sources/Capacitor/UIStatusBarManager+CAPHandleTapAction.swift new file mode 100644 index 0000000000..08d3858189 --- /dev/null +++ b/ios/Sources/Capacitor/UIStatusBarManager+CAPHandleTapAction.swift @@ -0,0 +1,53 @@ +// +// UIStatusBarManager+CAPHandleTapAction.swift +// Capacitor +// +// Copyright © 2024 Drifty Co. All rights reserved. +// + +import Foundation +import UIKit +import ObjectiveC + +extension UIStatusBarManager { + private static let swizzle: Void = { + let class_ = UIStatusBarManager.self + let originalSelector = Selector(("handleTapAction:")) + let swizzledSelector = #selector(UIStatusBarManager.nofity_handleTapAction(_:)) + + guard let originalMethod = class_getInstanceMethod(class_, originalSelector), + let swizzledMethod = class_getInstanceMethod(class_, swizzledSelector) else { + return + } + + let didAddMethod = class_addMethod( + class_, + originalSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod) + ) + + if didAddMethod { + class_replaceMethod( + class_, + swizzledSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod) + ) + } else { + method_exchangeImplementations(originalMethod, swizzledMethod) + } + }() + + static func ensureSwizzling() { + _ = swizzle + } + + @objc func nofity_handleTapAction(_ arg: Any) { + NotificationCenter.default.post( + name: Notification.Name(rawValue: "CapacitorStatusBarTappedNotification"), + object: nil + ) + nofity_handleTapAction(arg) + } +} diff --git a/ios/Capacitor/Capacitor/WKWebView+Capacitor.swift b/ios/Sources/Capacitor/WKWebView+Capacitor.swift similarity index 90% rename from ios/Capacitor/Capacitor/WKWebView+Capacitor.swift rename to ios/Sources/Capacitor/WKWebView+Capacitor.swift index 6bde0d3b36..a9037a6117 100644 --- a/ios/Capacitor/Capacitor/WKWebView+Capacitor.swift +++ b/ios/Sources/Capacitor/WKWebView+Capacitor.swift @@ -17,9 +17,18 @@ public extension CapacitorExtensionTypeWrapper where T == WKWebView { } } -private var associatedKeyboardFlagHandle: UInt8 = 0 +nonisolated(unsafe) private var associatedKeyboardFlagHandle: UInt8 = 0 internal extension WKWebView { + // Automatically initialize keyboard swizzling at class load time + private static let keyboardSwizzleInitializer: Void = { + _swizzleKeyboardMethods() + }() + + static func ensureKeyboardSwizzling() { + _ = keyboardSwizzleInitializer + } + // Our lazy property can't be represented in Obj-C so we need this simple wrapper. // swiftlint:disable identifier_name @objc static func _swizzleKeyboardMethods() { diff --git a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift b/ios/Sources/Capacitor/WebViewAssetHandler.swift similarity index 99% rename from ios/Capacitor/Capacitor/WebViewAssetHandler.swift rename to ios/Sources/Capacitor/WebViewAssetHandler.swift index acc8effb74..d9488ea3c5 100644 --- a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift +++ b/ios/Sources/Capacitor/WebViewAssetHandler.swift @@ -1,4 +1,5 @@ import Foundation +import WebKit import UniformTypeIdentifiers @objc(CAPWebViewAssetHandler) diff --git a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift b/ios/Sources/Capacitor/WebViewDelegationHandler.swift similarity index 100% rename from ios/Capacitor/Capacitor/WebViewDelegationHandler.swift rename to ios/Sources/Capacitor/WebViewDelegationHandler.swift diff --git a/ios/Capacitor/Capacitor/Capacitor.modulemap b/ios/Sources/CapacitorC/Capacitor.modulemap similarity index 100% rename from ios/Capacitor/Capacitor/Capacitor.modulemap rename to ios/Sources/CapacitorC/Capacitor.modulemap diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/AppDelegate.m b/ios/Sources/CapacitorCordova/AppDelegate.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/AppDelegate.m rename to ios/Sources/CapacitorCordova/AppDelegate.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.m b/ios/Sources/CapacitorCordova/CDVCommandDelegateImpl.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.m rename to ios/Sources/CapacitorCordova/CDVCommandDelegateImpl.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVConfigParser.m b/ios/Sources/CapacitorCordova/CDVConfigParser.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVConfigParser.m rename to ios/Sources/CapacitorCordova/CDVConfigParser.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.m b/ios/Sources/CapacitorCordova/CDVInvokedUrlCommand.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.m rename to ios/Sources/CapacitorCordova/CDVInvokedUrlCommand.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin+Resources.m b/ios/Sources/CapacitorCordova/CDVPlugin+Resources.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin+Resources.m rename to ios/Sources/CapacitorCordova/CDVPlugin+Resources.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.m b/ios/Sources/CapacitorCordova/CDVPlugin.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.m rename to ios/Sources/CapacitorCordova/CDVPlugin.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginManager.m b/ios/Sources/CapacitorCordova/CDVPluginManager.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginManager.m rename to ios/Sources/CapacitorCordova/CDVPluginManager.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.m b/ios/Sources/CapacitorCordova/CDVPluginResult.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.m rename to ios/Sources/CapacitorCordova/CDVPluginResult.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVURLProtocol.m b/ios/Sources/CapacitorCordova/CDVURLProtocol.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVURLProtocol.m rename to ios/Sources/CapacitorCordova/CDVURLProtocol.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVViewController.m b/ios/Sources/CapacitorCordova/CDVViewController.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVViewController.m rename to ios/Sources/CapacitorCordova/CDVViewController.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.m b/ios/Sources/CapacitorCordova/CDVWebViewProcessPoolFactory.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.m rename to ios/Sources/CapacitorCordova/CDVWebViewProcessPoolFactory.m diff --git a/ios/CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap b/ios/Sources/CapacitorCordova/CapacitorCordova.modulemap similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap rename to ios/Sources/CapacitorCordova/CapacitorCordova.modulemap diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/NSDictionary+CordovaPreferences.m b/ios/Sources/CapacitorCordova/NSDictionary+CordovaPreferences.m similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/NSDictionary+CordovaPreferences.m rename to ios/Sources/CapacitorCordova/NSDictionary+CordovaPreferences.m diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/AppDelegate.h b/ios/Sources/CapacitorCordova/include/AppDelegate.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/AppDelegate.h rename to ios/Sources/CapacitorCordova/include/AppDelegate.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDV.h b/ios/Sources/CapacitorCordova/include/CDV.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDV.h rename to ios/Sources/CapacitorCordova/include/CDV.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailability.h b/ios/Sources/CapacitorCordova/include/CDVAvailability.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailability.h rename to ios/Sources/CapacitorCordova/include/CDVAvailability.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailabilityDeprecated.h b/ios/Sources/CapacitorCordova/include/CDVAvailabilityDeprecated.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailabilityDeprecated.h rename to ios/Sources/CapacitorCordova/include/CDVAvailabilityDeprecated.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegate.h b/ios/Sources/CapacitorCordova/include/CDVCommandDelegate.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegate.h rename to ios/Sources/CapacitorCordova/include/CDVCommandDelegate.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.h b/ios/Sources/CapacitorCordova/include/CDVCommandDelegateImpl.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.h rename to ios/Sources/CapacitorCordova/include/CDVCommandDelegateImpl.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVConfigParser.h b/ios/Sources/CapacitorCordova/include/CDVConfigParser.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVConfigParser.h rename to ios/Sources/CapacitorCordova/include/CDVConfigParser.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.h b/ios/Sources/CapacitorCordova/include/CDVInvokedUrlCommand.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.h rename to ios/Sources/CapacitorCordova/include/CDVInvokedUrlCommand.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin+Resources.h b/ios/Sources/CapacitorCordova/include/CDVPlugin+Resources.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin+Resources.h rename to ios/Sources/CapacitorCordova/include/CDVPlugin+Resources.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.h b/ios/Sources/CapacitorCordova/include/CDVPlugin.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.h rename to ios/Sources/CapacitorCordova/include/CDVPlugin.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginManager.h b/ios/Sources/CapacitorCordova/include/CDVPluginManager.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginManager.h rename to ios/Sources/CapacitorCordova/include/CDVPluginManager.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.h b/ios/Sources/CapacitorCordova/include/CDVPluginResult.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.h rename to ios/Sources/CapacitorCordova/include/CDVPluginResult.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVScreenOrientationDelegate.h b/ios/Sources/CapacitorCordova/include/CDVScreenOrientationDelegate.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVScreenOrientationDelegate.h rename to ios/Sources/CapacitorCordova/include/CDVScreenOrientationDelegate.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVURLProtocol.h b/ios/Sources/CapacitorCordova/include/CDVURLProtocol.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVURLProtocol.h rename to ios/Sources/CapacitorCordova/include/CDVURLProtocol.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVViewController.h b/ios/Sources/CapacitorCordova/include/CDVViewController.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVViewController.h rename to ios/Sources/CapacitorCordova/include/CDVViewController.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.h b/ios/Sources/CapacitorCordova/include/CDVWebViewProcessPoolFactory.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVWebViewProcessPoolFactory.h rename to ios/Sources/CapacitorCordova/include/CDVWebViewProcessPoolFactory.h diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/NSDictionary+CordovaPreferences.h b/ios/Sources/CapacitorCordova/include/NSDictionary+CordovaPreferences.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/NSDictionary+CordovaPreferences.h rename to ios/Sources/CapacitorCordova/include/NSDictionary+CordovaPreferences.h diff --git a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.h b/ios/Sources/CapacitorObjC/include/CAPInstanceConfiguration.h similarity index 100% rename from ios/Capacitor/Capacitor/CAPInstanceConfiguration.h rename to ios/Sources/CapacitorObjC/include/CAPInstanceConfiguration.h diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h b/ios/Sources/CapacitorObjC/include/CAPInstanceDescriptor.h similarity index 100% rename from ios/Capacitor/Capacitor/CAPInstanceDescriptor.h rename to ios/Sources/CapacitorObjC/include/CAPInstanceDescriptor.h diff --git a/ios/Capacitor/Capacitor/CAPPlugin.h b/ios/Sources/CapacitorObjC/include/CAPPlugin.h similarity index 100% rename from ios/Capacitor/Capacitor/CAPPlugin.h rename to ios/Sources/CapacitorObjC/include/CAPPlugin.h diff --git a/ios/Capacitor/Capacitor/CAPPluginCall.h b/ios/Sources/CapacitorObjC/include/CAPPluginCall.h similarity index 100% rename from ios/Capacitor/Capacitor/CAPPluginCall.h rename to ios/Sources/CapacitorObjC/include/CAPPluginCall.h diff --git a/ios/Capacitor/Capacitor/CAPPluginMethod.h b/ios/Sources/CapacitorObjC/include/CAPPluginMethod.h similarity index 100% rename from ios/Capacitor/Capacitor/CAPPluginMethod.h rename to ios/Sources/CapacitorObjC/include/CAPPluginMethod.h diff --git a/ios/Capacitor/Capacitor/Capacitor.h b/ios/Sources/CapacitorObjC/include/Capacitor.h similarity index 100% rename from ios/Capacitor/Capacitor/Capacitor.h rename to ios/Sources/CapacitorObjC/include/Capacitor.h From fd837967681d37572c7274af23f079ad6173a0f8 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 17 Aug 2026 10:26:36 -0500 Subject: [PATCH 03/42] Remove CapacitorObjC and CapacitorC targets --- ios/Package.swift | 51 +++--- ios/Sources/CapacitorC/Capacitor.modulemap | 8 - .../include/CAPInstanceConfiguration.h | 38 ---- .../include/CAPInstanceDescriptor.h | 167 ------------------ ios/Sources/CapacitorObjC/include/CAPPlugin.h | 59 ------- .../CapacitorObjC/include/CAPPluginCall.h | 25 --- .../CapacitorObjC/include/CAPPluginMethod.h | 36 ---- ios/Sources/CapacitorObjC/include/Capacitor.h | 15 -- 8 files changed, 25 insertions(+), 374 deletions(-) delete mode 100644 ios/Sources/CapacitorC/Capacitor.modulemap delete mode 100644 ios/Sources/CapacitorObjC/include/CAPInstanceConfiguration.h delete mode 100644 ios/Sources/CapacitorObjC/include/CAPInstanceDescriptor.h delete mode 100644 ios/Sources/CapacitorObjC/include/CAPPlugin.h delete mode 100644 ios/Sources/CapacitorObjC/include/CAPPluginCall.h delete mode 100644 ios/Sources/CapacitorObjC/include/CAPPluginMethod.h delete mode 100644 ios/Sources/CapacitorObjC/include/Capacitor.h diff --git a/ios/Package.swift b/ios/Package.swift index b353fc28e5..49e9d9c651 100644 --- a/ios/Package.swift +++ b/ios/Package.swift @@ -7,49 +7,48 @@ let package = Package( products: [ .library( name: "Capacitor", - targets: ["Capacitor", "CapacitorObjC"] + targets: ["Capacitor"] ), .library( name: "CapacitorCordova", targets: ["CapacitorCordova"] ) ], + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-testing.git", from: "0.0.0") + ], targets: [ - // Pure ObjC core utilities (no dependencies) - .target( - name: "CapacitorC", - publicHeadersPath: "include", - cSettings: [ - .define("_FORTIFY_SOURCE", to: "2") - ] - ), - - // Pure Swift public API (depends on CapacitorC) .target( name: "Capacitor", - dependencies: ["CapacitorC"], - publicHeadersPath: "include" - ), - - // Objective-C bridge layer (depends on Capacitor to import Swift headers) - .target( - name: "CapacitorObjC", - dependencies: ["Capacitor"], - publicHeadersPath: "include", - cSettings: [ - .define("_FORTIFY_SOURCE", to: "2") + resources: [.copy("assets")], + swiftSettings: [ + .swiftLanguageMode(.v5) ] ), - - // Cordova legacy ObjC target .target( name: "CapacitorCordova", + dependencies: ["Capacitor"], publicHeadersPath: "include", cSettings: [ .headerSearchPath("include"), - .define("_FORTIFY_SOURCE", to: "2") + ], + linkerSettings: [ + .linkedFramework("UIKit"), + .linkedFramework("WebKit"), + .linkedFramework("MobileCoreServices"), + .linkedFramework("CFNetwork") + ] + ), + .testTarget( + name: "CapacitorTests", + dependencies: [ + "Capacitor", + .product(name: "Testing", package: "swift-testing") + ], + resources: [ + .copy("Resources/configurations") ] ) ], - swiftLanguageModes: [.v6] + swiftLanguageModes: [.v5] ) diff --git a/ios/Sources/CapacitorC/Capacitor.modulemap b/ios/Sources/CapacitorC/Capacitor.modulemap deleted file mode 100644 index b39eb8e8d1..0000000000 --- a/ios/Sources/CapacitorC/Capacitor.modulemap +++ /dev/null @@ -1,8 +0,0 @@ -framework module Capacitor { - umbrella header "Capacitor.h" - exclude header "CAPBridgedJSTypes.h" - exclude header "CAPBridgeViewController+CDVScreenOrientationDelegate.h" - - export * - module * { export * } -} diff --git a/ios/Sources/CapacitorObjC/include/CAPInstanceConfiguration.h b/ios/Sources/CapacitorObjC/include/CAPInstanceConfiguration.h deleted file mode 100644 index 171dbb4634..0000000000 --- a/ios/Sources/CapacitorObjC/include/CAPInstanceConfiguration.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef CAPInstanceConfiguration_h -#define CAPInstanceConfiguration_h - -@import UIKit; - -@class CAPInstanceDescriptor; - -NS_SWIFT_NAME(InstanceConfiguration) -@interface CAPInstanceConfiguration: NSObject -@property (nonatomic, readonly, nullable) NSString *appendedUserAgentString; -@property (nonatomic, readonly, nullable) NSString *overridenUserAgentString; -@property (nonatomic, readonly, nullable) UIColor *backgroundColor; -@property (nonatomic, readonly, nonnull) NSArray *allowedNavigationHostnames; -@property (nonatomic, readonly, nonnull) NSURL *localURL; -@property (nonatomic, readonly, nonnull) NSURL *serverURL; -@property (nonatomic, readonly, nullable) NSString *errorPath; -@property (nonatomic, readonly, nonnull) NSDictionary *pluginConfigurations; -@property (nonatomic, readonly) BOOL loggingEnabled; -@property (nonatomic, readonly) BOOL scrollingEnabled; -@property (nonatomic, readonly) BOOL zoomingEnabled; -@property (nonatomic, readonly) BOOL allowLinkPreviews; -@property (nonatomic, readonly) BOOL handleApplicationNotifications; -@property (nonatomic, readonly) BOOL isWebDebuggable; -@property (nonatomic, readonly) BOOL hasInitialFocus; -@property (nonatomic, readonly) BOOL cordovaDeployDisabled; -@property (nonatomic, readonly) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior; -@property (nonatomic, readonly, nonnull) NSURL *appLocation; -@property (nonatomic, readonly, nullable) NSString *appStartPath; -@property (nonatomic, readonly) BOOL limitsNavigationsToAppBoundDomains; -@property (nonatomic, readonly, nullable) NSString *preferredContentMode; - -@property (nonatomic, readonly, nonnull) NSDictionary *legacyConfig DEPRECATED_MSG_ATTRIBUTE("Use direct properties instead"); - -- (instancetype _Nonnull)initWithDescriptor:(CAPInstanceDescriptor* _Nonnull)descriptor isDebug:(BOOL)debug NS_SWIFT_NAME(init(with:isDebug:)); -- (instancetype _Nonnull)updatingAppLocation:(NSURL* _Nonnull)location NS_SWIFT_NAME(updatingAppLocation(_:)); -@end - -#endif /* CAPInstanceConfiguration_h */ diff --git a/ios/Sources/CapacitorObjC/include/CAPInstanceDescriptor.h b/ios/Sources/CapacitorObjC/include/CAPInstanceDescriptor.h deleted file mode 100644 index 3ca447e6f8..0000000000 --- a/ios/Sources/CapacitorObjC/include/CAPInstanceDescriptor.h +++ /dev/null @@ -1,167 +0,0 @@ -#ifndef CAPInstanceDescriptor_h -#define CAPInstanceDescriptor_h - -@import UIKit; - - -typedef NS_ENUM(NSInteger, CAPInstanceType) { - CAPInstanceTypeFixed NS_SWIFT_NAME(fixed), - CAPInstanceTypeVariable NS_SWIFT_NAME(variable) -} NS_SWIFT_NAME(InstanceType); - -typedef NS_OPTIONS(NSUInteger, CAPInstanceWarning) { - CAPInstanceWarningMissingAppDir NS_SWIFT_NAME(missingAppDir) = 1 << 0, - CAPInstanceWarningMissingFile NS_SWIFT_NAME(missingFile) = 1 << 1, - CAPInstanceWarningInvalidFile NS_SWIFT_NAME(invalidFile) = 1 << 2, - CAPInstanceWarningMissingCordovaFile NS_SWIFT_NAME(missingCordovaFile) = 1 << 3, - CAPInstanceWarningInvalidCordovaFile NS_SWIFT_NAME(invalidCordovaFile) = 1 << 4 -} NS_SWIFT_NAME(InstanceWarning); - -typedef NS_OPTIONS(NSUInteger, CAPInstanceLoggingBehavior) { - CAPInstanceLoggingBehaviorNone NS_SWIFT_NAME(none) = 1 << 0, - CAPInstanceLoggingBehaviorDebug NS_SWIFT_NAME(debug) = 1 << 1, - CAPInstanceLoggingBehaviorProduction NS_SWIFT_NAME(production) = 1 << 2, -} NS_SWIFT_NAME(InstanceLoggingBehavior); - -extern NSString * _Nonnull const CAPInstanceDescriptorDefaultScheme NS_SWIFT_UNAVAILABLE("Use InstanceDescriptorDefaults"); -extern NSString * _Nonnull const CAPInstanceDescriptorDefaultHostname NS_SWIFT_UNAVAILABLE("Use InstanceDescriptorDefaults"); - -NS_SWIFT_NAME(InstanceDescriptor) -@interface CAPInstanceDescriptor : NSObject -/** - @brief A value to append to the @c User-Agent string. Ignored if @c overridenUserAgentString is set. - @discussion Set by @c appendUserAgent in the configuration file. - */ -@property (nonatomic, copy, nullable) NSString *appendedUserAgentString; -/** - @brief A value that will completely replace the @c User-Agent string. Overrides @c appendedUserAgentString. - @discussion Set by @c overrideUserAgent in the configuration file. - */ -@property (nonatomic, copy, nullable) NSString *overridenUserAgentString; -/** - @brief The background color to set on the web view where content is not visible. - @discussion Set by @c backgroundColor in the configuration file. - */ -@property (nonatomic, retain, nullable) UIColor *backgroundColor; -/** - @brief Hostnames to which the web view is allowed to navigate without opening an external browser. - @discussion Set by @c allowNavigation in the configuration file. - */ -@property (nonatomic, copy, nonnull) NSArray *allowedNavigationHostnames; -/** - @brief The scheme that will be used for the server URL. - @discussion Defaults to @c capacitor. Set by @c server.iosScheme in the configuration file. - */ -@property (nonatomic, copy, nullable) NSString *urlScheme; -/** - @brief The path to a local html page to display in case of errors. - @discussion Defaults to nil. - */ -@property (nonatomic, copy, nullable) NSString *errorPath; -/** - @brief The hostname that will be used for the server URL. - @discussion Defaults to @c localhost. Set by @c server.hostname in the configuration file. - */ -@property (nonatomic, copy, nullable) NSString *urlHostname; -/** - @brief The fully formed URL that will be used as the server URL. - @discussion Defaults to nil, in which case the server URL will be constructed from @c urlScheme and @c urlHostname. If set, it will override the other properties. Set by @c server.url in the configuration file. - */ -@property (nonatomic, copy, nullable) NSString *serverURL; -/** - @brief The JSON dictionary that contains the plugin-specific configuration information. - @discussion Set by @c plugins in the configuration file. - */ -@property (nonatomic, retain, nonnull) NSDictionary *pluginConfigurations; -/** - @brief The build configurations under which logging should be enabled. - @discussion Defaults to @c debug. Set by @c loggingBehavior in the configuration file. - */ -@property (nonatomic, assign) CAPInstanceLoggingBehavior loggingBehavior; -/** - @brief Whether or not the web view can scroll. - @discussion Set by @c ios.scrollEnabled in the configuration file. Corresponds to @c isScrollEnabled on WKWebView. - */ -@property (nonatomic, assign) BOOL scrollingEnabled; -/** - @brief Whether or not the web view can zoom. - @discussion Set by @c zoomEnabled in the configuration file. - */ -@property (nonatomic, assign) BOOL zoomingEnabled; -/** - @brief Whether or not the web view will preview links. - @discussion Set by @c ios.allowsLinkPreview in the configuration file. Corresponds to @c allowsLinkPreview on WKWebView. - */ -@property (nonatomic, assign) BOOL allowLinkPreviews; -/** - @brief Whether or not the Capacitor runtime will set itself as the @c UNUserNotificationCenter delegate. - @discussion Defaults to @c true. Required to be @c true for notification plugins to work correctly. Set to @c false if your application will handle notifications independently. - */ -@property (nonatomic, assign) BOOL handleApplicationNotifications; -/** - @brief Enables web debugging by setting isInspectable of @c WKWebView to @c true on iOS 16.4 and greater - @discussion Defaults to true in debug mode and false in production - */ -@property (nonatomic, assign) BOOL isWebDebuggable; -/** - @brief Whether or not the webview will have focus. - @discussion Defaults to @c true. Set by @c ios.initialFocus in the configuration file. - */ -@property (nonatomic, assign) BOOL hasInitialFocus; - -/** - @brief How the web view will inset its content - @discussion Set by @c ios.contentInset in the configuration file. Corresponds to @c contentInsetAdjustmentBehavior on WKWebView. - */ -@property (nonatomic, assign) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior; -/** - @brief The base file URL from which Capacitor will load resources - @discussion Defaults to @c public/ located at the root of the application bundle. - */ -@property (nonatomic, copy, nonnull) NSURL *appLocation; -/** - @brief The path (relative to @c appLocation) which Capacitor will use for the inital URL at launch. - @discussion Defaults to nil, in which case Capacitor will attempt to load @c index.html. - */ -@property (nonatomic, copy, nullable) NSString *appStartPath; -/** - @brief Whether or not the Capacitor WebView will limit the navigation to @c WKAppBoundDomains listed in the Info.plist. - @discussion Defaults to @c false. Set by @c ios.limitsNavigationsToAppBoundDomains in the configuration file. Required to be @c true for plugins to work if the app includes @c WKAppBoundDomains in the Info.plist. - */ -@property (nonatomic, assign) BOOL limitsNavigationsToAppBoundDomains; -/** - @brief The content mode for the web view to use when it loads and renders web content. - @discussion Defaults to @c recommended. Set by @c ios.preferredContentMode in the configuration file. - */ -@property (nonatomic, copy, nullable) NSString *preferredContentMode; -/** - @brief The parser used to load the cofiguration for Cordova plugins. - */ -@property (nonatomic, copy, nonnull) NSObject *cordovaConfiguration; -/** - @brief Warnings generated during initialization. - */ -@property (nonatomic, assign) CAPInstanceWarning warnings; -/** - @brief The type of instance. - */ -@property (nonatomic, readonly) CAPInstanceType instanceType; -/** - @brief The JSON dictionary representing the contents of the configuration file. - @warning Deprecated. Do not use. - */ -@property (nonatomic, retain, nonnull) NSDictionary *legacyConfig; -/** - @brief Initialize the descriptor with the default environment. This assumes that the application was built with the help of the Capacitor CLI and that that the web app is located inside the application bundle at @c public/. - */ -- (instancetype _Nonnull)initAsDefault NS_SWIFT_NAME(init()); -/** - @brief Initialize the descriptor for use in other contexts. The app location is the one required parameter. - @param appURL The location of the folder containing the web app. - @param configURL The location of the Capacitor configuration file. - @param cordovaURL The location of the Cordova configuration file. - */ -- (instancetype _Nonnull)initAtLocation:(NSURL* _Nonnull)appURL configuration:(NSURL* _Nullable)configURL cordovaConfiguration:(NSURL* _Nullable)cordovaURL NS_SWIFT_NAME(init(at:configuration:cordovaConfiguration:)); -@end - -#endif /* CAPInstanceDescriptor_h */ diff --git a/ios/Sources/CapacitorObjC/include/CAPPlugin.h b/ios/Sources/CapacitorObjC/include/CAPPlugin.h deleted file mode 100644 index f22d9be2a0..0000000000 --- a/ios/Sources/CapacitorObjC/include/CAPPlugin.h +++ /dev/null @@ -1,59 +0,0 @@ -#import -#import - -@protocol CAPBridgeProtocol; -@class CAPPluginCall; - -@class PluginConfig; - -@interface CAPPlugin : NSObject - -@property (nonatomic, weak, nullable) WKWebView *webView; -@property (nonatomic, weak, nullable) id bridge; -@property (nonatomic, strong, nonnull) NSString *pluginId; -@property (nonatomic, strong, nonnull) NSString *pluginName; -@property (nonatomic, strong, nullable) NSMutableDictionary*> *eventListeners; -@property (nonatomic, strong, nullable) NSMutableDictionary *> *retainedEventArguments; -@property (nonatomic, assign) BOOL shouldStringifyDatesInCalls; - -- (instancetype _Nonnull) initWithBridge:(id _Nonnull) bridge pluginId:(NSString* _Nonnull) pluginId pluginName:(NSString* _Nonnull) pluginName DEPRECATED_MSG_ATTRIBUTE("This initializer is deprecated and is not suggested for use. Any data set through this init method will be overridden when it is loaded on the bridge."); -- (void)addEventListener:(NSString* _Nonnull)eventName listener:(CAPPluginCall* _Nonnull)listener; -- (void)removeEventListener:(NSString* _Nonnull)eventName listener:(CAPPluginCall* _Nonnull)listener; -- (void)notifyListeners:(NSString* _Nonnull)eventName data:(NSDictionary* _Nullable)data; -- (void)notifyListeners:(NSString* _Nonnull)eventName data:(NSDictionary* _Nullable)data retainUntilConsumed:(BOOL)retain; -- (NSArray* _Nullable)getListeners:(NSString* _Nonnull)eventName; -- (BOOL)hasListeners:(NSString* _Nonnull)eventName; -- (void)addListener:(CAPPluginCall* _Nonnull)call; -- (void)removeListener:(CAPPluginCall* _Nonnull)call; -- (void)removeAllListeners:(CAPPluginCall* _Nonnull)call; -/** - * Default implementation of the capacitor 3.0 permission pattern - */ -- (void)checkPermissions:(CAPPluginCall* _Nonnull)call; -- (void)requestPermissions:(CAPPluginCall* _Nonnull)call; -/** - * Give the plugins a chance to take control when a URL is about to be loaded in the WebView. - * Returning true causes the WebView to abort loading the URL. - * Returning false causes the WebView to continue loading the URL. - * Returning nil will defer to the default Capacitor policy - */ -- (NSNumber* _Nullable)shouldOverrideLoad:(WKNavigationAction* _Nonnull)navigationAction; -/** - * Allows plugins to hook into and respond to the WebView's URL authentication challenge. - * Returning false will defer to the default response of [.rejectProtectionSpace](https://developer.apple.com/documentation/Foundation/URLSession/AuthChallengeDisposition/rejectProtectionSpace). - */ -- (BOOL)handleWKWebViewURLAuthenticationChallenge:(NSURLAuthenticationChallenge* _Nonnull)challenge completionHandler:(void (^_Nonnull)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler; - -// Called after init if the plugin wants to do -// some loading so the plugin author doesn't -// need to override init() --(void)load; --(NSString* _Nonnull)getId; --(BOOL)getBool:(CAPPluginCall* _Nonnull) call field:(NSString* _Nonnull)field defaultValue:(BOOL)defaultValue DEPRECATED_MSG_ATTRIBUTE("Use accessors on CAPPluginCall instead. See CAPBridgedJSTypes.h for Obj-C implementations."); --(NSString* _Nullable)getString:(CAPPluginCall* _Nonnull)call field:(NSString* _Nonnull)field defaultValue:(NSString* _Nonnull)defaultValue DEPRECATED_MSG_ATTRIBUTE("Use accessors on CAPPluginCall instead. See CAPBridgedJSTypes.h for Obj-C implementations."); --(PluginConfig* _Nonnull)getConfig; --(void)setCenteredPopover:(UIViewController* _Nonnull) vc; --(void)setCenteredPopover:(UIViewController* _Nonnull) vc size:(CGSize) size; --(BOOL)supportsPopover DEPRECATED_MSG_ATTRIBUTE("All iOS 13+ devices support popover"); - -@end diff --git a/ios/Sources/CapacitorObjC/include/CAPPluginCall.h b/ios/Sources/CapacitorObjC/include/CAPPluginCall.h deleted file mode 100644 index 129dba7ff2..0000000000 --- a/ios/Sources/CapacitorObjC/include/CAPPluginCall.h +++ /dev/null @@ -1,25 +0,0 @@ -#import - -@class CAPPluginCall; -@class CAPPluginCallResult; -@class CAPPluginCallError; - -typedef void(^CAPPluginCallSuccessHandler)(CAPPluginCallResult *result, CAPPluginCall* call); -typedef void(^CAPPluginCallErrorHandler)(CAPPluginCallError *error); - -@interface CAPPluginCall : NSObject - -@property (nonatomic, assign) BOOL isSaved DEPRECATED_MSG_ATTRIBUTE("Use 'keepAlive' instead."); -@property (nonatomic, assign) BOOL keepAlive; -@property (nonatomic, strong) NSString *callbackId; -@property (nonatomic, strong) NSString *methodName; -@property (nonatomic, strong) NSDictionary *options; -@property (nonatomic, copy) CAPPluginCallSuccessHandler successHandler; -@property (nonatomic, copy) CAPPluginCallErrorHandler errorHandler; - -- (instancetype)initWithCallbackId:(NSString *)callbackId options:(NSDictionary *)options success:(CAPPluginCallSuccessHandler)success error:(CAPPluginCallErrorHandler)error DEPRECATED_MSG_ATTRIBUTE("Specify the method name as well."); - -- (instancetype)initWithCallbackId:(NSString *)callbackId methodName:(NSString *)methodName options:(NSDictionary *)options success:(CAPPluginCallSuccessHandler)success error:(CAPPluginCallErrorHandler)error; - -- (void)save DEPRECATED_MSG_ATTRIBUTE("Use the 'keepAlive' property instead."); -@end diff --git a/ios/Sources/CapacitorObjC/include/CAPPluginMethod.h b/ios/Sources/CapacitorObjC/include/CAPPluginMethod.h deleted file mode 100644 index 8c075383b3..0000000000 --- a/ios/Sources/CapacitorObjC/include/CAPPluginMethod.h +++ /dev/null @@ -1,36 +0,0 @@ -#import "CAPPluginCall.h" -#import "CAPPlugin.h" - -typedef enum { - CAPPluginMethodArgumentNotNullable, - CAPPluginMethodArgumentNullable -} CAPPluginMethodArgumentNullability; - -typedef NSString CAPPluginReturnType; - -/** - * Represents a single argument to a plugin method. - */ -@interface CAPPluginMethodArgument : NSObject - -@property (nonatomic, copy) NSString *name; -@property (nonatomic, assign) CAPPluginMethodArgumentNullability nullability; - -- (instancetype)initWithName:(NSString *)name nullability:(CAPPluginMethodArgumentNullability)nullability type:(NSString *)type; - -@end - -/** - * Represents a method that a plugin supports, with the ability - * to compute selectors and invoke the method. - */ -@interface CAPPluginMethod : NSObject - -@property (nonatomic, assign) SEL selector; -@property (nonatomic, strong) NSString *name; // Raw method name -@property (nonatomic, strong) CAPPluginReturnType *returnType; // Return type of method (i.e. callback/promise/sync) - -- (instancetype)initWithName:(NSString *)name returnType:(CAPPluginReturnType *)returnType; -- (instancetype)initWithSelector:(SEL)selector returnType:(CAPPluginReturnType *)returnType; - -@end diff --git a/ios/Sources/CapacitorObjC/include/Capacitor.h b/ios/Sources/CapacitorObjC/include/Capacitor.h deleted file mode 100644 index 7170982981..0000000000 --- a/ios/Sources/CapacitorObjC/include/Capacitor.h +++ /dev/null @@ -1,15 +0,0 @@ -#import - -//! Project version number for bridge. -FOUNDATION_EXPORT double CapacitorVersionNumber; - -//! Project version string for bridge. -FOUNDATION_EXPORT const unsigned char CapacitorVersionString[]; - -#import -#import -#import -#import -#import -#import - From 35dae30637571043315077951b57febc04570dd7 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 17 Aug 2026 11:06:14 -0500 Subject: [PATCH 04/42] Move more CapacitorCordova sources under ios/Sources for SPM --- ios/CapacitorCordova.podspec | 10 +++++----- .../CapacitorCordova/CDVWebViewProcessPoolFactory.m | 2 +- .../CapacitorCordova/PrivacyInfo.xcprivacy | 0 .../include/CDVWebViewProcessPoolFactory.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) rename ios/{CapacitorCordova => Sources}/CapacitorCordova/PrivacyInfo.xcprivacy (100%) diff --git a/ios/CapacitorCordova.podspec b/ios/CapacitorCordova.podspec index 98f6bc3474..0701132c3a 100644 --- a/ios/CapacitorCordova.podspec +++ b/ios/CapacitorCordova.podspec @@ -16,11 +16,11 @@ 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, 16.0 - 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" - s.resource_bundles = { 'CapacitorCordova' => ["#{prefix}CapacitorCordova/CapacitorCordova/PrivacyInfo.xcprivacy"] } + s.source_files = "#{prefix}Sources/CapacitorCordova/**/*.{h,m,swift}" + s.public_header_files = "#{prefix}Sources/CapacitorCordova/Classes/Public/*.h", + "#{prefix}Sources/CapacitorCordova/CapacitorCordova.h" + s.module_map = "#{prefix}Sources/CapacitorCordova/CapacitorCordova.modulemap" + s.resource_bundles = { 'CapacitorCordova' => ["#{prefix}Sources/CapacitorCordova/PrivacyInfo.xcprivacy"] } s.requires_arc = true s.dependency 'Capacitor', s.version.to_s s.framework = 'WebKit' diff --git a/ios/Sources/CapacitorCordova/CDVWebViewProcessPoolFactory.m b/ios/Sources/CapacitorCordova/CDVWebViewProcessPoolFactory.m index f232836d07..febda12a9b 100644 --- a/ios/Sources/CapacitorCordova/CDVWebViewProcessPoolFactory.m +++ b/ios/Sources/CapacitorCordova/CDVWebViewProcessPoolFactory.m @@ -19,7 +19,7 @@ Licensed to the Apache Software Foundation (ASF) under one @import Foundation; @import WebKit; -#import +#import "CDVWebViewProcessPoolFactory.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" diff --git a/ios/CapacitorCordova/CapacitorCordova/PrivacyInfo.xcprivacy b/ios/Sources/CapacitorCordova/PrivacyInfo.xcprivacy similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/PrivacyInfo.xcprivacy rename to ios/Sources/CapacitorCordova/PrivacyInfo.xcprivacy diff --git a/ios/Sources/CapacitorCordova/include/CDVWebViewProcessPoolFactory.h b/ios/Sources/CapacitorCordova/include/CDVWebViewProcessPoolFactory.h index ba0b913599..982684c205 100644 --- a/ios/Sources/CapacitorCordova/include/CDVWebViewProcessPoolFactory.h +++ b/ios/Sources/CapacitorCordova/include/CDVWebViewProcessPoolFactory.h @@ -18,7 +18,7 @@ */ #import -#import +#import "CDVAvailabilityDeprecated.h" /** @Metadata { From 78dd7d9e17cf514662b496c6796af89753035da2 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 17 Aug 2026 12:18:10 -0500 Subject: [PATCH 05/42] Move iOS test suite into ios/Tests for SPM, drop Xcode project --- .../Capacitor.xcodeproj/project.pbxproj | 1162 ----------------- .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/xcschemes/Capacitor.xcscheme | 87 -- .../contents.xcworkspacedata | 10 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - ios/Capacitor/Capacitor/Info.plist | 24 - ios/Capacitor/Capacitor/PrivacyInfo.xcprivacy | 14 - .../Capacitor/assets/native-bridge.js | 1039 --------------- .../CapacitorTests/BridgedTypesHelper.swift | 31 - .../CapacitorTests/BridgedTypesTests.m | 46 - .../CapacitorTests/BridgedTypesTests.swift | 202 --- .../CapacitorTests-Bridging-Header.h | 5 - .../CapacitorTests/CapacitorTests.swift | 38 - .../CapacitorTests/ConfigurationTests.swift | 198 --- ios/Capacitor/CapacitorTests/Info.plist | 22 - .../CapacitorTests/JSExportTests.swift | 24 - .../CapacitorTests/JSONSerializationWrapper.h | 9 - .../CapacitorTests/JSONSerializationWrapper.m | 24 - .../CapacitorTests/PluginCallAccessorTests.m | 98 -- .../CapacitorTests/RouterTests.swift | 39 - ios/Capacitor/CodableTests/CodableTests.swift | 194 --- .../CodableTests/DataCodableTests.swift | 155 --- .../CodableTests/URLCodableTests.swift | 62 - ios/Capacitor/TestsHostApp/AppDelegate.swift | 18 - .../AccentColor.colorset/Contents.json | 11 - .../AppIcon.appiconset/Contents.json | 98 -- .../Assets.xcassets/Contents.json | 6 - .../Base.lproj/LaunchScreen.storyboard | 25 - .../TestsHostApp/Base.lproj/Main.storyboard | 24 - ios/Capacitor/TestsHostApp/Info.plist | 66 - .../TestsHostApp/SceneDelegate.swift | 5 - .../TestsHostApp/ViewController.swift | 9 - ios/Package.swift | 8 +- .../BridgedTypesCoercionTests.swift | 56 + .../CapacitorTests/BridgedTypesTests.swift | 218 ++++ ios/Tests/CapacitorTests/CodableTests.swift | 195 +++ .../CapacitorTests/ConfigurationTests.swift | 192 +++ .../CapacitorTests/DataCodableTests.swift | 148 +++ .../CapacitorTests}/DateCodableTests.swift | 98 +- ios/Tests/CapacitorTests/JSExportTests.swift | 10 + .../JSONSerializationWrapper.swift | 16 + .../CapacitorTests}/NestedCodableTests.swift | 52 +- .../NonconformingFloatCodableTests.swift | 138 +- .../PluginCallAccessorTests.swift | 76 ++ .../Resources}/configurations/bad.json | 0 .../Resources}/configurations/flat.json | 0 .../Resources}/configurations/hidinglogs.json | 0 .../Resources}/configurations/hierarchy.json | 0 .../Resources}/configurations/nonjson.json | 0 .../Resources}/configurations/server.json | 0 ios/Tests/CapacitorTests/RouterTests.swift | 27 + .../CapacitorTests}/SuperCodableTests.swift | 106 +- .../CapacitorTests/URLCodableTests.swift | 58 + 53 files changed, 1184 insertions(+), 3975 deletions(-) delete mode 100644 ios/Capacitor/Capacitor.xcodeproj/project.pbxproj delete mode 100644 ios/Capacitor/Capacitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 ios/Capacitor/Capacitor.xcodeproj/xcshareddata/xcschemes/Capacitor.xcscheme delete mode 100644 ios/Capacitor/Capacitor.xcworkspace/contents.xcworkspacedata delete mode 100644 ios/Capacitor/Capacitor.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 ios/Capacitor/Capacitor/Info.plist delete mode 100644 ios/Capacitor/Capacitor/PrivacyInfo.xcprivacy delete mode 100644 ios/Capacitor/Capacitor/assets/native-bridge.js delete mode 100644 ios/Capacitor/CapacitorTests/BridgedTypesHelper.swift delete mode 100644 ios/Capacitor/CapacitorTests/BridgedTypesTests.m delete mode 100644 ios/Capacitor/CapacitorTests/BridgedTypesTests.swift delete mode 100644 ios/Capacitor/CapacitorTests/CapacitorTests-Bridging-Header.h delete mode 100644 ios/Capacitor/CapacitorTests/CapacitorTests.swift delete mode 100644 ios/Capacitor/CapacitorTests/ConfigurationTests.swift delete mode 100644 ios/Capacitor/CapacitorTests/Info.plist delete mode 100644 ios/Capacitor/CapacitorTests/JSExportTests.swift delete mode 100644 ios/Capacitor/CapacitorTests/JSONSerializationWrapper.h delete mode 100644 ios/Capacitor/CapacitorTests/JSONSerializationWrapper.m delete mode 100644 ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m delete mode 100644 ios/Capacitor/CapacitorTests/RouterTests.swift delete mode 100644 ios/Capacitor/CodableTests/CodableTests.swift delete mode 100644 ios/Capacitor/CodableTests/DataCodableTests.swift delete mode 100644 ios/Capacitor/CodableTests/URLCodableTests.swift delete mode 100644 ios/Capacitor/TestsHostApp/AppDelegate.swift delete mode 100644 ios/Capacitor/TestsHostApp/Assets.xcassets/AccentColor.colorset/Contents.json delete mode 100644 ios/Capacitor/TestsHostApp/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 ios/Capacitor/TestsHostApp/Assets.xcassets/Contents.json delete mode 100644 ios/Capacitor/TestsHostApp/Base.lproj/LaunchScreen.storyboard delete mode 100644 ios/Capacitor/TestsHostApp/Base.lproj/Main.storyboard delete mode 100644 ios/Capacitor/TestsHostApp/Info.plist delete mode 100644 ios/Capacitor/TestsHostApp/SceneDelegate.swift delete mode 100644 ios/Capacitor/TestsHostApp/ViewController.swift create mode 100644 ios/Tests/CapacitorTests/BridgedTypesCoercionTests.swift create mode 100644 ios/Tests/CapacitorTests/BridgedTypesTests.swift create mode 100644 ios/Tests/CapacitorTests/CodableTests.swift create mode 100644 ios/Tests/CapacitorTests/ConfigurationTests.swift create mode 100644 ios/Tests/CapacitorTests/DataCodableTests.swift rename ios/{Capacitor/CodableTests => Tests/CapacitorTests}/DateCodableTests.swift (62%) create mode 100644 ios/Tests/CapacitorTests/JSExportTests.swift create mode 100644 ios/Tests/CapacitorTests/JSONSerializationWrapper.swift rename ios/{Capacitor/CodableTests => Tests/CapacitorTests}/NestedCodableTests.swift (64%) rename ios/{Capacitor/CodableTests => Tests/CapacitorTests}/NonconformingFloatCodableTests.swift (50%) create mode 100644 ios/Tests/CapacitorTests/PluginCallAccessorTests.swift rename ios/{Capacitor/TestsHostApp => Tests/CapacitorTests/Resources}/configurations/bad.json (100%) rename ios/{Capacitor/TestsHostApp => Tests/CapacitorTests/Resources}/configurations/flat.json (100%) rename ios/{Capacitor/TestsHostApp => Tests/CapacitorTests/Resources}/configurations/hidinglogs.json (100%) rename ios/{Capacitor/TestsHostApp => Tests/CapacitorTests/Resources}/configurations/hierarchy.json (100%) rename ios/{Capacitor/TestsHostApp => Tests/CapacitorTests/Resources}/configurations/nonjson.json (100%) rename ios/{Capacitor/TestsHostApp => Tests/CapacitorTests/Resources}/configurations/server.json (100%) create mode 100644 ios/Tests/CapacitorTests/RouterTests.swift rename ios/{Capacitor/CodableTests => Tests/CapacitorTests}/SuperCodableTests.swift (60%) create mode 100644 ios/Tests/CapacitorTests/URLCodableTests.swift diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj deleted file mode 100644 index ebaa6b5be1..0000000000 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1162 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 48; - objects = { - -/* 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 */; }; - 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 */; }; - 50503EE91FC08595003606DC /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50503EDF1FC08594003606DC /* Capacitor.framework */; }; - 50503EEE1FC08595003606DC /* CapacitorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50503EED1FC08595003606DC /* CapacitorTests.swift */; }; - 6214934725509C3F006C36F9 /* CAPInstanceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6214934625509C3F006C36F9 /* CAPInstanceConfiguration.swift */; }; - 621ECCB72542045900D3D615 /* CAPBridgedJSTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCB42542045900D3D615 /* CAPBridgedJSTypes.m */; }; - 621ECCB82542045900D3D615 /* CAPBridgedJSTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 621ECCB62542045900D3D615 /* CAPBridgedJSTypes.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 621ECCBC2542046400D3D615 /* JSTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCBB2542046400D3D615 /* JSTypes.swift */; }; - 621ECCC3254204B700D3D615 /* BridgedTypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCC2254204B700D3D615 /* BridgedTypesTests.swift */; }; - 621ECCC8254204BE00D3D615 /* JSONSerializationWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCC6254204BE00D3D615 /* JSONSerializationWrapper.m */; }; - 621ECCD6254205BD00D3D615 /* CAPBridgeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCD4254205BD00D3D615 /* CAPBridgeProtocol.swift */; }; - 621ECCDA254205C400D3D615 /* CapacitorBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCD9254205C400D3D615 /* CapacitorBridge.swift */; }; - 621ECCE3254206A600D3D615 /* CAPApplicationDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621ECCE2254206A600D3D615 /* CAPApplicationDelegateProxy.swift */; }; - 623D68FA254C5037002D01D1 /* KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 623D68F9254C5037002D01D1 /* KeyPath.swift */; }; - 623D6909254C6FDF002D01D1 /* CAPInstanceDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 623D6907254C6FDF002D01D1 /* CAPInstanceDescriptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 623D690A254C6FDF002D01D1 /* CAPInstanceDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 623D6908254C6FDF002D01D1 /* CAPInstanceDescriptor.m */; }; - 623D6914254C7030002D01D1 /* CAPInstanceDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 623D6913254C7030002D01D1 /* CAPInstanceDescriptor.swift */; }; - 623D691D254C7462002D01D1 /* CAPInstanceConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 623D691B254C7462002D01D1 /* CAPInstanceConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 623D691E254C7462002D01D1 /* CAPInstanceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 623D691C254C7462002D01D1 /* CAPInstanceConfiguration.m */; }; - 625AF1ED258963C700869675 /* WebViewAssetHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625AF1EC258963C700869675 /* WebViewAssetHandler.swift */; }; - 6263686025F6EC0100576C1C /* PluginCallAccessorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6263685F25F6EC0100576C1C /* PluginCallAccessorTests.m */; }; - 626D2D992613B61E0046CE81 /* hidinglogs.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 626D2D902613B4BB0046CE81 /* hidinglogs.json */; }; - 62959B162524DA7800A3D7F1 /* CAPPluginCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959AE22524DA7700A3D7F1 /* CAPPluginCall.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 62959B172524DA7800A3D7F1 /* JSExport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AE32524DA7700A3D7F1 /* JSExport.swift */; }; - 62959B192524DA7800A3D7F1 /* CAPBridgedPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959AE52524DA7700A3D7F1 /* CAPBridgedPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 62959B1A2524DA7800A3D7F1 /* CAPPluginCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AE62524DA7700A3D7F1 /* CAPPluginCall.swift */; }; - 62959B1C2524DA7800A3D7F1 /* CAPPluginMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 62959AE82524DA7700A3D7F1 /* CAPPluginMethod.m */; }; - 62959B1D2524DA7800A3D7F1 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AE92524DA7700A3D7F1 /* UIColor.swift */; }; - 62959B222524DA7800A3D7F1 /* Console.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AEF2524DA7700A3D7F1 /* Console.swift */; }; - 62959B262524DA7800A3D7F1 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AF32524DA7700A3D7F1 /* WebView.swift */; }; - 62959B302524DA7800A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 62959AFE2524DA7700A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m */; }; - 62959B312524DA7800A3D7F1 /* JS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AFF2524DA7700A3D7F1 /* JS.swift */; }; - 62959B332524DA7800A3D7F1 /* CAPPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 62959B012524DA7700A3D7F1 /* CAPPlugin.m */; }; - 62959B362524DA7800A3D7F1 /* CAPBridgeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B042524DA7700A3D7F1 /* CAPBridgeViewController.swift */; }; - 62959B382524DA7800A3D7F1 /* CAPPluginCall.m in Sources */ = {isa = PBXBuildFile; fileRef = 62959B062524DA7700A3D7F1 /* CAPPluginCall.m */; }; - 62959B392524DA7800A3D7F1 /* CapacitorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B072524DA7700A3D7F1 /* CapacitorExtension.swift */; }; - 62959B3A2524DA7800A3D7F1 /* CAPLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B082524DA7700A3D7F1 /* CAPLog.swift */; }; - 62959B3B2524DA7800A3D7F1 /* CAPPluginMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B092524DA7700A3D7F1 /* CAPPluginMethod.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 62959B3C2524DA7800A3D7F1 /* CAPBridgeDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B0A2524DA7700A3D7F1 /* CAPBridgeDelegate.swift */; }; - 62959B412524DA7800A3D7F1 /* Capacitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B0F2524DA7700A3D7F1 /* Capacitor.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 62959B422524DA7800A3D7F1 /* DocLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B102524DA7700A3D7F1 /* DocLinks.swift */; }; - 62959B432524DA7800A3D7F1 /* Data+Capacitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B112524DA7700A3D7F1 /* Data+Capacitor.swift */; }; - 62959B452524DA7800A3D7F1 /* CAPPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B132524DA7700A3D7F1 /* CAPPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 62959B472524DA7800A3D7F1 /* CAPNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B152524DA7700A3D7F1 /* CAPNotifications.swift */; }; - 6296A77E253A2E49005A202A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6296A77D253A2E49005A202A /* AppDelegate.swift */; }; - 6296A782253A2E49005A202A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6296A781253A2E49005A202A /* ViewController.swift */; }; - 6296A7A0253A2E49005A202A /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6296A7A1253A2E49005A202A /* SceneDelegate.swift */; }; - 6296A785253A2E49005A202A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6296A783253A2E49005A202A /* Main.storyboard */; }; - 6296A787253A2E49005A202A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6296A786253A2E49005A202A /* Assets.xcassets */; }; - 6296A78A253A2E49005A202A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6296A788253A2E49005A202A /* LaunchScreen.storyboard */; }; - 62A91C3425535F5700861508 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A91C3325535F5700861508 /* ConfigurationTests.swift */; }; - 62A91C3F2553710E00861508 /* nonjson.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 62A91C392553710300861508 /* nonjson.json */; }; - 62ADC0CA25CB678000E914DE /* PluginCallResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ADC0C925CB678000E914DE /* PluginCallResult.swift */; }; - 62D43AF02581817500673C24 /* WKWebView+Capacitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D43AEF2581817500673C24 /* WKWebView+Capacitor.swift */; }; - 62D43B652582A13D00673C24 /* WKWebView+Capacitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D43B642582A13D00673C24 /* WKWebView+Capacitor.m */; }; - 62E0736125535E8700BAAADB /* server.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 62E0735225535E6500BAAADB /* server.json */; }; - 62E0736225535E8700BAAADB /* bad.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 62E0735325535E6500BAAADB /* bad.json */; }; - 62E0736325535E8700BAAADB /* flat.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 62E0735425535E6500BAAADB /* flat.json */; }; - 62E0736425535E8700BAAADB /* hierarchy.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 62E0735525535E6500BAAADB /* hierarchy.json */; }; - 62E207AE2588234500A78983 /* WebViewDelegationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E207AD2588234500A78983 /* WebViewDelegationHandler.swift */; }; - 62E79C722638B23300414164 /* JSExportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E79C712638B23300414164 /* JSExportTests.swift */; }; - 62E79CD7263A178B00414164 /* native-bridge.js in Resources */ = {isa = PBXBuildFile; fileRef = 62E79C572638AF7500414164 /* native-bridge.js */; }; - 62FABD1A25AE5C01007B3814 /* Array+Capacitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */; }; - 62FABD2325AE60BA007B3814 /* BridgedTypesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */; }; - 62FABD2B25AE6182007B3814 /* BridgedTypesHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */; }; - 952707712FD9DD2D0079E5D3 /* CAPSceneDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9527076F2FD9DD260079E5D3 /* CAPSceneDelegateProxy.swift */; }; - 957BD9402E78A4A50056874C /* SystemBars.swift in Sources */ = {isa = PBXBuildFile; fileRef = 957BD93E2E78A4A20056874C /* SystemBars.swift */; }; - A327E6B628DB8B2900CA8B0A /* HttpRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A327E6B228DB8B2800CA8B0A /* HttpRequestHandler.swift */; }; - A327E6B728DB8B2900CA8B0A /* CapacitorHttp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A327E6B428DB8B2900CA8B0A /* CapacitorHttp.swift */; }; - A327E6B828DB8B2900CA8B0A /* CapacitorUrlRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A327E6B528DB8B2900CA8B0A /* CapacitorUrlRequest.swift */; }; - A38C3D7728484E76004B3680 /* CapacitorCookies.swift in Sources */ = {isa = PBXBuildFile; fileRef = A38C3D7628484E76004B3680 /* CapacitorCookies.swift */; }; - A38C3D7B2848BE6F004B3680 /* CapacitorCookieManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A38C3D7A2848BE6F004B3680 /* CapacitorCookieManager.swift */; }; - A71289E627F380A500DADDF3 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71289E527F380A500DADDF3 /* Router.swift */; }; - A71289EB27F380FD00DADDF3 /* RouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71289EA27F380FD00DADDF3 /* RouterTests.swift */; }; - A7187FD22BD1CB7D00093C45 /* CAPPluginMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7187FD12BD1CB7D00093C45 /* CAPPluginMethod.swift */; }; - A76739792B98E09700795F7B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A76739782B98E09700795F7B /* PrivacyInfo.xcprivacy */; }; - A771ADEE2C8B845000AF234D /* DateCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A771ADED2C8B845000AF234D /* DateCodableTests.swift */; }; - A771ADF12C8B909100AF234D /* URLCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A771ADF02C8B909100AF234D /* URLCodableTests.swift */; }; - A7BE62CC2B486A5400165ACB /* KeyValueStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7BE62CB2B486A5400165ACB /* KeyValueStore.swift */; }; - A7D474D52C8BA8E8005620A8 /* DataCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D474D42C8BA8E8005620A8 /* DataCodableTests.swift */; }; - A7D474D82C8BA8FD005620A8 /* NonconformingFloatCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D474D72C8BA8FD005620A8 /* NonconformingFloatCodableTests.swift */; }; - A7D8B3522B238A840003FAD6 /* JSValueEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D8B3512B238A840003FAD6 /* JSValueEncoder.swift */; }; - A7D8B3632B263B8D0003FAD6 /* NestedCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D8B3622B263B8D0003FAD6 /* NestedCodableTests.swift */; }; - A7D8B3642B263B8D0003FAD6 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50503EDF1FC08594003606DC /* Capacitor.framework */; }; - A7D8B36A2B263B990003FAD6 /* CodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D8B3562B23B2110003FAD6 /* CodableTests.swift */; }; - A7D8B36E2B2692300003FAD6 /* SuperCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D8B36D2B2692300003FAD6 /* SuperCodableTests.swift */; }; - A7D9312F2B23710300FF59A2 /* JSValueDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D9312E2B23710300FF59A2 /* JSValueDecoder.swift */; }; - A7DB03AC29B001E300888AE9 /* CAPBridgedPlugin+getMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7DB03AB29B001E300888AE9 /* CAPBridgedPlugin+getMethod.swift */; }; - A7F7EDCD291EC75C0015B73B /* CAPPlugin+LoadInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F7EDCC291EC75C0015B73B /* CAPPlugin+LoadInstance.swift */; }; - A7F7EDD5292BE8520015B73B /* CAPInstancePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F7EDD4292BE8520015B73B /* CAPInstancePlugin.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 50503EEA1FC08595003606DC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 50503ED61FC08594003606DC /* Project object */; - proxyType = 1; - remoteGlobalIDString = 50503EDE1FC08594003606DC; - remoteInfo = Avocado; - }; - 6296A796253A2EAE005A202A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 50503ED61FC08594003606DC /* Project object */; - proxyType = 1; - remoteGlobalIDString = 6296A77A253A2E49005A202A; - remoteInfo = TestsHostApp; - }; - A7D8B3652B263B8D0003FAD6 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 50503ED61FC08594003606DC /* Project object */; - proxyType = 1; - remoteGlobalIDString = 50503EDE1FC08594003606DC; - remoteInfo = Capacitor; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 622BB9C32541FE1900A5DBCA /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = configurations; - dstSubfolderSpec = 1; - files = ( - 626D2D992613B61E0046CE81 /* hidinglogs.json in CopyFiles */, - 62A91C3F2553710E00861508 /* nonjson.json in CopyFiles */, - 62E0736125535E8700BAAADB /* server.json in CopyFiles */, - 62E0736225535E8700BAAADB /* bad.json in CopyFiles */, - 62E0736325535E8700BAAADB /* flat.json in CopyFiles */, - 62E0736425535E8700BAAADB /* hierarchy.json in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* 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 = ""; }; - 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; }; - 50503EDF1FC08594003606DC /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 50503EE81FC08595003606DC /* CapacitorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CapacitorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 50503EED1FC08595003606DC /* CapacitorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorTests.swift; sourceTree = ""; }; - 6214934625509C3F006C36F9 /* CAPInstanceConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAPInstanceConfiguration.swift; sourceTree = ""; }; - 621ECCB42542045900D3D615 /* CAPBridgedJSTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CAPBridgedJSTypes.m; sourceTree = ""; }; - 621ECCB62542045900D3D615 /* CAPBridgedJSTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPBridgedJSTypes.h; sourceTree = ""; }; - 621ECCBB2542046400D3D615 /* JSTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSTypes.swift; sourceTree = ""; }; - 621ECCC2254204B700D3D615 /* BridgedTypesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BridgedTypesTests.swift; sourceTree = ""; }; - 621ECCC6254204BE00D3D615 /* JSONSerializationWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONSerializationWrapper.m; sourceTree = ""; }; - 621ECCC7254204BE00D3D615 /* JSONSerializationWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONSerializationWrapper.h; sourceTree = ""; }; - 621ECCCD254204C400D3D615 /* CapacitorTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CapacitorTests-Bridging-Header.h"; sourceTree = ""; }; - 621ECCD4254205BD00D3D615 /* CAPBridgeProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPBridgeProtocol.swift; sourceTree = ""; }; - 621ECCD9254205C400D3D615 /* CapacitorBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapacitorBridge.swift; sourceTree = ""; }; - 621ECCE2254206A600D3D615 /* CAPApplicationDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPApplicationDelegateProxy.swift; sourceTree = ""; }; - 623D68F9254C5037002D01D1 /* KeyPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPath.swift; sourceTree = ""; }; - 623D6907254C6FDF002D01D1 /* CAPInstanceDescriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAPInstanceDescriptor.h; sourceTree = ""; }; - 623D6908254C6FDF002D01D1 /* CAPInstanceDescriptor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CAPInstanceDescriptor.m; sourceTree = ""; }; - 623D6913254C7030002D01D1 /* CAPInstanceDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAPInstanceDescriptor.swift; sourceTree = ""; }; - 623D691B254C7462002D01D1 /* CAPInstanceConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAPInstanceConfiguration.h; sourceTree = ""; }; - 623D691C254C7462002D01D1 /* CAPInstanceConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CAPInstanceConfiguration.m; sourceTree = ""; }; - 625AF1EC258963C700869675 /* WebViewAssetHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewAssetHandler.swift; sourceTree = ""; }; - 6263685F25F6EC0100576C1C /* PluginCallAccessorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PluginCallAccessorTests.m; sourceTree = ""; }; - 626D2D902613B4BB0046CE81 /* hidinglogs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = hidinglogs.json; sourceTree = ""; }; - 62959AE22524DA7700A3D7F1 /* CAPPluginCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPPluginCall.h; sourceTree = ""; }; - 62959AE32524DA7700A3D7F1 /* JSExport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSExport.swift; sourceTree = ""; }; - 62959AE52524DA7700A3D7F1 /* CAPBridgedPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPBridgedPlugin.h; sourceTree = ""; }; - 62959AE62524DA7700A3D7F1 /* CAPPluginCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPPluginCall.swift; sourceTree = ""; }; - 62959AE82524DA7700A3D7F1 /* CAPPluginMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CAPPluginMethod.m; sourceTree = ""; }; - 62959AE92524DA7700A3D7F1 /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; - 62959AEF2524DA7700A3D7F1 /* Console.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Console.swift; sourceTree = ""; }; - 62959AF32524DA7700A3D7F1 /* WebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = ""; }; - 62959AFE2524DA7700A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIStatusBarManager+CAPHandleTapAction.m"; sourceTree = ""; }; - 62959AFF2524DA7700A3D7F1 /* JS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JS.swift; sourceTree = ""; }; - 62959B012524DA7700A3D7F1 /* CAPPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CAPPlugin.m; sourceTree = ""; }; - 62959B042524DA7700A3D7F1 /* CAPBridgeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPBridgeViewController.swift; sourceTree = ""; }; - 62959B062524DA7700A3D7F1 /* CAPPluginCall.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CAPPluginCall.m; sourceTree = ""; }; - 62959B072524DA7700A3D7F1 /* CapacitorExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapacitorExtension.swift; sourceTree = ""; }; - 62959B082524DA7700A3D7F1 /* CAPLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPLog.swift; sourceTree = ""; }; - 62959B092524DA7700A3D7F1 /* CAPPluginMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPPluginMethod.h; sourceTree = ""; }; - 62959B0A2524DA7700A3D7F1 /* CAPBridgeDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPBridgeDelegate.swift; sourceTree = ""; }; - 62959B0F2524DA7700A3D7F1 /* Capacitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Capacitor.h; sourceTree = ""; }; - 62959B102524DA7700A3D7F1 /* DocLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocLinks.swift; sourceTree = ""; }; - 62959B112524DA7700A3D7F1 /* Data+Capacitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Capacitor.swift"; sourceTree = ""; }; - 62959B122524DA7700A3D7F1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 62959B132524DA7700A3D7F1 /* CAPPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPPlugin.h; sourceTree = ""; }; - 62959B152524DA7700A3D7F1 /* CAPNotifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAPNotifications.swift; sourceTree = ""; }; - 62959B8225253A9500A3D7F1 /* Capacitor.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = Capacitor.modulemap; sourceTree = ""; }; - 62959BBD2526510200A3D7F1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 6296A77B253A2E49005A202A /* TestsHostApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestsHostApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 6296A77D253A2E49005A202A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 6296A781253A2E49005A202A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - 6296A7A1253A2E49005A202A /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - 6296A784253A2E49005A202A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 6296A786253A2E49005A202A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 6296A789253A2E49005A202A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 6296A78B253A2E49005A202A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 62A91C3325535F5700861508 /* ConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationTests.swift; sourceTree = ""; }; - 62A91C392553710300861508 /* nonjson.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = nonjson.json; sourceTree = ""; }; - 62ADC0C925CB678000E914DE /* PluginCallResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginCallResult.swift; sourceTree = ""; }; - 62D43AEF2581817500673C24 /* WKWebView+Capacitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WKWebView+Capacitor.swift"; sourceTree = ""; }; - 62D43B642582A13D00673C24 /* WKWebView+Capacitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WKWebView+Capacitor.m"; sourceTree = ""; }; - 62E0735225535E6500BAAADB /* server.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = server.json; sourceTree = ""; }; - 62E0735325535E6500BAAADB /* bad.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = bad.json; sourceTree = ""; }; - 62E0735425535E6500BAAADB /* flat.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = flat.json; sourceTree = ""; }; - 62E0735525535E6500BAAADB /* hierarchy.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = hierarchy.json; sourceTree = ""; }; - 62E207AD2588234500A78983 /* WebViewDelegationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewDelegationHandler.swift; sourceTree = ""; }; - 62E79C572638AF7500414164 /* native-bridge.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "native-bridge.js"; sourceTree = ""; }; - 62E79C712638B23300414164 /* JSExportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSExportTests.swift; sourceTree = ""; }; - 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Capacitor.swift"; sourceTree = ""; }; - 62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BridgedTypesTests.m; sourceTree = ""; }; - 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgedTypesHelper.swift; sourceTree = ""; }; - 9527076F2FD9DD260079E5D3 /* CAPSceneDelegateProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAPSceneDelegateProxy.swift; sourceTree = ""; }; - 957BD93E2E78A4A20056874C /* SystemBars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemBars.swift; sourceTree = ""; }; - A327E6B228DB8B2800CA8B0A /* HttpRequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpRequestHandler.swift; sourceTree = ""; }; - A327E6B428DB8B2900CA8B0A /* CapacitorHttp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapacitorHttp.swift; sourceTree = ""; }; - A327E6B528DB8B2900CA8B0A /* CapacitorUrlRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapacitorUrlRequest.swift; sourceTree = ""; }; - A38C3D7628484E76004B3680 /* CapacitorCookies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorCookies.swift; sourceTree = ""; }; - A38C3D7A2848BE6F004B3680 /* CapacitorCookieManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorCookieManager.swift; sourceTree = ""; }; - A71289E527F380A500DADDF3 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; - A71289EA27F380FD00DADDF3 /* RouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterTests.swift; sourceTree = ""; }; - A7187FD12BD1CB7D00093C45 /* CAPPluginMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAPPluginMethod.swift; sourceTree = ""; }; - A76739782B98E09700795F7B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; - A771ADED2C8B845000AF234D /* DateCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateCodableTests.swift; sourceTree = ""; }; - A771ADF02C8B909100AF234D /* URLCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLCodableTests.swift; sourceTree = ""; }; - A7BE62CB2B486A5400165ACB /* KeyValueStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyValueStore.swift; sourceTree = ""; }; - A7D474D42C8BA8E8005620A8 /* DataCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataCodableTests.swift; sourceTree = ""; }; - A7D474D72C8BA8FD005620A8 /* NonconformingFloatCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonconformingFloatCodableTests.swift; sourceTree = ""; }; - A7D8B3512B238A840003FAD6 /* JSValueEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSValueEncoder.swift; sourceTree = ""; }; - A7D8B3562B23B2110003FAD6 /* CodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableTests.swift; sourceTree = ""; }; - A7D8B3602B263B8D0003FAD6 /* CodableTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CodableTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - A7D8B3622B263B8D0003FAD6 /* NestedCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedCodableTests.swift; sourceTree = ""; }; - A7D8B36D2B2692300003FAD6 /* SuperCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuperCodableTests.swift; sourceTree = ""; }; - A7D9312E2B23710300FF59A2 /* JSValueDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSValueDecoder.swift; sourceTree = ""; }; - A7DB03AB29B001E300888AE9 /* CAPBridgedPlugin+getMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CAPBridgedPlugin+getMethod.swift"; sourceTree = ""; }; - A7F7EDCC291EC75C0015B73B /* CAPPlugin+LoadInstance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CAPPlugin+LoadInstance.swift"; sourceTree = ""; }; - A7F7EDD4292BE8520015B73B /* CAPInstancePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAPInstancePlugin.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 50503EDB1FC08594003606DC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 501CBAA71FC0A723009B0D4D /* WebKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 50503EE51FC08595003606DC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 50503EE91FC08595003606DC /* Capacitor.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6296A778253A2E49005A202A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A7D8B35D2B263B8D0003FAD6 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A7D8B3642B263B8D0003FAD6 /* Capacitor.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 501CBAA51FC0A723009B0D4D /* Frameworks */ = { - isa = PBXGroup; - children = ( - 501CBAA61FC0A723009B0D4D /* WebKit.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 50503ED51FC08594003606DC = { - isa = PBXGroup; - children = ( - 62959AE12524DA7700A3D7F1 /* Capacitor */, - 50503EEC1FC08595003606DC /* CapacitorTests */, - 6296A77C253A2E49005A202A /* TestsHostApp */, - A7D8B3612B263B8D0003FAD6 /* CodableTests */, - 50503EE01FC08594003606DC /* Products */, - 501CBAA51FC0A723009B0D4D /* Frameworks */, - ); - sourceTree = ""; - }; - 50503EE01FC08594003606DC /* Products */ = { - isa = PBXGroup; - children = ( - 50503EDF1FC08594003606DC /* Capacitor.framework */, - 50503EE81FC08595003606DC /* CapacitorTests.xctest */, - 6296A77B253A2E49005A202A /* TestsHostApp.app */, - A7D8B3602B263B8D0003FAD6 /* CodableTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 50503EEC1FC08595003606DC /* CapacitorTests */ = { - isa = PBXGroup; - children = ( - 50503EED1FC08595003606DC /* CapacitorTests.swift */, - 621ECCC2254204B700D3D615 /* BridgedTypesTests.swift */, - 62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */, - 62A91C3325535F5700861508 /* ConfigurationTests.swift */, - 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */, - 621ECCC7254204BE00D3D615 /* JSONSerializationWrapper.h */, - 621ECCC6254204BE00D3D615 /* JSONSerializationWrapper.m */, - 6263685F25F6EC0100576C1C /* PluginCallAccessorTests.m */, - 621ECCCD254204C400D3D615 /* CapacitorTests-Bridging-Header.h */, - 62E79C712638B23300414164 /* JSExportTests.swift */, - 62959BBD2526510200A3D7F1 /* Info.plist */, - A71289EA27F380FD00DADDF3 /* RouterTests.swift */, - ); - path = CapacitorTests; - sourceTree = ""; - }; - 62959AE12524DA7700A3D7F1 /* Capacitor */ = { - isa = PBXGroup; - children = ( - 9527076F2FD9DD260079E5D3 /* CAPSceneDelegateProxy.swift */, - A7D9312C2B2370EF00FF59A2 /* Codable */, - 0F8F33B127DA980A003F49D6 /* PluginConfig.swift */, - A76739782B98E09700795F7B /* PrivacyInfo.xcprivacy */, - 62959B0F2524DA7700A3D7F1 /* Capacitor.h */, - 62959B132524DA7700A3D7F1 /* CAPPlugin.h */, - 62959B012524DA7700A3D7F1 /* CAPPlugin.m */, - A7F7EDCC291EC75C0015B73B /* CAPPlugin+LoadInstance.swift */, - 62959AE52524DA7700A3D7F1 /* CAPBridgedPlugin.h */, - A7DB03AB29B001E300888AE9 /* CAPBridgedPlugin+getMethod.swift */, - 62959B092524DA7700A3D7F1 /* CAPPluginMethod.h */, - 62959AE82524DA7700A3D7F1 /* CAPPluginMethod.m */, - A7187FD12BD1CB7D00093C45 /* CAPPluginMethod.swift */, - 62959AE22524DA7700A3D7F1 /* CAPPluginCall.h */, - 62959B062524DA7700A3D7F1 /* CAPPluginCall.m */, - 62959AE62524DA7700A3D7F1 /* CAPPluginCall.swift */, - A7F7EDD4292BE8520015B73B /* CAPInstancePlugin.swift */, - 62ADC0C925CB678000E914DE /* PluginCallResult.swift */, - 621ECCBB2542046400D3D615 /* JSTypes.swift */, - 621ECCB62542045900D3D615 /* CAPBridgedJSTypes.h */, - 621ECCB42542045900D3D615 /* CAPBridgedJSTypes.m */, - 623D68F9254C5037002D01D1 /* KeyPath.swift */, - 62959AFF2524DA7700A3D7F1 /* JS.swift */, - 62959AE32524DA7700A3D7F1 /* JSExport.swift */, - 621ECCD4254205BD00D3D615 /* CAPBridgeProtocol.swift */, - 621ECCD9254205C400D3D615 /* CapacitorBridge.swift */, - 62959B042524DA7700A3D7F1 /* CAPBridgeViewController.swift */, - 621ECCE2254206A600D3D615 /* CAPApplicationDelegateProxy.swift */, - 62959B0A2524DA7700A3D7F1 /* CAPBridgeDelegate.swift */, - 62E207AD2588234500A78983 /* WebViewDelegationHandler.swift */, - 625AF1EC258963C700869675 /* WebViewAssetHandler.swift */, - 62959AEA2524DA7700A3D7F1 /* Plugins */, - 623D6907254C6FDF002D01D1 /* CAPInstanceDescriptor.h */, - 623D6908254C6FDF002D01D1 /* CAPInstanceDescriptor.m */, - 623D6913254C7030002D01D1 /* CAPInstanceDescriptor.swift */, - 623D691B254C7462002D01D1 /* CAPInstanceConfiguration.h */, - 623D691C254C7462002D01D1 /* CAPInstanceConfiguration.m */, - 6214934625509C3F006C36F9 /* CAPInstanceConfiguration.swift */, - 62959B082524DA7700A3D7F1 /* CAPLog.swift */, - 62959B102524DA7700A3D7F1 /* DocLinks.swift */, - 62959B152524DA7700A3D7F1 /* CAPNotifications.swift */, - 62959B072524DA7700A3D7F1 /* CapacitorExtension.swift */, - 62959B112524DA7700A3D7F1 /* Data+Capacitor.swift */, - 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */, - 62D43AEF2581817500673C24 /* WKWebView+Capacitor.swift */, - 62D43B642582A13D00673C24 /* WKWebView+Capacitor.m */, - 62959AE92524DA7700A3D7F1 /* UIColor.swift */, - 62959AFE2524DA7700A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m */, - 62959B122524DA7700A3D7F1 /* Info.plist */, - 62959B8225253A9500A3D7F1 /* Capacitor.modulemap */, - 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */, - 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */, - 62E79C562638AF7500414164 /* assets */, - A71289E527F380A500DADDF3 /* Router.swift */, - A7BE62CB2B486A5400165ACB /* KeyValueStore.swift */, - 0F83E884285A332D006C43CB /* AppUUID.swift */, - ); - path = Capacitor; - sourceTree = ""; - }; - 62959AEA2524DA7700A3D7F1 /* Plugins */ = { - isa = PBXGroup; - children = ( - 957BD93E2E78A4A20056874C /* SystemBars.swift */, - A327E6B428DB8B2900CA8B0A /* CapacitorHttp.swift */, - A327E6B528DB8B2900CA8B0A /* CapacitorUrlRequest.swift */, - A327E6B228DB8B2800CA8B0A /* HttpRequestHandler.swift */, - 62959AEF2524DA7700A3D7F1 /* Console.swift */, - 62959AF32524DA7700A3D7F1 /* WebView.swift */, - A38C3D7628484E76004B3680 /* CapacitorCookies.swift */, - A38C3D7A2848BE6F004B3680 /* CapacitorCookieManager.swift */, - ); - path = Plugins; - sourceTree = ""; - }; - 6296A77C253A2E49005A202A /* TestsHostApp */ = { - isa = PBXGroup; - children = ( - 6296A77D253A2E49005A202A /* AppDelegate.swift */, - 6296A7A1253A2E49005A202A /* SceneDelegate.swift */, - 6296A781253A2E49005A202A /* ViewController.swift */, - 62E0735125535E6500BAAADB /* configurations */, - 6296A783253A2E49005A202A /* Main.storyboard */, - 6296A786253A2E49005A202A /* Assets.xcassets */, - 6296A788253A2E49005A202A /* LaunchScreen.storyboard */, - 6296A78B253A2E49005A202A /* Info.plist */, - ); - path = TestsHostApp; - sourceTree = ""; - }; - 62E0735125535E6500BAAADB /* configurations */ = { - isa = PBXGroup; - children = ( - 62E0735225535E6500BAAADB /* server.json */, - 62E0735325535E6500BAAADB /* bad.json */, - 62E0735425535E6500BAAADB /* flat.json */, - 626D2D902613B4BB0046CE81 /* hidinglogs.json */, - 62E0735525535E6500BAAADB /* hierarchy.json */, - 62A91C392553710300861508 /* nonjson.json */, - ); - path = configurations; - sourceTree = ""; - }; - 62E79C562638AF7500414164 /* assets */ = { - isa = PBXGroup; - children = ( - 62E79C572638AF7500414164 /* native-bridge.js */, - ); - path = assets; - sourceTree = ""; - }; - A7D8B3612B263B8D0003FAD6 /* CodableTests */ = { - isa = PBXGroup; - children = ( - A7D8B3622B263B8D0003FAD6 /* NestedCodableTests.swift */, - A7D8B3562B23B2110003FAD6 /* CodableTests.swift */, - A7D8B36D2B2692300003FAD6 /* SuperCodableTests.swift */, - A771ADED2C8B845000AF234D /* DateCodableTests.swift */, - A771ADF02C8B909100AF234D /* URLCodableTests.swift */, - A7D474D42C8BA8E8005620A8 /* DataCodableTests.swift */, - A7D474D72C8BA8FD005620A8 /* NonconformingFloatCodableTests.swift */, - ); - path = CodableTests; - sourceTree = ""; - }; - A7D9312C2B2370EF00FF59A2 /* Codable */ = { - isa = PBXGroup; - children = ( - A7D9312E2B23710300FF59A2 /* JSValueDecoder.swift */, - A7D8B3512B238A840003FAD6 /* JSValueEncoder.swift */, - ); - path = Codable; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 50503EDC1FC08594003606DC /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 62959B412524DA7800A3D7F1 /* Capacitor.h in Headers */, - 62959B452524DA7800A3D7F1 /* CAPPlugin.h in Headers */, - 62959B162524DA7800A3D7F1 /* CAPPluginCall.h in Headers */, - 62959B3B2524DA7800A3D7F1 /* CAPPluginMethod.h in Headers */, - 623D691D254C7462002D01D1 /* CAPInstanceConfiguration.h in Headers */, - 623D6909254C6FDF002D01D1 /* CAPInstanceDescriptor.h in Headers */, - 62959B192524DA7800A3D7F1 /* CAPBridgedPlugin.h in Headers */, - 621ECCB82542045900D3D615 /* CAPBridgedJSTypes.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 50503EDE1FC08594003606DC /* Capacitor */ = { - isa = PBXNativeTarget; - buildConfigurationList = 50503EF31FC08595003606DC /* Build configuration list for PBXNativeTarget "Capacitor" */; - buildPhases = ( - 50503EDA1FC08594003606DC /* Sources */, - 50503EDB1FC08594003606DC /* Frameworks */, - 50503EDC1FC08594003606DC /* Headers */, - 50503EDD1FC08594003606DC /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Capacitor; - productName = Avocado; - productReference = 50503EDF1FC08594003606DC /* Capacitor.framework */; - productType = "com.apple.product-type.framework"; - }; - 50503EE71FC08595003606DC /* CapacitorTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 50503EF61FC08595003606DC /* Build configuration list for PBXNativeTarget "CapacitorTests" */; - buildPhases = ( - 50503EE41FC08595003606DC /* Sources */, - 50503EE51FC08595003606DC /* Frameworks */, - 50503EE61FC08595003606DC /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 50503EEB1FC08595003606DC /* PBXTargetDependency */, - 6296A797253A2EAE005A202A /* PBXTargetDependency */, - ); - name = CapacitorTests; - productName = AvocadoTests; - productReference = 50503EE81FC08595003606DC /* CapacitorTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 6296A77A253A2E49005A202A /* TestsHostApp */ = { - isa = PBXNativeTarget; - buildConfigurationList = 6296A78F253A2E49005A202A /* Build configuration list for PBXNativeTarget "TestsHostApp" */; - buildPhases = ( - 6296A777253A2E49005A202A /* Sources */, - 6296A778253A2E49005A202A /* Frameworks */, - 6296A779253A2E49005A202A /* Resources */, - 622BB9C32541FE1900A5DBCA /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = TestsHostApp; - productName = TestsHostApp; - productReference = 6296A77B253A2E49005A202A /* TestsHostApp.app */; - productType = "com.apple.product-type.application"; - }; - A7D8B35F2B263B8D0003FAD6 /* CodableTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = A7D8B3672B263B8D0003FAD6 /* Build configuration list for PBXNativeTarget "CodableTests" */; - buildPhases = ( - A7D8B35C2B263B8D0003FAD6 /* Sources */, - A7D8B35D2B263B8D0003FAD6 /* Frameworks */, - A7D8B35E2B263B8D0003FAD6 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - A7D8B3662B263B8D0003FAD6 /* PBXTargetDependency */, - ); - name = CodableTests; - productName = CodableTests; - productReference = A7D8B3602B263B8D0003FAD6 /* CodableTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 50503ED61FC08594003606DC /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1500; - LastUpgradeCheck = 1240; - ORGANIZATIONNAME = "Drifty Co."; - TargetAttributes = { - 50503EDE1FC08594003606DC = { - CreatedOnToolsVersion = 9.0; - LastSwiftMigration = 0940; - ProvisioningStyle = Automatic; - }; - 50503EE71FC08595003606DC = { - CreatedOnToolsVersion = 9.0; - LastSwiftMigration = 1200; - ProvisioningStyle = Automatic; - TestTargetID = 6296A77A253A2E49005A202A; - }; - 6296A77A253A2E49005A202A = { - CreatedOnToolsVersion = 12.0; - ProvisioningStyle = Automatic; - }; - A7D8B35F2B263B8D0003FAD6 = { - CreatedOnToolsVersion = 15.0.1; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = 50503ED91FC08594003606DC /* Build configuration list for PBXProject "Capacitor" */; - compatibilityVersion = "Xcode 8.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 50503ED51FC08594003606DC; - productRefGroup = 50503EE01FC08594003606DC /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 50503EDE1FC08594003606DC /* Capacitor */, - 50503EE71FC08595003606DC /* CapacitorTests */, - 6296A77A253A2E49005A202A /* TestsHostApp */, - A7D8B35F2B263B8D0003FAD6 /* CodableTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 50503EDD1FC08594003606DC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A76739792B98E09700795F7B /* PrivacyInfo.xcprivacy in Resources */, - 62E79CD7263A178B00414164 /* native-bridge.js in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 50503EE61FC08595003606DC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6296A779253A2E49005A202A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 6296A78A253A2E49005A202A /* LaunchScreen.storyboard in Resources */, - 6296A787253A2E49005A202A /* Assets.xcassets in Resources */, - 6296A785253A2E49005A202A /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A7D8B35E2B263B8D0003FAD6 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 50503EDA1FC08594003606DC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A38C3D7728484E76004B3680 /* CapacitorCookies.swift in Sources */, - A71289E627F380A500DADDF3 /* Router.swift in Sources */, - A327E6B828DB8B2900CA8B0A /* CapacitorUrlRequest.swift in Sources */, - A7D9312F2B23710300FF59A2 /* JSValueDecoder.swift in Sources */, - 62959B362524DA7800A3D7F1 /* CAPBridgeViewController.swift in Sources */, - 621ECCB72542045900D3D615 /* CAPBridgedJSTypes.m in Sources */, - 621ECCD6254205BD00D3D615 /* CAPBridgeProtocol.swift in Sources */, - 62D43AF02581817500673C24 /* WKWebView+Capacitor.swift in Sources */, - 62959B432524DA7800A3D7F1 /* Data+Capacitor.swift in Sources */, - 62E207AE2588234500A78983 /* WebViewDelegationHandler.swift in Sources */, - A7187FD22BD1CB7D00093C45 /* CAPPluginMethod.swift in Sources */, - 621ECCBC2542046400D3D615 /* JSTypes.swift in Sources */, - 621ECCDA254205C400D3D615 /* CapacitorBridge.swift in Sources */, - 62959B382524DA7800A3D7F1 /* CAPPluginCall.m in Sources */, - 623D690A254C6FDF002D01D1 /* CAPInstanceDescriptor.m in Sources */, - A7D8B3522B238A840003FAD6 /* JSValueEncoder.swift in Sources */, - A7F7EDD5292BE8520015B73B /* CAPInstancePlugin.swift in Sources */, - A38C3D7B2848BE6F004B3680 /* CapacitorCookieManager.swift in Sources */, - 952707712FD9DD2D0079E5D3 /* CAPSceneDelegateProxy.swift in Sources */, - 62959B1D2524DA7800A3D7F1 /* UIColor.swift in Sources */, - 62959B332524DA7800A3D7F1 /* CAPPlugin.m in Sources */, - 62959B1C2524DA7800A3D7F1 /* CAPPluginMethod.m in Sources */, - 62ADC0CA25CB678000E914DE /* PluginCallResult.swift in Sources */, - 62959B472524DA7800A3D7F1 /* CAPNotifications.swift in Sources */, - 62D43B652582A13D00673C24 /* WKWebView+Capacitor.m in Sources */, - 62959B312524DA7800A3D7F1 /* JS.swift in Sources */, - 373A69F2255C95D0000A6F44 /* NotificationRouter.swift in Sources */, - 62959B1A2524DA7800A3D7F1 /* CAPPluginCall.swift in Sources */, - 62959B302524DA7800A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m in Sources */, - 62959B392524DA7800A3D7F1 /* CapacitorExtension.swift in Sources */, - A327E6B628DB8B2900CA8B0A /* HttpRequestHandler.swift in Sources */, - 957BD9402E78A4A50056874C /* SystemBars.swift in Sources */, - 62959B422524DA7800A3D7F1 /* DocLinks.swift in Sources */, - 62FABD1A25AE5C01007B3814 /* Array+Capacitor.swift in Sources */, - A7BE62CC2B486A5400165ACB /* KeyValueStore.swift in Sources */, - 62959B172524DA7800A3D7F1 /* JSExport.swift in Sources */, - 373A69C1255C9360000A6F44 /* NotificationHandlerProtocol.swift in Sources */, - 0F83E885285A332E006C43CB /* AppUUID.swift in Sources */, - 625AF1ED258963C700869675 /* WebViewAssetHandler.swift in Sources */, - A327E6B728DB8B2900CA8B0A /* CapacitorHttp.swift in Sources */, - 0F8F33B327DA980A003F49D6 /* PluginConfig.swift in Sources */, - 62959B3C2524DA7800A3D7F1 /* CAPBridgeDelegate.swift in Sources */, - 623D691E254C7462002D01D1 /* CAPInstanceConfiguration.m in Sources */, - 623D68FA254C5037002D01D1 /* KeyPath.swift in Sources */, - 62959B222524DA7800A3D7F1 /* Console.swift in Sources */, - 62959B3A2524DA7800A3D7F1 /* CAPLog.swift in Sources */, - A7DB03AC29B001E300888AE9 /* CAPBridgedPlugin+getMethod.swift in Sources */, - 6214934725509C3F006C36F9 /* CAPInstanceConfiguration.swift in Sources */, - 623D6914254C7030002D01D1 /* CAPInstanceDescriptor.swift in Sources */, - 621ECCE3254206A600D3D615 /* CAPApplicationDelegateProxy.swift in Sources */, - A7F7EDCD291EC75C0015B73B /* CAPPlugin+LoadInstance.swift in Sources */, - 62959B262524DA7800A3D7F1 /* WebView.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 50503EE41FC08595003606DC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 62E79C722638B23300414164 /* JSExportTests.swift in Sources */, - 50503EEE1FC08595003606DC /* CapacitorTests.swift in Sources */, - 62FABD2B25AE6182007B3814 /* BridgedTypesHelper.swift in Sources */, - 621ECCC8254204BE00D3D615 /* JSONSerializationWrapper.m in Sources */, - 62A91C3425535F5700861508 /* ConfigurationTests.swift in Sources */, - 62FABD2325AE60BA007B3814 /* BridgedTypesTests.m in Sources */, - 621ECCC3254204B700D3D615 /* BridgedTypesTests.swift in Sources */, - A71289EB27F380FD00DADDF3 /* RouterTests.swift in Sources */, - 6263686025F6EC0100576C1C /* PluginCallAccessorTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6296A777253A2E49005A202A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 6296A782253A2E49005A202A /* ViewController.swift in Sources */, - 6296A77E253A2E49005A202A /* AppDelegate.swift in Sources */, - 6296A7A0253A2E49005A202A /* SceneDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A7D8B35C2B263B8D0003FAD6 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A771ADEE2C8B845000AF234D /* DateCodableTests.swift in Sources */, - A7D8B3632B263B8D0003FAD6 /* NestedCodableTests.swift in Sources */, - A7D474D52C8BA8E8005620A8 /* DataCodableTests.swift in Sources */, - A7D8B36A2B263B990003FAD6 /* CodableTests.swift in Sources */, - A7D8B36E2B2692300003FAD6 /* SuperCodableTests.swift in Sources */, - A7D474D82C8BA8FD005620A8 /* NonconformingFloatCodableTests.swift in Sources */, - A771ADF12C8B909100AF234D /* URLCodableTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 50503EEB1FC08595003606DC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 50503EDE1FC08594003606DC /* Capacitor */; - targetProxy = 50503EEA1FC08595003606DC /* PBXContainerItemProxy */; - }; - 6296A797253A2EAE005A202A /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 6296A77A253A2E49005A202A /* TestsHostApp */; - targetProxy = 6296A796253A2EAE005A202A /* PBXContainerItemProxy */; - }; - A7D8B3662B263B8D0003FAD6 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 50503EDE1FC08594003606DC /* Capacitor */; - targetProxy = A7D8B3652B263B8D0003FAD6 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 6296A783253A2E49005A202A /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 6296A784253A2E49005A202A /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 6296A788253A2E49005A202A /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 6296A789253A2E49005A202A /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 50503EF11FC08595003606DC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 50503EF21FC08595003606DC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 5.0; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 50503EF41FC08595003606DC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Automatic; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)", - ); - INFOPLIST_FILE = Capacitor/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = Capacitor/Capacitor.modulemap; - PRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.Capacitor; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTS_MACCATALYST = YES; - SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 50503EF51FC08595003606DC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Automatic; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)", - ); - INFOPLIST_FILE = Capacitor/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = Capacitor/Capacitor.modulemap; - PRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.Capacitor; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTS_MACCATALYST = YES; - SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - 50503EF71FC08595003606DC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = CapacitorTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.CapacitorTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "CapacitorTests/CapacitorTests-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestsHostApp.app/TestsHostApp"; - }; - name = Debug; - }; - 50503EF81FC08595003606DC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = CapacitorTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.CapacitorTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "CapacitorTests/CapacitorTests-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestsHostApp.app/TestsHostApp"; - }; - name = Release; - }; - 6296A78C253A2E49005A202A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = TestsHostApp/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.TestsHostApp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 6296A78D253A2E49005A202A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = TestsHostApp/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.TestsHostApp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - A7D8B3682B263B8D0003FAD6 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.CodableTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - A7D8B3692B263B8D0003FAD6 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.CodableTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 50503ED91FC08594003606DC /* Build configuration list for PBXProject "Capacitor" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 50503EF11FC08595003606DC /* Debug */, - 50503EF21FC08595003606DC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 50503EF31FC08595003606DC /* Build configuration list for PBXNativeTarget "Capacitor" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 50503EF41FC08595003606DC /* Debug */, - 50503EF51FC08595003606DC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 50503EF61FC08595003606DC /* Build configuration list for PBXNativeTarget "CapacitorTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 50503EF71FC08595003606DC /* Debug */, - 50503EF81FC08595003606DC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 6296A78F253A2E49005A202A /* Build configuration list for PBXNativeTarget "TestsHostApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6296A78C253A2E49005A202A /* Debug */, - 6296A78D253A2E49005A202A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A7D8B3672B263B8D0003FAD6 /* Build configuration list for PBXNativeTarget "CodableTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A7D8B3682B263B8D0003FAD6 /* Debug */, - A7D8B3692B263B8D0003FAD6 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 50503ED61FC08594003606DC /* Project object */; -} diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Capacitor/Capacitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/ios/Capacitor/Capacitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/Capacitor/Capacitor.xcodeproj/xcshareddata/xcschemes/Capacitor.xcscheme b/ios/Capacitor/Capacitor.xcodeproj/xcshareddata/xcschemes/Capacitor.xcscheme deleted file mode 100644 index ed774e08e0..0000000000 --- a/ios/Capacitor/Capacitor.xcodeproj/xcshareddata/xcschemes/Capacitor.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Capacitor/Capacitor.xcworkspace/contents.xcworkspacedata b/ios/Capacitor/Capacitor.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 37b6585fc3..0000000000 --- a/ios/Capacitor/Capacitor.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/ios/Capacitor/Capacitor.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Capacitor/Capacitor.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/ios/Capacitor/Capacitor.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/Capacitor/Capacitor/Info.plist b/ios/Capacitor/Capacitor/Info.plist deleted file mode 100644 index 1007fd9dd7..0000000000 --- a/ios/Capacitor/Capacitor/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/ios/Capacitor/Capacitor/PrivacyInfo.xcprivacy b/ios/Capacitor/Capacitor/PrivacyInfo.xcprivacy deleted file mode 100644 index a1f9119d1f..0000000000 --- a/ios/Capacitor/Capacitor/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,14 +0,0 @@ - - - - - NSPrivacyAccessedAPITypes - - NSPrivacyCollectedDataTypes - - NSPrivacyTrackingDomains - - NSPrivacyTracking - - - diff --git a/ios/Capacitor/Capacitor/assets/native-bridge.js b/ios/Capacitor/Capacitor/assets/native-bridge.js deleted file mode 100644 index f5e7cc4403..0000000000 --- a/ios/Capacitor/Capacitor/assets/native-bridge.js +++ /dev/null @@ -1,1039 +0,0 @@ - -/*! Capacitor: https://capacitorjs.com/ - MIT License */ -/* Generated File. Do not edit. */ - -var nativeBridge = (function (exports) { - 'use strict'; - - var ExceptionCode; - (function (ExceptionCode) { - /** - * API is not implemented. - * - * This usually means the API can't be used because it is not implemented for - * the current platform. - */ - ExceptionCode["Unimplemented"] = "UNIMPLEMENTED"; - /** - * API is not available. - * - * This means the API can't be used right now because: - * - it is currently missing a prerequisite, such as network connectivity - * - it requires a particular platform or browser version - */ - ExceptionCode["Unavailable"] = "UNAVAILABLE"; - })(ExceptionCode || (ExceptionCode = {})); - class CapacitorException extends Error { - constructor(message, code, data) { - super(message); - this.message = message; - this.code = code; - this.data = data; - } - } - - // For removing exports for iOS/Android, keep let for reassignment - // eslint-disable-next-line - let dummy = {}; - const readFileAsBase64 = (file) => new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onloadend = () => { - const data = reader.result; - resolve(btoa(data)); - }; - reader.onerror = reject; - reader.readAsBinaryString(file); - }); - const convertFormData = async (formData) => { - const newFormData = []; - for (const pair of formData.entries()) { - const [key, value] = pair; - if (value instanceof File) { - const base64File = await readFileAsBase64(value); - newFormData.push({ - key, - value: base64File, - type: 'base64File', - contentType: value.type, - fileName: value.name, - }); - } - else { - newFormData.push({ key, value, type: 'string' }); - } - } - return newFormData; - }; - const convertBody = async (body, contentType) => { - if (body instanceof ReadableStream || body instanceof Uint8Array) { - let encodedData; - if (body instanceof ReadableStream) { - const reader = body.getReader(); - const chunks = []; - while (true) { - const { done, value } = await reader.read(); - if (done) - break; - chunks.push(value); - } - const concatenated = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0)); - let position = 0; - for (const chunk of chunks) { - concatenated.set(chunk, position); - position += chunk.length; - } - encodedData = concatenated; - } - else { - encodedData = body; - } - let data = new TextDecoder().decode(encodedData); - let type; - if (contentType === 'application/json') { - try { - data = JSON.parse(data); - } - catch (ignored) { - // ignore - } - type = 'json'; - } - else if (contentType === 'multipart/form-data') { - type = 'formData'; - } - else if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) { - type = 'image'; - } - else if (contentType === 'application/octet-stream') { - type = 'binary'; - } - else { - type = 'text'; - } - return { - data, - type, - headers: { 'Content-Type': contentType || 'application/octet-stream' }, - }; - } - else if (body instanceof URLSearchParams) { - return { - data: body.toString(), - type: 'text', - }; - } - else if (body instanceof FormData) { - return { - data: await convertFormData(body), - type: 'formData', - }; - } - else if (body instanceof File) { - const fileData = await readFileAsBase64(body); - return { - data: fileData, - type: 'file', - headers: { 'Content-Type': body.type }, - }; - } - return { data: body, type: 'json' }; - }; - const CAPACITOR_HTTP_INTERCEPTOR = '/_capacitor_http_interceptor_'; - const CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM = 'u'; - // TODO: export as Cap function - const isRelativeOrProxyUrl = (url) => !url || !(url.startsWith('http:') || url.startsWith('https:')) || url.indexOf(CAPACITOR_HTTP_INTERCEPTOR) > -1; - // TODO: export as Cap function - const createProxyUrl = (url, win) => { - var _a, _b; - if (isRelativeOrProxyUrl(url)) - return url; - const bridgeUrl = new URL((_b = (_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.getServerUrl()) !== null && _b !== void 0 ? _b : ''); - bridgeUrl.pathname = CAPACITOR_HTTP_INTERCEPTOR; - bridgeUrl.searchParams.append(CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM, url); - return bridgeUrl.toString(); - }; - const initBridge = (w) => { - const getPlatformId = (win) => { - var _a, _b; - if (win === null || win === void 0 ? void 0 : win.androidBridge) { - return 'android'; - } - else if ((_b = (_a = win === null || win === void 0 ? void 0 : win.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge) { - return 'ios'; - } - else { - return 'web'; - } - }; - const convertFileSrcServerUrl = (webviewServerUrl, filePath) => { - if (typeof filePath === 'string') { - if (filePath.startsWith('/')) { - return webviewServerUrl + '/_capacitor_file_' + filePath; - } - else if (filePath.startsWith('file://')) { - return webviewServerUrl + filePath.replace('file://', '/_capacitor_file_'); - } - else if (filePath.startsWith('content://')) { - return webviewServerUrl + filePath.replace('content:/', '/_capacitor_content_'); - } - } - return filePath; - }; - const initEvents = (win, cap) => { - cap.addListener = (pluginName, eventName, callback) => { - const callbackId = cap.nativeCallback(pluginName, 'addListener', { - eventName: eventName, - }, callback); - return { - remove: async () => { - var _a; - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.debug('Removing listener', pluginName, eventName); - cap.removeListener(pluginName, callbackId, eventName, callback); - }, - }; - }; - cap.removeListener = (pluginName, callbackId, eventName, callback) => { - cap.nativeCallback(pluginName, 'removeListener', { - callbackId: callbackId, - eventName: eventName, - }, callback); - }; - cap.createEvent = (eventName, eventData) => { - const doc = win.document; - if (doc) { - const ev = doc.createEvent('Events'); - ev.initEvent(eventName, false, false); - if (eventData && typeof eventData === 'object') { - for (const i in eventData) { - // eslint-disable-next-line no-prototype-builtins - if (eventData.hasOwnProperty(i)) { - ev[i] = eventData[i]; - } - } - } - return ev; - } - return null; - }; - cap.triggerEvent = (eventName, target, eventData) => { - const doc = win.document; - const cordova = win.cordova; - eventData = eventData || {}; - const ev = cap.createEvent(eventName, eventData); - if (ev) { - if (target === 'document') { - if (cordova === null || cordova === void 0 ? void 0 : cordova.fireDocumentEvent) { - cordova.fireDocumentEvent(eventName, eventData); - return true; - } - else if (doc === null || doc === void 0 ? void 0 : doc.dispatchEvent) { - return doc.dispatchEvent(ev); - } - } - else if (target === 'window' && win.dispatchEvent) { - return win.dispatchEvent(ev); - } - else if (doc === null || doc === void 0 ? void 0 : doc.querySelector) { - const targetEl = doc.querySelector(target); - if (targetEl) { - return targetEl.dispatchEvent(ev); - } - } - } - return false; - }; - win.Capacitor = cap; - }; - const initLegacyHandlers = (win, cap) => { - // define cordova if it's not there already - win.cordova = win.cordova || {}; - const doc = win.document; - const nav = win.navigator; - if (nav) { - nav.app = nav.app || {}; - nav.app.exitApp = () => { - var _a; - if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { - win.console.warn('App plugin not installed'); - } - else { - cap.nativeCallback('App', 'exitApp', {}); - } - }; - } - if (doc) { - const docAddEventListener = doc.addEventListener; - doc.addEventListener = (...args) => { - var _a; - const eventName = args[0]; - const handler = args[1]; - if (eventName === 'deviceready' && handler) { - Promise.resolve().then(handler); - } - else if (eventName === 'backbutton' && cap.Plugins.App) { - // Add a dummy listener so Capacitor doesn't do the default - // back button action - if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { - win.console.warn('App plugin not installed'); - } - else { - cap.Plugins.App.addListener('backButton', () => { - // ignore - }); - } - } - return docAddEventListener.apply(doc, args); - }; - } - win.Capacitor = cap; - }; - const initVendor = (win, cap) => { - const Ionic = (win.Ionic = win.Ionic || {}); - const IonicWebView = (Ionic.WebView = Ionic.WebView || {}); - const Plugins = cap.Plugins; - IonicWebView.getServerBasePath = (callback) => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.getServerBasePath().then((result) => { - callback(result.path); - }); - }; - IonicWebView.setServerAssetPath = (path) => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerAssetPath({ path }); - }; - IonicWebView.setServerBasePath = (path) => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerBasePath({ path }); - }; - IonicWebView.persistServerBasePath = () => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.persistServerBasePath(); - }; - IonicWebView.convertFileSrc = (url) => cap.convertFileSrc(url); - win.Capacitor = cap; - win.Ionic.WebView = IonicWebView; - }; - const initLogger = (win, cap) => { - const BRIDGED_CONSOLE_METHODS = ['debug', 'error', 'info', 'log', 'trace', 'warn']; - const createLogFromNative = (c) => (result) => { - if (isFullConsole(c)) { - const success = result.success === true; - const tagStyles = success - ? 'font-style: italic; font-weight: lighter; color: gray' - : 'font-style: italic; font-weight: lighter; color: red'; - c.groupCollapsed('%cresult %c' + result.pluginId + '.' + result.methodName + ' (#' + result.callbackId + ')', tagStyles, 'font-style: italic; font-weight: bold; color: #444'); - if (result.success === false) { - c.error(result.error); - } - else { - c.dir(JSON.stringify(result.data)); - } - c.groupEnd(); - } - else { - if (result.success === false) { - c.error('LOG FROM NATIVE', result.error); - } - else { - c.log('LOG FROM NATIVE', result.data); - } - } - }; - const createLogToNative = (c) => (call) => { - if (isFullConsole(c)) { - c.groupCollapsed('%cnative %c' + call.pluginId + '.' + call.methodName + ' (#' + call.callbackId + ')', 'font-weight: lighter; color: gray', 'font-weight: bold; color: #000'); - c.dir(call); - c.groupEnd(); - } - else { - c.log('LOG TO NATIVE: ', call); - } - }; - const isFullConsole = (c) => { - if (!c) { - return false; - } - return typeof c.groupCollapsed === 'function' || typeof c.groupEnd === 'function' || typeof c.dir === 'function'; - }; - const serializeConsoleMessage = (msg) => { - try { - if (typeof msg === 'object') { - msg = JSON.stringify(msg); - } - return String(msg); - } - catch (e) { - return ''; - } - }; - const platform = getPlatformId(win); - if (platform == 'android' && typeof win.CapacitorSystemBarsAndroidInterface !== 'undefined') { - // add DOM ready listener for System Bars - document.addEventListener('DOMContentLoaded', function () { - win.CapacitorSystemBarsAndroidInterface.onDOMReady(); - }); - } - if (platform == 'android' || platform == 'ios') { - // patch document.cookie on Android/iOS - win.CapacitorCookiesDescriptor = - Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') || - Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie'); - let doPatchCookies = false; - // check if capacitor cookies is disabled before patching - if (platform === 'ios') { - // Use prompt to synchronously get capacitor cookies config. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorCookies.isEnabled', - }; - const isCookiesEnabled = prompt(JSON.stringify(payload)); - if (isCookiesEnabled === 'true') { - doPatchCookies = true; - } - } - else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { - const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled(); - if (isCookiesEnabled === true) { - doPatchCookies = true; - } - } - if (doPatchCookies) { - Object.defineProperty(document, 'cookie', { - get: function () { - var _a, _b, _c; - if (platform === 'ios') { - // Use prompt to synchronously get cookies. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorCookies.get', - }; - const res = prompt(JSON.stringify(payload)); - return res; - } - else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { - // return original document.cookie since Android does not support filtering of `httpOnly` cookies - return (_c = (_b = (_a = win.CapacitorCookiesDescriptor) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(document)) !== null && _c !== void 0 ? _c : ''; - } - }, - set: function (val) { - const cookiePairs = val.split(';'); - const domainSection = val.toLowerCase().split('domain=')[1]; - const domain = cookiePairs.length > 1 && domainSection != null && domainSection.length > 0 - ? domainSection.split(';')[0].trim() - : ''; - if (platform === 'ios') { - // Use prompt to synchronously set cookies. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorCookies.set', - action: val, - domain, - }; - prompt(JSON.stringify(payload)); - } - else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { - win.CapacitorCookiesAndroidInterface.setCookie(domain, val); - } - }, - }); - } - // patch fetch / XHR on Android/iOS - // store original fetch & XHR functions - win.CapacitorWebFetch = window.fetch; - win.CapacitorWebXMLHttpRequest = { - abort: window.XMLHttpRequest.prototype.abort, - constructor: window.XMLHttpRequest.prototype.constructor, - fullObject: window.XMLHttpRequest, - getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, - getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, - open: window.XMLHttpRequest.prototype.open, - prototype: window.XMLHttpRequest.prototype, - send: window.XMLHttpRequest.prototype.send, - setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, - }; - let doPatchHttp = false; - // check if capacitor http is disabled before patching - if (platform === 'ios') { - // Use prompt to synchronously get capacitor http config. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorHttp', - }; - const isHttpEnabled = prompt(JSON.stringify(payload)); - if (isHttpEnabled === 'true') { - doPatchHttp = true; - } - } - else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') { - const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled(); - if (isHttpEnabled === true) { - doPatchHttp = true; - } - } - if (doPatchHttp) { - // fetch patch - window.fetch = async (resource, options) => { - const headers = new Headers(options === null || options === void 0 ? void 0 : options.headers); - const contentType = headers.get('Content-Type') || headers.get('content-type'); - if ((options === null || options === void 0 ? void 0 : options.body) instanceof FormData && - (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/form-data')) && - !contentType.includes('boundary')) { - headers.delete('Content-Type'); - headers.delete('content-type'); - options.headers = headers; - } - const request = new Request(resource, options); - if (request.url.startsWith(`${cap.getServerUrl()}/`)) { - return win.CapacitorWebFetch(resource, options); - } - const { method } = request; - if (method.toLocaleUpperCase() === 'GET' || - method.toLocaleUpperCase() === 'HEAD' || - method.toLocaleUpperCase() === 'OPTIONS' || - method.toLocaleUpperCase() === 'TRACE') { - // a workaround for following android webview issue: - // https://issues.chromium.org/issues/40450316 - // Sets the user-agent header to a custom value so that its not stripped - // on its way to the native layer - if (platform === 'android' && (options === null || options === void 0 ? void 0 : options.headers)) { - const userAgent = headers.get('User-Agent') || headers.get('user-agent'); - if (userAgent !== null) { - headers.set('x-cap-user-agent', userAgent); - options.headers = headers; - } - } - if (typeof resource === 'string') { - return await win.CapacitorWebFetch(createProxyUrl(resource, win), options); - } - else if (resource instanceof URL) { - const modifiedURL = new URL(createProxyUrl(resource.toString(), win)); - return await win.CapacitorWebFetch(modifiedURL, options); - } - else if (resource instanceof Request) { - const modifiedRequest = new Request(createProxyUrl(resource.url, win), resource); - return await win.CapacitorWebFetch(modifiedRequest, options); - } - } - const tag = `CapacitorHttp fetch ${Date.now()} ${resource}`; - console.time(tag); - try { - const { body } = request; - const optionHeaders = Object.fromEntries(request.headers.entries()); - const { data: requestData, type, headers: requestHeaders, } = await convertBody((options === null || options === void 0 ? void 0 : options.body) || body || undefined, optionHeaders['Content-Type'] || optionHeaders['content-type']); - const nativeHeaders = Object.assign(Object.assign({}, requestHeaders), optionHeaders); - if (platform === 'android') { - if (headers.has('User-Agent')) { - nativeHeaders['User-Agent'] = headers.get('User-Agent'); - } - if (headers.has('user-agent')) { - nativeHeaders['user-agent'] = headers.get('user-agent'); - } - } - const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', { - url: request.url, - method: method, - data: requestData, - dataType: type, - headers: nativeHeaders, - }); - const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type']; - let data = (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json')) - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - // use null data for 204 No Content HTTP response - if (nativeResponse.status === 204) { - data = null; - } - // intercept & parse response before returning - const response = new Response(data, { - headers: nativeResponse.headers, - status: nativeResponse.status, - }); - /* - * copy url to response, `cordova-plugin-ionic` uses this url from the response - * we need `Object.defineProperty` because url is an inherited getter on the Response - * see: https://stackoverflow.com/a/57382543 - * */ - Object.defineProperty(response, 'url', { - value: nativeResponse.url, - }); - console.timeEnd(tag); - return response; - } - catch (error) { - console.timeEnd(tag); - return Promise.reject(error); - } - }; - window.XMLHttpRequest = function () { - const xhr = new win.CapacitorWebXMLHttpRequest.constructor(); - Object.defineProperties(xhr, { - _headers: { - value: {}, - writable: true, - }, - _method: { - value: xhr.method, - writable: true, - }, - }); - const prototype = win.CapacitorWebXMLHttpRequest.prototype; - const isProgressEventAvailable = () => typeof ProgressEvent !== 'undefined' && ProgressEvent.prototype instanceof Event; - // XHR patch abort - prototype.abort = function () { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.abort.call(this); - } - this.readyState = 0; - setTimeout(() => { - this.dispatchEvent(new Event('abort')); - this.dispatchEvent(new Event('loadend')); - }); - }; - // XHR patch open - prototype.open = function (method, url) { - this._method = method.toLocaleUpperCase(); - this._url = url; - if (!this._method || - this._method === 'GET' || - this._method === 'HEAD' || - this._method === 'OPTIONS' || - this._method === 'TRACE') { - if (isRelativeOrProxyUrl(url)) { - return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); - } - this._url = createProxyUrl(this._url, win); - return win.CapacitorWebXMLHttpRequest.open.call(this, method, this._url); - } - Object.defineProperties(this, { - readyState: { - get: function () { - var _a; - return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0; - }, - set: function (val) { - this._readyState = val; - setTimeout(() => { - this.dispatchEvent(new Event('readystatechange')); - }); - }, - }, - }); - setTimeout(() => { - this.dispatchEvent(new Event('loadstart')); - }); - this.readyState = 1; - }; - // XHR patch set request header - prototype.setRequestHeader = function (header, value) { - // a workaround for the following android web view issue: - // https://issues.chromium.org/issues/40450316 - // Sets the user-agent header to a custom value so that its not stripped - // on its way to the native layer - if (platform === 'android' && (header === 'User-Agent' || header === 'user-agent')) { - header = 'x-cap-user-agent'; - } - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); - } - this._headers[header] = value; - }; - // XHR patch send - prototype.send = function (body) { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.send.call(this, body); - } - const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; - console.time(tag); - try { - this.readyState = 2; - Object.defineProperties(this, { - response: { - value: '', - writable: true, - }, - responseText: { - value: '', - writable: true, - }, - responseURL: { - value: '', - writable: true, - }, - status: { - value: 0, - writable: true, - }, - }); - convertBody(body).then(({ data, type, headers }) => { - let otherHeaders = this._headers != null && Object.keys(this._headers).length > 0 ? this._headers : undefined; - if (body instanceof FormData) { - if (!this._headers['Content-Type'] && !this._headers['content-type']) { - otherHeaders = Object.assign(Object.assign({}, otherHeaders), { 'Content-Type': `multipart/form-data; boundary=----WebKitFormBoundary${Math.random().toString(36).substring(2, 15)}` }); - } - } - // intercept request & pass to the bridge - cap - .nativePromise('CapacitorHttp', 'request', { - url: this._url, - method: this._method, - data: data !== null ? data : undefined, - headers: Object.assign(Object.assign({}, headers), otherHeaders), - dataType: type, - }) - .then((nativeResponse) => { - var _a; - // intercept & parse response before returning - if (this.readyState == 2) { - //TODO: Add progress event emission on native side - if (isProgressEventAvailable()) { - this.dispatchEvent(new ProgressEvent('progress', { - lengthComputable: true, - loaded: nativeResponse.data.length, - total: nativeResponse.data.length, - })); - } - this._headers = nativeResponse.headers; - this.status = nativeResponse.status; - if (this.responseType === '' || this.responseType === 'text') { - this.response = - typeof nativeResponse.data !== 'string' - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - } - else { - this.response = nativeResponse.data; - } - this.responseText = ((_a = (nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type'])) === null || _a === void 0 ? void 0 : _a.startsWith('application/json')) - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - this.responseURL = nativeResponse.url; - this.readyState = 4; - setTimeout(() => { - this.dispatchEvent(new Event('load')); - this.dispatchEvent(new Event('loadend')); - }); - } - console.timeEnd(tag); - }) - .catch((error) => { - this.status = error.status; - this._headers = error.headers; - this.response = error.data; - this.responseText = JSON.stringify(error.data); - this.responseURL = error.url; - this.readyState = 4; - if (isProgressEventAvailable()) { - this.dispatchEvent(new ProgressEvent('progress', { - lengthComputable: false, - loaded: 0, - total: 0, - })); - } - setTimeout(() => { - this.dispatchEvent(new Event('error')); - this.dispatchEvent(new Event('loadend')); - }); - console.timeEnd(tag); - }); - }); - } - catch (error) { - this.status = 500; - this._headers = {}; - this.response = error; - this.responseText = error.toString(); - this.responseURL = this._url; - this.readyState = 4; - if (isProgressEventAvailable()) { - this.dispatchEvent(new ProgressEvent('progress', { - lengthComputable: false, - loaded: 0, - total: 0, - })); - } - setTimeout(() => { - this.dispatchEvent(new Event('error')); - this.dispatchEvent(new Event('loadend')); - }); - console.timeEnd(tag); - } - }; - // XHR patch getAllResponseHeaders - prototype.getAllResponseHeaders = function () { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); - } - let returnString = ''; - for (const key in this._headers) { - if (key != 'Set-Cookie') { - returnString += key + ': ' + this._headers[key] + '\r\n'; - } - } - return returnString; - }; - // XHR patch getResponseHeader - prototype.getResponseHeader = function (name) { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); - } - return this._headers[name]; - }; - Object.setPrototypeOf(xhr, prototype); - return xhr; - }; - Object.assign(window.XMLHttpRequest, win.CapacitorWebXMLHttpRequest.fullObject); - } - } - // patch window.console on iOS and store original console fns - const isIos = getPlatformId(win) === 'ios'; - if (win.console && isIos) { - Object.defineProperties(win.console, BRIDGED_CONSOLE_METHODS.reduce((props, method) => { - const consoleMethod = win.console[method].bind(win.console); - props[method] = { - value: (...args) => { - const msgs = [...args]; - cap.toNative('Console', 'log', { - level: method, - message: msgs.map(serializeConsoleMessage).join(' '), - }); - return consoleMethod(...args); - }, - }; - return props; - }, {})); - } - cap.logJs = (msg, level) => { - switch (level) { - case 'error': - win.console.error(msg); - break; - case 'warn': - win.console.warn(msg); - break; - case 'info': - win.console.info(msg); - break; - default: - win.console.log(msg); - } - }; - cap.logToNative = createLogToNative(win.console); - cap.logFromNative = createLogFromNative(win.console); - cap.handleError = (err) => win.console.error(err); - win.Capacitor = cap; - }; - function initNativeBridge(win) { - const cap = win.Capacitor || {}; - // keep a collection of callbacks for native response data - const callbacks = new Map(); - const webviewServerUrl = typeof win.WEBVIEW_SERVER_URL === 'string' ? win.WEBVIEW_SERVER_URL : ''; - cap.getServerUrl = () => webviewServerUrl; - cap.convertFileSrc = (filePath) => convertFileSrcServerUrl(webviewServerUrl, filePath); - // Counter of callback ids, randomized to avoid - // any issues during reloads if a call comes back with - // an existing callback id from an old session - let callbackIdCount = Math.floor(Math.random() * 134217728); - let postToNative = null; - const isNativePlatform = () => true; - const getPlatform = () => getPlatformId(win); - cap.getPlatform = getPlatform; - cap.isPluginAvailable = (name) => Object.prototype.hasOwnProperty.call(cap.Plugins, name); - cap.isNativePlatform = isNativePlatform; - // create the postToNative() fn if needed - if (getPlatformId(win) === 'android') { - // android platform - postToNative = (data) => { - var _a; - try { - win.androidBridge.postMessage(JSON.stringify(data)); - } - catch (e) { - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e); - } - }; - } - else if (getPlatformId(win) === 'ios') { - // ios platform - postToNative = (data) => { - var _a; - try { - data.type = data.type ? data.type : 'message'; - win.webkit.messageHandlers.bridge.postMessage(data); - } - catch (e) { - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e); - } - }; - } - cap.handleWindowError = (msg, url, lineNo, columnNo, err) => { - const str = msg.toLowerCase(); - if (str.indexOf('script error') > -1) ; - else { - const errObj = { - type: 'js.error', - error: { - message: msg, - url: url, - line: lineNo, - col: columnNo, - errorObject: JSON.stringify(err), - }, - }; - if (err !== null) { - cap.handleError(err); - } - postToNative(errObj); - } - return false; - }; - if (cap.DEBUG) { - window.onerror = cap.handleWindowError; - } - initLogger(win, cap); - /** - * Send a plugin method call to the native layer - */ - cap.toNative = (pluginName, methodName, options, storedCallback) => { - var _a, _b; - try { - if (typeof postToNative === 'function') { - let callbackId = '-1'; - if (storedCallback && - (typeof storedCallback.callback === 'function' || typeof storedCallback.resolve === 'function')) { - // store the call for later lookup - callbackId = String(++callbackIdCount); - callbacks.set(callbackId, storedCallback); - } - const callData = { - callbackId: callbackId, - pluginId: pluginName, - methodName: methodName, - options: options || {}, - }; - if (cap.isLoggingEnabled && pluginName !== 'Console') { - cap.logToNative(callData); - } - // post the call data to native - postToNative(callData); - return callbackId; - } - else { - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(`implementation unavailable for: ${pluginName}`); - } - } - catch (e) { - (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); - } - return null; - }; - if (win === null || win === void 0 ? void 0 : win.androidBridge) { - win.androidBridge.onmessage = function (event) { - returnResult(JSON.parse(event.data)); - }; - } - /** - * Process a response from the native layer. - */ - cap.fromNative = (result) => { - returnResult(result); - }; - const returnResult = (result) => { - var _a, _b; - if (cap.isLoggingEnabled && result.pluginId !== 'Console') { - cap.logFromNative(result); - } - // get the stored call, if it exists - try { - const storedCall = callbacks.get(result.callbackId); - if (storedCall) { - // looks like we've got a stored call - if (result.error) { - // ensure stacktraces by copying error properties to an Error - result.error = Object.keys(result.error).reduce((err, key) => { - // use any type to avoid importing util and compiling most of .ts files - err[key] = result.error[key]; - return err; - }, new cap.Exception('')); - } - if (typeof storedCall.callback === 'function') { - // callback - if (result.success) { - storedCall.callback(result.data); - } - else { - storedCall.callback(null, result.error); - } - } - else if (typeof storedCall.resolve === 'function') { - // promise - if (result.success) { - storedCall.resolve(result.data); - } - else { - storedCall.reject(result.error); - } - // no need to keep this stored callback - // around for a one time resolve promise - callbacks.delete(result.callbackId); - } - } - else if (!result.success && result.error) { - // no stored callback, but if there was an error let's log it - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(result.error); - } - if (result.save === false) { - callbacks.delete(result.callbackId); - } - } - catch (e) { - (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); - } - // always delete to prevent memory leaks - // overkill but we're not sure what apps will do with this data - delete result.data; - delete result.error; - }; - cap.nativeCallback = (pluginName, methodName, options, callback) => { - if (typeof options === 'function') { - console.warn(`Using a callback as the 'options' parameter of 'nativeCallback()' is deprecated.`); - callback = options; - options = null; - } - return cap.toNative(pluginName, methodName, options, { callback }); - }; - cap.nativePromise = (pluginName, methodName, options) => { - return new Promise((resolve, reject) => { - cap.toNative(pluginName, methodName, options, { - resolve: resolve, - reject: reject, - }); - }); - }; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - cap.withPlugin = (_pluginId, _fn) => dummy; - cap.Exception = CapacitorException; - initEvents(win, cap); - initLegacyHandlers(win, cap); - initVendor(win, cap); - win.Capacitor = cap; - } - initNativeBridge(w); - }; - initBridge(typeof globalThis !== 'undefined' - ? globalThis - : typeof self !== 'undefined' - ? self - : typeof window !== 'undefined' - ? window - : typeof global !== 'undefined' - ? global - : {}); - - dummy = initBridge; - - Object.defineProperty(exports, '__esModule', { value: true }); - - return exports; - -})({}); diff --git a/ios/Capacitor/CapacitorTests/BridgedTypesHelper.swift b/ios/Capacitor/CapacitorTests/BridgedTypesHelper.swift deleted file mode 100644 index 9df3f786a8..0000000000 --- a/ios/Capacitor/CapacitorTests/BridgedTypesHelper.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Foundation -@testable import Capacitor - -enum BridgeTypeError: Error { - case badCast -} - -@objc class BridgedTypesHelper: NSObject { - @objc static let shared = BridgedTypesHelper() - - var untypedArray: [Any] { - return [] - } - - @objc func validTransformationOf(array: [Any]) -> [Any] { - let result = JSTypes.coerceArrayToJSArray(array)!.capacitor.replacingNullValues() - return result.capacitor.replacingOptionalValues() as [Any] - } - - @objc func invalidTransformationOf(array: [Any]) -> [Any] { - let result = JSTypes.coerceArrayToJSArray(array)!.capacitor.replacingNullValues() - return result as [Any] - } - - @objc func testCast(of array: [Any], atIndex index: Int) throws -> Any { - if let castArray = array as? [JSValue] { - return castArray[index] as Any - } - throw BridgeTypeError.badCast - } -} diff --git a/ios/Capacitor/CapacitorTests/BridgedTypesTests.m b/ios/Capacitor/CapacitorTests/BridgedTypesTests.m deleted file mode 100644 index 9d11f66c4c..0000000000 --- a/ios/Capacitor/CapacitorTests/BridgedTypesTests.m +++ /dev/null @@ -1,46 +0,0 @@ -#import -#import -#import "CapacitorTests-Swift.h" - -// interface for this class -@interface BridgedTypesTestsObjc : XCTestCase -@end - -@implementation BridgedTypesTestsObjc - -- (void)setUp { - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. -} - -- (void)testNullHandling { - NSArray* source = @[@"test", [NSNull null], @3]; - NSArray* result = [[BridgedTypesHelper shared] validTransformationOfArray:source]; - NSError *error = nil; - // test that the replaced null value exists - id value = [result objectAtIndex:1]; - XCTAssertNotNil(value); - XCTAssertTrue([value isKindOfClass:[NSNull class]]); - // test that the null value casts to non-optional - value = [[BridgedTypesHelper shared] testCastOf:result atIndex:1 error:&error]; - XCTAssertNotNil(value); - XCTAssertNil(error); -} - -- (void)testOptionalHandling { - NSArray* source = @[@"test", [NSNull null], @3]; - NSArray* result = [[BridgedTypesHelper shared] invalidTransformationOfArray:source]; - NSError *error = nil; - // test that the removed null value, now optional, is automatically transformed back into a NSNull - id value = [result objectAtIndex:1]; - XCTAssertNotNil(value); - XCTAssertTrue([value isKindOfClass:[NSNull class]]); - // test that the optional value fails to cast to non-optional - value = [[BridgedTypesHelper shared] testCastOf:result atIndex:1 error:&error]; - XCTAssertNil(value); - XCTAssertNotNil(error); -} -@end diff --git a/ios/Capacitor/CapacitorTests/BridgedTypesTests.swift b/ios/Capacitor/CapacitorTests/BridgedTypesTests.swift deleted file mode 100644 index f88b4e320e..0000000000 --- a/ios/Capacitor/CapacitorTests/BridgedTypesTests.swift +++ /dev/null @@ -1,202 +0,0 @@ -import XCTest - -@testable import Capacitor - -class TestContainer: NSObject, JSValueContainer { - var coercedDictionary: [AnyHashable: Any] = [:] - - public static var jsDateFormatter: ISO8601DateFormatter = { - return ISO8601DateFormatter() - }() - - public var jsObjectRepresentation: JSObject { - return coercedDictionary as? JSObject ?? [:] - } -} - -class BridgedTypesTests: XCTestCase { - static var unserializedDictionary: [AnyHashable: Any] = [:] - static var deserializedDictionary: [AnyHashable: Any] = [:] - - var unserializedDictionary: [AnyHashable: Any] = [:] - var deserializedDictionary: [AnyHashable: Any] = [:] - var testContainer = TestContainer() - - override class func setUp() { - let formatter = ISO8601DateFormatter() - // an ISO 8601 string does not necessarily include subsecond precision, so we can't just capture the current date - // or else we won't be able to compare the objects since they could differ by milliseconds or nanoseonds. so instead - // we use a fixed timestamp at a whole hour. - let date = NSDate(timeIntervalSinceReferenceDate: 632854800) - let subDictionary: [AnyHashable: Any] = ["testIntArray": [0, 1, 2], "testStringArray": ["1", "2", "3"], "testDictionary":["foo":"bar"]] - var dictionary: [AnyHashable: Any] = ["testInt": 1 as Int, "testFloat": Float.pi, "testBool": true as Bool, "testString": "Some string value", "testChild": subDictionary, "testDateString": formatter.string(from: date as Date)] - let serializer = JSONSerializationWrapper(dictionary: dictionary)! - var unwrappedResult = serializer.unwrappedResult()! - // date objects are not handled by the JSON serializer, so we have to insert these after the roundtrip - unwrappedResult["testDateObject"] = date - dictionary["testDateObject"] = date - unserializedDictionary = dictionary - deserializedDictionary = unwrappedResult - } - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - unserializedDictionary = BridgedTypesTests.unserializedDictionary - deserializedDictionary = BridgedTypesTests.deserializedDictionary - testContainer.coercedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testTranslation() throws { - XCTAssertTrue(unserializedDictionary.count > 0) - XCTAssertTrue(deserializedDictionary.count > 0) - XCTAssertTrue(testContainer.coercedDictionary.count > 0) - } - - func testCastingFailure() throws { - var castResult = deserializedDictionary as? JSObject - XCTAssertNil(castResult) - - castResult = unserializedDictionary as? JSObject - XCTAssertNil(castResult) - } - - func testCoercionSuccess() throws { - let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary) - XCTAssertNotNil(coercedResult) - } - - func testRoundtripEquality() throws { - let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! - let foo: NSDictionary = coercedResult as NSDictionary - let bar: NSDictionary = unserializedDictionary as NSDictionary - - XCTAssertEqual(foo, bar) - } - - func testTypeEquavalency() throws { - let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! - let coercedFloat = coercedResult["testFloat"] as? Float - let sourceFloat = unserializedDictionary["testFloat"] as? Float - let resultFloat = deserializedDictionary["testFloat"] as? Float - - XCTAssertNotNil(coercedFloat) - XCTAssertNotNil(sourceFloat) - XCTAssertNotNil(resultFloat) - - XCTAssertEqual(coercedFloat, sourceFloat) - XCTAssertEqual(sourceFloat, resultFloat) - XCTAssertEqual(coercedFloat, Float.pi) - } - - func testNumberWrapping() throws { - // the original number is a swift primitive float - let sourceFloat = unserializedDictionary["testFloat"]! - XCTAssertTrue(type(of: sourceFloat) == Float.self) - - // but after serialization/deserilization, it will be wrapped as an NSNumber - let wrappedFloat = deserializedDictionary["testFloat"]! - let underlyingType: AnyObject.Type = NSClassFromString("__NSCFNumber")! - XCTAssertTrue(type(of: wrappedFloat) == underlyingType.self) - - // coercion will keep the NSNumber type since there's no way to recover it - let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! - let coercedFloat = coercedResult["testFloat"]! - XCTAssertTrue(type(of: coercedFloat) == underlyingType.self) - - // but the cast accessor should restore it - let castFloat = testContainer.getFloat("testFloat")! - XCTAssertTrue(type(of: castFloat) == Float.self) - XCTAssertEqual(sourceFloat as! Float, castFloat) - } - - func testDateObject() throws { - let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! - let date = coercedResult["testDateObject"] as! Date - XCTAssertNotNil(date) - XCTAssertTrue(type(of: date) == Date.self) - } - - func testDateParsing() throws { - let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! - let formatter = ISO8601DateFormatter() - let parsedDate = formatter.date(from: coercedResult["testDateString"] as! String)! - let dateObject = coercedResult["testDateObject"] as! Date - XCTAssertNotNil(parsedDate) - XCTAssertNotNil(dateObject) - XCTAssertTrue(dateObject.compare(parsedDate) == .orderedSame) - } - - func testDateExtensions() throws { - let parsedDate = testContainer.getDate("testDateString")! - let dateObject = testContainer.getDate("testDateObject")! - XCTAssertNotNil(parsedDate) - XCTAssertNotNil(dateObject) - XCTAssertTrue(dateObject.compare(parsedDate) == .orderedSame) - } - - func testDateCoercion() throws { - let stringifiedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary, formattingDatesAsStrings: true)! - let unstringifiedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary, formattingDatesAsStrings: false)! - let stringifiedValue = stringifiedDictionary["testDateObject"]! - let unstringifiedValue = unstringifiedDictionary["testDateObject"]! - XCTAssertTrue(type(of: stringifiedValue) == String.self) - XCTAssertTrue(type(of: unstringifiedValue) == Date.self) - XCTAssertEqual(stringifiedValue as! String, stringifiedDictionary["testDateString"] as! String) - } - - func testDateResultWrapping() throws { - let result = try PluginCallResult.dictionary(["date": unserializedDictionary["testDateObject"]!]).jsonRepresentation() - XCTAssertEqual(result, "{\"date\":\"\(unserializedDictionary["testDateString"] as! String)\"}") - } - - func testResultMerging() throws { - let result = try PluginCallResult.dictionary(["number": 1]).jsonRepresentation(includingFields: ["string":"foo"]) - // ordering of the pairs should be non-deterministic - if result != "{\"string\":\"foo\",\"number\":1}" && result != "{\"number\":1,\"string\":\"foo\"}" { - XCTAssert(false) - } - } - - func testNullWrapping() throws { - let dictionary: [AnyHashable: Any] = ["testInt": 1 as Int, "testNull": NSNull()] - let coercedDictionary = JSTypes.coerceDictionaryToJSObject(dictionary)! - XCTAssertNotNil(coercedDictionary) - XCTAssertEqual(coercedDictionary.count, 2) - XCTAssertTrue(coercedDictionary["testNull"]! is NSNull) - } - - func testNullTransformation() throws { - let array: [Any] = [1, NSNull(), "test string"] - let coercedArray = JSTypes.coerceArrayToJSArray(array)! - XCTAssertNotNil(coercedArray) - XCTAssertEqual(coercedArray.count, 3) - XCTAssertTrue(type(of: coercedArray[1]) == NSNull.self) - let filteredArray = coercedArray.capacitor.replacingNullValues() - XCTAssertEqual(filteredArray.count, 3) - XCTAssertNil(filteredArray[1]) - let restoredArray = filteredArray.capacitor.replacingOptionalValues() - XCTAssertEqual(restoredArray.count, 3) - XCTAssertNotNil(restoredArray[1]) - XCTAssertTrue(restoredArray[0] is NSNumber) - XCTAssertTrue(restoredArray[1] is NSNull) - XCTAssertTrue(restoredArray[2] is String) - } - - func testSparseArrayCastSuccess() throws { - let array: [Any] = ["test string 1", "test string 2", NSNull()] - let sparseArray = JSTypes.coerceArrayToJSArray(array)?.capacitor.replacingNullValues() as? [String?] - XCTAssertNotNil(sparseArray) - XCTAssertEqual(sparseArray!.count, 3) - XCTAssertNil(sparseArray![2]) - } - - func testSparseArrayCastFailure() throws { - let array: [Any] = ["test string 1", 1, NSNull()] - let sparseArray = JSTypes.coerceArrayToJSArray(array)?.capacitor.replacingNullValues() as? [String?] - XCTAssertNil(sparseArray) - } -} diff --git a/ios/Capacitor/CapacitorTests/CapacitorTests-Bridging-Header.h b/ios/Capacitor/CapacitorTests/CapacitorTests-Bridging-Header.h deleted file mode 100644 index b0f308df84..0000000000 --- a/ios/Capacitor/CapacitorTests/CapacitorTests-Bridging-Header.h +++ /dev/null @@ -1,5 +0,0 @@ -// -// Use this file to import your target's public headers that you would like to expose to Swift. -// - -#import "JSONSerializationWrapper.h" diff --git a/ios/Capacitor/CapacitorTests/CapacitorTests.swift b/ios/Capacitor/CapacitorTests/CapacitorTests.swift deleted file mode 100644 index 45427ea3a7..0000000000 --- a/ios/Capacitor/CapacitorTests/CapacitorTests.swift +++ /dev/null @@ -1,38 +0,0 @@ -import XCTest -@testable import Capacitor - -class MockBridgeViewController: CAPBridgeViewController { -} - -class MockAssetHandler: WebViewAssetHandler { -} - -class MockDelegationHandler: WebViewDelegationHandler { -} - -class MockBridge: CapacitorBridge { - override public func registerPlugins() { - Swift.print("REGISTER PLUGINS") - } -} - -class CapacitorTests: XCTestCase { - var bridge: MockBridge? - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - let descriptor = InstanceDescriptor.init() - bridge = MockBridge( - with: InstanceConfiguration(with: descriptor, isDebug: true), - delegate: MockBridgeViewController(), - assetHandler: MockAssetHandler(router: CapacitorRouter()), - delegationHandler: MockDelegationHandler() - ) - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } -} diff --git a/ios/Capacitor/CapacitorTests/ConfigurationTests.swift b/ios/Capacitor/CapacitorTests/ConfigurationTests.swift deleted file mode 100644 index ca67ec0c6d..0000000000 --- a/ios/Capacitor/CapacitorTests/ConfigurationTests.swift +++ /dev/null @@ -1,198 +0,0 @@ -import XCTest - -@testable import Capacitor - -class ConfigurationTests: XCTestCase { - enum ConfigFile: String, CaseIterable { - case flat = "flat" - case nested = "hierarchy" - case server = "server" - case invalid = "bad" - case deprecated = "hidinglogs" - case nonparsable = "nonjson" - } - static var files: [ConfigFile: URL] = [:] - - override class func setUp() { - for file in ConfigFile.allCases { - if let url = Bundle.main.url(forResource: file.rawValue, withExtension: "json", subdirectory: "configurations") { - files[file] = url - } - } - } - - override func setUpWithError() throws { - XCTAssert(ConfigurationTests.files.count == ConfigFile.allCases.count, "Not all configuration files were located") - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testDefaultErrors() throws { - let descriptor = InstanceDescriptor.init() - XCTAssertTrue(descriptor.warnings.contains(.missingAppDir)) - XCTAssertTrue(descriptor.warnings.contains(.missingFile)) - } - - func testMissingAppDetection() throws { - var url = Bundle.main.resourceURL! - url.appendPathComponent("app", isDirectory: true) - let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil) - XCTAssertTrue(descriptor.warnings.contains(.missingAppDir), "A missing app directory was ignored") - } - - func testFailedParsing() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.nonparsable], cordovaConfiguration: nil) - XCTAssertTrue(descriptor.warnings.contains(.invalidFile)) - } - - func testDefaults() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil) - XCTAssertNil(descriptor.backgroundColor) - XCTAssertEqual(descriptor.urlScheme, "capacitor") - XCTAssertEqual(descriptor.urlHostname, "localhost") - XCTAssertNil(descriptor.serverURL) - XCTAssertTrue(descriptor.scrollingEnabled) - XCTAssertEqual(descriptor.loggingBehavior, .debug) - XCTAssertTrue(descriptor.allowLinkPreviews) - XCTAssertEqual(descriptor.contentInsetAdjustmentBehavior, .never) - } - - func testDeprecatedParsing() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.deprecated], cordovaConfiguration: nil) - #warning("Is this supposed to fail?") - XCTExpectFailure { - XCTAssertEqual(descriptor.loggingBehavior, .none) - } - } - - func testDeprecatedOverrideParsing() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.server], cordovaConfiguration: nil) - XCTAssertEqual(descriptor.loggingBehavior, .production) - } - - func testTopLevelParsing() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.flat], cordovaConfiguration: nil) - XCTAssertEqual(descriptor.backgroundColor, UIColor(red: 1, green: 1, blue: 1, alpha: 1)) - XCTAssertEqual(descriptor.overridenUserAgentString, "level 1 override") - XCTAssertEqual(descriptor.appendedUserAgentString, "level 1 append") - XCTAssertEqual(descriptor.loggingBehavior, .debug) - } - - func testNestedParsing() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.nested], cordovaConfiguration: nil) - XCTAssertEqual(descriptor.backgroundColor, UIColor(red: 0, green: 0, blue: 0, alpha: 1)) - XCTAssertEqual(descriptor.overridenUserAgentString, "level 2 override") - XCTAssertEqual(descriptor.appendedUserAgentString, "level 2 append") - XCTAssertEqual(descriptor.loggingBehavior, .none) - XCTAssertFalse(descriptor.scrollingEnabled) - XCTAssertEqual(descriptor.contentInsetAdjustmentBehavior, .scrollableAxes) - } - - func testServerParsing() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.server], cordovaConfiguration: nil) - XCTAssertEqual(descriptor.urlScheme, "override") - XCTAssertEqual(descriptor.urlHostname, "myhost") - XCTAssertEqual(descriptor.serverURL, "http://192.168.100.1:2057") - } - - func testBadDataParsing() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.invalid], cordovaConfiguration: nil) - XCTAssertNil(descriptor.backgroundColor) - XCTAssertEqual(descriptor.loggingBehavior, .debug) - XCTAssertEqual(descriptor.contentInsetAdjustmentBehavior, .never) - } - - func testBadDataTransformation() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.invalid], cordovaConfiguration: nil) - let configuration = InstanceConfiguration(with: descriptor, isDebug: true) - #warning("Address this. These tests haven't been run during CI since maybe ever?") - XCTExpectFailure { - XCTAssertEqual(configuration.serverURL, URL(string: "capacitor://myhost"), "Invalid server.url and invalid ioScheme were not ignored") - } - } - - func testServerTransformation() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.server], cordovaConfiguration: nil) - let configuration = InstanceConfiguration(with: descriptor, isDebug: true) - XCTAssertEqual(configuration.serverURL, URL(string: "http://192.168.100.1:2057")) - XCTAssertEqual(configuration.localURL, URL(string: "override://myhost")) - } - - func testPluginConfig() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.flat], cordovaConfiguration: nil) - let configuration = InstanceConfiguration(with: descriptor, isDebug: true) - let value = configuration.getPluginConfig("SplashScreen").getInt("launchShowDuration", 0) - XCTAssertEqual(value, 1) - } - - func testLegacyConfig() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - // a top-level legacy key is exposed through the direct property accessor - let flatDescriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.flat], cordovaConfiguration: nil) - let flatConfiguration = InstanceConfiguration(with: flatDescriptor, isDebug: true) - XCTAssertEqual(flatConfiguration.overridenUserAgentString, "level 1 override") - // a platform-specific legacy key overrides the top-level one - let nestedDescriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.nested], cordovaConfiguration: nil) - let nestedConfiguration = InstanceConfiguration(with: nestedDescriptor, isDebug: true) - XCTAssertEqual(nestedConfiguration.overridenUserAgentString, "level 2 override") - } - - func testNavigationRules() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: ConfigurationTests.files[.server], cordovaConfiguration: nil) - let configuration = InstanceConfiguration(with: descriptor, isDebug: true) - XCTAssertTrue(configuration.shouldAllowNavigation(to: "ionic.io")) - XCTAssertTrue(configuration.shouldAllowNavigation(to: "ionic.io".uppercased())) - XCTAssertTrue(configuration.shouldAllowNavigation(to: "test.capacitorjs.com")) - XCTAssertTrue(configuration.shouldAllowNavigation(to: "192.168.0.1")) - XCTAssertTrue(configuration.shouldAllowNavigation(to: "subdomain.test.ionicframework.com")) - XCTAssertTrue(configuration.shouldAllowNavigation(to: "wildcard1.wildcard2.example.com")) - XCTAssertFalse(configuration.shouldAllowNavigation(to: "wildcard1.example.com")) - XCTAssertFalse(configuration.shouldAllowNavigation(to: "google.com")) - XCTAssertFalse(configuration.shouldAllowNavigation(to: "192.168.0.2")) - XCTAssertFalse(configuration.shouldAllowNavigation(to: "ionicframework.com")) - } - - func testNoLoggingTransformation() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil) - descriptor.loggingBehavior = .none - var configuration = InstanceConfiguration(with: descriptor, isDebug: false) - XCTAssertFalse(configuration.loggingEnabled) - configuration = InstanceConfiguration(with: descriptor, isDebug: true) - XCTAssertFalse(configuration.loggingEnabled) - } - - func testDebugLoggingTransformation() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil) - descriptor.loggingBehavior = .debug - var configuration = InstanceConfiguration(with: descriptor, isDebug: false) - XCTAssertFalse(configuration.loggingEnabled) - configuration = InstanceConfiguration(with: descriptor, isDebug: true) - XCTAssertTrue(configuration.loggingEnabled) - } - - func testProductionLoggingTransformation() throws { - let url = Bundle.main.url(forResource: "configurations", withExtension: "")! - let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil) - descriptor.loggingBehavior = .production - var configuration = InstanceConfiguration(with: descriptor, isDebug: false) - XCTAssertTrue(configuration.loggingEnabled) - configuration = InstanceConfiguration(with: descriptor, isDebug: true) - XCTAssertTrue(configuration.loggingEnabled) - } -} diff --git a/ios/Capacitor/CapacitorTests/Info.plist b/ios/Capacitor/CapacitorTests/Info.plist deleted file mode 100644 index 6c40a6cd0c..0000000000 --- a/ios/Capacitor/CapacitorTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/ios/Capacitor/CapacitorTests/JSExportTests.swift b/ios/Capacitor/CapacitorTests/JSExportTests.swift deleted file mode 100644 index 0cd57593b5..0000000000 --- a/ios/Capacitor/CapacitorTests/JSExportTests.swift +++ /dev/null @@ -1,24 +0,0 @@ -import XCTest - -@testable import Capacitor - -class JSExportTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testBridgeBundle() throws { - let contentController = WKUserContentController() - do { - try Capacitor.JSExport.exportBridgeJS(userContentController: contentController) - } - catch { - XCTFail() - } - } -} diff --git a/ios/Capacitor/CapacitorTests/JSONSerializationWrapper.h b/ios/Capacitor/CapacitorTests/JSONSerializationWrapper.h deleted file mode 100644 index 96ca66281b..0000000000 --- a/ios/Capacitor/CapacitorTests/JSONSerializationWrapper.h +++ /dev/null @@ -1,9 +0,0 @@ -#import - -@interface JSONSerializationWrapper : NSObject -@property (nonatomic, copy) NSDictionary* _Nonnull dictionary; - -- (instancetype _Nullable)initWithDictionary:(NSDictionary* _Nonnull)options; -- (NSDictionary * _Nullable)unwrappedResult; - -@end diff --git a/ios/Capacitor/CapacitorTests/JSONSerializationWrapper.m b/ios/Capacitor/CapacitorTests/JSONSerializationWrapper.m deleted file mode 100644 index 6316c9c88b..0000000000 --- a/ios/Capacitor/CapacitorTests/JSONSerializationWrapper.m +++ /dev/null @@ -1,24 +0,0 @@ - -#import "JSONSerializationWrapper.h" - -@implementation JSONSerializationWrapper - -- (instancetype)initWithDictionary:(NSDictionary *)dictionary { - self = [super init]; - if (self != nil) { - _dictionary = dictionary; - } - return self; -} - -- (NSDictionary *)unwrappedResult { - NSError* error = nil; - NSData* serializedData = [NSJSONSerialization dataWithJSONObject:[self dictionary] options:NSJSONWritingPrettyPrinted error:&error]; - if (serializedData != nil) { - NSDictionary* result = [NSJSONSerialization JSONObjectWithData:serializedData options:0 error:&error]; - return result; - } - return nil; -} - -@end diff --git a/ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m b/ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m deleted file mode 100644 index 4d665aeec1..0000000000 --- a/ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m +++ /dev/null @@ -1,98 +0,0 @@ -#import -#import -#import -#import "CapacitorTests-Swift.h" - -// interface for this class -@interface PluginCallAccessorTests : XCTestCase -@property (strong, nonatomic) CAPPluginCall* call; -@end - -@implementation PluginCallAccessorTests - -- (void)setUp { - [super setUp]; - NSDate* date = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:632854800]; - NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init]; - NSDictionary* options = @{@"testString":@"foo", - @"testDict": @{@"testSubkey":@"sub value"}, - @"testFloat": @3.14159, - @"testDateObject": date, - @"testDateString": [formatter stringFromDate:date], - @"testBoolTrue": @TRUE, - @"testBoolFalse": @FALSE}; - [self setCall:[[CAPPluginCall alloc] initWithCallbackId:@"test" methodName:@"test" options:options success:NULL error:NULL]]; -} - -- (void)testStringAccessor { - NSString* value = [[self call] getString:@"testString" defaultValue:NULL]; - XCTAssertEqual(value, @"foo"); - - value = [[self call] getString:@"badString" defaultValue:NULL]; - XCTAssertNil(value); - - value = [[self call] getString:@"badString" defaultValue:@"default"]; - XCTAssertEqual(value, @"default"); -} - -- (void)testDateObjectAccessor { - NSDate* value = [[self call] getDate:@"testDateObject" defaultValue:NULL]; - XCTAssertEqual([value timeIntervalSinceReferenceDate], 632854800); - - value = [[self call] getDate:@"badString" defaultValue:NULL]; - XCTAssertNil(value); - - NSDate *defaultDate = [NSDate date]; - value = [[self call] getDate:@"badString" defaultValue:defaultDate]; - XCTAssertEqual(value, defaultDate); -} - -- (void)testDateStringAccessor { - NSDate* objectValue = [[self call] getDate:@"testDateObject" defaultValue:NULL]; - NSDate* stringValue = [[self call] getDate:@"testDateString" defaultValue:NULL]; - XCTAssertNotNil(objectValue); - XCTAssertNotNil(stringValue); - XCTAssertEqual(objectValue, stringValue); -} - -- (void)testObjectAccessor { - NSDictionary* value = [[self call] getObject:@"testDict" defaultValue:NULL]; - XCTAssertEqual([value objectForKey:@"testSubkey"], @"sub value"); - - value = [[self call] getObject:@"badString" defaultValue:NULL]; - XCTAssertNil(value); - - value = [[self call] getObject:@"badString" defaultValue:@{@"defaultKey":@"default"}]; - XCTAssertEqual([value objectForKey:@"defaultKey"], @"default"); -} - -- (void)testNumberAccessor { - NSNumber* value = [[self call] getNumber:@"testFloat" defaultValue:NULL]; - XCTAssertNotNil(value); - XCTAssertTrue([value isEqualToNumber:@3.14159]); - - value = [[self call] getNumber:@"badString" defaultValue:NULL]; - XCTAssertNil(value); - - value = [[self call] getNumber:@"badString" defaultValue:@100]; - XCTAssertEqual([value intValue], 100); - - value = [[self call] getNumber:@"testBoolTrue" defaultValue:NULL]; - XCTAssertNotNil(value); - XCTAssertEqual([value boolValue], TRUE); -} - -- (void)testBoolAccessor { - BOOL value = [[self call] getBool:@"testBoolTrue" defaultValue:false]; - XCTAssertTrue(value); - - value = [[self call] getBool:@"testBoolFalse" defaultValue:true]; - XCTAssertFalse(value); - - value = [[self call] getBool:@"badString" defaultValue:true]; - XCTAssertTrue(value); - - value = [[self call] getBool:@"badString" defaultValue:false]; - XCTAssertFalse(value); -} -@end diff --git a/ios/Capacitor/CapacitorTests/RouterTests.swift b/ios/Capacitor/CapacitorTests/RouterTests.swift deleted file mode 100644 index 181551ee72..0000000000 --- a/ios/Capacitor/CapacitorTests/RouterTests.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// RouterTests.swift -// CapacitorTests -// -// Created by Steven Sherry on 3/29/22. -// Copyright © 2022 Drifty Co. All rights reserved. -// - -import XCTest -@testable import Capacitor - -class RouterTests: XCTestCase { - - func testRouterReturnsIndexWhenProvidedEmptyPath() { - checkRouter(path: "", expected: "/index.html") - } - - func testRouterReturnsIndexWhenProviedPathWithoutExtension() { - checkRouter(path: "/a/valid/path/no/ext", expected: "/index.html") - } - - func testRouterReturnsPathWhenProvidedValidPath() { - checkRouter(path: "/a/valid/path.ext", expected: "/a/valid/path.ext") - } - - func testRouterReturnsPathWhenProvidedValidPathWithExtensionAndSpaces() { - checkRouter(path: "/a/valid/file path.ext", expected: "/a/valid/file path.ext") - } - - func checkRouter(path: String, expected: String) { - XCTContext.runActivity(named: "router creates route path correctly") { _ in - var router = CapacitorRouter() - XCTAssertEqual(router.route(for: path), expected) - router.basePath = "/A/Route" - XCTAssertEqual(router.route(for: path), "/A/Route" + expected) - } - } - -} diff --git a/ios/Capacitor/CodableTests/CodableTests.swift b/ios/Capacitor/CodableTests/CodableTests.swift deleted file mode 100644 index bfdbe57162..0000000000 --- a/ios/Capacitor/CodableTests/CodableTests.swift +++ /dev/null @@ -1,194 +0,0 @@ -// -// JSValueDecoderTest.swift -// CapacitorTests -// -// Created by Steven Sherry on 12/8/23. -// Copyright © 2023 Drifty Co. All rights reserved. -// - -import XCTest -import Capacitor - -private struct Pet: Codable, Equatable { - var name: String - var breed: String - var isVaccinated: Bool -} - -private struct Person: Codable, Equatable { - var name: String - var age: UInt - var pet: Pet? - var family: [Person]? -} - -private let rawPet: JSObject = [ - "name": "Penny", - "breed": "Chihuahua", - "isVaccinated": true -] - -private let rawPeople: JSArray = [ - [ "name": "Anakin", - "age": 41 as NSNumber - ], - [ "name": "Leia", - "age": 20 as NSNumber - ] -] - -private let rawPerson: JSObject = [ - "name": "Luke", - "age": 20 as NSNumber, - "pet": rawPet, - "family": rawPeople -] - -private let person = Person( - name: "Luke", - age: 20, - pet: .init( - name: "Penny", - breed: "Chihuahua", - isVaccinated: true - ), - family: [ - Person(name: "Anakin", age: 41), - Person(name: "Leia", age: 20) - ] -) - -final class JSValueDecoderTest: XCTestCase { - func testDecode_when_provided_a_valid_keyed_container_for_the_target_type__decoding_is_successful() throws { - let decoder = JSValueDecoder() - let decodedPerson = try decoder.decode(Person.self, from: rawPerson) - XCTAssertEqual(decodedPerson, person) - } - - func testDecode__when_provided_a_valid_unkeyed_container_for_the_target_type__decoding_is_successful() throws { - let decoder = JSValueDecoder() - let decodedPeople = try decoder.decode([Person].self, from: rawPeople) - XCTAssertEqual(person.family, decodedPeople) - } - - func testDecode__when_provided_a_single_value_for_the_target_type__decoding_is_successful() throws { - let decoder = JSValueDecoder() - let decodedNumber = try decoder.decode(UInt.self, from: 100 as NSNumber) - XCTAssertEqual(100, decodedNumber) - } - - func testDecode__when_provided_an_invalid_keyed_container_for_the_target_type__decoding_fails() throws { - let decoder = JSValueDecoder() - var invalidRawPerson = rawPerson - invalidRawPerson["name"] = nil - XCTAssertThrowsError(try decoder.decode(Person.self, from: invalidRawPerson)) - } - - func testDecode__when_provided_an_invalid_unkeyed_container_for_the_target_type__decoding_fails() throws { - let decoder = JSValueDecoder() - var invalidRawPeople = try XCTUnwrap(rawPeople as? [JSObject]) - invalidRawPeople[0]["name"] = nil - XCTAssertThrowsError(try decoder.decode([Person].self, from: invalidRawPeople)) - } - - func testDecode__when_provided_an_invalid_single_value_type_for_the_input_value__decoding_fails() throws { - let decoder = JSValueDecoder() - XCTAssertThrowsError(try decoder.decode(UInt.self, from: -1 as NSNumber)) - } - - func testDecode__when_provided_a_valid_nested_array__decoding_is_successful() throws { - let decoder = JSValueDecoder() - let nestedPeople: JSArray = [rawPeople, rawPeople] - let decodedPeople = try decoder.decode([[Person]].self, from: nestedPeople) - XCTAssertEqual([person.family, person.family], decodedPeople) - } - - func testDecode_when_attempting_to_decode_a_class__decoding_fails() throws { - class Pet: Decodable { - var name: String - var breed: String - var isVaccinated: String - init(name: String, breed: String, isVaccinated: String) { - self.name = name - self.breed = breed - self.isVaccinated = isVaccinated - } - } - - let decoder = JSValueDecoder() - XCTAssertThrowsError(try decoder.decode(Pet.self, from: rawPet)) - } - - func testDecode__when_nsnull_explicitly_present_in_container__it_correctly_decodes_to_nil() throws { - let decoder = JSValueDecoder() - var rawPerson = rawPerson - rawPerson["pet"] = NSNull() - - let decodedPerson = try decoder.decode(Person.self, from: rawPerson) - XCTAssertNil(decodedPerson.pet) - } -} - -final class JSValueEncoderTest: XCTestCase { - func testEncode__when_provided_with_an_instance_of_nonclass_codable_instance__encoding_succeeds() throws { - let encoder = JSValueEncoder() - let encodedValue = try encoder.encode(person) - let encodedObject = try XCTUnwrap(encodedValue as? JSObject) - - let name = try XCTUnwrap(encodedObject["name"] as? String) - XCTAssertEqual(person.name, name) - let age = try XCTUnwrap(encodedObject["age"] as? NSNumber) - XCTAssertEqual(person.age as NSNumber, age) - - let pet = try XCTUnwrap(encodedObject["pet"] as? JSObject) - let petName = try XCTUnwrap(pet["name"] as? String) - XCTAssertEqual(person.pet?.name, petName) - let petBreed = try XCTUnwrap(pet["breed"] as? String) - XCTAssertEqual(person.pet?.breed, petBreed) - let petIsVaccinated = try XCTUnwrap(pet["isVaccinated"] as? Bool) - XCTAssertEqual(person.pet?.isVaccinated, petIsVaccinated) - - let family = try XCTUnwrap(encodedObject["family"] as? [JSObject]) - XCTAssertEqual(person.family?.count, family.count) - let aniName = try XCTUnwrap(family[0]["name"] as? String) - XCTAssertEqual(person.family?[0].name, aniName) - let aniAge = try XCTUnwrap(family[0]["age"] as? NSNumber) - XCTAssertEqual(person.family?[0].age as? NSNumber, aniAge) - - let leiaName = try XCTUnwrap(family[1]["name"] as? String) - XCTAssertEqual(person.family?[1].name, leiaName) - let leiaAge = try XCTUnwrap(family[1]["age"] as? NSNumber) - XCTAssertEqual(person.family?[1].age as? NSNumber, leiaAge) - } - - func testEncode__when_provided_an_instance_of_a_nested_unkeyed_container__encoding_succedds() throws { - let encoder = JSValueEncoder() - let encodedValue = try encoder.encode([person.family, person.family]) - let encodedArray = try XCTUnwrap(encodedValue as? [[JSObject]]) - XCTAssertEqual(encodedArray.count, 2) - XCTAssertEqual(encodedArray[0].count, 2) - XCTAssertEqual(encodedArray[1].count, 2) - - let family = try XCTUnwrap(person.family) - - XCTAssertEqual(family[0].name, encodedArray[0][0]["name"] as? String) - XCTAssertEqual(family[0].name, encodedArray[1][0]["name"] as? String) - XCTAssertEqual(family[0].age as NSNumber, encodedArray[0][0]["age"] as? NSNumber) - XCTAssertEqual(family[0].age as NSNumber, encodedArray[1][0]["age"] as? NSNumber) - XCTAssertEqual(family[1].name, encodedArray[0][1]["name"] as? String) - XCTAssertEqual(family[1].name, encodedArray[1][1]["name"] as? String) - XCTAssertEqual(family[1].age as NSNumber, encodedArray[0][1]["age"] as? NSNumber) - XCTAssertEqual(family[1].age as NSNumber, encodedArray[1][1]["age"] as? NSNumber) - } - - func testEncode__when_nil_is_present_in_value__and_optional_encoding_is_set_to_explicit_nulls__it_is_encoded_as_nsnull() throws { - struct Test: Encodable { - var name: String? - } - - let explicitEncoder = JSValueEncoder(optionalEncodingStrategy: .explicitNulls) - let encoded = try XCTUnwrap(try explicitEncoder.encode(Test()) as? JSObject) - XCTAssertTrue(encoded["name"] is NSNull) - XCTAssertNotNil(encoded["name"]) - } -} diff --git a/ios/Capacitor/CodableTests/DataCodableTests.swift b/ios/Capacitor/CodableTests/DataCodableTests.swift deleted file mode 100644 index 112b2bc155..0000000000 --- a/ios/Capacitor/CodableTests/DataCodableTests.swift +++ /dev/null @@ -1,155 +0,0 @@ -// -// DataCodableTests.swift -// CodableTests -// -// Created by Steven Sherry on 9/6/24. -// Copyright © 2024 Drifty Co. All rights reserved. -// - -import XCTest -import Capacitor - -private struct Foo: Codable, Equatable { - var data: Data -} - -private let jsonString = #"{ "key": "value" }"# -private let jsonData = jsonString.data(using: .utf8)! -private let jsonByteArray: [NSNumber] = [123, 32, 34, 107, 101, 121, 34, 58, 32, 34, 118, 97, 108, 117, 101, 34, 32, 125] -private let jsonBase64 = "eyAia2V5IjogInZhbHVlIiB9" - -class JSValueDecoderDataTests: XCTestCase { - func testDecode_data__default_root() throws { - let decoder = JSValueDecoder() - let result = try decoder.decode(Data.self, from: jsonByteArray) - XCTAssertEqual(result, jsonData) - } - - func testDecode_data__default_array() throws { - let decoder = JSValueDecoder() - let result = try decoder.decode([Data].self, from: [jsonByteArray, jsonByteArray]) - XCTAssertEqual(result, [jsonData, jsonData]) - } - - func testDecode_data__default_struct() throws { - let decoder = JSValueDecoder() - let result = try decoder.decode(Foo.self, from: ["data": jsonByteArray]) - XCTAssertEqual(result, .init(data: jsonData)) - } - - func testDecode_data__base64_root() throws { - let decoder = JSValueDecoder(dataDecodingStrategy: .base64) - let result = try decoder.decode(Data.self, from: jsonBase64) - XCTAssertEqual(result, jsonData) - } - - func testDecode_data__base64_array() throws { - let decoder = JSValueDecoder(dataDecodingStrategy: .base64) - let result = try decoder.decode([Data].self, from: [jsonBase64, jsonBase64]) - XCTAssertEqual(result, [jsonData, jsonData]) - } - - func testDecode_data__base64_struct() throws { - let decoder = JSValueDecoder(dataDecodingStrategy: .base64) - let result = try decoder.decode(Foo.self, from: ["data": jsonBase64]) - XCTAssertEqual(result, .init(data: jsonData)) - } - - let customStrategy = JSValueDecoder.DataDecodingStrategy.custom { decoder in - var container = try decoder.unkeyedContainer() - var byteArray: [UInt8] = [] - while !container.isAtEnd { - byteArray.append(try container.decode(UInt8.self)) - } - return Data(byteArray) - } - - func testDecode_data__custom_root() throws { - let decoder = JSValueDecoder(dataDecodingStrategy: customStrategy) - let result = try decoder.decode(Data.self, from: jsonByteArray) - XCTAssertEqual(result, jsonData) - } - - func testDecode_data__custom_array() throws { - let decoder = JSValueDecoder(dataDecodingStrategy: customStrategy) - let result = try decoder.decode([Data].self, from: [jsonByteArray, jsonByteArray]) - XCTAssertEqual(result, [jsonData, jsonData]) - } - - func testDecode_data__custom_struct() throws { - let decoder = JSValueDecoder(dataDecodingStrategy: customStrategy) - let result = try decoder.decode(Foo.self, from: ["data": jsonByteArray]) - XCTAssertEqual(result, .init(data: jsonData)) - } -} - -class JSValueEncoderDataTests: XCTestCase { - func testEncode_data__default_root() throws { - let encoder = JSValueEncoder() - let rawResult = try encoder.encode(jsonData) - let result = try XCTUnwrap(rawResult as? [NSNumber]) - XCTAssertEqual(result, jsonByteArray) - } - - func testEncode_data__default_array() throws { - let encoder = JSValueEncoder() - let rawResult = try encoder.encode([jsonData, jsonData]) - let result = try XCTUnwrap(rawResult as? [[NSNumber]]) - XCTAssertEqual(result, [jsonByteArray, jsonByteArray]) - } - - func testEncode_data__default_struct() throws { - let encoder = JSValueEncoder() - let rawResult = try encoder.encode(Foo(data: jsonData)) - let result = try XCTUnwrap(rawResult as? [String: [NSNumber]]) - XCTAssertEqual(result, ["data": jsonByteArray]) - } - - func testEncode_data__base64_root() throws { - let encoder = JSValueEncoder(dataEncodingStrategy: .base64) - let rawResult = try encoder.encode(jsonData) - let result = try XCTUnwrap(rawResult as? String) - XCTAssertEqual(result, jsonBase64) - } - - func testEncode_data__base64_array() throws { - let encoder = JSValueEncoder(dataEncodingStrategy: .base64) - let rawResult = try encoder.encode([jsonData, jsonData]) - let result = try XCTUnwrap(rawResult as? [String]) - XCTAssertEqual(result, [jsonBase64, jsonBase64]) - } - - func testEncode_data__base64_struct() throws { - let encoder = JSValueEncoder(dataEncodingStrategy: .base64) - let rawResult = try encoder.encode(Foo(data: jsonData)) - let result = try XCTUnwrap(rawResult as? [String: String]) - XCTAssertEqual(result, ["data": jsonBase64]) - } - - let customStrategy = JSValueEncoder.DataEncodingStrategy.custom { data, encoder in - let byteArray = data.map { $0 } - var unkeyedContainer = encoder.unkeyedContainer() - try unkeyedContainer.encode(contentsOf: byteArray) - } - - func testEncode_data__custom_root() throws { - let encoder = JSValueEncoder(dataEncodingStrategy: customStrategy) - let rawResult = try encoder.encode(jsonData) - let result = try XCTUnwrap(rawResult as? [NSNumber]) - XCTAssertEqual(result, jsonByteArray) - } - - func testEncode_data__custom_array() throws { - let encoder = JSValueEncoder(dataEncodingStrategy: customStrategy) - let rawResult = try encoder.encode([jsonData, jsonData]) - let result = try XCTUnwrap(rawResult as? [[NSNumber]]) - XCTAssertEqual(result, [jsonByteArray, jsonByteArray]) - } - - func testEncode_data__custom_struct() throws { - let encoder = JSValueEncoder(dataEncodingStrategy: customStrategy) - let rawResult = try encoder.encode(Foo(data: jsonData)) - let result = try XCTUnwrap(rawResult as? [String: [NSNumber]]) - XCTAssertEqual(result, ["data": jsonByteArray]) - } -} diff --git a/ios/Capacitor/CodableTests/URLCodableTests.swift b/ios/Capacitor/CodableTests/URLCodableTests.swift deleted file mode 100644 index 33a739a31e..0000000000 --- a/ios/Capacitor/CodableTests/URLCodableTests.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// URLCodableTests.swift -// CodableTests -// -// Created by Steven Sherry on 9/6/24. -// Copyright © 2024 Drifty Co. All rights reserved. -// - -import XCTest -import Capacitor - -private let urlString = "https://capacitorjs.com" -private let url = URL(string: urlString)! - -private struct Website: Codable, Equatable { - var url: URL -} - -class JSValueDecoderURLTests: XCTestCase { - let decoder = JSValueDecoder() - - func testDecode_url__root() throws { - let result = try decoder.decode(URL.self, from: urlString) - XCTAssertEqual(result, url) - } - - func testDecode_url__array() throws { - let result = try decoder.decode([URL].self, from: [urlString, urlString]) - XCTAssertEqual(result, [url, url]) - } - - func testDecode_url__struct() throws { - let result = try decoder.decode(Website.self, from: ["url": urlString]) - XCTAssertEqual(result, .init(url: url)) - } - - func testDecode_url__fails_when_invalid_url_string_is_provided() { - XCTAssertThrowsError(try decoder.decode(URL.self, from: "🐞://🐞.com/🐞")) - } -} - -class JSValueEncoderURLTests: XCTestCase { - let encoder = JSValueEncoder() - - func testEncode_url__root() throws { - let rawResult = try encoder.encode(url) - let result = try XCTUnwrap(rawResult as? String) - XCTAssertEqual(result, urlString) - } - - func testEncode_url__array() throws { - let rawResult = try encoder.encode([url, url]) - let result = try XCTUnwrap(rawResult as? [String]) - XCTAssertEqual(result, [urlString, urlString]) - } - - func testEncode_url__struct() throws { - let rawResult = try encoder.encode(Website(url: url)) - let result = try XCTUnwrap(rawResult as? [String: String]) - XCTAssertEqual(result, ["url": urlString]) - } -} diff --git a/ios/Capacitor/TestsHostApp/AppDelegate.swift b/ios/Capacitor/TestsHostApp/AppDelegate.swift deleted file mode 100644 index 9107e3c451..0000000000 --- a/ios/Capacitor/TestsHostApp/AppDelegate.swift +++ /dev/null @@ -1,18 +0,0 @@ -import UIKit - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } - - func application(_ application: UIApplication, - configurationForConnecting connectingSceneSession: UISceneSession, - options: UIScene.ConnectionOptions) -> UISceneConfiguration { - let config = UISceneConfiguration(name: "Default Configuration", - sessionRole: connectingSceneSession.role) - config.delegateClass = SceneDelegate.self - return config - } -} diff --git a/ios/Capacitor/TestsHostApp/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/Capacitor/TestsHostApp/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb87897008..0000000000 --- a/ios/Capacitor/TestsHostApp/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ios/Capacitor/TestsHostApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Capacitor/TestsHostApp/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 9221b9bb1a..0000000000 --- a/ios/Capacitor/TestsHostApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ios/Capacitor/TestsHostApp/Assets.xcassets/Contents.json b/ios/Capacitor/TestsHostApp/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596a7..0000000000 --- a/ios/Capacitor/TestsHostApp/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ios/Capacitor/TestsHostApp/Base.lproj/LaunchScreen.storyboard b/ios/Capacitor/TestsHostApp/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 865e9329f3..0000000000 --- a/ios/Capacitor/TestsHostApp/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Capacitor/TestsHostApp/Base.lproj/Main.storyboard b/ios/Capacitor/TestsHostApp/Base.lproj/Main.storyboard deleted file mode 100644 index 25a763858e..0000000000 --- a/ios/Capacitor/TestsHostApp/Base.lproj/Main.storyboard +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Capacitor/TestsHostApp/Info.plist b/ios/Capacitor/TestsHostApp/Info.plist deleted file mode 100644 index 5b531f7b27..0000000000 --- a/ios/Capacitor/TestsHostApp/Info.plist +++ /dev/null @@ -1,66 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate - UISceneStoryboardFile - Main - - - - - UIApplicationSupportsIndirectInputEvents - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/ios/Capacitor/TestsHostApp/SceneDelegate.swift b/ios/Capacitor/TestsHostApp/SceneDelegate.swift deleted file mode 100644 index e0be6e9cfe..0000000000 --- a/ios/Capacitor/TestsHostApp/SceneDelegate.swift +++ /dev/null @@ -1,5 +0,0 @@ -import UIKit - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? -} diff --git a/ios/Capacitor/TestsHostApp/ViewController.swift b/ios/Capacitor/TestsHostApp/ViewController.swift deleted file mode 100644 index ae980e1ea3..0000000000 --- a/ios/Capacitor/TestsHostApp/ViewController.swift +++ /dev/null @@ -1,9 +0,0 @@ -import UIKit - -class ViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view. - } -} diff --git a/ios/Package.swift b/ios/Package.swift index 49e9d9c651..c9a0fc3a47 100644 --- a/ios/Package.swift +++ b/ios/Package.swift @@ -14,16 +14,13 @@ let package = Package( targets: ["CapacitorCordova"] ) ], - dependencies: [ - .package(url: "https://github.com/swiftlang/swift-testing.git", from: "0.0.0") - ], targets: [ .target( name: "Capacitor", resources: [.copy("assets")], swiftSettings: [ .swiftLanguageMode(.v5) - ] + ], ), .target( name: "CapacitorCordova", @@ -42,8 +39,7 @@ let package = Package( .testTarget( name: "CapacitorTests", dependencies: [ - "Capacitor", - .product(name: "Testing", package: "swift-testing") + "Capacitor" ], resources: [ .copy("Resources/configurations") diff --git a/ios/Tests/CapacitorTests/BridgedTypesCoercionTests.swift b/ios/Tests/CapacitorTests/BridgedTypesCoercionTests.swift new file mode 100644 index 0000000000..e149fcb4b8 --- /dev/null +++ b/ios/Tests/CapacitorTests/BridgedTypesCoercionTests.swift @@ -0,0 +1,56 @@ +import Foundation +import Testing +@testable import Capacitor + +private enum BridgedTypesCoercionError: Error { + case badCast +} + +private enum BridgedTypesCoercionHelper { + static func validTransformation(of array: [Any]) -> [Any] { + let result = JSTypes.coerceArrayToJSArray(array)!.capacitor.replacingNullValues() + return result.capacitor.replacingOptionalValues() as [Any] + } + + static func invalidTransformation(of array: [Any]) -> [Any] { + let result = JSTypes.coerceArrayToJSArray(array)!.capacitor.replacingNullValues() + return result as [Any] + } + + static func testCast(of array: [Any], atIndex index: Int) throws -> Any { + if let castArray = array as? [JSValue] { + return castArray[index] as Any + } + throw BridgedTypesCoercionError.badCast + } +} + +struct BridgedTypesCoercionTests { + @Test func nullHandling() throws { + let source: [Any] = ["test", NSNull(), 3] + let result = BridgedTypesCoercionHelper.validTransformation(of: source) + + // the replaced null value exists + let value = result[1] + #expect(value is NSNull) + + // the null value casts to non-optional + let castValue = try BridgedTypesCoercionHelper.testCast(of: result, atIndex: 1) + #expect(castValue is NSNull) + } + + @Test func optionalHandling() throws { + let source: [Any] = ["test", NSNull(), 3] + let result = BridgedTypesCoercionHelper.invalidTransformation(of: source) + + // bridging the optional-holding array to NSArray (as happens when passing values across + // the JS bridge) coerces the removed null value's `nil` back into an NSNull + let value = (result as NSArray).object(at: 1) + #expect(value is NSNull) + + // the optional value fails to cast to non-optional + #expect(throws: BridgedTypesCoercionError.badCast) { + try BridgedTypesCoercionHelper.testCast(of: result, atIndex: 1) + } + } +} diff --git a/ios/Tests/CapacitorTests/BridgedTypesTests.swift b/ios/Tests/CapacitorTests/BridgedTypesTests.swift new file mode 100644 index 0000000000..36e7bbbbef --- /dev/null +++ b/ios/Tests/CapacitorTests/BridgedTypesTests.swift @@ -0,0 +1,218 @@ +import Foundation +import Testing +@testable import Capacitor + +private class TestContainer: NSObject, JSValueContainer { + var coercedDictionary: [AnyHashable: Any] = [:] + + public static var jsDateFormatter: ISO8601DateFormatter = { + return ISO8601DateFormatter() + }() + + public var jsObjectRepresentation: JSObject { + return coercedDictionary as? JSObject ?? [:] + } +} + +struct BridgedTypesTests { + private static let fixture = BridgedTypesFixture() + + private struct BridgedTypesFixture { + let unserializedDictionary: [AnyHashable: Any] + let deserializedDictionary: [AnyHashable: Any] + + init() { + let formatter = ISO8601DateFormatter() + let date = NSDate(timeIntervalSinceReferenceDate: 632854800) + let subDictionary: [AnyHashable: Any] = [ + "testIntArray": [0, 1, 2], + "testStringArray": ["1", "2", "3"], + "testDictionary": ["foo": "bar"] + ] + var dictionary: [AnyHashable: Any] = [ + "testInt": 1 as Int, + "testFloat": Float.pi, + "testBool": true as Bool, + "testString": "Some string value", + "testChild": subDictionary, + "testDateString": formatter.string(from: date as Date) + ] + let serializer = JSONSerializationWrapper(dictionary: dictionary)! + var unwrappedResult = serializer.unwrappedResult()! + unwrappedResult["testDateObject"] = date + dictionary["testDateObject"] = date + self.unserializedDictionary = dictionary + self.deserializedDictionary = unwrappedResult + } + } + + @Test func testTranslation() throws { + let unserializedDictionary = Self.fixture.unserializedDictionary + let deserializedDictionary = Self.fixture.deserializedDictionary + let testContainer = TestContainer() + testContainer.coercedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! + + #expect(unserializedDictionary.count > 0) + #expect(deserializedDictionary.count > 0) + #expect(testContainer.coercedDictionary.count > 0) + } + + @Test func testCastingFailure() throws { + let deserializedDictionary = Self.fixture.deserializedDictionary + let unserializedDictionary = Self.fixture.unserializedDictionary + + var castResult = deserializedDictionary as? JSObject + #expect(castResult == nil) + + castResult = unserializedDictionary as? JSObject + #expect(castResult == nil) + } + + @Test func testCoercionSuccess() throws { + let deserializedDictionary = Self.fixture.deserializedDictionary + let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary) + #expect(coercedResult != nil) + } + + @Test func testRoundtripEquality() throws { + let deserializedDictionary = Self.fixture.deserializedDictionary + let unserializedDictionary = Self.fixture.unserializedDictionary + let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! + let foo: NSDictionary = coercedResult as NSDictionary + let bar: NSDictionary = unserializedDictionary as NSDictionary + + #expect(foo == bar) + } + + @Test func testTypeEquivalency() throws { + let deserializedDictionary = Self.fixture.deserializedDictionary + let unserializedDictionary = Self.fixture.unserializedDictionary + let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! + let coercedFloat = coercedResult["testFloat"] as? Float + let sourceFloat = unserializedDictionary["testFloat"] as? Float + let resultFloat = deserializedDictionary["testFloat"] as? Float + + #expect(coercedFloat != nil) + #expect(sourceFloat != nil) + #expect(resultFloat != nil) + + #expect(coercedFloat == sourceFloat) + #expect(sourceFloat == resultFloat) + #expect(coercedFloat == Float.pi) + } + + @Test func testNumberWrapping() throws { + let deserializedDictionary = Self.fixture.deserializedDictionary + let unserializedDictionary = Self.fixture.unserializedDictionary + let testContainer = TestContainer() + testContainer.coercedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! + + let sourceFloat = unserializedDictionary["testFloat"]! + #expect(type(of: sourceFloat) == Float.self) + + let wrappedFloat = deserializedDictionary["testFloat"]! + let underlyingType: AnyObject.Type = NSClassFromString("__NSCFNumber")! + #expect(type(of: wrappedFloat) == underlyingType.self) + + let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! + let coercedFloat = coercedResult["testFloat"]! + #expect(type(of: coercedFloat) == underlyingType.self) + + let castFloat = testContainer.getFloat("testFloat")! + #expect(type(of: castFloat) == Float.self) + #expect((sourceFloat as! Float) == castFloat) + } + + @Test func testDateObject() throws { + let deserializedDictionary = Self.fixture.deserializedDictionary + let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! + let date = coercedResult["testDateObject"] as! Date + #expect(date != nil) + #expect(type(of: date) == Date.self) + } + + @Test func testDateParsing() throws { + let deserializedDictionary = Self.fixture.deserializedDictionary + let coercedResult = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! + let formatter = ISO8601DateFormatter() + let parsedDate = formatter.date(from: coercedResult["testDateString"] as! String)! + let dateObject = coercedResult["testDateObject"] as! Date + #expect(parsedDate != nil) + #expect(dateObject != nil) + #expect(dateObject.compare(parsedDate) == .orderedSame) + } + + @Test func testDateExtensions() throws { + let deserializedDictionary = Self.fixture.deserializedDictionary + let testContainer = TestContainer() + testContainer.coercedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary)! + + let parsedDate = testContainer.getDate("testDateString")! + let dateObject = testContainer.getDate("testDateObject")! + #expect(parsedDate != nil) + #expect(dateObject != nil) + #expect(dateObject.compare(parsedDate) == .orderedSame) + } + + @Test func testDateCoercion() throws { + let deserializedDictionary = Self.fixture.deserializedDictionary + let stringifiedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary, formattingDatesAsStrings: true)! + let unstringifiedDictionary = JSTypes.coerceDictionaryToJSObject(deserializedDictionary, formattingDatesAsStrings: false)! + let stringifiedValue = stringifiedDictionary["testDateObject"]! + let unstringifiedValue = unstringifiedDictionary["testDateObject"]! + #expect(type(of: stringifiedValue) == String.self) + #expect(type(of: unstringifiedValue) == Date.self) + #expect((stringifiedValue as! String) == (stringifiedDictionary["testDateString"] as! String)) + } + + @Test func testDateResultWrapping() throws { + let unserializedDictionary = Self.fixture.unserializedDictionary + let result = try PluginCallResult.dictionary(["date": unserializedDictionary["testDateObject"]!]).jsonRepresentation() + #expect(result == "{\"date\":\"\(unserializedDictionary["testDateString"] as! String)\"}") + } + + @Test func testResultMerging() throws { + let result = try PluginCallResult.dictionary(["number": 1]).jsonRepresentation(includingFields: ["string": "foo"]) + let isValid = result == "{\"string\":\"foo\",\"number\":1}" || result == "{\"number\":1,\"string\":\"foo\"}" + #expect(isValid) + } + + @Test func testNullWrapping() throws { + let dictionary: [AnyHashable: Any] = ["testInt": 1 as Int, "testNull": NSNull()] + let coercedDictionary = JSTypes.coerceDictionaryToJSObject(dictionary)! + #expect(coercedDictionary != nil) + #expect(coercedDictionary.count == 2) + #expect(coercedDictionary["testNull"]! is NSNull) + } + + @Test func testNullTransformation() throws { + let array: [Any] = [1, NSNull(), "test string"] + let coercedArray = JSTypes.coerceArrayToJSArray(array)! + #expect(coercedArray != nil) + #expect(coercedArray.count == 3) + #expect(type(of: coercedArray[1]) == NSNull.self) + let filteredArray = coercedArray.capacitor.replacingNullValues() + #expect(filteredArray.count == 3) + #expect(filteredArray[1] == nil) + let restoredArray = filteredArray.capacitor.replacingOptionalValues() + #expect(restoredArray.count == 3) + #expect(restoredArray[1] != nil) + #expect(restoredArray[0] is NSNumber) + #expect(restoredArray[1] is NSNull) + #expect(restoredArray[2] is String) + } + + @Test func testSparseArrayCastSuccess() throws { + let array: [Any] = ["test string 1", "test string 2", NSNull()] + let sparseArray = JSTypes.coerceArrayToJSArray(array)?.capacitor.replacingNullValues() as? [String?] + #expect(sparseArray != nil) + #expect(sparseArray!.count == 3) + #expect(sparseArray![2] == nil) + } + + @Test func testSparseArrayCastFailure() throws { + let array: [Any] = ["test string 1", 1, NSNull()] + let sparseArray = JSTypes.coerceArrayToJSArray(array)?.capacitor.replacingNullValues() as? [String?] + #expect(sparseArray == nil) + } +} diff --git a/ios/Tests/CapacitorTests/CodableTests.swift b/ios/Tests/CapacitorTests/CodableTests.swift new file mode 100644 index 0000000000..972ef394f2 --- /dev/null +++ b/ios/Tests/CapacitorTests/CodableTests.swift @@ -0,0 +1,195 @@ +import Foundation +import Testing +import Capacitor + +private struct Pet: Codable, Equatable { + var name: String + var breed: String + var isVaccinated: Bool +} + +private struct Person: Codable, Equatable { + var name: String + var age: UInt + var pet: Pet? + var family: [Person]? +} + +private let rawPet: JSObject = [ + "name": "Penny", + "breed": "Chihuahua", + "isVaccinated": true +] + +private let rawPeople: JSArray = [ + [ "name": "Anakin", + "age": 41 as NSNumber + ], + [ "name": "Leia", + "age": 20 as NSNumber + ] +] + +private let rawPerson: JSObject = [ + "name": "Luke", + "age": 20 as NSNumber, + "pet": rawPet, + "family": rawPeople +] + +private let person = Person( + name: "Luke", + age: 20, + pet: .init( + name: "Penny", + breed: "Chihuahua", + isVaccinated: true + ), + family: [ + Person(name: "Anakin", age: 41), + Person(name: "Leia", age: 20) + ] +) + +struct JSValueDecoderTests { + @Test func decodingValidKeyedContainerSucceeds() throws { + let decoder = JSValueDecoder() + let decodedPerson = try decoder.decode(Person.self, from: rawPerson) + #expect(decodedPerson == person) + } + + @Test func decodingValidUnkeyedContainerSucceeds() throws { + let decoder = JSValueDecoder() + let decodedPeople = try decoder.decode([Person].self, from: rawPeople) + #expect(person.family == decodedPeople) + } + + @Test func decodingSingleValueSucceeds() throws { + let decoder = JSValueDecoder() + let decodedNumber = try decoder.decode(UInt.self, from: 100 as NSNumber) + #expect(decodedNumber == 100) + } + + @Test func decodingInvalidKeyedContainerFails() throws { + let decoder = JSValueDecoder() + var invalidRawPerson = rawPerson + invalidRawPerson["name"] = nil + #expect(throws: DecodingError.self) { + try decoder.decode(Person.self, from: invalidRawPerson) + } + } + + @Test func decodingInvalidUnkeyedContainerFails() throws { + let decoder = JSValueDecoder() + var invalidRawPeople = try #require(rawPeople as? [JSObject]) + invalidRawPeople[0]["name"] = nil + #expect(throws: DecodingError.self) { + try decoder.decode([Person].self, from: invalidRawPeople) + } + } + + @Test func decodingInvalidSingleValueTypeFails() throws { + let decoder = JSValueDecoder() + #expect(throws: DecodingError.self) { + try decoder.decode(UInt.self, from: -1 as NSNumber) + } + } + + @Test func decodingValidNestedArraySucceeds() throws { + let decoder = JSValueDecoder() + let nestedPeople: JSArray = [rawPeople, rawPeople] + let decodedPeople = try decoder.decode([[Person]].self, from: nestedPeople) + #expect([person.family, person.family] == decodedPeople) + } + + @Test func decodingClassFails() throws { + class Pet: Decodable { + var name: String + var breed: String + var isVaccinated: String + init(name: String, breed: String, isVaccinated: String) { + self.name = name + self.breed = breed + self.isVaccinated = isVaccinated + } + } + + let decoder = JSValueDecoder() + #expect(throws: DecodingError.self) { + try decoder.decode(Pet.self, from: rawPet) + } + } + + @Test func decodingNSNullToNilSucceeds() throws { + let decoder = JSValueDecoder() + var rawPersonWithNull = rawPerson + rawPersonWithNull["pet"] = NSNull() + + let decodedPerson = try decoder.decode(Person.self, from: rawPersonWithNull) + #expect(decodedPerson.pet == nil) + } +} + +struct JSValueEncoderTests { + @Test func encodingNonclassCodableSucceeds() throws { + let encoder = JSValueEncoder() + let encodedValue = try encoder.encode(person) + let encodedObject = try #require(encodedValue as? JSObject) + + let name = try #require(encodedObject["name"] as? String) + #expect(person.name == name) + let age = try #require(encodedObject["age"] as? NSNumber) + #expect(person.age as NSNumber == age) + + let pet = try #require(encodedObject["pet"] as? JSObject) + let petName = try #require(pet["name"] as? String) + #expect(person.pet?.name == petName) + let petBreed = try #require(pet["breed"] as? String) + #expect(person.pet?.breed == petBreed) + let petIsVaccinated = try #require(pet["isVaccinated"] as? Bool) + #expect(person.pet?.isVaccinated == petIsVaccinated) + + let family = try #require(encodedObject["family"] as? [JSObject]) + #expect(person.family?.count == family.count) + let aniName = try #require(family[0]["name"] as? String) + #expect(person.family?[0].name == aniName) + let aniAge = try #require(family[0]["age"] as? NSNumber) + #expect(person.family?[0].age as? NSNumber == aniAge) + + let leiaName = try #require(family[1]["name"] as? String) + #expect(person.family?[1].name == leiaName) + let leiaAge = try #require(family[1]["age"] as? NSNumber) + #expect(person.family?[1].age as? NSNumber == leiaAge) + } + + @Test func encodingNestedUnkeyedContainerSucceeds() throws { + let encoder = JSValueEncoder() + let encodedValue = try encoder.encode([person.family, person.family]) + let encodedArray = try #require(encodedValue as? [[JSObject]]) + #expect(encodedArray.count == 2) + #expect(encodedArray[0].count == 2) + #expect(encodedArray[1].count == 2) + + let family = try #require(person.family) + + #expect(family[0].name == encodedArray[0][0]["name"] as? String) + #expect(family[0].name == encodedArray[1][0]["name"] as? String) + #expect(family[0].age as NSNumber == encodedArray[0][0]["age"] as? NSNumber) + #expect(family[0].age as NSNumber == encodedArray[1][0]["age"] as? NSNumber) + #expect(family[1].name == encodedArray[0][1]["name"] as? String) + #expect(family[1].name == encodedArray[1][1]["name"] as? String) + #expect(family[1].age as NSNumber == encodedArray[0][1]["age"] as? NSNumber) + #expect(family[1].age as NSNumber == encodedArray[1][1]["age"] as? NSNumber) + } + + @Test func encodingNilWithExplicitNullsSucceeds() throws { + struct Test: Encodable { + var name: String? + } + + let explicitEncoder = JSValueEncoder(optionalEncodingStrategy: .explicitNulls) + let encoded = try #require(try explicitEncoder.encode(Test()) as? JSObject) + #expect(encoded["name"] is NSNull) + #expect(encoded["name"] != nil) + } +} diff --git a/ios/Tests/CapacitorTests/ConfigurationTests.swift b/ios/Tests/CapacitorTests/ConfigurationTests.swift new file mode 100644 index 0000000000..f87be78e14 --- /dev/null +++ b/ios/Tests/CapacitorTests/ConfigurationTests.swift @@ -0,0 +1,192 @@ +import Foundation +import Testing +import UIKit +@testable import Capacitor + +struct ConfigurationTests { + enum ConfigFile: String, CaseIterable { + case flat = "flat" + case nested = "hierarchy" + case server = "server" + case invalid = "bad" + case deprecated = "hidinglogs" + case nonparsable = "nonjson" + } + + private static let configFiles = loadConfigFiles() + + private static func loadConfigFiles() -> [ConfigFile: URL] { + var files: [ConfigFile: URL] = [:] + for file in ConfigFile.allCases { + if let url = Bundle.module.url(forResource: file.rawValue, withExtension: "json", subdirectory: "configurations") { + files[file] = url + } + } + return files + } + + private func getConfigURL() -> URL { + Bundle.module.resourceURL?.appendingPathComponent("configurations") ?? + Bundle.module.resourceURL ?? Bundle.main.resourceURL ?? URL(fileURLWithPath: "/") + } + + @Test func defaultErrors() throws { + let descriptor = InstanceDescriptor.init() + #expect(descriptor.warnings.contains(.missingAppDir)) + #expect(descriptor.warnings.contains(.missingFile)) + } + + @Test func missingAppDetection() throws { + var url = getConfigURL() + url.appendPathComponent("app", isDirectory: true) + let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil) + #expect(descriptor.warnings.contains(.missingAppDir)) + } + + @Test func failedParsing() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.nonparsable], cordovaConfiguration: nil) + #expect(descriptor.warnings.contains(.invalidFile)) + } + + @Test func defaults() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil) + #expect(descriptor.backgroundColor == nil) + #expect(descriptor.urlScheme == "capacitor") + #expect(descriptor.urlHostname == "localhost") + #expect(descriptor.serverURL == nil) + #expect(descriptor.scrollingEnabled == true) + #expect(descriptor.loggingBehavior == .debug) + #expect(descriptor.allowLinkPreviews == true) + #expect(descriptor.contentInsetAdjustmentBehavior == .never) + } + + @Test func deprecatedParsing() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.deprecated], cordovaConfiguration: nil) + #expect(descriptor.loggingBehavior != .none) + } + + @Test func deprecatedOverrideParsing() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.server], cordovaConfiguration: nil) + #expect(descriptor.loggingBehavior == .production) + } + + @Test func topLevelParsing() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.flat], cordovaConfiguration: nil) + #expect(descriptor.backgroundColor == UIColor(red: 1, green: 1, blue: 1, alpha: 1)) + #expect(descriptor.overridenUserAgentString == "level 1 override") + #expect(descriptor.appendedUserAgentString == "level 1 append") + #expect(descriptor.loggingBehavior == .debug) + } + + @Test func nestedParsing() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.nested], cordovaConfiguration: nil) + #expect(descriptor.backgroundColor == UIColor(red: 0, green: 0, blue: 0, alpha: 1)) + #expect(descriptor.overridenUserAgentString == "level 2 override") + #expect(descriptor.appendedUserAgentString == "level 2 append") + #expect(descriptor.loggingBehavior == .none) + #expect(descriptor.scrollingEnabled == false) + #expect(descriptor.contentInsetAdjustmentBehavior == .scrollableAxes) + } + + @Test func serverParsing() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.server], cordovaConfiguration: nil) + #expect(descriptor.urlScheme == "override") + #expect(descriptor.urlHostname == "myhost") + #expect(descriptor.serverURL == "http://192.168.100.1:2057") + } + + @Test func badDataParsing() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.invalid], cordovaConfiguration: nil) + #expect(descriptor.backgroundColor == nil) + #expect(descriptor.loggingBehavior == .debug) + #expect(descriptor.contentInsetAdjustmentBehavior == .never) + } + + @Test func badDataTransformation() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.invalid], cordovaConfiguration: nil) + let configuration = InstanceConfiguration(with: descriptor, isDebug: true) + #expect(configuration.serverURL != URL(string: "capacitor://myhost")) + } + + @Test func serverTransformation() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.server], cordovaConfiguration: nil) + let configuration = InstanceConfiguration(with: descriptor, isDebug: true) + #expect(configuration.serverURL == URL(string: "http://192.168.100.1:2057")) + #expect(configuration.localURL == URL(string: "override://myhost")) + } + + @Test func pluginConfig() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.flat], cordovaConfiguration: nil) + let configuration = InstanceConfiguration(with: descriptor, isDebug: true) + let value = configuration.getPluginConfig("SplashScreen").getInt("launchShowDuration", 0) + #expect(value == 1) + } + + @Test func legacyConfig() throws { + let url = getConfigURL() + let flatDescriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.flat], cordovaConfiguration: nil) + let flatConfiguration = InstanceConfiguration(with: flatDescriptor, isDebug: true) + #expect(flatConfiguration.overridenUserAgentString == "level 1 override") + + let nestedDescriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.nested], cordovaConfiguration: nil) + let nestedConfiguration = InstanceConfiguration(with: nestedDescriptor, isDebug: true) + #expect(nestedConfiguration.overridenUserAgentString == "level 2 override") + } + + @Test func navigationRules() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: Self.configFiles[.server], cordovaConfiguration: nil) + let configuration = InstanceConfiguration(with: descriptor, isDebug: true) + #expect(configuration.shouldAllowNavigation(to: "ionic.io") == true) + #expect(configuration.shouldAllowNavigation(to: "ionic.io".uppercased()) == true) + #expect(configuration.shouldAllowNavigation(to: "test.capacitorjs.com") == true) + #expect(configuration.shouldAllowNavigation(to: "192.168.0.1") == true) + #expect(configuration.shouldAllowNavigation(to: "subdomain.test.ionicframework.com") == true) + #expect(configuration.shouldAllowNavigation(to: "wildcard1.wildcard2.example.com") == true) + #expect(configuration.shouldAllowNavigation(to: "wildcard1.example.com") == false) + #expect(configuration.shouldAllowNavigation(to: "google.com") == false) + #expect(configuration.shouldAllowNavigation(to: "192.168.0.2") == false) + #expect(configuration.shouldAllowNavigation(to: "ionicframework.com") == false) + } + + @Test func noLoggingTransformation() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil) + descriptor.loggingBehavior = .none + var configuration = InstanceConfiguration(with: descriptor, isDebug: false) + #expect(configuration.loggingEnabled == false) + configuration = InstanceConfiguration(with: descriptor, isDebug: true) + #expect(configuration.loggingEnabled == false) + } + + @Test func debugLoggingTransformation() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil) + descriptor.loggingBehavior = .debug + var configuration = InstanceConfiguration(with: descriptor, isDebug: false) + #expect(configuration.loggingEnabled == false) + configuration = InstanceConfiguration(with: descriptor, isDebug: true) + #expect(configuration.loggingEnabled == true) + } + + @Test func productionLoggingTransformation() throws { + let url = getConfigURL() + let descriptor = InstanceDescriptor.init(at: url, configuration: nil, cordovaConfiguration: nil) + descriptor.loggingBehavior = .production + var configuration = InstanceConfiguration(with: descriptor, isDebug: false) + #expect(configuration.loggingEnabled == true) + configuration = InstanceConfiguration(with: descriptor, isDebug: true) + #expect(configuration.loggingEnabled == true) + } +} diff --git a/ios/Tests/CapacitorTests/DataCodableTests.swift b/ios/Tests/CapacitorTests/DataCodableTests.swift new file mode 100644 index 0000000000..01645a9c33 --- /dev/null +++ b/ios/Tests/CapacitorTests/DataCodableTests.swift @@ -0,0 +1,148 @@ +import Foundation +import Testing +import Capacitor + +private struct Foo: Codable, Equatable { + var data: Data +} + +private let jsonString = #"{ "key": "value" }"# +private let jsonData = jsonString.data(using: .utf8)! +private let jsonByteArray: [NSNumber] = [123, 32, 34, 107, 101, 121, 34, 58, 32, 34, 118, 97, 108, 117, 101, 34, 32, 125] +private let jsonBase64 = "eyAia2V5IjogInZhbHVlIiB9" + +private let customDecodingStrategy = JSValueDecoder.DataDecodingStrategy.custom { decoder in + var container = try decoder.unkeyedContainer() + var byteArray: [UInt8] = [] + while !container.isAtEnd { + byteArray.append(try container.decode(UInt8.self)) + } + return Data(byteArray) +} + +private let customEncodingStrategy = JSValueEncoder.DataEncodingStrategy.custom { data, encoder in + let byteArray = data.map { $0 } + var unkeyedContainer = encoder.unkeyedContainer() + try unkeyedContainer.encode(contentsOf: byteArray) +} + +struct JSValueDecoderDataTests { + @Test func decodingDataDefaultRoot() throws { + let decoder = JSValueDecoder() + let result = try decoder.decode(Data.self, from: jsonByteArray) + #expect(result == jsonData) + } + + @Test func decodingDataDefaultArray() throws { + let decoder = JSValueDecoder() + let result = try decoder.decode([Data].self, from: [jsonByteArray, jsonByteArray]) + #expect(result == [jsonData, jsonData]) + } + + @Test func decodingDataDefaultStruct() throws { + let decoder = JSValueDecoder() + let result = try decoder.decode(Foo.self, from: ["data": jsonByteArray]) + #expect(result == .init(data: jsonData)) + } + + @Test func decodingDataBase64Root() throws { + let decoder = JSValueDecoder(dataDecodingStrategy: .base64) + let result = try decoder.decode(Data.self, from: jsonBase64) + #expect(result == jsonData) + } + + @Test func decodingDataBase64Array() throws { + let decoder = JSValueDecoder(dataDecodingStrategy: .base64) + let result = try decoder.decode([Data].self, from: [jsonBase64, jsonBase64]) + #expect(result == [jsonData, jsonData]) + } + + @Test func decodingDataBase64Struct() throws { + let decoder = JSValueDecoder(dataDecodingStrategy: .base64) + let result = try decoder.decode(Foo.self, from: ["data": jsonBase64]) + #expect(result == .init(data: jsonData)) + } + + @Test func decodingDataCustomRoot() throws { + let decoder = JSValueDecoder(dataDecodingStrategy: customDecodingStrategy) + let result = try decoder.decode(Data.self, from: jsonByteArray) + #expect(result == jsonData) + } + + @Test func decodingDataCustomArray() throws { + let decoder = JSValueDecoder(dataDecodingStrategy: customDecodingStrategy) + let result = try decoder.decode([Data].self, from: [jsonByteArray, jsonByteArray]) + #expect(result == [jsonData, jsonData]) + } + + @Test func decodingDataCustomStruct() throws { + let decoder = JSValueDecoder(dataDecodingStrategy: customDecodingStrategy) + let result = try decoder.decode(Foo.self, from: ["data": jsonByteArray]) + #expect(result == .init(data: jsonData)) + } +} + +struct JSValueEncoderDataTests { + @Test func encodingDataDefaultRoot() throws { + let encoder = JSValueEncoder() + let rawResult = try encoder.encode(jsonData) + let result = try #require(rawResult as? [NSNumber]) + #expect(result == jsonByteArray) + } + + @Test func encodingDataDefaultArray() throws { + let encoder = JSValueEncoder() + let rawResult = try encoder.encode([jsonData, jsonData]) + let result = try #require(rawResult as? [[NSNumber]]) + #expect(result == [jsonByteArray, jsonByteArray]) + } + + @Test func encodingDataDefaultStruct() throws { + let encoder = JSValueEncoder() + let rawResult = try encoder.encode(Foo(data: jsonData)) + let result = try #require(rawResult as? [String: [NSNumber]]) + #expect(result == ["data": jsonByteArray]) + } + + @Test func encodingDataBase64Root() throws { + let encoder = JSValueEncoder(dataEncodingStrategy: .base64) + let rawResult = try encoder.encode(jsonData) + let result = try #require(rawResult as? String) + #expect(result == jsonBase64) + } + + @Test func encodingDataBase64Array() throws { + let encoder = JSValueEncoder(dataEncodingStrategy: .base64) + let rawResult = try encoder.encode([jsonData, jsonData]) + let result = try #require(rawResult as? [String]) + #expect(result == [jsonBase64, jsonBase64]) + } + + @Test func encodingDataBase64Struct() throws { + let encoder = JSValueEncoder(dataEncodingStrategy: .base64) + let rawResult = try encoder.encode(Foo(data: jsonData)) + let result = try #require(rawResult as? [String: String]) + #expect(result == ["data": jsonBase64]) + } + + @Test func encodingDataCustomRoot() throws { + let encoder = JSValueEncoder(dataEncodingStrategy: customEncodingStrategy) + let rawResult = try encoder.encode(jsonData) + let result = try #require(rawResult as? [NSNumber]) + #expect(result == jsonByteArray) + } + + @Test func encodingDataCustomArray() throws { + let encoder = JSValueEncoder(dataEncodingStrategy: customEncodingStrategy) + let rawResult = try encoder.encode([jsonData, jsonData]) + let result = try #require(rawResult as? [[NSNumber]]) + #expect(result == [jsonByteArray, jsonByteArray]) + } + + @Test func encodingDataCustomStruct() throws { + let encoder = JSValueEncoder(dataEncodingStrategy: customEncodingStrategy) + let rawResult = try encoder.encode(Foo(data: jsonData)) + let result = try #require(rawResult as? [String: [NSNumber]]) + #expect(result == ["data": jsonByteArray]) + } +} diff --git a/ios/Capacitor/CodableTests/DateCodableTests.swift b/ios/Tests/CapacitorTests/DateCodableTests.swift similarity index 62% rename from ios/Capacitor/CodableTests/DateCodableTests.swift rename to ios/Tests/CapacitorTests/DateCodableTests.swift index 656832acff..69319330ed 100644 --- a/ios/Capacitor/CodableTests/DateCodableTests.swift +++ b/ios/Tests/CapacitorTests/DateCodableTests.swift @@ -1,15 +1,7 @@ -// -// DateCodableTests.swift -// CodableTests -// -// Created by Steven Sherry on 9/6/24. -// Copyright © 2024 Drifty Co. All rights reserved. -// - -import XCTest +import Foundation +import Testing import Capacitor -// Fixture data that all refers to the same Date and Time private let timeIntervalSinceReferenceDate: TimeInterval = 747268580 private let referenceDate = Date(timeIntervalSinceReferenceDate: timeIntervalSinceReferenceDate) private let secondsSince1970 = 1725575780 as Double @@ -24,45 +16,45 @@ private let formatter: DateFormatter = { formatter.locale = .init(identifier: "en_US") return formatter }() -private let formatted = "Sep 5, 2024 at 5:36:20 PM CDT" +private let formatted = "Sep 5, 2024 at 5:36:20 PM CDT" private struct Foo: Codable, Equatable { var date: Date } -final class JSValueDecoderDateTests: XCTestCase { - func testDecode_date__default() throws { +struct JSValueDecoderDateTests { + @Test func decodingDateDefault() throws { let reference = timeIntervalSinceReferenceDate let decoder = JSValueDecoder() let result = try decoder.decode(Date.self, from: reference) - XCTAssertEqual(result, referenceDate) + #expect(result == referenceDate) } - func testDecode_date__secondsSince1970() throws { + @Test func decodingDateSecondsSince1970() throws { let decoder = JSValueDecoder(dateDecodingStrategy: .secondsSince1970) let result = try decoder.decode(Date.self, from: secondsSince1970) - XCTAssertEqual(result, referenceDate) + #expect(result == referenceDate) } - func testDecode_date__millisecondsSince1970() throws { + @Test func decodingDateMillisecondsSince1970() throws { let decoder = JSValueDecoder(dateDecodingStrategy: .millisecondsSince1970) let result = try decoder.decode(Date.self, from: millisecondsSince1970) - XCTAssertEqual(result, referenceDate) + #expect(result == referenceDate) } - func testDecode_date__iso8601() throws { + @Test func decodingDateISO8601() throws { let decoder = JSValueDecoder(dateDecodingStrategy: .iso8601) let result = try decoder.decode(Date.self, from: iso8601) - XCTAssertEqual(result, referenceDate) + #expect(result == referenceDate) } - func testDecode_date__formatted() throws { + @Test func decodingDateFormatted() throws { let decoder = JSValueDecoder(dateDecodingStrategy: .formatted(formatter)) let result = try decoder.decode(Date.self, from: formatted) - XCTAssertEqual(result, referenceDate) + #expect(result == referenceDate) } - func testDecode_date__custom() throws { + @Test func decodingDateCustom() throws { let strategy = JSValueDecoder.DateDecodingStrategy.custom { decoder in let container = try decoder.singleValueContainer() let referenceDateString = try container.decode(String.self) @@ -75,61 +67,61 @@ final class JSValueDecoderDateTests: XCTestCase { let referenceString = "\(timeIntervalSinceReferenceDate)" let decoder = JSValueDecoder(dateDecodingStrategy: strategy) let result = try decoder.decode(Date.self, from: referenceString) - XCTAssertEqual(result, referenceDate) + #expect(result == referenceDate) } - func testDecode_date__array() throws { + @Test func decodingDateArray() throws { let dateArray = [iso8601, iso8601] let decoder = JSValueDecoder(dateDecodingStrategy: .iso8601) let result = try decoder.decode([Date].self, from: dateArray) - XCTAssertEqual(result, [referenceDate, referenceDate]) + #expect(result == [referenceDate, referenceDate]) } - func testDecode_date__struct() throws { + @Test func decodingDateStruct() throws { let value = ["date": iso8601] as JSObject let decoder = JSValueDecoder(dateDecodingStrategy: .iso8601) let result = try decoder.decode(Foo.self, from: value) - XCTAssertEqual(result, Foo(date: referenceDate)) + #expect(result == Foo(date: referenceDate)) } } -final class JSValueEncoderDateTests: XCTestCase { - func testEncode_date__default() throws { +struct JSValueEncoderDateTests { + @Test func encodingDateDefault() throws { let encoder = JSValueEncoder() let rawResult = try encoder.encode(referenceDate) - let result = try XCTUnwrap(rawResult as? Double) - XCTAssertEqual(result, timeIntervalSinceReferenceDate) + let result = try #require(rawResult as? Double) + #expect(result == timeIntervalSinceReferenceDate) } - func testEncode_date__secondsSince1970() throws { + @Test func encodingDateSecondsSince1970() throws { let encoder = JSValueEncoder(dateEncodingStrategy: .secondsSince1970) let rawResult = try encoder.encode(referenceDate) - let result = try XCTUnwrap(rawResult as? Double) - XCTAssertEqual(result, secondsSince1970) + let result = try #require(rawResult as? Double) + #expect(result == secondsSince1970) } - func testEncode_date__millisecondsSince1970() throws { + @Test func encodingDateMillisecondsSince1970() throws { let encoder = JSValueEncoder(dateEncodingStrategy: .millisecondsSince1970) let rawResult = try encoder.encode(referenceDate) - let result = try XCTUnwrap(rawResult as? Double) - XCTAssertEqual(result, millisecondsSince1970) + let result = try #require(rawResult as? Double) + #expect(result == millisecondsSince1970) } - func testEncode_date__iso8601() throws { + @Test func encodingDateISO8601() throws { let encoder = JSValueEncoder(dateEncodingStrategy: .iso8601) let rawResult = try encoder.encode(referenceDate) - let result = try XCTUnwrap(rawResult as? String) - XCTAssertEqual(result, iso8601) + let result = try #require(rawResult as? String) + #expect(result == iso8601) } - func testEncode_date__formatted() throws { + @Test func encodingDateFormatted() throws { let encoder = JSValueEncoder(dateEncodingStrategy: .formatted(formatter)) let rawResult = try encoder.encode(referenceDate) - let result = try XCTUnwrap(rawResult as? String) - XCTAssertEqual(result, formatted) + let result = try #require(rawResult as? String) + #expect(result == formatted) } - func testEncode_date__custom() throws { + @Test func encodingDateCustom() throws { let strategy = JSValueEncoder.DateEncodingStrategy.custom { date, encoder in var container = encoder.singleValueContainer() try container.encode("\(date.timeIntervalSinceReferenceDate)") @@ -137,22 +129,22 @@ final class JSValueEncoderDateTests: XCTestCase { let encoder = JSValueEncoder(dateEncodingStrategy: strategy) let rawResult = try encoder.encode(referenceDate) - let result = try XCTUnwrap(rawResult as? String) - XCTAssertEqual(result, "\(timeIntervalSinceReferenceDate)") + let result = try #require(rawResult as? String) + #expect(result == "\(timeIntervalSinceReferenceDate)") } - func testEncode_date__array() throws { + @Test func encodingDateArray() throws { let encoder = JSValueEncoder(dateEncodingStrategy: .iso8601) let array = [referenceDate, referenceDate] let rawResult = try encoder.encode(array) - let result = try XCTUnwrap(rawResult as? [String]) - XCTAssertEqual(result, [iso8601, iso8601]) + let result = try #require(rawResult as? [String]) + #expect(result == [iso8601, iso8601]) } - func testEncode_date__struct() throws { + @Test func encodingDateStruct() throws { let encoder = JSValueEncoder(dateEncodingStrategy: .iso8601) let rawResult = try encoder.encode(Foo(date: referenceDate)) - let result = try XCTUnwrap(rawResult as? [String: String]) - XCTAssertEqual(result, ["date": iso8601]) + let result = try #require(rawResult as? [String: String]) + #expect(result == ["date": iso8601]) } } diff --git a/ios/Tests/CapacitorTests/JSExportTests.swift b/ios/Tests/CapacitorTests/JSExportTests.swift new file mode 100644 index 0000000000..6d2f9805cb --- /dev/null +++ b/ios/Tests/CapacitorTests/JSExportTests.swift @@ -0,0 +1,10 @@ +import Testing +import WebKit +@testable import Capacitor + +struct JSExportTests { + @Test func bridgeBundleExports() throws { + let contentController = WKUserContentController() + try Capacitor.JSExport.exportBridgeJS(userContentController: contentController) + } +} diff --git a/ios/Tests/CapacitorTests/JSONSerializationWrapper.swift b/ios/Tests/CapacitorTests/JSONSerializationWrapper.swift new file mode 100644 index 0000000000..898382c7ff --- /dev/null +++ b/ios/Tests/CapacitorTests/JSONSerializationWrapper.swift @@ -0,0 +1,16 @@ +import Foundation + +final class JSONSerializationWrapper { + let dictionary: [AnyHashable: Any] + + init?(dictionary: [AnyHashable: Any]) { + self.dictionary = dictionary + } + + func unwrappedResult() -> [AnyHashable: Any]? { + guard let serializedData = try? JSONSerialization.data(withJSONObject: dictionary, options: [.prettyPrinted]) else { + return nil + } + return try? JSONSerialization.jsonObject(with: serializedData, options: []) as? [AnyHashable: Any] + } +} diff --git a/ios/Capacitor/CodableTests/NestedCodableTests.swift b/ios/Tests/CapacitorTests/NestedCodableTests.swift similarity index 64% rename from ios/Capacitor/CodableTests/NestedCodableTests.swift rename to ios/Tests/CapacitorTests/NestedCodableTests.swift index 32ac2f68e0..89ef5ad351 100644 --- a/ios/Capacitor/CodableTests/NestedCodableTests.swift +++ b/ios/Tests/CapacitorTests/NestedCodableTests.swift @@ -1,15 +1,8 @@ -// -// CodableTests.swift -// CodableTests -// -// Created by Steven Sherry on 12/10/23. -// Copyright © 2023 Drifty Co. All rights reserved. -// - -import XCTest +import Foundation +import Testing import Capacitor -final class NestedCodableTests: XCTestCase { +struct NestedCodableTests { private let nestedData: JSObject = [ "id": 1, "user": [ @@ -30,34 +23,32 @@ final class NestedCodableTests: XCTestCase { reviewCount: 4 ) - func testDecode__when_decoding_a_decodable_value_with_a_custom_implementation_with_nested_values__it_successfully_decodes() throws { + @Test func decodingNestedValueWithCustomImplementation() throws { let decoder = JSValueDecoder() let decoded = try decoder.decode(Flattened.self, from: nestedData) - XCTAssertEqual(decoded, flatData) + #expect(decoded == flatData) } - func testEncode__when_encoding_an_encodable_value_with_a_custom_implementation_with_nested_values__it_successfully_encodes() throws { + @Test func encodingNestedValueWithCustomImplementation() throws { let encoder = JSValueEncoder() - let encoded = try XCTUnwrap(try encoder.encode(flatData) as? JSObject) - - print(encoded) - let encodedId = try XCTUnwrap(encoded["id"] as? NSNumber) - let encodedUser = try XCTUnwrap(encoded["user"] as? JSObject) - let encodedUserName = try XCTUnwrap(encodedUser["userName"] as? String) - let encodedRealInfo = try XCTUnwrap(encodedUser["realInfo"] as? JSObject) - let encodedFullName = try XCTUnwrap(encodedRealInfo["fullName"] as? String) - let encodedReviewCount = try XCTUnwrap(encoded["reviewCount"] as? JSArray) - let encodedCountEntry = try XCTUnwrap(encodedReviewCount[0] as? JSObject) - let encodedCount = try XCTUnwrap(encodedCountEntry["count"] as? NSNumber) - - XCTAssertEqual(encodedId, flatData.id as NSNumber) - XCTAssertEqual(encodedUserName, flatData.userName) - XCTAssertEqual(encodedFullName, flatData.fullName) - XCTAssertEqual(encodedCount, flatData.reviewCount as NSNumber) + let encoded = try #require(try encoder.encode(flatData) as? JSObject) + + let encodedId = try #require(encoded["id"] as? NSNumber) + let encodedUser = try #require(encoded["user"] as? JSObject) + let encodedUserName = try #require(encodedUser["userName"] as? String) + let encodedRealInfo = try #require(encodedUser["realInfo"] as? JSObject) + let encodedFullName = try #require(encodedRealInfo["fullName"] as? String) + let encodedReviewCount = try #require(encoded["reviewCount"] as? JSArray) + let encodedCountEntry = try #require(encodedReviewCount[0] as? JSObject) + let encodedCount = try #require(encodedCountEntry["count"] as? NSNumber) + + #expect(encodedId == flatData.id as NSNumber) + #expect(encodedUserName == flatData.userName) + #expect(encodedFullName == flatData.fullName) + #expect(encodedCount == flatData.reviewCount as NSNumber) } } -// Example taken from https://stackoverflow.com/questions/44549310/how-to-decode-a-nested-json-struct-with-swift-decodable-protocol private struct Flattened: Equatable { let id: Int let userName: String @@ -83,7 +74,6 @@ extension Flattened: Decodable { } init(from decoder: Decoder) throws { - // id let container = try decoder.container(keyedBy: RootKeys.self) id = try container.decode(Int.self, forKey: .id) let userContainer = try container.nestedContainer(keyedBy: UserKeys.self, forKey: .user) diff --git a/ios/Capacitor/CodableTests/NonconformingFloatCodableTests.swift b/ios/Tests/CapacitorTests/NonconformingFloatCodableTests.swift similarity index 50% rename from ios/Capacitor/CodableTests/NonconformingFloatCodableTests.swift rename to ios/Tests/CapacitorTests/NonconformingFloatCodableTests.swift index 9cfdb36156..69112d16a8 100644 --- a/ios/Capacitor/CodableTests/NonconformingFloatCodableTests.swift +++ b/ios/Tests/CapacitorTests/NonconformingFloatCodableTests.swift @@ -1,42 +1,34 @@ -// -// NonconformingFloatCodableTests.swift -// CodableTests -// -// Created by Steven Sherry on 9/6/24. -// Copyright © 2024 Drifty Co. All rights reserved. -// - -import XCTest +import Testing import Capacitor private struct Foo: Codable, Equatable { var number: Double } -class JSValueEncoderNonConformingFloatTests: XCTestCase { - func testEncode_float__default_root() throws { +struct JSValueEncoderNonConformingFloatTests { + @Test func encodingFloatDefaultRoot() throws { let encoder = JSValueEncoder() let rawResult = try encoder.encode(Double.infinity) - let result = try XCTUnwrap(rawResult as? Double) - XCTAssertEqual(result, .infinity) + let result = try #require(rawResult as? Double) + #expect(result == .infinity) } - func testEncode_float__default_array() throws { + @Test func encodingFloatDefaultArray() throws { let encoder = JSValueEncoder() let rawResult = try encoder.encode([Double.infinity, -.infinity, .nan]) - let result = try XCTUnwrap(rawResult as? [Double]) - XCTAssertEqual(result[0...1], [.infinity, -.infinity]) - XCTAssertTrue(result[2].isNaN) + let result = try #require(rawResult as? [Double]) + #expect(result[0...1] == [.infinity, -.infinity]) + #expect(result[2].isNaN) } - func testEncode_float__default_struct() throws { + @Test func encodingFloatDefaultStruct() throws { let encoder = JSValueEncoder() let rawResult = try encoder.encode(Foo.init(number: .infinity)) - let result = try XCTUnwrap(rawResult as? [String: Double]) - XCTAssertEqual(result, ["number": .infinity]) + let result = try #require(rawResult as? [String: Double]) + #expect(result == ["number": .infinity]) } - func testEncode_float__convertToString_root() throws { + @Test func encodingFloatConvertToStringRoot() throws { let encoder = JSValueEncoder( nonConformingFloatEncodingStategy: .convertToString( positiveInfinity: "pos", @@ -46,19 +38,19 @@ class JSValueEncoderNonConformingFloatTests: XCTestCase { ) var rawResult = try encoder.encode(Double.infinity) - var result = try XCTUnwrap(rawResult as? String) - XCTAssertEqual(result, "pos") + var result = try #require(rawResult as? String) + #expect(result == "pos") rawResult = try encoder.encode(-Double.infinity) - result = try XCTUnwrap(rawResult as? String) - XCTAssertEqual(result, "neg") + result = try #require(rawResult as? String) + #expect(result == "neg") rawResult = try encoder.encode(Double.nan) - result = try XCTUnwrap(rawResult as? String) - XCTAssertEqual(result, "nan") + result = try #require(rawResult as? String) + #expect(result == "nan") } - func testEncode_float__convertToString_array() throws { + @Test func encodingFloatConvertToStringArray() throws { let encoder = JSValueEncoder( nonConformingFloatEncodingStategy: .convertToString( positiveInfinity: "pos", @@ -68,11 +60,11 @@ class JSValueEncoderNonConformingFloatTests: XCTestCase { ) let rawResult = try encoder.encode([Double.infinity, -.infinity, .nan]) - let result = try XCTUnwrap(rawResult as? [String]) - XCTAssertEqual(result, ["pos", "neg", "nan"]) + let result = try #require(rawResult as? [String]) + #expect(result == ["pos", "neg", "nan"]) } - func testEncode_float__convertToString_struct() throws { + @Test func encodingFloatConvertToStringStruct() throws { let encoder = JSValueEncoder( nonConformingFloatEncodingStategy: .convertToString( positiveInfinity: "pos", @@ -82,92 +74,104 @@ class JSValueEncoderNonConformingFloatTests: XCTestCase { ) var rawResult = try encoder.encode(Foo(number: .infinity)) - var result = try XCTUnwrap(rawResult as? [String: String]) - XCTAssertEqual(result, ["number": "pos"]) + var result = try #require(rawResult as? [String: String]) + #expect(result == ["number": "pos"]) rawResult = try encoder.encode(Foo(number: -.infinity)) - result = try XCTUnwrap(rawResult as? [String: String]) - XCTAssertEqual(result, ["number": "neg"]) + result = try #require(rawResult as? [String: String]) + #expect(result == ["number": "neg"]) rawResult = try encoder.encode(Foo(number: .nan)) - result = try XCTUnwrap(rawResult as? [String: String]) - XCTAssertEqual(result, ["number": "nan"]) + result = try #require(rawResult as? [String: String]) + #expect(result == ["number": "nan"]) } - func testEncode_float__throw_root() throws { + @Test func encodingFloatThrowRoot() throws { let encoder = JSValueEncoder(nonConformingFloatEncodingStategy: .throw) - XCTAssertThrowsError(try encoder.encode(Double.infinity)) + #expect(throws: EncodingError.self) { + try encoder.encode(Double.infinity) + } } - func testEncode_float__throw_array() throws { + @Test func encodingFloatThrowArray() throws { let encoder = JSValueEncoder(nonConformingFloatEncodingStategy: .throw) - XCTAssertThrowsError(try encoder.encode([Double.infinity, -.infinity, .nan])) + #expect(throws: EncodingError.self) { + try encoder.encode([Double.infinity, -.infinity, .nan]) + } } - func testEncode_float__throw_struct() throws { + @Test func encodingFloatThrowStruct() throws { let encoder = JSValueEncoder(nonConformingFloatEncodingStategy: .throw) - XCTAssertThrowsError(try encoder.encode(Foo(number: .infinity))) + #expect(throws: EncodingError.self) { + try encoder.encode(Foo(number: .infinity)) + } } } -class JSValueDecoderNonConformingFloatTests: XCTestCase { - func testDecode_float__default_root() throws { +struct JSValueDecoderNonConformingFloatTests { + @Test func decodingFloatDefaultRoot() throws { let decoder = JSValueDecoder() let result = try decoder.decode(Double.self, from: Double.infinity) - XCTAssertEqual(result, .infinity) + #expect(result == .infinity) } - func testDecode_float__default_array() throws { + @Test func decodingFloatDefaultArray() throws { let decoder = JSValueDecoder() let result = try decoder.decode([Double].self, from: [Double.infinity, Double.infinity]) - XCTAssertEqual(result, [.infinity, .infinity]) + #expect(result == [.infinity, .infinity]) } - func testDecode_float__default_struct() throws { + @Test func decodingFloatDefaultStruct() throws { let decoder = JSValueDecoder() let result = try decoder.decode(Foo.self, from: ["number": Double.infinity]) - XCTAssertEqual(result, .init(number: .infinity)) + #expect(result == .init(number: .infinity)) } - func testDecode_float__throw_root() throws { + @Test func decodingFloatThrowRoot() throws { let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .throw) - XCTAssertThrowsError(try decoder.decode(Double.self, from: Double.infinity)) + #expect(throws: DecodingError.self) { + try decoder.decode(Double.self, from: Double.infinity) + } } - func testDecode_float__throw_array() throws { + @Test func decodingFloatThrowArray() throws { let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .throw) - XCTAssertThrowsError(try decoder.decode([Double].self, from: [Double.infinity, Double.infinity])) + #expect(throws: DecodingError.self) { + try decoder.decode([Double].self, from: [Double.infinity, Double.infinity]) + } } - func testDecode_float__throw_struct() throws { + @Test func decodingFloatThrowStruct() throws { let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .throw) - XCTAssertThrowsError(try decoder.decode(Foo.self, from: ["number": Double.infinity])) + #expect(throws: DecodingError.self) { + try decoder.decode(Foo.self, from: ["number": Double.infinity]) + } } - func testDecode_float__convertFromString_root() throws { + @Test func decodingFloatConvertFromStringRoot() throws { let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .convertFromString(positiveInfinity: "pos", negativeInfinity: "neg", nan: "nan")) var result = try decoder.decode(Double.self, from: "pos") - XCTAssertEqual(result, .infinity) + #expect(result == .infinity) result = try decoder.decode(Double.self, from: "neg") - XCTAssertEqual(result, -.infinity) + #expect(result == -.infinity) result = try decoder.decode(Double.self, from: "nan") - XCTAssertTrue(result.isNaN) + #expect(result.isNaN) } - func testDecode_float__convertFromString_array() throws { + @Test func decodingFloatConvertFromStringArray() throws { let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .convertFromString(positiveInfinity: "pos", negativeInfinity: "neg", nan: "nan")) let result = try decoder.decode([Double].self, from: ["pos", "neg", "nan"]) - XCTAssertEqual(result[0...1], [.infinity, -.infinity]) - XCTAssertTrue(result[2].isNaN) + #expect(result[0...1] == [.infinity, -.infinity]) + #expect(result[2].isNaN) } - func testDecode_float__convertFromString_struct() throws { + @Test func decodingFloatConvertFromStringStruct() throws { let decoder = JSValueDecoder(nonConformingFloatDecodingStrategy: .convertFromString(positiveInfinity: "pos", negativeInfinity: "neg", nan: "nan")) var result = try decoder.decode(Foo.self, from: ["number": "pos"]) - XCTAssertEqual(result, .init(number: .infinity)) + #expect(result == .init(number: .infinity)) result = try decoder.decode(Foo.self, from: ["number": "neg"]) - XCTAssertEqual(result, .init(number: -.infinity)) + #expect(result == .init(number: -.infinity)) result = try decoder.decode(Foo.self, from: ["number": "nan"]) - XCTAssertTrue(result.number.isNaN) + #expect(result.number.isNaN) } } diff --git a/ios/Tests/CapacitorTests/PluginCallAccessorTests.swift b/ios/Tests/CapacitorTests/PluginCallAccessorTests.swift new file mode 100644 index 0000000000..f89a221b86 --- /dev/null +++ b/ios/Tests/CapacitorTests/PluginCallAccessorTests.swift @@ -0,0 +1,76 @@ +import Foundation +import Testing +@testable import Capacitor + +struct PluginCallAccessorTests { + private static let referenceDate = Date(timeIntervalSinceReferenceDate: 632854800) + + private static func makeCall() -> CAPPluginCall { + let formatter = ISO8601DateFormatter() + let options: [String: Any] = [ + "testString": "foo", + "testDict": ["testSubkey": "sub value"], + "testFloat": 3.14159, + "testDateObject": referenceDate, + "testDateString": formatter.string(from: referenceDate), + "testBoolTrue": true, + "testBoolFalse": false + ] + return CAPPluginCall(callbackId: "test", methodName: "test", options: options, success: { _, _ in }, error: { _ in }) + } + + @Test func stringAccessor() { + let call = Self.makeCall() + #expect(call.getString("testString") == "foo") + #expect(call.getString("badString") == nil) + #expect(call.getString("badString", defaultValue: "default") == "default") + } + + @Test func dateObjectAccessor() { + let call = Self.makeCall() + #expect(call.getDate("testDateObject")?.timeIntervalSinceReferenceDate == 632854800) + #expect(call.getDate("badString") == nil) + + let defaultDate = Date() + #expect(call.getDate("badString", defaultValue: defaultDate) == defaultDate) + } + + @Test func dateStringAccessor() { + let call = Self.makeCall() + let objectValue = call.getDate("testDateObject") + let stringValue = call.getDate("testDateString") + #expect(objectValue != nil) + #expect(stringValue != nil) + #expect(objectValue == stringValue) + } + + @Test func objectAccessor() { + let call = Self.makeCall() + let value = call.getObject("testDict") + #expect(value?["testSubkey"] as? String == "sub value") + #expect(call.getObject("badString") == nil) + } + + @Test func numberAccessor() { + let call = Self.makeCall() + var value = call.getNumber("testFloat") + #expect(value == NSNumber(value: 3.14159)) + + value = call.getNumber("badString") + #expect(value == nil) + + value = call.getNumber("badString", defaultValue: 100) + #expect(value?.intValue == 100) + + value = call.getNumber("testBoolTrue") + #expect(value?.boolValue == true) + } + + @Test func boolAccessor() { + let call = Self.makeCall() + #expect(call.getBool("testBoolTrue", defaultValue: false) == true) + #expect(call.getBool("testBoolFalse", defaultValue: true) == false) + #expect(call.getBool("badString", defaultValue: true) == true) + #expect(call.getBool("badString", defaultValue: false) == false) + } +} diff --git a/ios/Capacitor/TestsHostApp/configurations/bad.json b/ios/Tests/CapacitorTests/Resources/configurations/bad.json similarity index 100% rename from ios/Capacitor/TestsHostApp/configurations/bad.json rename to ios/Tests/CapacitorTests/Resources/configurations/bad.json diff --git a/ios/Capacitor/TestsHostApp/configurations/flat.json b/ios/Tests/CapacitorTests/Resources/configurations/flat.json similarity index 100% rename from ios/Capacitor/TestsHostApp/configurations/flat.json rename to ios/Tests/CapacitorTests/Resources/configurations/flat.json diff --git a/ios/Capacitor/TestsHostApp/configurations/hidinglogs.json b/ios/Tests/CapacitorTests/Resources/configurations/hidinglogs.json similarity index 100% rename from ios/Capacitor/TestsHostApp/configurations/hidinglogs.json rename to ios/Tests/CapacitorTests/Resources/configurations/hidinglogs.json diff --git a/ios/Capacitor/TestsHostApp/configurations/hierarchy.json b/ios/Tests/CapacitorTests/Resources/configurations/hierarchy.json similarity index 100% rename from ios/Capacitor/TestsHostApp/configurations/hierarchy.json rename to ios/Tests/CapacitorTests/Resources/configurations/hierarchy.json diff --git a/ios/Capacitor/TestsHostApp/configurations/nonjson.json b/ios/Tests/CapacitorTests/Resources/configurations/nonjson.json similarity index 100% rename from ios/Capacitor/TestsHostApp/configurations/nonjson.json rename to ios/Tests/CapacitorTests/Resources/configurations/nonjson.json diff --git a/ios/Capacitor/TestsHostApp/configurations/server.json b/ios/Tests/CapacitorTests/Resources/configurations/server.json similarity index 100% rename from ios/Capacitor/TestsHostApp/configurations/server.json rename to ios/Tests/CapacitorTests/Resources/configurations/server.json diff --git a/ios/Tests/CapacitorTests/RouterTests.swift b/ios/Tests/CapacitorTests/RouterTests.swift new file mode 100644 index 0000000000..2a33861a17 --- /dev/null +++ b/ios/Tests/CapacitorTests/RouterTests.swift @@ -0,0 +1,27 @@ +import Testing +@testable import Capacitor + +struct RouterTests { + @Test func routerReturnsIndexWhenProvidedEmptyPath() { + checkRouter(path: "", expected: "/index.html") + } + + @Test func routerReturnsIndexWhenProvidedPathWithoutExtension() { + checkRouter(path: "/a/valid/path/no/ext", expected: "/index.html") + } + + @Test func routerReturnsPathWhenProvidedValidPath() { + checkRouter(path: "/a/valid/path.ext", expected: "/a/valid/path.ext") + } + + @Test func routerReturnsPathWhenProvidedValidPathWithExtensionAndSpaces() { + checkRouter(path: "/a/valid/file path.ext", expected: "/a/valid/file path.ext") + } + + private func checkRouter(path: String, expected: String) { + var router = CapacitorRouter() + #expect(router.route(for: path) == expected) + router.basePath = "/A/Route" + #expect(router.route(for: path) == "/A/Route" + expected) + } +} diff --git a/ios/Capacitor/CodableTests/SuperCodableTests.swift b/ios/Tests/CapacitorTests/SuperCodableTests.swift similarity index 60% rename from ios/Capacitor/CodableTests/SuperCodableTests.swift rename to ios/Tests/CapacitorTests/SuperCodableTests.swift index 7a7035809d..99f4fe1d33 100644 --- a/ios/Capacitor/CodableTests/SuperCodableTests.swift +++ b/ios/Tests/CapacitorTests/SuperCodableTests.swift @@ -1,61 +1,54 @@ -// -// SuperCodableTests.swift -// CodableTests -// -// Created by Steven Sherry on 12/10/23. -// Copyright © 2023 Drifty Co. All rights reserved. -// - -import XCTest +import Foundation +import Testing import Capacitor -final class SuperCodableTests: XCTestCase { - // MARK: Keyed Super Encoding/Decoding - func testEncode__when_given_a_value_that_encodes_to_a_keyed_superEncoder_without_specifying_a_key__it_encodes_the_super_container_with_the_string_key_super() throws { +struct SuperCodableTests { + @Test func encodingKeyedSuperEncoderWithoutKeyUsesDefaultKey() throws { let sut = JSValueEncoder() let value = KeyedSubSuper(bool: true) let encoded = try sut.encodeJSObject(value) - let bool = try XCTUnwrap(encoded["bool"] as? Bool) - XCTAssertTrue(bool) - let superObject = try XCTUnwrap(encoded["super"] as? JSObject) - let number = try XCTUnwrap(superObject["number"] as? NSNumber) - XCTAssertEqual(0, number) - let string = try XCTUnwrap(superObject["string"] as? String) - XCTAssertEqual("empty", string) + let bool = try #require(encoded["bool"] as? Bool) + #expect(bool == true) + let superObject = try #require(encoded["super"] as? JSObject) + let number = try #require(superObject["number"] as? NSNumber) + #expect(number == 0) + let string = try #require(superObject["string"] as? String) + #expect(string == "empty") } - func testEncode__when_given_a_value_that_encodes_to_a_keyed_superEncoder_with_a_specific_key__it_encodes_the_super_container_with_the_provided_key() throws { + + @Test func encodingKeyedSuperEncoderWithSpecificKey() throws { let sut = JSValueEncoder() let value = KeyedSubSuperKeyed(bool: false) value.number = 5 value.string = "encoding" let encoded = try sut.encodeJSObject(value) - let bool = try XCTUnwrap(encoded["bool"] as? Bool) - XCTAssertFalse(bool) - let superObject = try XCTUnwrap(encoded["info"] as? JSObject) - let number = try XCTUnwrap(superObject["number"] as? NSNumber) - XCTAssertEqual(5, number) - let string = try XCTUnwrap(superObject["string"] as? String) - XCTAssertEqual("encoding", string) + let bool = try #require(encoded["bool"] as? Bool) + #expect(bool == false) + let superObject = try #require(encoded["info"] as? JSObject) + let number = try #require(superObject["number"] as? NSNumber) + #expect(number == 5) + let string = try #require(superObject["string"] as? String) + #expect(string == "encoding") } - func testEncode__when_given_a_value_that_encodes_its_superclass_without_a_superEncoder__it_encodes_the_entire_structure_flattened() throws { + @Test func encodingSuperclassWithoutSuperEncoderFlattenStructure() throws { let sut = JSValueEncoder() let value = KeyedSubSuperFlat(bool: true) value.number = 10 value.string = "flattened" let encoded = try sut.encodeJSObject(value) - let bool = try XCTUnwrap(encoded["bool"] as? Bool) - XCTAssertTrue(bool) - let number = try XCTUnwrap(encoded["number"] as? NSNumber) - XCTAssertEqual(10, number) - let string = try XCTUnwrap(encoded["string"] as? String) - XCTAssertEqual("flattened", string) + let bool = try #require(encoded["bool"] as? Bool) + #expect(bool == true) + let number = try #require(encoded["number"] as? NSNumber) + #expect(number == 10) + let string = try #require(encoded["string"] as? String) + #expect(string == "flattened") } - func testDecode__when_given_a_value_that_decodes_its_superclass_without_specifying_a_key__it_will_attempt_to_decode_the_super_container_from_the_super_key() throws { + @Test func decodingSuperclassWithoutKeyUsesSuperKey() throws { let sut = JSValueDecoder() let value: JSObject = [ "super": [ @@ -66,12 +59,12 @@ final class SuperCodableTests: XCTestCase { ] let decoded = try sut.decode(KeyedSubSuper.self, from: value) - XCTAssertTrue(decoded.bool) - XCTAssertEqual(decoded.number, 5) - XCTAssertEqual(decoded.string, "super decoding") + #expect(decoded.bool == true) + #expect(decoded.number == 5) + #expect(decoded.string == "super decoding") } - func testDecode__when_given_a_value_that_decodes_its_superclass_with_a_specific_key__it_will_attempt_to_decode_the_super_container_from_the_specified_key() throws { + @Test func decodingSuperclassWithSpecificKey() throws { let sut = JSValueDecoder() let value: JSObject = [ "info": [ @@ -82,12 +75,12 @@ final class SuperCodableTests: XCTestCase { ] let decoded = try sut.decode(KeyedSubSuperKeyed.self, from: value) - XCTAssertFalse(decoded.bool) - XCTAssertEqual(decoded.number, 9) - XCTAssertEqual(decoded.string, "info decoding") + #expect(decoded.bool == false) + #expect(decoded.number == 9) + #expect(decoded.string == "info decoding") } - func testDecode__when_given_a_value_that_decodes_its_superclass_without_a_superContainer__it_will_attempt_to_decode_a_flat_structure() throws { + @Test func decodingSuperclassWithoutSuperContainerDecodeFlatStructure() throws { let sut = JSValueDecoder() let value: JSObject = [ "number": 20, @@ -96,35 +89,34 @@ final class SuperCodableTests: XCTestCase { ] let decoded = try sut.decode(KeyedSubSuperFlat.self, from: value) - XCTAssertTrue(decoded.bool) - XCTAssertEqual(decoded.number, 20) - XCTAssertEqual(decoded.string, "flat decoding") + #expect(decoded.bool == true) + #expect(decoded.number == 20) + #expect(decoded.string == "flat decoding") } - // MARK: Unkeyed Super Encoding/Decoding - func testEncode__when_given_a_value_that_encodes_its_superclass_with_a_superContainer__it_will_encode_the_super_container_as_a_nested_array() throws { + @Test func encodingUnkeyedSuperEncoderNestedArray() throws { let sut = JSValueEncoder() let value = UnkeyedSubSuper(bool: true) value.number = -3 value.string = "unkeyed encoding" - let encoded = try XCTUnwrap(try sut.encode(value) as? JSArray) - XCTAssertEqual(encoded[0] as? Bool, true) - let nested = try XCTUnwrap(encoded[1] as? JSArray) - XCTAssertEqual(nested[0] as? NSNumber, -3) - XCTAssertEqual(nested[1] as? String, "unkeyed encoding") + let encoded = try #require(try sut.encode(value) as? JSArray) + #expect(encoded[0] as? Bool == true) + let nested = try #require(encoded[1] as? JSArray) + #expect(nested[0] as? NSNumber == -3) + #expect(nested[1] as? String == "unkeyed encoding") } - func testDecode__when_given_a_type_that_decodes_its_superclass_with_a_superContainer__it_will_decode_the_superclass_as_a_nested_array() throws { + @Test func decodingUnkeyedSuperEncoderNestedArray() throws { let sut = JSValueDecoder() let value: JSArray = [ true, [4, "unkeyed decoding"] ] let decoded = try sut.decode(UnkeyedSubSuper.self, from: value) - XCTAssertTrue(decoded.bool) - XCTAssertEqual(decoded.number, 4) - XCTAssertEqual(decoded.string, "unkeyed decoding") + #expect(decoded.bool == true) + #expect(decoded.number == 4) + #expect(decoded.string == "unkeyed decoding") } } diff --git a/ios/Tests/CapacitorTests/URLCodableTests.swift b/ios/Tests/CapacitorTests/URLCodableTests.swift new file mode 100644 index 0000000000..1b5c7645a1 --- /dev/null +++ b/ios/Tests/CapacitorTests/URLCodableTests.swift @@ -0,0 +1,58 @@ +import Foundation +import Testing +import Capacitor + +private let urlString = "https://capacitorjs.com" +private let url = URL(string: urlString)! + +private struct Website: Codable, Equatable { + var url: URL +} + +struct JSValueDecoderURLTests { + let decoder = JSValueDecoder() + + @Test func decodingURLRoot() throws { + let result = try decoder.decode(URL.self, from: urlString) + #expect(result == url) + } + + @Test func decodingURLArray() throws { + let result = try decoder.decode([URL].self, from: [urlString, urlString]) + #expect(result == [url, url]) + } + + @Test func decodingURLStruct() throws { + let result = try decoder.decode(Website.self, from: ["url": urlString]) + #expect(result == .init(url: url)) + } + + @Test func decodingURLFailsWithInvalidString() throws { + let decoder = JSValueDecoder() + #expect(throws: DecodingError.self) { + try decoder.decode(URL.self, from: "🐞://🐞.com/🐞") + } + } +} + +struct JSValueEncoderURLTests { + let encoder = JSValueEncoder() + + @Test func encodingURLRoot() throws { + let rawResult = try encoder.encode(url) + let result = try #require(rawResult as? String) + #expect(result == urlString) + } + + @Test func encodingURLArray() throws { + let rawResult = try encoder.encode([url, url]) + let result = try #require(rawResult as? [String]) + #expect(result == [urlString, urlString]) + } + + @Test func encodingURLStruct() throws { + let rawResult = try encoder.encode(Website(url: url)) + let result = try #require(rawResult as? [String: String]) + #expect(result == ["url": urlString]) + } +} From feec12fea20a68d5a4a914458a68d5487ebb19a0 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 17 Aug 2026 13:21:00 -0500 Subject: [PATCH 06/42] add missing imports, resources, and podspec paths --- ios/Capacitor.podspec | 7 +- ios/Sources/Capacitor/Array+Capacitor.swift | 2 + .../CAPApplicationDelegateProxy.swift | 1 + ios/Sources/Capacitor/CAPBridgeDelegate.swift | 1 + .../Capacitor/CAPInstanceDescriptor.swift | 10 +- ios/Sources/Capacitor/CAPInstancePlugin.swift | 2 + ios/Sources/Capacitor/CAPNotifications.swift | 2 + ios/Sources/Capacitor/CAPPlugin.swift | 19 +- ios/Sources/Capacitor/CAPPluginCall.swift | 18 +- .../Capacitor/CAPSceneDelegateProxy.swift | 1 + ios/Sources/Capacitor/CapacitorBridge.swift | 10 +- ios/Sources/Capacitor/JSExport.swift | 8 +- .../Plugins/CapacitorCookieManager.swift | 1 + .../Capacitor/Plugins/SystemBars.swift | 8 +- ios/Sources/Capacitor/PrivacyInfo.xcprivacy | 14 + ios/Sources/Capacitor/UIColor.swift | 2 + ios/Sources/Capacitor/assets/native-bridge.js | 1039 +++++++++++++++++ 17 files changed, 1113 insertions(+), 32 deletions(-) create mode 100644 ios/Sources/Capacitor/PrivacyInfo.xcprivacy create mode 100644 ios/Sources/Capacitor/assets/native-bridge.js diff --git a/ios/Capacitor.podspec b/ios/Capacitor.podspec index 01ce20591e..4d9a20601b 100644 --- a/ios/Capacitor.podspec +++ b/ios/Capacitor.podspec @@ -15,9 +15,8 @@ Pod::Spec.new do |s| s.ios.deployment_target = '16.0' s.authors = { 'Ionic Team' => 'hi@ionicframework.com' } s.source = { git: 'https://github.com/ionic-team/capacitor.git', tag: package['version'] } - 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"] - s.resource_bundles = { 'Capacitor' => ["#{prefix}Capacitor/Capacitor/PrivacyInfo.xcprivacy"] } + s.source_files = "#{prefix}Sources/Capacitor/**/*.{swift,h,m}" + s.resources = ["#{prefix}Sources/Capacitor/assets/native-bridge.js"] + s.resource_bundles = { 'Capacitor' => ["#{prefix}Sources/Capacitor/PrivacyInfo.xcprivacy"] } s.swift_version = '5.1' end diff --git a/ios/Sources/Capacitor/Array+Capacitor.swift b/ios/Sources/Capacitor/Array+Capacitor.swift index e9d7a6f810..7c9e4db595 100644 --- a/ios/Sources/Capacitor/Array+Capacitor.swift +++ b/ios/Sources/Capacitor/Array+Capacitor.swift @@ -1,3 +1,5 @@ +import Foundation + // convenience wrappers to transform Arrays between NSNull and Optional values, for interoperability with Obj-C extension Array: CapacitorExtension {} extension CapacitorExtensionTypeWrapper where T == [JSValue] { diff --git a/ios/Sources/Capacitor/CAPApplicationDelegateProxy.swift b/ios/Sources/Capacitor/CAPApplicationDelegateProxy.swift index 37ec2e023d..2a563aed54 100644 --- a/ios/Sources/Capacitor/CAPApplicationDelegateProxy.swift +++ b/ios/Sources/Capacitor/CAPApplicationDelegateProxy.swift @@ -1,4 +1,5 @@ import Foundation +import UIKit @objc(CAPApplicationDelegateProxy) public class ApplicationDelegateProxy: NSObject, UIApplicationDelegate { diff --git a/ios/Sources/Capacitor/CAPBridgeDelegate.swift b/ios/Sources/Capacitor/CAPBridgeDelegate.swift index 845ac18fd2..c0524148c1 100644 --- a/ios/Sources/Capacitor/CAPBridgeDelegate.swift +++ b/ios/Sources/Capacitor/CAPBridgeDelegate.swift @@ -1,4 +1,5 @@ import Foundation +import WebKit public protocol CAPBridgeDelegate: AnyObject { var bridgedWebView: WKWebView? { get } diff --git a/ios/Sources/Capacitor/CAPInstanceDescriptor.swift b/ios/Sources/Capacitor/CAPInstanceDescriptor.swift index 36a39dbbef..3bee942ea4 100644 --- a/ios/Sources/Capacitor/CAPInstanceDescriptor.swift +++ b/ios/Sources/Capacitor/CAPInstanceDescriptor.swift @@ -6,6 +6,8 @@ // import Foundation +import UIKit +import WebKit @objc public enum InstanceType: Int { case fixed = 0 @@ -178,18 +180,14 @@ extension InstanceDescriptor { @objc public func normalize() { // first, make sure the scheme is valid var schemeValid = false - if let scheme = urlScheme, WKWebView.handlesURLScheme(scheme) == false, - scheme.range(of: "^[a-z][a-z0-9.+-]*$", options: [.regularExpression, .caseInsensitive], range: nil, locale: nil) != nil { + if WKWebView.handlesURLScheme(urlScheme) == false, + urlScheme.range(of: "^[a-z][a-z0-9.+-]*$", options: [.regularExpression, .caseInsensitive], range: nil, locale: nil) != nil { schemeValid = true } if !schemeValid { // reset to the default urlScheme = InstanceDescriptorDefaults.scheme } - // make sure we have a hostname - if urlHostname == nil { - urlHostname = InstanceDescriptorDefaults.hostname - } // now validate the server.url var urlValid = false if let server = serverURL, URL(string: server) != nil { diff --git a/ios/Sources/Capacitor/CAPInstancePlugin.swift b/ios/Sources/Capacitor/CAPInstancePlugin.swift index 0a73e434f1..5cc76f74f3 100644 --- a/ios/Sources/Capacitor/CAPInstancePlugin.swift +++ b/ios/Sources/Capacitor/CAPInstancePlugin.swift @@ -6,5 +6,7 @@ // Copyright © 2022 Drifty Co. All rights reserved. // +import Foundation + /// A CAPPlugin subclass meant to be explicitly initialized by the caller and not the bridge. @objc open class CAPInstancePlugin: CAPPlugin {} diff --git a/ios/Sources/Capacitor/CAPNotifications.swift b/ios/Sources/Capacitor/CAPNotifications.swift index 19272e63b9..4a9c8f38f1 100644 --- a/ios/Sources/Capacitor/CAPNotifications.swift +++ b/ios/Sources/Capacitor/CAPNotifications.swift @@ -1,3 +1,5 @@ +import UserNotifications + /** Notificaton types for NotificationCenter and NSNotificationCenter diff --git a/ios/Sources/Capacitor/CAPPlugin.swift b/ios/Sources/Capacitor/CAPPlugin.swift index 7e982560e5..9c34a8f038 100644 --- a/ios/Sources/Capacitor/CAPPlugin.swift +++ b/ios/Sources/Capacitor/CAPPlugin.swift @@ -22,6 +22,17 @@ import UIKit self.shouldStringifyDatesInCalls = true } + @objc required override public init() { + super.init() + self.bridge = nil + self.webView = nil + self.pluginId = "" + self.pluginName = "" + self.eventListeners = NSMutableDictionary() + self.retainedEventArguments = NSMutableDictionary() + self.shouldStringifyDatesInCalls = true + } + @objc public func getId() -> String { return pluginName } @@ -35,8 +46,8 @@ import UIKit return call.getString(field, defaultValue: defaultValue) ?? defaultValue } - @objc public func getConfig() -> PluginConfig? { - guard let bridge = bridge else { return nil } + @objc public func getConfig() -> PluginConfig { + guard let bridge = bridge else { return PluginConfig(config: [:]) } return bridge.config.getPluginConfig(pluginName) } @@ -76,7 +87,7 @@ import UIKit guard let listenersForEvent = eventListeners.object(forKey: eventName) as? [CAPPluginCall] else { if retainUntilConsumed { if retainedEventArguments.object(forKey: eventName) == nil { - retainedEventArguments.setObject(NSMutableArray(), forKey: eventName) + retainedEventArguments.setValue(NSMutableArray(), forKey: eventName) } (retainedEventArguments.object(forKey: eventName) as? NSMutableArray)?.add(data ?? [:]) } @@ -177,7 +188,7 @@ import UIKit } @objc public func handleWKWebViewURLAuthenticationChallenge( - _ challenge: NSURLAuthenticationChallenge, + _ challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void ) -> Bool { return false diff --git a/ios/Sources/Capacitor/CAPPluginCall.swift b/ios/Sources/Capacitor/CAPPluginCall.swift index c997215b91..f438007bef 100644 --- a/ios/Sources/Capacitor/CAPPluginCall.swift +++ b/ios/Sources/Capacitor/CAPPluginCall.swift @@ -47,8 +47,12 @@ public typealias CAPPluginCallErrorHandler = (CAPPluginCallError) -> Void // MARK: - Accessors + public func getString(_ key: String) -> String? { + options[key] as? String + } + @objc public func getString(_ key: String, defaultValue: String? = nil) -> String? { - (options[key] as? String) ?? defaultValue + getString(key) ?? defaultValue } @objc public func getNumber(_ key: String, defaultValue: NSNumber? = nil) -> NSNumber? { @@ -77,9 +81,9 @@ public typealias CAPPluginCallErrorHandler = (CAPPluginCallError) -> Void options[key] as? [Any] } - @objc public func getDate(_ key: String, defaultValue: Date? = nil) -> Date? { + public func getDate(_ key: String) -> Date? { guard let value = options[key] else { - return defaultValue + return nil } if let date = value as? Date { @@ -87,10 +91,14 @@ public typealias CAPPluginCallErrorHandler = (CAPPluginCallError) -> Void } if let dateString = value as? String { - return BridgedJSValueContainer.jsDateFormatter.date(from: dateString) + return Self.jsDateFormatter.date(from: dateString) } - return defaultValue + return nil + } + + @objc public func getDate(_ key: String, defaultValue: Date? = nil) -> Date? { + getDate(key) ?? defaultValue } // MARK: - Deprecated diff --git a/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift b/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift index b55d8a9366..1c4514d5f6 100644 --- a/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift +++ b/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift @@ -7,6 +7,7 @@ // import Foundation +import UIKit @objc(CAPSceneDelegateProxy) public class SceneDelegateProxy: NSObject, UISceneDelegate { diff --git a/ios/Sources/Capacitor/CapacitorBridge.swift b/ios/Sources/Capacitor/CapacitorBridge.swift index 7a29ffb542..2e45a74229 100644 --- a/ios/Sources/Capacitor/CapacitorBridge.swift +++ b/ios/Sources/Capacitor/CapacitorBridge.swift @@ -256,7 +256,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { do { 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) + let registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData) for plugin in registrationList.packageClassList { if let pluginClass = NSClassFromString(plugin), pluginClass is CAPPlugin.Type { @@ -451,11 +451,9 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { } }) - if let pluginCall = pluginCall { - plugin.perform(selector, with: pluginCall) - if pluginCall.keepAlive { - self?.saveCall(pluginCall) - } + plugin.perform(selector, with: pluginCall) + if pluginCall.keepAlive { + self?.saveCall(pluginCall) } // let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime diff --git a/ios/Sources/Capacitor/JSExport.swift b/ios/Sources/Capacitor/JSExport.swift index 35fc2a74b6..1092162a4d 100644 --- a/ios/Sources/Capacitor/JSExport.swift +++ b/ios/Sources/Capacitor/JSExport.swift @@ -109,7 +109,7 @@ internal class JSExport { } private static func createPluginHeaderMethod(method: CAPPluginMethod) -> PluginHeaderMethod { - var rtype = method.returnType + var rtype: String? = method.returnType if rtype == "none" { rtype = nil } @@ -117,8 +117,8 @@ internal class JSExport { } private static func generateMethod(pluginClassName: String, method: CAPPluginMethod) -> String { - let methodName = method.name! - let returnType = method.returnType! + let methodName = method.name + let returnType = method.returnType var paramList = [String]() // add the catch-all @@ -140,7 +140,7 @@ internal class JSExport { var lines = [String]() // Create the function declaration - lines.append("t['\(method.name!)'] = function(\(paramString)) {") + lines.append("t['\(methodName)'] = function(\(paramString)) {") // Create the call to Capacitor ... if returnType == CAPPluginReturnNone { diff --git a/ios/Sources/Capacitor/Plugins/CapacitorCookieManager.swift b/ios/Sources/Capacitor/Plugins/CapacitorCookieManager.swift index 782c245b0b..2f4025d7dc 100644 --- a/ios/Sources/Capacitor/Plugins/CapacitorCookieManager.swift +++ b/ios/Sources/Capacitor/Plugins/CapacitorCookieManager.swift @@ -1,4 +1,5 @@ import Foundation +import WebKit public class CapacitorWKCookieObserver: NSObject, WKHTTPCookieStoreObserver { // Sync WKWebView Cookies to HTTPCookieStorage diff --git a/ios/Sources/Capacitor/Plugins/SystemBars.swift b/ios/Sources/Capacitor/Plugins/SystemBars.swift index 2c7d6753cc..98c5f2f532 100644 --- a/ios/Sources/Capacitor/Plugins/SystemBars.swift +++ b/ios/Sources/Capacitor/Plugins/SystemBars.swift @@ -1,4 +1,5 @@ import Foundation +import UIKit @objc(CAPSystemBarsPlugin) public class CAPSystemBarsPlugin: CAPPlugin, CAPBridgedPlugin { @@ -20,13 +21,14 @@ public class CAPSystemBarsPlugin: CAPPlugin, CAPBridgedPlugin { } @objc override public func load() { - let hidden = getConfig().getBoolean("hidden", false) + let config = getConfig() + let hidden = config.getBoolean("hidden", false) - if let style = getConfig().getString("style", "DEFAULT") { + if let style = config.getString("style", "DEFAULT") { setStyle(style: style) } - if let animation = getConfig().getString("animation") { + if let animation = config.getString("animation") { setAnimation(animation: animation) } diff --git a/ios/Sources/Capacitor/PrivacyInfo.xcprivacy b/ios/Sources/Capacitor/PrivacyInfo.xcprivacy new file mode 100644 index 0000000000..a1f9119d1f --- /dev/null +++ b/ios/Sources/Capacitor/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyAccessedAPITypes + + NSPrivacyCollectedDataTypes + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + + diff --git a/ios/Sources/Capacitor/UIColor.swift b/ios/Sources/Capacitor/UIColor.swift index b33bd3d4a6..ebfc6e6f91 100644 --- a/ios/Sources/Capacitor/UIColor.swift +++ b/ios/Sources/Capacitor/UIColor.swift @@ -1,3 +1,5 @@ +import UIKit + extension UIColor: CapacitorExtension {} public extension CapacitorExtensionTypeWrapper where T: UIColor { // disable linting for the short variable names, since that's the point of the method diff --git a/ios/Sources/Capacitor/assets/native-bridge.js b/ios/Sources/Capacitor/assets/native-bridge.js new file mode 100644 index 0000000000..f5e7cc4403 --- /dev/null +++ b/ios/Sources/Capacitor/assets/native-bridge.js @@ -0,0 +1,1039 @@ + +/*! Capacitor: https://capacitorjs.com/ - MIT License */ +/* Generated File. Do not edit. */ + +var nativeBridge = (function (exports) { + 'use strict'; + + var ExceptionCode; + (function (ExceptionCode) { + /** + * API is not implemented. + * + * This usually means the API can't be used because it is not implemented for + * the current platform. + */ + ExceptionCode["Unimplemented"] = "UNIMPLEMENTED"; + /** + * API is not available. + * + * This means the API can't be used right now because: + * - it is currently missing a prerequisite, such as network connectivity + * - it requires a particular platform or browser version + */ + ExceptionCode["Unavailable"] = "UNAVAILABLE"; + })(ExceptionCode || (ExceptionCode = {})); + class CapacitorException extends Error { + constructor(message, code, data) { + super(message); + this.message = message; + this.code = code; + this.data = data; + } + } + + // For removing exports for iOS/Android, keep let for reassignment + // eslint-disable-next-line + let dummy = {}; + const readFileAsBase64 = (file) => new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => { + const data = reader.result; + resolve(btoa(data)); + }; + reader.onerror = reject; + reader.readAsBinaryString(file); + }); + const convertFormData = async (formData) => { + const newFormData = []; + for (const pair of formData.entries()) { + const [key, value] = pair; + if (value instanceof File) { + const base64File = await readFileAsBase64(value); + newFormData.push({ + key, + value: base64File, + type: 'base64File', + contentType: value.type, + fileName: value.name, + }); + } + else { + newFormData.push({ key, value, type: 'string' }); + } + } + return newFormData; + }; + const convertBody = async (body, contentType) => { + if (body instanceof ReadableStream || body instanceof Uint8Array) { + let encodedData; + if (body instanceof ReadableStream) { + const reader = body.getReader(); + const chunks = []; + while (true) { + const { done, value } = await reader.read(); + if (done) + break; + chunks.push(value); + } + const concatenated = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0)); + let position = 0; + for (const chunk of chunks) { + concatenated.set(chunk, position); + position += chunk.length; + } + encodedData = concatenated; + } + else { + encodedData = body; + } + let data = new TextDecoder().decode(encodedData); + let type; + if (contentType === 'application/json') { + try { + data = JSON.parse(data); + } + catch (ignored) { + // ignore + } + type = 'json'; + } + else if (contentType === 'multipart/form-data') { + type = 'formData'; + } + else if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) { + type = 'image'; + } + else if (contentType === 'application/octet-stream') { + type = 'binary'; + } + else { + type = 'text'; + } + return { + data, + type, + headers: { 'Content-Type': contentType || 'application/octet-stream' }, + }; + } + else if (body instanceof URLSearchParams) { + return { + data: body.toString(), + type: 'text', + }; + } + else if (body instanceof FormData) { + return { + data: await convertFormData(body), + type: 'formData', + }; + } + else if (body instanceof File) { + const fileData = await readFileAsBase64(body); + return { + data: fileData, + type: 'file', + headers: { 'Content-Type': body.type }, + }; + } + return { data: body, type: 'json' }; + }; + const CAPACITOR_HTTP_INTERCEPTOR = '/_capacitor_http_interceptor_'; + const CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM = 'u'; + // TODO: export as Cap function + const isRelativeOrProxyUrl = (url) => !url || !(url.startsWith('http:') || url.startsWith('https:')) || url.indexOf(CAPACITOR_HTTP_INTERCEPTOR) > -1; + // TODO: export as Cap function + const createProxyUrl = (url, win) => { + var _a, _b; + if (isRelativeOrProxyUrl(url)) + return url; + const bridgeUrl = new URL((_b = (_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.getServerUrl()) !== null && _b !== void 0 ? _b : ''); + bridgeUrl.pathname = CAPACITOR_HTTP_INTERCEPTOR; + bridgeUrl.searchParams.append(CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM, url); + return bridgeUrl.toString(); + }; + const initBridge = (w) => { + const getPlatformId = (win) => { + var _a, _b; + if (win === null || win === void 0 ? void 0 : win.androidBridge) { + return 'android'; + } + else if ((_b = (_a = win === null || win === void 0 ? void 0 : win.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge) { + return 'ios'; + } + else { + return 'web'; + } + }; + const convertFileSrcServerUrl = (webviewServerUrl, filePath) => { + if (typeof filePath === 'string') { + if (filePath.startsWith('/')) { + return webviewServerUrl + '/_capacitor_file_' + filePath; + } + else if (filePath.startsWith('file://')) { + return webviewServerUrl + filePath.replace('file://', '/_capacitor_file_'); + } + else if (filePath.startsWith('content://')) { + return webviewServerUrl + filePath.replace('content:/', '/_capacitor_content_'); + } + } + return filePath; + }; + const initEvents = (win, cap) => { + cap.addListener = (pluginName, eventName, callback) => { + const callbackId = cap.nativeCallback(pluginName, 'addListener', { + eventName: eventName, + }, callback); + return { + remove: async () => { + var _a; + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.debug('Removing listener', pluginName, eventName); + cap.removeListener(pluginName, callbackId, eventName, callback); + }, + }; + }; + cap.removeListener = (pluginName, callbackId, eventName, callback) => { + cap.nativeCallback(pluginName, 'removeListener', { + callbackId: callbackId, + eventName: eventName, + }, callback); + }; + cap.createEvent = (eventName, eventData) => { + const doc = win.document; + if (doc) { + const ev = doc.createEvent('Events'); + ev.initEvent(eventName, false, false); + if (eventData && typeof eventData === 'object') { + for (const i in eventData) { + // eslint-disable-next-line no-prototype-builtins + if (eventData.hasOwnProperty(i)) { + ev[i] = eventData[i]; + } + } + } + return ev; + } + return null; + }; + cap.triggerEvent = (eventName, target, eventData) => { + const doc = win.document; + const cordova = win.cordova; + eventData = eventData || {}; + const ev = cap.createEvent(eventName, eventData); + if (ev) { + if (target === 'document') { + if (cordova === null || cordova === void 0 ? void 0 : cordova.fireDocumentEvent) { + cordova.fireDocumentEvent(eventName, eventData); + return true; + } + else if (doc === null || doc === void 0 ? void 0 : doc.dispatchEvent) { + return doc.dispatchEvent(ev); + } + } + else if (target === 'window' && win.dispatchEvent) { + return win.dispatchEvent(ev); + } + else if (doc === null || doc === void 0 ? void 0 : doc.querySelector) { + const targetEl = doc.querySelector(target); + if (targetEl) { + return targetEl.dispatchEvent(ev); + } + } + } + return false; + }; + win.Capacitor = cap; + }; + const initLegacyHandlers = (win, cap) => { + // define cordova if it's not there already + win.cordova = win.cordova || {}; + const doc = win.document; + const nav = win.navigator; + if (nav) { + nav.app = nav.app || {}; + nav.app.exitApp = () => { + var _a; + if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { + win.console.warn('App plugin not installed'); + } + else { + cap.nativeCallback('App', 'exitApp', {}); + } + }; + } + if (doc) { + const docAddEventListener = doc.addEventListener; + doc.addEventListener = (...args) => { + var _a; + const eventName = args[0]; + const handler = args[1]; + if (eventName === 'deviceready' && handler) { + Promise.resolve().then(handler); + } + else if (eventName === 'backbutton' && cap.Plugins.App) { + // Add a dummy listener so Capacitor doesn't do the default + // back button action + if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { + win.console.warn('App plugin not installed'); + } + else { + cap.Plugins.App.addListener('backButton', () => { + // ignore + }); + } + } + return docAddEventListener.apply(doc, args); + }; + } + win.Capacitor = cap; + }; + const initVendor = (win, cap) => { + const Ionic = (win.Ionic = win.Ionic || {}); + const IonicWebView = (Ionic.WebView = Ionic.WebView || {}); + const Plugins = cap.Plugins; + IonicWebView.getServerBasePath = (callback) => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.getServerBasePath().then((result) => { + callback(result.path); + }); + }; + IonicWebView.setServerAssetPath = (path) => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerAssetPath({ path }); + }; + IonicWebView.setServerBasePath = (path) => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerBasePath({ path }); + }; + IonicWebView.persistServerBasePath = () => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.persistServerBasePath(); + }; + IonicWebView.convertFileSrc = (url) => cap.convertFileSrc(url); + win.Capacitor = cap; + win.Ionic.WebView = IonicWebView; + }; + const initLogger = (win, cap) => { + const BRIDGED_CONSOLE_METHODS = ['debug', 'error', 'info', 'log', 'trace', 'warn']; + const createLogFromNative = (c) => (result) => { + if (isFullConsole(c)) { + const success = result.success === true; + const tagStyles = success + ? 'font-style: italic; font-weight: lighter; color: gray' + : 'font-style: italic; font-weight: lighter; color: red'; + c.groupCollapsed('%cresult %c' + result.pluginId + '.' + result.methodName + ' (#' + result.callbackId + ')', tagStyles, 'font-style: italic; font-weight: bold; color: #444'); + if (result.success === false) { + c.error(result.error); + } + else { + c.dir(JSON.stringify(result.data)); + } + c.groupEnd(); + } + else { + if (result.success === false) { + c.error('LOG FROM NATIVE', result.error); + } + else { + c.log('LOG FROM NATIVE', result.data); + } + } + }; + const createLogToNative = (c) => (call) => { + if (isFullConsole(c)) { + c.groupCollapsed('%cnative %c' + call.pluginId + '.' + call.methodName + ' (#' + call.callbackId + ')', 'font-weight: lighter; color: gray', 'font-weight: bold; color: #000'); + c.dir(call); + c.groupEnd(); + } + else { + c.log('LOG TO NATIVE: ', call); + } + }; + const isFullConsole = (c) => { + if (!c) { + return false; + } + return typeof c.groupCollapsed === 'function' || typeof c.groupEnd === 'function' || typeof c.dir === 'function'; + }; + const serializeConsoleMessage = (msg) => { + try { + if (typeof msg === 'object') { + msg = JSON.stringify(msg); + } + return String(msg); + } + catch (e) { + return ''; + } + }; + const platform = getPlatformId(win); + if (platform == 'android' && typeof win.CapacitorSystemBarsAndroidInterface !== 'undefined') { + // add DOM ready listener for System Bars + document.addEventListener('DOMContentLoaded', function () { + win.CapacitorSystemBarsAndroidInterface.onDOMReady(); + }); + } + if (platform == 'android' || platform == 'ios') { + // patch document.cookie on Android/iOS + win.CapacitorCookiesDescriptor = + Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') || + Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie'); + let doPatchCookies = false; + // check if capacitor cookies is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor cookies config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.isEnabled', + }; + const isCookiesEnabled = prompt(JSON.stringify(payload)); + if (isCookiesEnabled === 'true') { + doPatchCookies = true; + } + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled(); + if (isCookiesEnabled === true) { + doPatchCookies = true; + } + } + if (doPatchCookies) { + Object.defineProperty(document, 'cookie', { + get: function () { + var _a, _b, _c; + if (platform === 'ios') { + // Use prompt to synchronously get cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.get', + }; + const res = prompt(JSON.stringify(payload)); + return res; + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + // return original document.cookie since Android does not support filtering of `httpOnly` cookies + return (_c = (_b = (_a = win.CapacitorCookiesDescriptor) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(document)) !== null && _c !== void 0 ? _c : ''; + } + }, + set: function (val) { + const cookiePairs = val.split(';'); + const domainSection = val.toLowerCase().split('domain=')[1]; + const domain = cookiePairs.length > 1 && domainSection != null && domainSection.length > 0 + ? domainSection.split(';')[0].trim() + : ''; + if (platform === 'ios') { + // Use prompt to synchronously set cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.set', + action: val, + domain, + }; + prompt(JSON.stringify(payload)); + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + win.CapacitorCookiesAndroidInterface.setCookie(domain, val); + } + }, + }); + } + // patch fetch / XHR on Android/iOS + // store original fetch & XHR functions + win.CapacitorWebFetch = window.fetch; + win.CapacitorWebXMLHttpRequest = { + abort: window.XMLHttpRequest.prototype.abort, + constructor: window.XMLHttpRequest.prototype.constructor, + fullObject: window.XMLHttpRequest, + getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, + getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, + open: window.XMLHttpRequest.prototype.open, + prototype: window.XMLHttpRequest.prototype, + send: window.XMLHttpRequest.prototype.send, + setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, + }; + let doPatchHttp = false; + // check if capacitor http is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor http config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorHttp', + }; + const isHttpEnabled = prompt(JSON.stringify(payload)); + if (isHttpEnabled === 'true') { + doPatchHttp = true; + } + } + else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') { + const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled(); + if (isHttpEnabled === true) { + doPatchHttp = true; + } + } + if (doPatchHttp) { + // fetch patch + window.fetch = async (resource, options) => { + const headers = new Headers(options === null || options === void 0 ? void 0 : options.headers); + const contentType = headers.get('Content-Type') || headers.get('content-type'); + if ((options === null || options === void 0 ? void 0 : options.body) instanceof FormData && + (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/form-data')) && + !contentType.includes('boundary')) { + headers.delete('Content-Type'); + headers.delete('content-type'); + options.headers = headers; + } + const request = new Request(resource, options); + if (request.url.startsWith(`${cap.getServerUrl()}/`)) { + return win.CapacitorWebFetch(resource, options); + } + const { method } = request; + if (method.toLocaleUpperCase() === 'GET' || + method.toLocaleUpperCase() === 'HEAD' || + method.toLocaleUpperCase() === 'OPTIONS' || + method.toLocaleUpperCase() === 'TRACE') { + // a workaround for following android webview issue: + // https://issues.chromium.org/issues/40450316 + // Sets the user-agent header to a custom value so that its not stripped + // on its way to the native layer + if (platform === 'android' && (options === null || options === void 0 ? void 0 : options.headers)) { + const userAgent = headers.get('User-Agent') || headers.get('user-agent'); + if (userAgent !== null) { + headers.set('x-cap-user-agent', userAgent); + options.headers = headers; + } + } + if (typeof resource === 'string') { + return await win.CapacitorWebFetch(createProxyUrl(resource, win), options); + } + else if (resource instanceof URL) { + const modifiedURL = new URL(createProxyUrl(resource.toString(), win)); + return await win.CapacitorWebFetch(modifiedURL, options); + } + else if (resource instanceof Request) { + const modifiedRequest = new Request(createProxyUrl(resource.url, win), resource); + return await win.CapacitorWebFetch(modifiedRequest, options); + } + } + const tag = `CapacitorHttp fetch ${Date.now()} ${resource}`; + console.time(tag); + try { + const { body } = request; + const optionHeaders = Object.fromEntries(request.headers.entries()); + const { data: requestData, type, headers: requestHeaders, } = await convertBody((options === null || options === void 0 ? void 0 : options.body) || body || undefined, optionHeaders['Content-Type'] || optionHeaders['content-type']); + const nativeHeaders = Object.assign(Object.assign({}, requestHeaders), optionHeaders); + if (platform === 'android') { + if (headers.has('User-Agent')) { + nativeHeaders['User-Agent'] = headers.get('User-Agent'); + } + if (headers.has('user-agent')) { + nativeHeaders['user-agent'] = headers.get('user-agent'); + } + } + const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', { + url: request.url, + method: method, + data: requestData, + dataType: type, + headers: nativeHeaders, + }); + const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type']; + let data = (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json')) + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + // use null data for 204 No Content HTTP response + if (nativeResponse.status === 204) { + data = null; + } + // intercept & parse response before returning + const response = new Response(data, { + headers: nativeResponse.headers, + status: nativeResponse.status, + }); + /* + * copy url to response, `cordova-plugin-ionic` uses this url from the response + * we need `Object.defineProperty` because url is an inherited getter on the Response + * see: https://stackoverflow.com/a/57382543 + * */ + Object.defineProperty(response, 'url', { + value: nativeResponse.url, + }); + console.timeEnd(tag); + return response; + } + catch (error) { + console.timeEnd(tag); + return Promise.reject(error); + } + }; + window.XMLHttpRequest = function () { + const xhr = new win.CapacitorWebXMLHttpRequest.constructor(); + Object.defineProperties(xhr, { + _headers: { + value: {}, + writable: true, + }, + _method: { + value: xhr.method, + writable: true, + }, + }); + const prototype = win.CapacitorWebXMLHttpRequest.prototype; + const isProgressEventAvailable = () => typeof ProgressEvent !== 'undefined' && ProgressEvent.prototype instanceof Event; + // XHR patch abort + prototype.abort = function () { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.abort.call(this); + } + this.readyState = 0; + setTimeout(() => { + this.dispatchEvent(new Event('abort')); + this.dispatchEvent(new Event('loadend')); + }); + }; + // XHR patch open + prototype.open = function (method, url) { + this._method = method.toLocaleUpperCase(); + this._url = url; + if (!this._method || + this._method === 'GET' || + this._method === 'HEAD' || + this._method === 'OPTIONS' || + this._method === 'TRACE') { + if (isRelativeOrProxyUrl(url)) { + return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); + } + this._url = createProxyUrl(this._url, win); + return win.CapacitorWebXMLHttpRequest.open.call(this, method, this._url); + } + Object.defineProperties(this, { + readyState: { + get: function () { + var _a; + return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0; + }, + set: function (val) { + this._readyState = val; + setTimeout(() => { + this.dispatchEvent(new Event('readystatechange')); + }); + }, + }, + }); + setTimeout(() => { + this.dispatchEvent(new Event('loadstart')); + }); + this.readyState = 1; + }; + // XHR patch set request header + prototype.setRequestHeader = function (header, value) { + // a workaround for the following android web view issue: + // https://issues.chromium.org/issues/40450316 + // Sets the user-agent header to a custom value so that its not stripped + // on its way to the native layer + if (platform === 'android' && (header === 'User-Agent' || header === 'user-agent')) { + header = 'x-cap-user-agent'; + } + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); + } + this._headers[header] = value; + }; + // XHR patch send + prototype.send = function (body) { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.send.call(this, body); + } + const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; + console.time(tag); + try { + this.readyState = 2; + Object.defineProperties(this, { + response: { + value: '', + writable: true, + }, + responseText: { + value: '', + writable: true, + }, + responseURL: { + value: '', + writable: true, + }, + status: { + value: 0, + writable: true, + }, + }); + convertBody(body).then(({ data, type, headers }) => { + let otherHeaders = this._headers != null && Object.keys(this._headers).length > 0 ? this._headers : undefined; + if (body instanceof FormData) { + if (!this._headers['Content-Type'] && !this._headers['content-type']) { + otherHeaders = Object.assign(Object.assign({}, otherHeaders), { 'Content-Type': `multipart/form-data; boundary=----WebKitFormBoundary${Math.random().toString(36).substring(2, 15)}` }); + } + } + // intercept request & pass to the bridge + cap + .nativePromise('CapacitorHttp', 'request', { + url: this._url, + method: this._method, + data: data !== null ? data : undefined, + headers: Object.assign(Object.assign({}, headers), otherHeaders), + dataType: type, + }) + .then((nativeResponse) => { + var _a; + // intercept & parse response before returning + if (this.readyState == 2) { + //TODO: Add progress event emission on native side + if (isProgressEventAvailable()) { + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: true, + loaded: nativeResponse.data.length, + total: nativeResponse.data.length, + })); + } + this._headers = nativeResponse.headers; + this.status = nativeResponse.status; + if (this.responseType === '' || this.responseType === 'text') { + this.response = + typeof nativeResponse.data !== 'string' + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + } + else { + this.response = nativeResponse.data; + } + this.responseText = ((_a = (nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type'])) === null || _a === void 0 ? void 0 : _a.startsWith('application/json')) + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + this.responseURL = nativeResponse.url; + this.readyState = 4; + setTimeout(() => { + this.dispatchEvent(new Event('load')); + this.dispatchEvent(new Event('loadend')); + }); + } + console.timeEnd(tag); + }) + .catch((error) => { + this.status = error.status; + this._headers = error.headers; + this.response = error.data; + this.responseText = JSON.stringify(error.data); + this.responseURL = error.url; + this.readyState = 4; + if (isProgressEventAvailable()) { + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + })); + } + setTimeout(() => { + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + console.timeEnd(tag); + }); + }); + } + catch (error) { + this.status = 500; + this._headers = {}; + this.response = error; + this.responseText = error.toString(); + this.responseURL = this._url; + this.readyState = 4; + if (isProgressEventAvailable()) { + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + })); + } + setTimeout(() => { + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + console.timeEnd(tag); + } + }; + // XHR patch getAllResponseHeaders + prototype.getAllResponseHeaders = function () { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); + } + let returnString = ''; + for (const key in this._headers) { + if (key != 'Set-Cookie') { + returnString += key + ': ' + this._headers[key] + '\r\n'; + } + } + return returnString; + }; + // XHR patch getResponseHeader + prototype.getResponseHeader = function (name) { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); + } + return this._headers[name]; + }; + Object.setPrototypeOf(xhr, prototype); + return xhr; + }; + Object.assign(window.XMLHttpRequest, win.CapacitorWebXMLHttpRequest.fullObject); + } + } + // patch window.console on iOS and store original console fns + const isIos = getPlatformId(win) === 'ios'; + if (win.console && isIos) { + Object.defineProperties(win.console, BRIDGED_CONSOLE_METHODS.reduce((props, method) => { + const consoleMethod = win.console[method].bind(win.console); + props[method] = { + value: (...args) => { + const msgs = [...args]; + cap.toNative('Console', 'log', { + level: method, + message: msgs.map(serializeConsoleMessage).join(' '), + }); + return consoleMethod(...args); + }, + }; + return props; + }, {})); + } + cap.logJs = (msg, level) => { + switch (level) { + case 'error': + win.console.error(msg); + break; + case 'warn': + win.console.warn(msg); + break; + case 'info': + win.console.info(msg); + break; + default: + win.console.log(msg); + } + }; + cap.logToNative = createLogToNative(win.console); + cap.logFromNative = createLogFromNative(win.console); + cap.handleError = (err) => win.console.error(err); + win.Capacitor = cap; + }; + function initNativeBridge(win) { + const cap = win.Capacitor || {}; + // keep a collection of callbacks for native response data + const callbacks = new Map(); + const webviewServerUrl = typeof win.WEBVIEW_SERVER_URL === 'string' ? win.WEBVIEW_SERVER_URL : ''; + cap.getServerUrl = () => webviewServerUrl; + cap.convertFileSrc = (filePath) => convertFileSrcServerUrl(webviewServerUrl, filePath); + // Counter of callback ids, randomized to avoid + // any issues during reloads if a call comes back with + // an existing callback id from an old session + let callbackIdCount = Math.floor(Math.random() * 134217728); + let postToNative = null; + const isNativePlatform = () => true; + const getPlatform = () => getPlatformId(win); + cap.getPlatform = getPlatform; + cap.isPluginAvailable = (name) => Object.prototype.hasOwnProperty.call(cap.Plugins, name); + cap.isNativePlatform = isNativePlatform; + // create the postToNative() fn if needed + if (getPlatformId(win) === 'android') { + // android platform + postToNative = (data) => { + var _a; + try { + win.androidBridge.postMessage(JSON.stringify(data)); + } + catch (e) { + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e); + } + }; + } + else if (getPlatformId(win) === 'ios') { + // ios platform + postToNative = (data) => { + var _a; + try { + data.type = data.type ? data.type : 'message'; + win.webkit.messageHandlers.bridge.postMessage(data); + } + catch (e) { + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e); + } + }; + } + cap.handleWindowError = (msg, url, lineNo, columnNo, err) => { + const str = msg.toLowerCase(); + if (str.indexOf('script error') > -1) ; + else { + const errObj = { + type: 'js.error', + error: { + message: msg, + url: url, + line: lineNo, + col: columnNo, + errorObject: JSON.stringify(err), + }, + }; + if (err !== null) { + cap.handleError(err); + } + postToNative(errObj); + } + return false; + }; + if (cap.DEBUG) { + window.onerror = cap.handleWindowError; + } + initLogger(win, cap); + /** + * Send a plugin method call to the native layer + */ + cap.toNative = (pluginName, methodName, options, storedCallback) => { + var _a, _b; + try { + if (typeof postToNative === 'function') { + let callbackId = '-1'; + if (storedCallback && + (typeof storedCallback.callback === 'function' || typeof storedCallback.resolve === 'function')) { + // store the call for later lookup + callbackId = String(++callbackIdCount); + callbacks.set(callbackId, storedCallback); + } + const callData = { + callbackId: callbackId, + pluginId: pluginName, + methodName: methodName, + options: options || {}, + }; + if (cap.isLoggingEnabled && pluginName !== 'Console') { + cap.logToNative(callData); + } + // post the call data to native + postToNative(callData); + return callbackId; + } + else { + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(`implementation unavailable for: ${pluginName}`); + } + } + catch (e) { + (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); + } + return null; + }; + if (win === null || win === void 0 ? void 0 : win.androidBridge) { + win.androidBridge.onmessage = function (event) { + returnResult(JSON.parse(event.data)); + }; + } + /** + * Process a response from the native layer. + */ + cap.fromNative = (result) => { + returnResult(result); + }; + const returnResult = (result) => { + var _a, _b; + if (cap.isLoggingEnabled && result.pluginId !== 'Console') { + cap.logFromNative(result); + } + // get the stored call, if it exists + try { + const storedCall = callbacks.get(result.callbackId); + if (storedCall) { + // looks like we've got a stored call + if (result.error) { + // ensure stacktraces by copying error properties to an Error + result.error = Object.keys(result.error).reduce((err, key) => { + // use any type to avoid importing util and compiling most of .ts files + err[key] = result.error[key]; + return err; + }, new cap.Exception('')); + } + if (typeof storedCall.callback === 'function') { + // callback + if (result.success) { + storedCall.callback(result.data); + } + else { + storedCall.callback(null, result.error); + } + } + else if (typeof storedCall.resolve === 'function') { + // promise + if (result.success) { + storedCall.resolve(result.data); + } + else { + storedCall.reject(result.error); + } + // no need to keep this stored callback + // around for a one time resolve promise + callbacks.delete(result.callbackId); + } + } + else if (!result.success && result.error) { + // no stored callback, but if there was an error let's log it + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(result.error); + } + if (result.save === false) { + callbacks.delete(result.callbackId); + } + } + catch (e) { + (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); + } + // always delete to prevent memory leaks + // overkill but we're not sure what apps will do with this data + delete result.data; + delete result.error; + }; + cap.nativeCallback = (pluginName, methodName, options, callback) => { + if (typeof options === 'function') { + console.warn(`Using a callback as the 'options' parameter of 'nativeCallback()' is deprecated.`); + callback = options; + options = null; + } + return cap.toNative(pluginName, methodName, options, { callback }); + }; + cap.nativePromise = (pluginName, methodName, options) => { + return new Promise((resolve, reject) => { + cap.toNative(pluginName, methodName, options, { + resolve: resolve, + reject: reject, + }); + }); + }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + cap.withPlugin = (_pluginId, _fn) => dummy; + cap.Exception = CapacitorException; + initEvents(win, cap); + initLegacyHandlers(win, cap); + initVendor(win, cap); + win.Capacitor = cap; + } + initNativeBridge(w); + }; + initBridge(typeof globalThis !== 'undefined' + ? globalThis + : typeof self !== 'undefined' + ? self + : typeof window !== 'undefined' + ? window + : typeof global !== 'undefined' + ? global + : {}); + + dummy = initBridge; + + Object.defineProperty(exports, '__esModule', { value: true }); + + return exports; + +})({}); From 19d9b2614b208a13b2256aa7959be886652fbd7e Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 17 Aug 2026 14:04:08 -0500 Subject: [PATCH 07/42] Split CapacitorCordova into separate Cordova and CapacitorCordova SPM targets --- ios/CapacitorCordova.podspec | 11 +- .../project.pbxproj | 499 ------------------ .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../CapacitorCordova/Info.plist | 26 - ios/Package.swift | 9 +- .../CapacitorCordova}/Plugin.swift | 1 + .../AppDelegate.m | 0 .../CDVCommandDelegateImpl.m | 0 .../CDVConfigParser.m | 0 .../CDVInvokedUrlCommand.m | 0 .../CDVPlugin+Resources.m | 0 .../{CapacitorCordova => Cordova}/CDVPlugin.m | 0 .../CDVPluginManager.m | 0 .../CDVPluginResult.m | 0 .../CDVURLProtocol.m | 0 .../CDVViewController.m | 0 .../CDVWebViewProcessPoolFactory.m | 0 .../Cordova}/CapacitorCordova.h | 0 .../CapacitorCordova.modulemap | 0 .../NSDictionary+CordovaPreferences.m | 0 .../PrivacyInfo.xcprivacy | 0 .../include/AppDelegate.h | 0 .../include/CDV.h | 0 .../include/CDVAvailability.h | 0 .../include/CDVAvailabilityDeprecated.h | 0 .../include/CDVCommandDelegate.h | 0 .../include/CDVCommandDelegateImpl.h | 0 .../include/CDVConfigParser.h | 0 .../include/CDVInvokedUrlCommand.h | 0 .../include/CDVPlugin+Resources.h | 0 .../include/CDVPlugin.h | 0 .../include/CDVPluginManager.h | 0 .../include/CDVPluginResult.h | 0 .../include/CDVScreenOrientationDelegate.h | 0 .../include/CDVURLProtocol.h | 0 .../include/CDVViewController.h | 0 .../include/CDVWebViewProcessPoolFactory.h | 0 .../include/NSDictionary+CordovaPreferences.h | 0 38 files changed, 13 insertions(+), 541 deletions(-) delete mode 100644 ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj delete mode 100644 ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 ios/CapacitorCordova/CapacitorCordova/Info.plist rename ios/{CapacitorCordova/CapacitorCordova/Classes/Public => Sources/CapacitorCordova}/Plugin.swift (99%) rename ios/Sources/{CapacitorCordova => Cordova}/AppDelegate.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CDVCommandDelegateImpl.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CDVConfigParser.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CDVInvokedUrlCommand.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CDVPlugin+Resources.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CDVPlugin.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CDVPluginManager.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CDVPluginResult.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CDVURLProtocol.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CDVViewController.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CDVWebViewProcessPoolFactory.m (100%) rename ios/{CapacitorCordova/CapacitorCordova => Sources/Cordova}/CapacitorCordova.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/CapacitorCordova.modulemap (100%) rename ios/Sources/{CapacitorCordova => Cordova}/NSDictionary+CordovaPreferences.m (100%) rename ios/Sources/{CapacitorCordova => Cordova}/PrivacyInfo.xcprivacy (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/AppDelegate.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDV.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVAvailability.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVAvailabilityDeprecated.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVCommandDelegate.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVCommandDelegateImpl.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVConfigParser.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVInvokedUrlCommand.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVPlugin+Resources.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVPlugin.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVPluginManager.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVPluginResult.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVScreenOrientationDelegate.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVURLProtocol.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVViewController.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/CDVWebViewProcessPoolFactory.h (100%) rename ios/Sources/{CapacitorCordova => Cordova}/include/NSDictionary+CordovaPreferences.h (100%) diff --git a/ios/CapacitorCordova.podspec b/ios/CapacitorCordova.podspec index 0701132c3a..e5d7cf82a4 100644 --- a/ios/CapacitorCordova.podspec +++ b/ios/CapacitorCordova.podspec @@ -16,11 +16,12 @@ 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, 16.0 - s.source_files = "#{prefix}Sources/CapacitorCordova/**/*.{h,m,swift}" - s.public_header_files = "#{prefix}Sources/CapacitorCordova/Classes/Public/*.h", - "#{prefix}Sources/CapacitorCordova/CapacitorCordova.h" - s.module_map = "#{prefix}Sources/CapacitorCordova/CapacitorCordova.modulemap" - s.resource_bundles = { 'CapacitorCordova' => ["#{prefix}Sources/CapacitorCordova/PrivacyInfo.xcprivacy"] } + s.source_files = "#{prefix}Sources/Cordova/**/*.{h,m}", + "#{prefix}Sources/CapacitorCordova/**/*.swift" + s.public_header_files = "#{prefix}Sources/Cordova/include/*.h", + "#{prefix}Sources/Cordova/CapacitorCordova.h" + s.module_map = "#{prefix}Sources/Cordova/CapacitorCordova.modulemap" + s.resource_bundles = { 'CapacitorCordova' => ["#{prefix}Sources/Cordova/PrivacyInfo.xcprivacy"] } s.requires_arc = true s.dependency 'Capacitor', s.version.to_s s.framework = 'WebKit' diff --git a/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj b/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj deleted file mode 100644 index 59d25512a5..0000000000 --- a/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj +++ /dev/null @@ -1,499 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 48; - objects = { - -/* Begin PBXBuildFile section */ - 0B61A7E52B114AA00035F2DB /* CDVWebViewProcessPoolFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B61A7E32B114A9F0035F2DB /* CDVWebViewProcessPoolFactory.m */; }; - 0B61A7E62B114AA00035F2DB /* CDVWebViewProcessPoolFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B61A7E42B114AA00035F2DB /* CDVWebViewProcessPoolFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1DBF5C182E9E908A00FAC24F /* CDVAvailabilityDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DBF5C172E9E908A00FAC24F /* CDVAvailabilityDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F4F657C2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F4F657A2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.m */; }; - 2F4F657D2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F4F657B2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F5C86E11FE94845004B09C7 /* CapacitorCordova.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C86DF1FE94845004B09C7 /* CapacitorCordova.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F5C871D1FE98418004B09C7 /* CDVPluginResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F5C87121FE98417004B09C7 /* CDVPluginResult.m */; }; - 2F5C871E1FE98418004B09C7 /* CDV.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C87131FE98417004B09C7 /* CDV.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F5C871F1FE98418004B09C7 /* CDVCommandDelegateImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F5C87141FE98417004B09C7 /* CDVCommandDelegateImpl.m */; }; - 2F5C87201FE98418004B09C7 /* CDVInvokedUrlCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F5C87151FE98417004B09C7 /* CDVInvokedUrlCommand.m */; }; - 2F5C87211FE98418004B09C7 /* CDVPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F5C87161FE98417004B09C7 /* CDVPlugin.m */; }; - 2F5C87221FE98418004B09C7 /* CDVCommandDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C87171FE98417004B09C7 /* CDVCommandDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F5C87231FE98418004B09C7 /* CDVCommandDelegateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C87181FE98417004B09C7 /* CDVCommandDelegateImpl.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F5C87241FE98418004B09C7 /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C87191FE98418004B09C7 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F5C87251FE98418004B09C7 /* CDVAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C871A1FE98418004B09C7 /* CDVAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F5C87261FE98418004B09C7 /* CDVPluginResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C871B1FE98418004B09C7 /* CDVPluginResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F5C87271FE98418004B09C7 /* CDVPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F5C871C1FE98418004B09C7 /* CDVPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F856BFF203DEB320047344A /* CDVViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F856BFD203DEB320047344A /* CDVViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F856C00203DEB320047344A /* CDVViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F856BFE203DEB320047344A /* CDVViewController.m */; }; - 2F8AC283217F3A20008C2C33 /* CDVURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F8AC281217F3A20008C2C33 /* CDVURLProtocol.m */; }; - 2F8AC284217F3A20008C2C33 /* CDVURLProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F8AC282217F3A20008C2C33 /* CDVURLProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2F92AB5224D9ABA000954A4A /* CDVPlugin+Resources.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F92AB5024D9ABA000954A4A /* CDVPlugin+Resources.m */; }; - 2F92AB5324D9ABA000954A4A /* CDVPlugin+Resources.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F92AB5124D9ABA000954A4A /* CDVPlugin+Resources.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2FAD9772203C77B9000D30F8 /* CDVConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FAD9770203C77B8000D30F8 /* CDVConfigParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2FAD9773203C77B9000D30F8 /* CDVConfigParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 2FAD9771203C77B9000D30F8 /* CDVConfigParser.m */; }; - 2FE19E2A20473160002A4E89 /* AppDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FE19E2820473160002A4E89 /* AppDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2FE19E2B20473160002A4E89 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2FE19E2920473160002A4E89 /* AppDelegate.m */; }; - 62959B66252524CD00A3D7F1 /* CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B65252524CD00A3D7F1 /* CDVScreenOrientationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 62959B6A252524D700A3D7F1 /* CDVPluginManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B68252524D700A3D7F1 /* CDVPluginManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 62959B6B252524D700A3D7F1 /* CDVPluginManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 62959B69252524D700A3D7F1 /* CDVPluginManager.m */; }; - A76739742B98CC7800795F7B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A76739732B98CC7800795F7B /* PrivacyInfo.xcprivacy */; }; - D4DA0E9B2FFD28C60031AA74 /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DA0E9A2FFD28C60031AA74 /* Plugin.swift */; }; - D4DA0ED52FFD573E0031AA74 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4DA0ED42FFD573E0031AA74 /* Capacitor.framework */; }; - D4DA0ED62FFD573E0031AA74 /* Capacitor.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D4DA0ED42FFD573E0031AA74 /* Capacitor.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - D4DA0ED72FFD573E0031AA74 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - D4DA0ED62FFD573E0031AA74 /* Capacitor.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 0B61A7E32B114A9F0035F2DB /* CDVWebViewProcessPoolFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWebViewProcessPoolFactory.m; sourceTree = ""; }; - 0B61A7E42B114AA00035F2DB /* CDVWebViewProcessPoolFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewProcessPoolFactory.h; sourceTree = ""; }; - 1DBF5C172E9E908A00FAC24F /* CDVAvailabilityDeprecated.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDVAvailabilityDeprecated.h; sourceTree = ""; }; - 2F4F657A2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+CordovaPreferences.m"; sourceTree = ""; }; - 2F4F657B2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+CordovaPreferences.h"; sourceTree = ""; }; - 2F5C86DC1FE94845004B09C7 /* Cordova.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cordova.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 2F5C86DF1FE94845004B09C7 /* CapacitorCordova.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CapacitorCordova.h; sourceTree = ""; }; - 2F5C86E01FE94845004B09C7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 2F5C87121FE98417004B09C7 /* CDVPluginResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPluginResult.m; sourceTree = ""; }; - 2F5C87131FE98417004B09C7 /* CDV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDV.h; sourceTree = ""; }; - 2F5C87141FE98417004B09C7 /* CDVCommandDelegateImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVCommandDelegateImpl.m; sourceTree = ""; }; - 2F5C87151FE98417004B09C7 /* CDVInvokedUrlCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVInvokedUrlCommand.m; sourceTree = ""; }; - 2F5C87161FE98417004B09C7 /* CDVPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPlugin.m; sourceTree = ""; }; - 2F5C87171FE98417004B09C7 /* CDVCommandDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandDelegate.h; sourceTree = ""; }; - 2F5C87181FE98417004B09C7 /* CDVCommandDelegateImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandDelegateImpl.h; sourceTree = ""; }; - 2F5C87191FE98418004B09C7 /* CDVInvokedUrlCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVInvokedUrlCommand.h; sourceTree = ""; }; - 2F5C871A1FE98418004B09C7 /* CDVAvailability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAvailability.h; sourceTree = ""; }; - 2F5C871B1FE98418004B09C7 /* CDVPluginResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPluginResult.h; sourceTree = ""; }; - 2F5C871C1FE98418004B09C7 /* CDVPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPlugin.h; sourceTree = ""; }; - 2F856BFD203DEB320047344A /* CDVViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVViewController.h; sourceTree = ""; }; - 2F856BFE203DEB320047344A /* CDVViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVViewController.m; sourceTree = ""; }; - 2F8AC281217F3A20008C2C33 /* CDVURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVURLProtocol.m; sourceTree = ""; }; - 2F8AC282217F3A20008C2C33 /* CDVURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVURLProtocol.h; sourceTree = ""; }; - 2F92AB5024D9ABA000954A4A /* CDVPlugin+Resources.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CDVPlugin+Resources.m"; sourceTree = ""; }; - 2F92AB5124D9ABA000954A4A /* CDVPlugin+Resources.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CDVPlugin+Resources.h"; sourceTree = ""; }; - 2FAD9770203C77B8000D30F8 /* CDVConfigParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVConfigParser.h; sourceTree = ""; }; - 2FAD9771203C77B9000D30F8 /* CDVConfigParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVConfigParser.m; sourceTree = ""; }; - 2FE19E2820473160002A4E89 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 2FE19E2920473160002A4E89 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 62959B5F252522CB00A3D7F1 /* CapacitorCordova.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = CapacitorCordova.modulemap; sourceTree = ""; }; - 62959B65252524CD00A3D7F1 /* CDVScreenOrientationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVScreenOrientationDelegate.h; sourceTree = ""; }; - 62959B68252524D700A3D7F1 /* CDVPluginManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPluginManager.h; sourceTree = ""; }; - 62959B69252524D700A3D7F1 /* CDVPluginManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPluginManager.m; sourceTree = ""; }; - A76739732B98CC7800795F7B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; - D4DA0E962FFD28680031AA74 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D4DA0E9A2FFD28C60031AA74 /* Plugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Plugin.swift; sourceTree = ""; }; - D4DA0ED42FFD573E0031AA74 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 2F5C86D81FE94845004B09C7 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D4DA0ED52FFD573E0031AA74 /* Capacitor.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 2F5C86D21FE94845004B09C7 = { - isa = PBXGroup; - children = ( - 2F5C86DE1FE94845004B09C7 /* CapacitorCordova */, - D4DA0E952FFD28680031AA74 /* Frameworks */, - 2F5C86DD1FE94845004B09C7 /* Products */, - ); - sourceTree = ""; - }; - 2F5C86DD1FE94845004B09C7 /* Products */ = { - isa = PBXGroup; - children = ( - 2F5C86DC1FE94845004B09C7 /* Cordova.framework */, - ); - name = Products; - sourceTree = ""; - }; - 2F5C86DE1FE94845004B09C7 /* CapacitorCordova */ = { - isa = PBXGroup; - children = ( - 2F5C86E71FE94859004B09C7 /* Classes */, - 2F5C86DF1FE94845004B09C7 /* CapacitorCordova.h */, - 62959B5F252522CB00A3D7F1 /* CapacitorCordova.modulemap */, - 2F5C86E01FE94845004B09C7 /* Info.plist */, - A76739732B98CC7800795F7B /* PrivacyInfo.xcprivacy */, - ); - path = CapacitorCordova; - sourceTree = ""; - }; - 2F5C86E71FE94859004B09C7 /* Classes */ = { - isa = PBXGroup; - children = ( - 2F5C86E81FE94861004B09C7 /* Public */, - ); - path = Classes; - sourceTree = ""; - }; - 2F5C86E81FE94861004B09C7 /* Public */ = { - isa = PBXGroup; - children = ( - D4DA0E9A2FFD28C60031AA74 /* Plugin.swift */, - 2FE19E2820473160002A4E89 /* AppDelegate.h */, - 2FE19E2920473160002A4E89 /* AppDelegate.m */, - 2F5C87131FE98417004B09C7 /* CDV.h */, - 2F5C871A1FE98418004B09C7 /* CDVAvailability.h */, - 1DBF5C172E9E908A00FAC24F /* CDVAvailabilityDeprecated.h */, - 2F5C87171FE98417004B09C7 /* CDVCommandDelegate.h */, - 2F5C87141FE98417004B09C7 /* CDVCommandDelegateImpl.m */, - 2F5C87181FE98417004B09C7 /* CDVCommandDelegateImpl.h */, - 2FAD9770203C77B8000D30F8 /* CDVConfigParser.h */, - 2FAD9771203C77B9000D30F8 /* CDVConfigParser.m */, - 2F5C87191FE98418004B09C7 /* CDVInvokedUrlCommand.h */, - 2F5C87151FE98417004B09C7 /* CDVInvokedUrlCommand.m */, - 2F92AB5124D9ABA000954A4A /* CDVPlugin+Resources.h */, - 2F92AB5024D9ABA000954A4A /* CDVPlugin+Resources.m */, - 2F5C871C1FE98418004B09C7 /* CDVPlugin.h */, - 2F5C87161FE98417004B09C7 /* CDVPlugin.m */, - 62959B68252524D700A3D7F1 /* CDVPluginManager.h */, - 62959B69252524D700A3D7F1 /* CDVPluginManager.m */, - 2F5C871B1FE98418004B09C7 /* CDVPluginResult.h */, - 2F5C87121FE98417004B09C7 /* CDVPluginResult.m */, - 62959B65252524CD00A3D7F1 /* CDVScreenOrientationDelegate.h */, - 2F8AC282217F3A20008C2C33 /* CDVURLProtocol.h */, - 2F8AC281217F3A20008C2C33 /* CDVURLProtocol.m */, - 2F4F657B2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.h */, - 2F4F657A2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.m */, - 2F856BFD203DEB320047344A /* CDVViewController.h */, - 2F856BFE203DEB320047344A /* CDVViewController.m */, - 0B61A7E42B114AA00035F2DB /* CDVWebViewProcessPoolFactory.h */, - 0B61A7E32B114A9F0035F2DB /* CDVWebViewProcessPoolFactory.m */, - ); - path = Public; - sourceTree = ""; - }; - D4DA0E952FFD28680031AA74 /* Frameworks */ = { - isa = PBXGroup; - children = ( - D4DA0ED42FFD573E0031AA74 /* Capacitor.framework */, - D4DA0E962FFD28680031AA74 /* Capacitor.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 2F5C86D91FE94845004B09C7 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 2FE19E2A20473160002A4E89 /* AppDelegate.h in Headers */, - 2F5C871E1FE98418004B09C7 /* CDV.h in Headers */, - 2F5C87231FE98418004B09C7 /* CDVCommandDelegateImpl.h in Headers */, - 2F5C87251FE98418004B09C7 /* CDVAvailability.h in Headers */, - 2F5C87271FE98418004B09C7 /* CDVPlugin.h in Headers */, - 2F5C87261FE98418004B09C7 /* CDVPluginResult.h in Headers */, - 2F5C87221FE98418004B09C7 /* CDVCommandDelegate.h in Headers */, - 2F5C87241FE98418004B09C7 /* CDVInvokedUrlCommand.h in Headers */, - 2FAD9772203C77B9000D30F8 /* CDVConfigParser.h in Headers */, - 2F856BFF203DEB320047344A /* CDVViewController.h in Headers */, - 2F4F657D2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.h in Headers */, - 2F8AC284217F3A20008C2C33 /* CDVURLProtocol.h in Headers */, - 2F92AB5324D9ABA000954A4A /* CDVPlugin+Resources.h in Headers */, - 62959B66252524CD00A3D7F1 /* CDVScreenOrientationDelegate.h in Headers */, - 0B61A7E62B114AA00035F2DB /* CDVWebViewProcessPoolFactory.h in Headers */, - 62959B6A252524D700A3D7F1 /* CDVPluginManager.h in Headers */, - 1DBF5C182E9E908A00FAC24F /* CDVAvailabilityDeprecated.h in Headers */, - 2F5C86E11FE94845004B09C7 /* CapacitorCordova.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 2F5C86DB1FE94845004B09C7 /* Cordova */ = { - isa = PBXNativeTarget; - buildConfigurationList = 2F5C86E41FE94845004B09C7 /* Build configuration list for PBXNativeTarget "Cordova" */; - buildPhases = ( - 2F5C86D71FE94845004B09C7 /* Sources */, - 2F5C86D81FE94845004B09C7 /* Frameworks */, - 2F5C86D91FE94845004B09C7 /* Headers */, - 2F5C86DA1FE94845004B09C7 /* Resources */, - D4DA0ED72FFD573E0031AA74 /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Cordova; - productName = AvocadoCordova; - productReference = 2F5C86DC1FE94845004B09C7 /* Cordova.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 2F5C86D31FE94845004B09C7 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1220; - ORGANIZATIONNAME = jcesarmobile; - TargetAttributes = { - 2F5C86DB1FE94845004B09C7 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 2650; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = 2F5C86D61FE94845004B09C7 /* Build configuration list for PBXProject "CapacitorCordova" */; - compatibilityVersion = "Xcode 8.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 2F5C86D21FE94845004B09C7; - productRefGroup = 2F5C86DD1FE94845004B09C7 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 2F5C86DB1FE94845004B09C7 /* Cordova */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 2F5C86DA1FE94845004B09C7 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A76739742B98CC7800795F7B /* PrivacyInfo.xcprivacy in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 2F5C86D71FE94845004B09C7 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 62959B6B252524D700A3D7F1 /* CDVPluginManager.m in Sources */, - 2F5C871F1FE98418004B09C7 /* CDVCommandDelegateImpl.m in Sources */, - 2F5C871D1FE98418004B09C7 /* CDVPluginResult.m in Sources */, - 2F4F657C2091F1FD00EAA994 /* NSDictionary+CordovaPreferences.m in Sources */, - 0B61A7E52B114AA00035F2DB /* CDVWebViewProcessPoolFactory.m in Sources */, - 2F5C87211FE98418004B09C7 /* CDVPlugin.m in Sources */, - 2F92AB5224D9ABA000954A4A /* CDVPlugin+Resources.m in Sources */, - 2FAD9773203C77B9000D30F8 /* CDVConfigParser.m in Sources */, - 2F856C00203DEB320047344A /* CDVViewController.m in Sources */, - 2F5C87201FE98418004B09C7 /* CDVInvokedUrlCommand.m in Sources */, - D4DA0E9B2FFD28C60031AA74 /* Plugin.swift in Sources */, - 2FE19E2B20473160002A4E89 /* AppDelegate.m in Sources */, - 2F8AC283217F3A20008C2C33 /* CDVURLProtocol.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 2F5C86E21FE94845004B09C7 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 2F5C86E31FE94845004B09C7 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 2F5C86E51FE94845004B09C7 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Automatic; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = CapacitorCordova/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = CapacitorCordova/CapacitorCordova.modulemap; - PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.ios.CapacitorCordova; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 2F5C86E61FE94845004B09C7 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Automatic; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = CapacitorCordova/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = CapacitorCordova/CapacitorCordova.modulemap; - PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.ios.CapacitorCordova; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 2F5C86D61FE94845004B09C7 /* Build configuration list for PBXProject "CapacitorCordova" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 2F5C86E21FE94845004B09C7 /* Debug */, - 2F5C86E31FE94845004B09C7 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 2F5C86E41FE94845004B09C7 /* Build configuration list for PBXNativeTarget "Cordova" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 2F5C86E51FE94845004B09C7 /* Debug */, - 2F5C86E61FE94845004B09C7 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 2F5C86D31FE94845004B09C7 /* Project object */; -} diff --git a/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/CapacitorCordova/CapacitorCordova/Info.plist b/ios/CapacitorCordova/CapacitorCordova/Info.plist deleted file mode 100644 index 1483c473b4..0000000000 --- a/ios/CapacitorCordova/CapacitorCordova/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Cordova - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/ios/Package.swift b/ios/Package.swift index c9a0fc3a47..80e3203bd8 100644 --- a/ios/Package.swift +++ b/ios/Package.swift @@ -11,7 +11,7 @@ let package = Package( ), .library( name: "CapacitorCordova", - targets: ["CapacitorCordova"] + targets: ["Cordova", "CapacitorCordova"] ) ], targets: [ @@ -23,8 +23,7 @@ let package = Package( ], ), .target( - name: "CapacitorCordova", - dependencies: ["Capacitor"], + name: "Cordova", publicHeadersPath: "include", cSettings: [ .headerSearchPath("include"), @@ -36,6 +35,10 @@ let package = Package( .linkedFramework("CFNetwork") ] ), + .target( + name: "CapacitorCordova", + dependencies: ["Capacitor", "Cordova"] + ), .testTarget( name: "CapacitorTests", dependencies: [ diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift b/ios/Sources/CapacitorCordova/Plugin.swift similarity index 99% rename from ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift rename to ios/Sources/CapacitorCordova/Plugin.swift index d58c4ca33f..40e9657e8b 100644 --- a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift +++ b/ios/Sources/CapacitorCordova/Plugin.swift @@ -1,4 +1,5 @@ import Capacitor +import Cordova @objc(CordovaPlugin) public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { diff --git a/ios/Sources/CapacitorCordova/AppDelegate.m b/ios/Sources/Cordova/AppDelegate.m similarity index 100% rename from ios/Sources/CapacitorCordova/AppDelegate.m rename to ios/Sources/Cordova/AppDelegate.m diff --git a/ios/Sources/CapacitorCordova/CDVCommandDelegateImpl.m b/ios/Sources/Cordova/CDVCommandDelegateImpl.m similarity index 100% rename from ios/Sources/CapacitorCordova/CDVCommandDelegateImpl.m rename to ios/Sources/Cordova/CDVCommandDelegateImpl.m diff --git a/ios/Sources/CapacitorCordova/CDVConfigParser.m b/ios/Sources/Cordova/CDVConfigParser.m similarity index 100% rename from ios/Sources/CapacitorCordova/CDVConfigParser.m rename to ios/Sources/Cordova/CDVConfigParser.m diff --git a/ios/Sources/CapacitorCordova/CDVInvokedUrlCommand.m b/ios/Sources/Cordova/CDVInvokedUrlCommand.m similarity index 100% rename from ios/Sources/CapacitorCordova/CDVInvokedUrlCommand.m rename to ios/Sources/Cordova/CDVInvokedUrlCommand.m diff --git a/ios/Sources/CapacitorCordova/CDVPlugin+Resources.m b/ios/Sources/Cordova/CDVPlugin+Resources.m similarity index 100% rename from ios/Sources/CapacitorCordova/CDVPlugin+Resources.m rename to ios/Sources/Cordova/CDVPlugin+Resources.m diff --git a/ios/Sources/CapacitorCordova/CDVPlugin.m b/ios/Sources/Cordova/CDVPlugin.m similarity index 100% rename from ios/Sources/CapacitorCordova/CDVPlugin.m rename to ios/Sources/Cordova/CDVPlugin.m diff --git a/ios/Sources/CapacitorCordova/CDVPluginManager.m b/ios/Sources/Cordova/CDVPluginManager.m similarity index 100% rename from ios/Sources/CapacitorCordova/CDVPluginManager.m rename to ios/Sources/Cordova/CDVPluginManager.m diff --git a/ios/Sources/CapacitorCordova/CDVPluginResult.m b/ios/Sources/Cordova/CDVPluginResult.m similarity index 100% rename from ios/Sources/CapacitorCordova/CDVPluginResult.m rename to ios/Sources/Cordova/CDVPluginResult.m diff --git a/ios/Sources/CapacitorCordova/CDVURLProtocol.m b/ios/Sources/Cordova/CDVURLProtocol.m similarity index 100% rename from ios/Sources/CapacitorCordova/CDVURLProtocol.m rename to ios/Sources/Cordova/CDVURLProtocol.m diff --git a/ios/Sources/CapacitorCordova/CDVViewController.m b/ios/Sources/Cordova/CDVViewController.m similarity index 100% rename from ios/Sources/CapacitorCordova/CDVViewController.m rename to ios/Sources/Cordova/CDVViewController.m diff --git a/ios/Sources/CapacitorCordova/CDVWebViewProcessPoolFactory.m b/ios/Sources/Cordova/CDVWebViewProcessPoolFactory.m similarity index 100% rename from ios/Sources/CapacitorCordova/CDVWebViewProcessPoolFactory.m rename to ios/Sources/Cordova/CDVWebViewProcessPoolFactory.m diff --git a/ios/CapacitorCordova/CapacitorCordova/CapacitorCordova.h b/ios/Sources/Cordova/CapacitorCordova.h similarity index 100% rename from ios/CapacitorCordova/CapacitorCordova/CapacitorCordova.h rename to ios/Sources/Cordova/CapacitorCordova.h diff --git a/ios/Sources/CapacitorCordova/CapacitorCordova.modulemap b/ios/Sources/Cordova/CapacitorCordova.modulemap similarity index 100% rename from ios/Sources/CapacitorCordova/CapacitorCordova.modulemap rename to ios/Sources/Cordova/CapacitorCordova.modulemap diff --git a/ios/Sources/CapacitorCordova/NSDictionary+CordovaPreferences.m b/ios/Sources/Cordova/NSDictionary+CordovaPreferences.m similarity index 100% rename from ios/Sources/CapacitorCordova/NSDictionary+CordovaPreferences.m rename to ios/Sources/Cordova/NSDictionary+CordovaPreferences.m diff --git a/ios/Sources/CapacitorCordova/PrivacyInfo.xcprivacy b/ios/Sources/Cordova/PrivacyInfo.xcprivacy similarity index 100% rename from ios/Sources/CapacitorCordova/PrivacyInfo.xcprivacy rename to ios/Sources/Cordova/PrivacyInfo.xcprivacy diff --git a/ios/Sources/CapacitorCordova/include/AppDelegate.h b/ios/Sources/Cordova/include/AppDelegate.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/AppDelegate.h rename to ios/Sources/Cordova/include/AppDelegate.h diff --git a/ios/Sources/CapacitorCordova/include/CDV.h b/ios/Sources/Cordova/include/CDV.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDV.h rename to ios/Sources/Cordova/include/CDV.h diff --git a/ios/Sources/CapacitorCordova/include/CDVAvailability.h b/ios/Sources/Cordova/include/CDVAvailability.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVAvailability.h rename to ios/Sources/Cordova/include/CDVAvailability.h diff --git a/ios/Sources/CapacitorCordova/include/CDVAvailabilityDeprecated.h b/ios/Sources/Cordova/include/CDVAvailabilityDeprecated.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVAvailabilityDeprecated.h rename to ios/Sources/Cordova/include/CDVAvailabilityDeprecated.h diff --git a/ios/Sources/CapacitorCordova/include/CDVCommandDelegate.h b/ios/Sources/Cordova/include/CDVCommandDelegate.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVCommandDelegate.h rename to ios/Sources/Cordova/include/CDVCommandDelegate.h diff --git a/ios/Sources/CapacitorCordova/include/CDVCommandDelegateImpl.h b/ios/Sources/Cordova/include/CDVCommandDelegateImpl.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVCommandDelegateImpl.h rename to ios/Sources/Cordova/include/CDVCommandDelegateImpl.h diff --git a/ios/Sources/CapacitorCordova/include/CDVConfigParser.h b/ios/Sources/Cordova/include/CDVConfigParser.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVConfigParser.h rename to ios/Sources/Cordova/include/CDVConfigParser.h diff --git a/ios/Sources/CapacitorCordova/include/CDVInvokedUrlCommand.h b/ios/Sources/Cordova/include/CDVInvokedUrlCommand.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVInvokedUrlCommand.h rename to ios/Sources/Cordova/include/CDVInvokedUrlCommand.h diff --git a/ios/Sources/CapacitorCordova/include/CDVPlugin+Resources.h b/ios/Sources/Cordova/include/CDVPlugin+Resources.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVPlugin+Resources.h rename to ios/Sources/Cordova/include/CDVPlugin+Resources.h diff --git a/ios/Sources/CapacitorCordova/include/CDVPlugin.h b/ios/Sources/Cordova/include/CDVPlugin.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVPlugin.h rename to ios/Sources/Cordova/include/CDVPlugin.h diff --git a/ios/Sources/CapacitorCordova/include/CDVPluginManager.h b/ios/Sources/Cordova/include/CDVPluginManager.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVPluginManager.h rename to ios/Sources/Cordova/include/CDVPluginManager.h diff --git a/ios/Sources/CapacitorCordova/include/CDVPluginResult.h b/ios/Sources/Cordova/include/CDVPluginResult.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVPluginResult.h rename to ios/Sources/Cordova/include/CDVPluginResult.h diff --git a/ios/Sources/CapacitorCordova/include/CDVScreenOrientationDelegate.h b/ios/Sources/Cordova/include/CDVScreenOrientationDelegate.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVScreenOrientationDelegate.h rename to ios/Sources/Cordova/include/CDVScreenOrientationDelegate.h diff --git a/ios/Sources/CapacitorCordova/include/CDVURLProtocol.h b/ios/Sources/Cordova/include/CDVURLProtocol.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVURLProtocol.h rename to ios/Sources/Cordova/include/CDVURLProtocol.h diff --git a/ios/Sources/CapacitorCordova/include/CDVViewController.h b/ios/Sources/Cordova/include/CDVViewController.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVViewController.h rename to ios/Sources/Cordova/include/CDVViewController.h diff --git a/ios/Sources/CapacitorCordova/include/CDVWebViewProcessPoolFactory.h b/ios/Sources/Cordova/include/CDVWebViewProcessPoolFactory.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/CDVWebViewProcessPoolFactory.h rename to ios/Sources/Cordova/include/CDVWebViewProcessPoolFactory.h diff --git a/ios/Sources/CapacitorCordova/include/NSDictionary+CordovaPreferences.h b/ios/Sources/Cordova/include/NSDictionary+CordovaPreferences.h similarity index 100% rename from ios/Sources/CapacitorCordova/include/NSDictionary+CordovaPreferences.h rename to ios/Sources/Cordova/include/NSDictionary+CordovaPreferences.h From c2e09c8a30d26c2693c43baf3ea4b218c1b3fa21 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 17 Aug 2026 15:31:24 -0500 Subject: [PATCH 08/42] Move Package.swift to repo root and add explicit target paths --- ios/Package.swift => Package.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) rename ios/Package.swift => Package.swift (84%) diff --git a/ios/Package.swift b/Package.swift similarity index 84% rename from ios/Package.swift rename to Package.swift index 80e3203bd8..623ab65814 100644 --- a/ios/Package.swift +++ b/Package.swift @@ -17,13 +17,16 @@ let package = Package( targets: [ .target( name: "Capacitor", + path: "ios/Sources/Capacitor", resources: [.copy("assets")], swiftSettings: [ .swiftLanguageMode(.v5) ], + ), .target( name: "Cordova", + path: "ios/Sources/Cordova", publicHeadersPath: "include", cSettings: [ .headerSearchPath("include"), @@ -37,13 +40,15 @@ let package = Package( ), .target( name: "CapacitorCordova", - dependencies: ["Capacitor", "Cordova"] + dependencies: ["Capacitor", "Cordova"], + path: "ios/Sources/CapacitorCordova", ), .testTarget( name: "CapacitorTests", dependencies: [ "Capacitor" ], + path: "ios/Tests/CapacitorTests", resources: [ .copy("Resources/configurations") ] From 90b4faea7edb8b7e856f449b23403da42d2c4428 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 17 Aug 2026 15:43:37 -0500 Subject: [PATCH 09/42] Expose Cordova as a separate SPM library product --- Package.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Package.swift b/Package.swift index 623ab65814..dc0383f631 100644 --- a/Package.swift +++ b/Package.swift @@ -9,6 +9,10 @@ let package = Package( name: "Capacitor", targets: ["Capacitor"] ), + .library( + name: "Cordova", + targets: ["Cordova"] + ), .library( name: "CapacitorCordova", targets: ["Cordova", "CapacitorCordova"] From d51f9eaa39287d8bf1a074b15d0484ff5f603749 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 18 Aug 2026 09:51:55 -0500 Subject: [PATCH 10/42] updating cli to support new spm template --- cli/src/ios/update.ts | 7 ++++- cli/src/util/cordova-ios.ts | 27 ++++++++++++------- cli/src/util/spm.ts | 14 +++++++--- ios-spm-template/App/CapApp-SPM/Package.swift | 8 +++--- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 93a1ad4b55..3f4f009a1b 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -65,13 +65,18 @@ async function updatePluginFiles(config: Config, plugins: Plugin[], deployment: await Promise.all( validSPMPackages.map(async (plugin) => { const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); + const majorCapVersion = major(iosPlatformVersion); + if (majorCapVersion >= 9) { + // Capacitor 9+ uses a branch-based dependency, so there's no version to reconcile. + return; + } + const packageSwiftPath = join(plugin.rootPath, 'Package.swift'); let content = await readFile(packageSwiftPath, { encoding: 'utf-8' }); const regex = new RegExp( 'url:\\s*"https://github.com/ionic-team/capacitor-swift-pm\\.git",\\s*from:\\s*"([^"]+)"', ); const version = content.match(regex)?.[1]; - const majorCapVersion = major(iosPlatformVersion); if (version && major(version) != majorCapVersion) { const preCapVersion = prerelease(iosPlatformVersion); const forceVersion = preCapVersion ? iosPlatformVersion : `${majorCapVersion}.0.0`; diff --git a/cli/src/util/cordova-ios.ts b/cli/src/util/cordova-ios.ts index 371bd96d33..f097d4ed29 100644 --- a/cli/src/util/cordova-ios.ts +++ b/cli/src/util/cordova-ios.ts @@ -1,5 +1,6 @@ import { copy, readFile, writeFile, remove } from 'fs-extra'; import { join } from 'path'; +import { major } from 'semver'; import { getCapacitorPackageVersion } from '../common'; import { needsStaticPod } from '../cordova'; @@ -353,18 +354,26 @@ export async function generateCordovaPackageFile(p: Plugin, config: Config): Pro publicHeadersPath: "."`; } + const useSourceSPM = major(iosPlatformVersion) >= 9; + const capacitorPackageName = useSourceSPM ? 'capacitor' : 'capacitor-swift-pm'; + const capacitorPackageUrl = useSourceSPM + ? 'https://github.com/ionic-team/capacitor' + : 'https://github.com/ionic-team/capacitor-swift-pm.git'; + const capacitorPackageVersionSuffix = useSourceSPM + ? ` branch: "feature/source-spm"` + : ` from: "${iosPlatformVersion}"`; + const platformTag = getPluginPlatform(p, platform); if (platformTag.$?.package) { const packageSwiftPath = join(p.rootPath, 'Package.swift'); let content = await readFile(packageSwiftPath, { encoding: 'utf-8' }); - content = content.replace(`apache`, `ionic-team`).replaceAll(`cordova-ios`, `capacitor-swift-pm`); - content = setAllStringIn( - content, - `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, - `)`, - ` from: "${iosPlatformVersion}"`, - ); + content = content.replace(`apache`, `ionic-team`); + // The source repo is referenced as "cordova-ios.git" in URLs; source-SPM's package has no .git suffix. + content = useSourceSPM + ? content.replaceAll(`cordova-ios.git`, capacitorPackageName).replaceAll(`cordova-ios`, capacitorPackageName) + : content.replaceAll(`cordova-ios`, capacitorPackageName); + content = setAllStringIn(content, `url: "${capacitorPackageUrl}",`, `)`, capacitorPackageVersionSuffix); await writeFile(packageSwiftPath, content); } else { const content = `// swift-tools-version: 5.9 @@ -381,13 +390,13 @@ let package = Package( ) ], dependencies: [ - .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "${iosPlatformVersion}") + .package(url: "${capacitorPackageUrl}",${capacitorPackageVersionSuffix}) ], targets: [ .target( name: "${p.name}", dependencies: [ - .product(name: "Cordova", package: "capacitor-swift-pm") + .product(name: "Cordova", package: "${capacitorPackageName}") ], path: "."${headersText} ) diff --git a/cli/src/util/spm.ts b/cli/src/util/spm.ts index 2c8cacc672..fbaf137a88 100644 --- a/cli/src/util/spm.ts +++ b/cli/src/util/spm.ts @@ -3,6 +3,7 @@ import { tmpdir } from 'os'; import { join, relative, resolve } from 'path'; import type { PlistObject } from 'plist'; import { build, parse } from 'plist'; +import { major } from 'semver'; import { extract } from 'tar'; import { getCapacitorPackageVersion } from '../common'; @@ -105,7 +106,12 @@ export async function generatePackageText(config: Config, plugins: Plugin[]): Pr const enableCordova = cordovaPlugins.length > 0; const packageTraits = config.app.extConfig.experimental?.ios?.spm?.packageTraits ?? {}; const packageOptions = config.app.extConfig.experimental?.ios?.spm?.packageOptions ?? {}; - const swiftToolsVersion = config.app.extConfig.experimental?.ios?.spm?.swiftToolsVersion ?? '5.9'; + const swiftToolsVersion = config.app.extConfig.experimental?.ios?.spm?.swiftToolsVersion ?? '6.3'; + const useSourceSPM = major(iosPlatformVersion) >= 9; + const capacitorPackageName = useSourceSPM ? 'capacitor' : 'capacitor-swift-pm'; + const capacitorPackageDependency = useSourceSPM + ? `.package(url: "https://github.com/ionic-team/capacitor", branch: "feature/source-spm")` + : `.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "${iosPlatformVersion}")`; let packageSwiftText = `// swift-tools-version: ${swiftToolsVersion} import PackageDescription @@ -120,7 +126,7 @@ let package = Package( targets: ["CapApp-SPM"]) ], dependencies: [ - .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "${iosPlatformVersion}")`; + ${capacitorPackageDependency}`; for (const plugin of plugins) { if (getPluginType(plugin, config.ios.name) === PluginType.Cordova) { @@ -165,10 +171,10 @@ let package = Package( .target( name: "CapApp-SPM", dependencies: [ - .product(name: "Capacitor", package: "capacitor-swift-pm")`; + .product(name: "Capacitor", package: "${capacitorPackageName}")`; if (enableCordova) { - packageSwiftText += `,\n .product(name: "Cordova", package: "capacitor-swift-pm")`; + packageSwiftText += `,\n .product(name: "Cordova", package: "${capacitorPackageName}")`; } for (const plugin of plugins) { diff --git a/ios-spm-template/App/CapApp-SPM/Package.swift b/ios-spm-template/App/CapApp-SPM/Package.swift index e3309a275a..0ef9255f70 100644 --- a/ios-spm-template/App/CapApp-SPM/Package.swift +++ b/ios-spm-template/App/CapApp-SPM/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.9 +// swift-tools-version: 6.3 import PackageDescription // DO NOT MODIFY THIS FILE - managed by Capacitor CLI commands @@ -11,14 +11,14 @@ let package = Package( targets: ["CapApp-SPM"]) ], dependencies: [ - .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0") + .package(url: "https://github.com/ionic-team/capacitor", branch: "feature/source-spm") ], targets: [ .target( name: "CapApp-SPM", dependencies: [ - .product(name: "Capacitor", package: "capacitor-swift-pm"), - .product(name: "Cordova", package: "capacitor-swift-pm") + .product(name: "Capacitor", package: "capacitor"), + .product(name: "Cordova", package: "capacitor") ] ) ] From 7528a5fb6bb45478918b5359931919e545dbcd2a Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 18 Aug 2026 15:45:56 -0500 Subject: [PATCH 11/42] Use Bundle.module for native-bridge.js when built via SPM --- ios/Sources/Capacitor/JSExport.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ios/Sources/Capacitor/JSExport.swift b/ios/Sources/Capacitor/JSExport.swift index 1092162a4d..8f933eb123 100644 --- a/ios/Sources/Capacitor/JSExport.swift +++ b/ios/Sources/Capacitor/JSExport.swift @@ -24,7 +24,11 @@ internal class JSExport { } static func exportBridgeJS(userContentController: WKUserContentController) throws { + #if SWIFT_PACKAGE + let capBundle = Bundle.module + #else let capBundle = Bundle(for: Self.self) + #endif guard let jsUrl = capBundle.url(forResource: "native-bridge", withExtension: "js") else { CAPLog.print("ERROR: Required native-bridge.js file in Capacitor not found. Bridge will not function!") throw CapacitorBridgeError.errorExportingCoreJS From 40abeed63cd31c5179f73679c659177baa2cee25 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 18 Aug 2026 16:03:57 -0500 Subject: [PATCH 12/42] Fix native-bridge.js lookup path for SPM builds --- ios/Sources/Capacitor/JSExport.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/Sources/Capacitor/JSExport.swift b/ios/Sources/Capacitor/JSExport.swift index 8f933eb123..0a4b352b1f 100644 --- a/ios/Sources/Capacitor/JSExport.swift +++ b/ios/Sources/Capacitor/JSExport.swift @@ -25,11 +25,11 @@ internal class JSExport { static func exportBridgeJS(userContentController: WKUserContentController) throws { #if SWIFT_PACKAGE - let capBundle = Bundle.module + let jsUrl = Bundle.module.url(forResource: "native-bridge", withExtension: "js", subdirectory: "assets") #else - let capBundle = Bundle(for: Self.self) + let jsUrl = Bundle(for: Self.self).url(forResource: "native-bridge", withExtension: "js") #endif - guard let jsUrl = capBundle.url(forResource: "native-bridge", withExtension: "js") else { + guard let jsUrl else { CAPLog.print("ERROR: Required native-bridge.js file in Capacitor not found. Bridge will not function!") throw CapacitorBridgeError.errorExportingCoreJS } From ffb389e4b4149526b2c6bd09176c4e0d7dfbfa00 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 19 Aug 2026 17:29:41 -0500 Subject: [PATCH 13/42] Point SPM package dependency to next branch instead of feature/source-spm --- cli/src/util/spm.ts | 2 +- ios-spm-template/App/CapApp-SPM/Package.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/util/spm.ts b/cli/src/util/spm.ts index fbaf137a88..9245abc3cd 100644 --- a/cli/src/util/spm.ts +++ b/cli/src/util/spm.ts @@ -110,7 +110,7 @@ export async function generatePackageText(config: Config, plugins: Plugin[]): Pr const useSourceSPM = major(iosPlatformVersion) >= 9; const capacitorPackageName = useSourceSPM ? 'capacitor' : 'capacitor-swift-pm'; const capacitorPackageDependency = useSourceSPM - ? `.package(url: "https://github.com/ionic-team/capacitor", branch: "feature/source-spm")` + ? `.package(url: "https://github.com/ionic-team/capacitor", branch: "next")` : `.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "${iosPlatformVersion}")`; let packageSwiftText = `// swift-tools-version: ${swiftToolsVersion} diff --git a/ios-spm-template/App/CapApp-SPM/Package.swift b/ios-spm-template/App/CapApp-SPM/Package.swift index 0ef9255f70..542ef9c365 100644 --- a/ios-spm-template/App/CapApp-SPM/Package.swift +++ b/ios-spm-template/App/CapApp-SPM/Package.swift @@ -11,7 +11,7 @@ let package = Package( targets: ["CapApp-SPM"]) ], dependencies: [ - .package(url: "https://github.com/ionic-team/capacitor", branch: "feature/source-spm") + .package(url: "https://github.com/ionic-team/capacitor", branch: "next") ], targets: [ .target( From 609054843c03569496cd40fb517ad6eccbb805fd Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 19 Aug 2026 18:14:25 -0500 Subject: [PATCH 14/42] fixing tests --- ios/Tests/CapacitorTests/DateCodableTests.swift | 2 +- ios/Tests/CapacitorTests/JSExportTests.swift | 2 +- ios/package.json | 9 ++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ios/Tests/CapacitorTests/DateCodableTests.swift b/ios/Tests/CapacitorTests/DateCodableTests.swift index 69319330ed..5578059765 100644 --- a/ios/Tests/CapacitorTests/DateCodableTests.swift +++ b/ios/Tests/CapacitorTests/DateCodableTests.swift @@ -16,7 +16,7 @@ private let formatter: DateFormatter = { formatter.locale = .init(identifier: "en_US") return formatter }() -private let formatted = "Sep 5, 2024 at 5:36:20 PM CDT" +private let formatted = "Sep 5, 2024 at 5:36:20\u{202F}PM CDT" private struct Foo: Codable, Equatable { var date: Date diff --git a/ios/Tests/CapacitorTests/JSExportTests.swift b/ios/Tests/CapacitorTests/JSExportTests.swift index 6d2f9805cb..7a9c800324 100644 --- a/ios/Tests/CapacitorTests/JSExportTests.swift +++ b/ios/Tests/CapacitorTests/JSExportTests.swift @@ -3,7 +3,7 @@ import WebKit @testable import Capacitor struct JSExportTests { - @Test func bridgeBundleExports() throws { + @Test @MainActor func bridgeBundleExports() throws { let contentController = WKUserContentController() try Capacitor.JSExport.exportBridgeJS(userContentController: contentController) } diff --git a/ios/package.json b/ios/package.json index 2bc19350bb..6283736249 100644 --- a/ios/package.json +++ b/ios/package.json @@ -13,16 +13,15 @@ "url": "https://github.com/ionic-team/capacitor/issues" }, "files": [ - "Capacitor/Capacitor/", - "CapacitorCordova/CapacitorCordova/", + "Sources/", "Capacitor.podspec", "CapacitorCordova.podspec", "scripts/pods_helpers.rb" ], "scripts": { - "verify": "npm run xc:build:Capacitor && npm run xc:build:CapacitorCordova", - "xc:build:Capacitor": "cd Capacitor && xcodebuild clean test -workspace Capacitor.xcworkspace -scheme Capacitor -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.0.1' && cd ..", - "xc:build:CapacitorCordova": "cd Capacitor && xcodebuild clean build -workspace Capacitor.xcworkspace -scheme Cordova && cd .." + "verify": "npm run xc:build && npm run xc:test", + "xc:build": "cd .. && xcodebuild build -scheme Capacitor-Package -destination 'generic/platform=iOS Simulator'", + "xc:test": "cd .. && xcodebuild test -scheme Capacitor-Package -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.0.1' -collect-test-diagnostics never" }, "peerDependencies": { "@capacitor/core": "^9.0.0-alpha.6" From 5cbe5a88eb8f06d613d0bdc9ec2e502c07bf30a6 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 19 Aug 2026 18:20:14 -0500 Subject: [PATCH 15/42] fmt --- ios-spm-template/App/CapApp-SPM/Package.swift | 2 +- ios/Sources/Capacitor/CAPPlugin.swift | 4 +- ios/Sources/Capacitor/CAPPluginCall.swift | 1 - .../Capacitor/InstanceDescriptor.swift | 2 +- ios/Sources/Capacitor/assets/native-bridge.js | 2027 +++++++++-------- .../CapacitorTests/BridgedTypesTests.swift | 4 +- .../CapacitorTests/ConfigurationTests.swift | 2 +- 7 files changed, 1044 insertions(+), 998 deletions(-) diff --git a/ios-spm-template/App/CapApp-SPM/Package.swift b/ios-spm-template/App/CapApp-SPM/Package.swift index 542ef9c365..b46d6e9560 100644 --- a/ios-spm-template/App/CapApp-SPM/Package.swift +++ b/ios-spm-template/App/CapApp-SPM/Package.swift @@ -11,7 +11,7 @@ let package = Package( targets: ["CapApp-SPM"]) ], dependencies: [ - .package(url: "https://github.com/ionic-team/capacitor", branch: "next") + .package(url: "https://github.com/ionic-team/capacitor", branch: "next") ], targets: [ .target( diff --git a/ios/Sources/Capacitor/CAPPlugin.swift b/ios/Sources/Capacitor/CAPPlugin.swift index 9c34a8f038..6d5d12752d 100644 --- a/ios/Sources/Capacitor/CAPPlugin.swift +++ b/ios/Sources/Capacitor/CAPPlugin.swift @@ -22,7 +22,7 @@ import UIKit self.shouldStringifyDatesInCalls = true } - @objc required override public init() { + @objc override public required init() { super.init() self.bridge = nil self.webView = nil @@ -188,7 +188,7 @@ import UIKit } @objc public func handleWKWebViewURLAuthenticationChallenge( - _ challenge: URLAuthenticationChallenge, + _ challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void ) -> Bool { return false diff --git a/ios/Sources/Capacitor/CAPPluginCall.swift b/ios/Sources/Capacitor/CAPPluginCall.swift index f438007bef..fa7fe53b8d 100644 --- a/ios/Sources/Capacitor/CAPPluginCall.swift +++ b/ios/Sources/Capacitor/CAPPluginCall.swift @@ -113,7 +113,6 @@ public typealias CAPPluginCallErrorHandler = (CAPPluginCallError) -> Void } } - // MARK: - JSValue Representation extension CAPPluginCall: JSValueContainer { diff --git a/ios/Sources/Capacitor/InstanceDescriptor.swift b/ios/Sources/Capacitor/InstanceDescriptor.swift index 89013fade4..225d31b228 100644 --- a/ios/Sources/Capacitor/InstanceDescriptor.swift +++ b/ios/Sources/Capacitor/InstanceDescriptor.swift @@ -41,7 +41,7 @@ open class InstanceDescriptor: NSObject { // MARK: - Initialization - @objc public override init() { + @objc override public init() { self.instanceType = .fixed let publicURL = Bundle.main.url(forResource: "public", withExtension: nil) self.appLocation = publicURL ?? Bundle.main.resourceURL ?? URL(fileURLWithPath: "/") diff --git a/ios/Sources/Capacitor/assets/native-bridge.js b/ios/Sources/Capacitor/assets/native-bridge.js index f5e7cc4403..d16b9d5689 100644 --- a/ios/Sources/Capacitor/assets/native-bridge.js +++ b/ios/Sources/Capacitor/assets/native-bridge.js @@ -1,1039 +1,1086 @@ - /*! Capacitor: https://capacitorjs.com/ - MIT License */ /* Generated File. Do not edit. */ var nativeBridge = (function (exports) { - 'use strict'; + 'use strict'; - var ExceptionCode; - (function (ExceptionCode) { - /** - * API is not implemented. - * - * This usually means the API can't be used because it is not implemented for - * the current platform. - */ - ExceptionCode["Unimplemented"] = "UNIMPLEMENTED"; - /** - * API is not available. - * - * This means the API can't be used right now because: - * - it is currently missing a prerequisite, such as network connectivity - * - it requires a particular platform or browser version - */ - ExceptionCode["Unavailable"] = "UNAVAILABLE"; - })(ExceptionCode || (ExceptionCode = {})); - class CapacitorException extends Error { - constructor(message, code, data) { - super(message); - this.message = message; - this.code = code; - this.data = data; - } + var ExceptionCode; + (function (ExceptionCode) { + /** + * API is not implemented. + * + * This usually means the API can't be used because it is not implemented for + * the current platform. + */ + ExceptionCode['Unimplemented'] = 'UNIMPLEMENTED'; + /** + * API is not available. + * + * This means the API can't be used right now because: + * - it is currently missing a prerequisite, such as network connectivity + * - it requires a particular platform or browser version + */ + ExceptionCode['Unavailable'] = 'UNAVAILABLE'; + })(ExceptionCode || (ExceptionCode = {})); + class CapacitorException extends Error { + constructor(message, code, data) { + super(message); + this.message = message; + this.code = code; + this.data = data; } + } - // For removing exports for iOS/Android, keep let for reassignment - // eslint-disable-next-line - let dummy = {}; - const readFileAsBase64 = (file) => new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onloadend = () => { - const data = reader.result; - resolve(btoa(data)); - }; - reader.onerror = reject; - reader.readAsBinaryString(file); + // For removing exports for iOS/Android, keep let for reassignment + // eslint-disable-next-line + let dummy = {}; + const readFileAsBase64 = (file) => + new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => { + const data = reader.result; + resolve(btoa(data)); + }; + reader.onerror = reject; + reader.readAsBinaryString(file); }); - const convertFormData = async (formData) => { - const newFormData = []; - for (const pair of formData.entries()) { - const [key, value] = pair; - if (value instanceof File) { - const base64File = await readFileAsBase64(value); - newFormData.push({ - key, - value: base64File, - type: 'base64File', - contentType: value.type, - fileName: value.name, - }); - } - else { - newFormData.push({ key, value, type: 'string' }); - } + const convertFormData = async (formData) => { + const newFormData = []; + for (const pair of formData.entries()) { + const [key, value] = pair; + if (value instanceof File) { + const base64File = await readFileAsBase64(value); + newFormData.push({ + key, + value: base64File, + type: 'base64File', + contentType: value.type, + fileName: value.name, + }); + } else { + newFormData.push({ key, value, type: 'string' }); + } + } + return newFormData; + }; + const convertBody = async (body, contentType) => { + if (body instanceof ReadableStream || body instanceof Uint8Array) { + let encodedData; + if (body instanceof ReadableStream) { + const reader = body.getReader(); + const chunks = []; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + chunks.push(value); + } + const concatenated = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0)); + let position = 0; + for (const chunk of chunks) { + concatenated.set(chunk, position); + position += chunk.length; + } + encodedData = concatenated; + } else { + encodedData = body; + } + let data = new TextDecoder().decode(encodedData); + let type; + if (contentType === 'application/json') { + try { + data = JSON.parse(data); + } catch (ignored) { + // ignore } - return newFormData; + type = 'json'; + } else if (contentType === 'multipart/form-data') { + type = 'formData'; + } else if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) { + type = 'image'; + } else if (contentType === 'application/octet-stream') { + type = 'binary'; + } else { + type = 'text'; + } + return { + data, + type, + headers: { 'Content-Type': contentType || 'application/octet-stream' }, + }; + } else if (body instanceof URLSearchParams) { + return { + data: body.toString(), + type: 'text', + }; + } else if (body instanceof FormData) { + return { + data: await convertFormData(body), + type: 'formData', + }; + } else if (body instanceof File) { + const fileData = await readFileAsBase64(body); + return { + data: fileData, + type: 'file', + headers: { 'Content-Type': body.type }, + }; + } + return { data: body, type: 'json' }; + }; + const CAPACITOR_HTTP_INTERCEPTOR = '/_capacitor_http_interceptor_'; + const CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM = 'u'; + // TODO: export as Cap function + const isRelativeOrProxyUrl = (url) => + !url || !(url.startsWith('http:') || url.startsWith('https:')) || url.indexOf(CAPACITOR_HTTP_INTERCEPTOR) > -1; + // TODO: export as Cap function + const createProxyUrl = (url, win) => { + var _a, _b; + if (isRelativeOrProxyUrl(url)) return url; + const bridgeUrl = new URL( + (_b = (_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.getServerUrl()) !== null && _b !== void 0 + ? _b + : '', + ); + bridgeUrl.pathname = CAPACITOR_HTTP_INTERCEPTOR; + bridgeUrl.searchParams.append(CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM, url); + return bridgeUrl.toString(); + }; + const initBridge = (w) => { + const getPlatformId = (win) => { + var _a, _b; + if (win === null || win === void 0 ? void 0 : win.androidBridge) { + return 'android'; + } else if ( + (_b = + (_a = win === null || win === void 0 ? void 0 : win.webkit) === null || _a === void 0 + ? void 0 + : _a.messageHandlers) === null || _b === void 0 + ? void 0 + : _b.bridge + ) { + return 'ios'; + } else { + return 'web'; + } }; - const convertBody = async (body, contentType) => { - if (body instanceof ReadableStream || body instanceof Uint8Array) { - let encodedData; - if (body instanceof ReadableStream) { - const reader = body.getReader(); - const chunks = []; - while (true) { - const { done, value } = await reader.read(); - if (done) - break; - chunks.push(value); - } - const concatenated = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0)); - let position = 0; - for (const chunk of chunks) { - concatenated.set(chunk, position); - position += chunk.length; - } - encodedData = concatenated; - } - else { - encodedData = body; - } - let data = new TextDecoder().decode(encodedData); - let type; - if (contentType === 'application/json') { - try { - data = JSON.parse(data); - } - catch (ignored) { - // ignore - } - type = 'json'; - } - else if (contentType === 'multipart/form-data') { - type = 'formData'; + const convertFileSrcServerUrl = (webviewServerUrl, filePath) => { + if (typeof filePath === 'string') { + if (filePath.startsWith('/')) { + return webviewServerUrl + '/_capacitor_file_' + filePath; + } else if (filePath.startsWith('file://')) { + return webviewServerUrl + filePath.replace('file://', '/_capacitor_file_'); + } else if (filePath.startsWith('content://')) { + return webviewServerUrl + filePath.replace('content:/', '/_capacitor_content_'); + } + } + return filePath; + }; + const initEvents = (win, cap) => { + cap.addListener = (pluginName, eventName, callback) => { + const callbackId = cap.nativeCallback( + pluginName, + 'addListener', + { + eventName: eventName, + }, + callback, + ); + return { + remove: async () => { + var _a; + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 + ? void 0 + : _a.debug('Removing listener', pluginName, eventName); + cap.removeListener(pluginName, callbackId, eventName, callback); + }, + }; + }; + cap.removeListener = (pluginName, callbackId, eventName, callback) => { + cap.nativeCallback( + pluginName, + 'removeListener', + { + callbackId: callbackId, + eventName: eventName, + }, + callback, + ); + }; + cap.createEvent = (eventName, eventData) => { + const doc = win.document; + if (doc) { + const ev = doc.createEvent('Events'); + ev.initEvent(eventName, false, false); + if (eventData && typeof eventData === 'object') { + for (const i in eventData) { + // eslint-disable-next-line no-prototype-builtins + if (eventData.hasOwnProperty(i)) { + ev[i] = eventData[i]; + } } - else if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) { - type = 'image'; + } + return ev; + } + return null; + }; + cap.triggerEvent = (eventName, target, eventData) => { + const doc = win.document; + const cordova = win.cordova; + eventData = eventData || {}; + const ev = cap.createEvent(eventName, eventData); + if (ev) { + if (target === 'document') { + if (cordova === null || cordova === void 0 ? void 0 : cordova.fireDocumentEvent) { + cordova.fireDocumentEvent(eventName, eventData); + return true; + } else if (doc === null || doc === void 0 ? void 0 : doc.dispatchEvent) { + return doc.dispatchEvent(ev); } - else if (contentType === 'application/octet-stream') { - type = 'binary'; + } else if (target === 'window' && win.dispatchEvent) { + return win.dispatchEvent(ev); + } else if (doc === null || doc === void 0 ? void 0 : doc.querySelector) { + const targetEl = doc.querySelector(target); + if (targetEl) { + return targetEl.dispatchEvent(ev); } - else { - type = 'text'; + } + } + return false; + }; + win.Capacitor = cap; + }; + const initLegacyHandlers = (win, cap) => { + // define cordova if it's not there already + win.cordova = win.cordova || {}; + const doc = win.document; + const nav = win.navigator; + if (nav) { + nav.app = nav.app || {}; + nav.app.exitApp = () => { + var _a; + if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { + win.console.warn('App plugin not installed'); + } else { + cap.nativeCallback('App', 'exitApp', {}); + } + }; + } + if (doc) { + const docAddEventListener = doc.addEventListener; + doc.addEventListener = (...args) => { + var _a; + const eventName = args[0]; + const handler = args[1]; + if (eventName === 'deviceready' && handler) { + Promise.resolve().then(handler); + } else if (eventName === 'backbutton' && cap.Plugins.App) { + // Add a dummy listener so Capacitor doesn't do the default + // back button action + if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { + win.console.warn('App plugin not installed'); + } else { + cap.Plugins.App.addListener('backButton', () => { + // ignore + }); } - return { - data, - type, - headers: { 'Content-Type': contentType || 'application/octet-stream' }, - }; + } + return docAddEventListener.apply(doc, args); + }; + } + win.Capacitor = cap; + }; + const initVendor = (win, cap) => { + const Ionic = (win.Ionic = win.Ionic || {}); + const IonicWebView = (Ionic.WebView = Ionic.WebView || {}); + const Plugins = cap.Plugins; + IonicWebView.getServerBasePath = (callback) => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 + ? void 0 + : _a.getServerBasePath().then((result) => { + callback(result.path); + }); + }; + IonicWebView.setServerAssetPath = (path) => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 + ? void 0 + : _a.setServerAssetPath({ path }); + }; + IonicWebView.setServerBasePath = (path) => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 + ? void 0 + : _a.setServerBasePath({ path }); + }; + IonicWebView.persistServerBasePath = () => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 + ? void 0 + : _a.persistServerBasePath(); + }; + IonicWebView.convertFileSrc = (url) => cap.convertFileSrc(url); + win.Capacitor = cap; + win.Ionic.WebView = IonicWebView; + }; + const initLogger = (win, cap) => { + const BRIDGED_CONSOLE_METHODS = ['debug', 'error', 'info', 'log', 'trace', 'warn']; + const createLogFromNative = (c) => (result) => { + if (isFullConsole(c)) { + const success = result.success === true; + const tagStyles = success + ? 'font-style: italic; font-weight: lighter; color: gray' + : 'font-style: italic; font-weight: lighter; color: red'; + c.groupCollapsed( + '%cresult %c' + result.pluginId + '.' + result.methodName + ' (#' + result.callbackId + ')', + tagStyles, + 'font-style: italic; font-weight: bold; color: #444', + ); + if (result.success === false) { + c.error(result.error); + } else { + c.dir(JSON.stringify(result.data)); + } + c.groupEnd(); + } else { + if (result.success === false) { + c.error('LOG FROM NATIVE', result.error); + } else { + c.log('LOG FROM NATIVE', result.data); + } } - else if (body instanceof URLSearchParams) { - return { - data: body.toString(), - type: 'text', - }; + }; + const createLogToNative = (c) => (call) => { + if (isFullConsole(c)) { + c.groupCollapsed( + '%cnative %c' + call.pluginId + '.' + call.methodName + ' (#' + call.callbackId + ')', + 'font-weight: lighter; color: gray', + 'font-weight: bold; color: #000', + ); + c.dir(call); + c.groupEnd(); + } else { + c.log('LOG TO NATIVE: ', call); } - else if (body instanceof FormData) { - return { - data: await convertFormData(body), - type: 'formData', - }; + }; + const isFullConsole = (c) => { + if (!c) { + return false; } - else if (body instanceof File) { - const fileData = await readFileAsBase64(body); - return { - data: fileData, - type: 'file', - headers: { 'Content-Type': body.type }, - }; + return ( + typeof c.groupCollapsed === 'function' || typeof c.groupEnd === 'function' || typeof c.dir === 'function' + ); + }; + const serializeConsoleMessage = (msg) => { + try { + if (typeof msg === 'object') { + msg = JSON.stringify(msg); + } + return String(msg); + } catch (e) { + return ''; } - return { data: body, type: 'json' }; - }; - const CAPACITOR_HTTP_INTERCEPTOR = '/_capacitor_http_interceptor_'; - const CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM = 'u'; - // TODO: export as Cap function - const isRelativeOrProxyUrl = (url) => !url || !(url.startsWith('http:') || url.startsWith('https:')) || url.indexOf(CAPACITOR_HTTP_INTERCEPTOR) > -1; - // TODO: export as Cap function - const createProxyUrl = (url, win) => { - var _a, _b; - if (isRelativeOrProxyUrl(url)) - return url; - const bridgeUrl = new URL((_b = (_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.getServerUrl()) !== null && _b !== void 0 ? _b : ''); - bridgeUrl.pathname = CAPACITOR_HTTP_INTERCEPTOR; - bridgeUrl.searchParams.append(CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM, url); - return bridgeUrl.toString(); - }; - const initBridge = (w) => { - const getPlatformId = (win) => { - var _a, _b; - if (win === null || win === void 0 ? void 0 : win.androidBridge) { - return 'android'; - } - else if ((_b = (_a = win === null || win === void 0 ? void 0 : win.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge) { - return 'ios'; + }; + const platform = getPlatformId(win); + if (platform == 'android' && typeof win.CapacitorSystemBarsAndroidInterface !== 'undefined') { + // add DOM ready listener for System Bars + document.addEventListener('DOMContentLoaded', function () { + win.CapacitorSystemBarsAndroidInterface.onDOMReady(); + }); + } + if (platform == 'android' || platform == 'ios') { + // patch document.cookie on Android/iOS + win.CapacitorCookiesDescriptor = + Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') || + Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie'); + let doPatchCookies = false; + // check if capacitor cookies is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor cookies config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.isEnabled', + }; + const isCookiesEnabled = prompt(JSON.stringify(payload)); + if (isCookiesEnabled === 'true') { + doPatchCookies = true; + } + } else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled(); + if (isCookiesEnabled === true) { + doPatchCookies = true; + } + } + if (doPatchCookies) { + Object.defineProperty(document, 'cookie', { + get: function () { + var _a, _b, _c; + if (platform === 'ios') { + // Use prompt to synchronously get cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.get', + }; + const res = prompt(JSON.stringify(payload)); + return res; + } else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + // return original document.cookie since Android does not support filtering of `httpOnly` cookies + return (_c = + (_b = (_a = win.CapacitorCookiesDescriptor) === null || _a === void 0 ? void 0 : _a.get) === null || + _b === void 0 + ? void 0 + : _b.call(document)) !== null && _c !== void 0 + ? _c + : ''; + } + }, + set: function (val) { + const cookiePairs = val.split(';'); + const domainSection = val.toLowerCase().split('domain=')[1]; + const domain = + cookiePairs.length > 1 && domainSection != null && domainSection.length > 0 + ? domainSection.split(';')[0].trim() + : ''; + if (platform === 'ios') { + // Use prompt to synchronously set cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.set', + action: val, + domain, + }; + prompt(JSON.stringify(payload)); + } else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + win.CapacitorCookiesAndroidInterface.setCookie(domain, val); + } + }, + }); + } + // patch fetch / XHR on Android/iOS + // store original fetch & XHR functions + win.CapacitorWebFetch = window.fetch; + win.CapacitorWebXMLHttpRequest = { + abort: window.XMLHttpRequest.prototype.abort, + constructor: window.XMLHttpRequest.prototype.constructor, + fullObject: window.XMLHttpRequest, + getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, + getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, + open: window.XMLHttpRequest.prototype.open, + prototype: window.XMLHttpRequest.prototype, + send: window.XMLHttpRequest.prototype.send, + setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, + }; + let doPatchHttp = false; + // check if capacitor http is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor http config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorHttp', + }; + const isHttpEnabled = prompt(JSON.stringify(payload)); + if (isHttpEnabled === 'true') { + doPatchHttp = true; + } + } else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') { + const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled(); + if (isHttpEnabled === true) { + doPatchHttp = true; + } + } + if (doPatchHttp) { + // fetch patch + window.fetch = async (resource, options) => { + const headers = new Headers(options === null || options === void 0 ? void 0 : options.headers); + const contentType = headers.get('Content-Type') || headers.get('content-type'); + if ( + (options === null || options === void 0 ? void 0 : options.body) instanceof FormData && + (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/form-data')) && + !contentType.includes('boundary') + ) { + headers.delete('Content-Type'); + headers.delete('content-type'); + options.headers = headers; } - else { - return 'web'; + const request = new Request(resource, options); + if (request.url.startsWith(`${cap.getServerUrl()}/`)) { + return win.CapacitorWebFetch(resource, options); } - }; - const convertFileSrcServerUrl = (webviewServerUrl, filePath) => { - if (typeof filePath === 'string') { - if (filePath.startsWith('/')) { - return webviewServerUrl + '/_capacitor_file_' + filePath; + const { method } = request; + if ( + method.toLocaleUpperCase() === 'GET' || + method.toLocaleUpperCase() === 'HEAD' || + method.toLocaleUpperCase() === 'OPTIONS' || + method.toLocaleUpperCase() === 'TRACE' + ) { + // a workaround for following android webview issue: + // https://issues.chromium.org/issues/40450316 + // Sets the user-agent header to a custom value so that its not stripped + // on its way to the native layer + if (platform === 'android' && (options === null || options === void 0 ? void 0 : options.headers)) { + const userAgent = headers.get('User-Agent') || headers.get('user-agent'); + if (userAgent !== null) { + headers.set('x-cap-user-agent', userAgent); + options.headers = headers; } - else if (filePath.startsWith('file://')) { - return webviewServerUrl + filePath.replace('file://', '/_capacitor_file_'); + } + if (typeof resource === 'string') { + return await win.CapacitorWebFetch(createProxyUrl(resource, win), options); + } else if (resource instanceof URL) { + const modifiedURL = new URL(createProxyUrl(resource.toString(), win)); + return await win.CapacitorWebFetch(modifiedURL, options); + } else if (resource instanceof Request) { + const modifiedRequest = new Request(createProxyUrl(resource.url, win), resource); + return await win.CapacitorWebFetch(modifiedRequest, options); + } + } + const tag = `CapacitorHttp fetch ${Date.now()} ${resource}`; + console.time(tag); + try { + const { body } = request; + const optionHeaders = Object.fromEntries(request.headers.entries()); + const { + data: requestData, + type, + headers: requestHeaders, + } = await convertBody( + (options === null || options === void 0 ? void 0 : options.body) || body || undefined, + optionHeaders['Content-Type'] || optionHeaders['content-type'], + ); + const nativeHeaders = Object.assign(Object.assign({}, requestHeaders), optionHeaders); + if (platform === 'android') { + if (headers.has('User-Agent')) { + nativeHeaders['User-Agent'] = headers.get('User-Agent'); } - else if (filePath.startsWith('content://')) { - return webviewServerUrl + filePath.replace('content:/', '/_capacitor_content_'); + if (headers.has('user-agent')) { + nativeHeaders['user-agent'] = headers.get('user-agent'); } + } + const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', { + url: request.url, + method: method, + data: requestData, + dataType: type, + headers: nativeHeaders, + }); + const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type']; + let data = ( + contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json') + ) + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + // use null data for 204 No Content HTTP response + if (nativeResponse.status === 204) { + data = null; + } + // intercept & parse response before returning + const response = new Response(data, { + headers: nativeResponse.headers, + status: nativeResponse.status, + }); + /* + * copy url to response, `cordova-plugin-ionic` uses this url from the response + * we need `Object.defineProperty` because url is an inherited getter on the Response + * see: https://stackoverflow.com/a/57382543 + * */ + Object.defineProperty(response, 'url', { + value: nativeResponse.url, + }); + console.timeEnd(tag); + return response; + } catch (error) { + console.timeEnd(tag); + return Promise.reject(error); } - return filePath; - }; - const initEvents = (win, cap) => { - cap.addListener = (pluginName, eventName, callback) => { - const callbackId = cap.nativeCallback(pluginName, 'addListener', { - eventName: eventName, - }, callback); - return { - remove: async () => { - var _a; - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.debug('Removing listener', pluginName, eventName); - cap.removeListener(pluginName, callbackId, eventName, callback); - }, - }; - }; - cap.removeListener = (pluginName, callbackId, eventName, callback) => { - cap.nativeCallback(pluginName, 'removeListener', { - callbackId: callbackId, - eventName: eventName, - }, callback); + }; + window.XMLHttpRequest = function () { + const xhr = new win.CapacitorWebXMLHttpRequest.constructor(); + Object.defineProperties(xhr, { + _headers: { + value: {}, + writable: true, + }, + _method: { + value: xhr.method, + writable: true, + }, + }); + const prototype = win.CapacitorWebXMLHttpRequest.prototype; + const isProgressEventAvailable = () => + typeof ProgressEvent !== 'undefined' && ProgressEvent.prototype instanceof Event; + // XHR patch abort + prototype.abort = function () { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.abort.call(this); + } + this.readyState = 0; + setTimeout(() => { + this.dispatchEvent(new Event('abort')); + this.dispatchEvent(new Event('loadend')); + }); }; - cap.createEvent = (eventName, eventData) => { - const doc = win.document; - if (doc) { - const ev = doc.createEvent('Events'); - ev.initEvent(eventName, false, false); - if (eventData && typeof eventData === 'object') { - for (const i in eventData) { - // eslint-disable-next-line no-prototype-builtins - if (eventData.hasOwnProperty(i)) { - ev[i] = eventData[i]; - } - } - } - return ev; + // XHR patch open + prototype.open = function (method, url) { + this._method = method.toLocaleUpperCase(); + this._url = url; + if ( + !this._method || + this._method === 'GET' || + this._method === 'HEAD' || + this._method === 'OPTIONS' || + this._method === 'TRACE' + ) { + if (isRelativeOrProxyUrl(url)) { + return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); } - return null; + this._url = createProxyUrl(this._url, win); + return win.CapacitorWebXMLHttpRequest.open.call(this, method, this._url); + } + Object.defineProperties(this, { + readyState: { + get: function () { + var _a; + return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0; + }, + set: function (val) { + this._readyState = val; + setTimeout(() => { + this.dispatchEvent(new Event('readystatechange')); + }); + }, + }, + }); + setTimeout(() => { + this.dispatchEvent(new Event('loadstart')); + }); + this.readyState = 1; }; - cap.triggerEvent = (eventName, target, eventData) => { - const doc = win.document; - const cordova = win.cordova; - eventData = eventData || {}; - const ev = cap.createEvent(eventName, eventData); - if (ev) { - if (target === 'document') { - if (cordova === null || cordova === void 0 ? void 0 : cordova.fireDocumentEvent) { - cordova.fireDocumentEvent(eventName, eventData); - return true; - } - else if (doc === null || doc === void 0 ? void 0 : doc.dispatchEvent) { - return doc.dispatchEvent(ev); - } - } - else if (target === 'window' && win.dispatchEvent) { - return win.dispatchEvent(ev); - } - else if (doc === null || doc === void 0 ? void 0 : doc.querySelector) { - const targetEl = doc.querySelector(target); - if (targetEl) { - return targetEl.dispatchEvent(ev); - } - } - } - return false; + // XHR patch set request header + prototype.setRequestHeader = function (header, value) { + // a workaround for the following android web view issue: + // https://issues.chromium.org/issues/40450316 + // Sets the user-agent header to a custom value so that its not stripped + // on its way to the native layer + if (platform === 'android' && (header === 'User-Agent' || header === 'user-agent')) { + header = 'x-cap-user-agent'; + } + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); + } + this._headers[header] = value; }; - win.Capacitor = cap; - }; - const initLegacyHandlers = (win, cap) => { - // define cordova if it's not there already - win.cordova = win.cordova || {}; - const doc = win.document; - const nav = win.navigator; - if (nav) { - nav.app = nav.app || {}; - nav.app.exitApp = () => { - var _a; - if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { - win.console.warn('App plugin not installed'); - } - else { - cap.nativeCallback('App', 'exitApp', {}); - } - }; - } - if (doc) { - const docAddEventListener = doc.addEventListener; - doc.addEventListener = (...args) => { - var _a; - const eventName = args[0]; - const handler = args[1]; - if (eventName === 'deviceready' && handler) { - Promise.resolve().then(handler); + // XHR patch send + prototype.send = function (body) { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.send.call(this, body); + } + const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; + console.time(tag); + try { + this.readyState = 2; + Object.defineProperties(this, { + response: { + value: '', + writable: true, + }, + responseText: { + value: '', + writable: true, + }, + responseURL: { + value: '', + writable: true, + }, + status: { + value: 0, + writable: true, + }, + }); + convertBody(body).then(({ data, type, headers }) => { + let otherHeaders = + this._headers != null && Object.keys(this._headers).length > 0 ? this._headers : undefined; + if (body instanceof FormData) { + if (!this._headers['Content-Type'] && !this._headers['content-type']) { + otherHeaders = Object.assign(Object.assign({}, otherHeaders), { + 'Content-Type': `multipart/form-data; boundary=----WebKitFormBoundary${Math.random().toString(36).substring(2, 15)}`, + }); } - else if (eventName === 'backbutton' && cap.Plugins.App) { - // Add a dummy listener so Capacitor doesn't do the default - // back button action - if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { - win.console.warn('App plugin not installed'); + } + // intercept request & pass to the bridge + cap + .nativePromise('CapacitorHttp', 'request', { + url: this._url, + method: this._method, + data: data !== null ? data : undefined, + headers: Object.assign(Object.assign({}, headers), otherHeaders), + dataType: type, + }) + .then((nativeResponse) => { + var _a; + // intercept & parse response before returning + if (this.readyState == 2) { + //TODO: Add progress event emission on native side + if (isProgressEventAvailable()) { + this.dispatchEvent( + new ProgressEvent('progress', { + lengthComputable: true, + loaded: nativeResponse.data.length, + total: nativeResponse.data.length, + }), + ); } - else { - cap.Plugins.App.addListener('backButton', () => { - // ignore - }); + this._headers = nativeResponse.headers; + this.status = nativeResponse.status; + if (this.responseType === '' || this.responseType === 'text') { + this.response = + typeof nativeResponse.data !== 'string' + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + } else { + this.response = nativeResponse.data; } - } - return docAddEventListener.apply(doc, args); - }; - } - win.Capacitor = cap; - }; - const initVendor = (win, cap) => { - const Ionic = (win.Ionic = win.Ionic || {}); - const IonicWebView = (Ionic.WebView = Ionic.WebView || {}); - const Plugins = cap.Plugins; - IonicWebView.getServerBasePath = (callback) => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.getServerBasePath().then((result) => { - callback(result.path); + this.responseText = ( + (_a = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type']) === + null || _a === void 0 + ? void 0 + : _a.startsWith('application/json') + ) + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + this.responseURL = nativeResponse.url; + this.readyState = 4; + setTimeout(() => { + this.dispatchEvent(new Event('load')); + this.dispatchEvent(new Event('loadend')); + }); + } + console.timeEnd(tag); + }) + .catch((error) => { + this.status = error.status; + this._headers = error.headers; + this.response = error.data; + this.responseText = JSON.stringify(error.data); + this.responseURL = error.url; + this.readyState = 4; + if (isProgressEventAvailable()) { + this.dispatchEvent( + new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + }), + ); + } + setTimeout(() => { + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + console.timeEnd(tag); + }); }); - }; - IonicWebView.setServerAssetPath = (path) => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerAssetPath({ path }); - }; - IonicWebView.setServerBasePath = (path) => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerBasePath({ path }); - }; - IonicWebView.persistServerBasePath = () => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.persistServerBasePath(); - }; - IonicWebView.convertFileSrc = (url) => cap.convertFileSrc(url); - win.Capacitor = cap; - win.Ionic.WebView = IonicWebView; - }; - const initLogger = (win, cap) => { - const BRIDGED_CONSOLE_METHODS = ['debug', 'error', 'info', 'log', 'trace', 'warn']; - const createLogFromNative = (c) => (result) => { - if (isFullConsole(c)) { - const success = result.success === true; - const tagStyles = success - ? 'font-style: italic; font-weight: lighter; color: gray' - : 'font-style: italic; font-weight: lighter; color: red'; - c.groupCollapsed('%cresult %c' + result.pluginId + '.' + result.methodName + ' (#' + result.callbackId + ')', tagStyles, 'font-style: italic; font-weight: bold; color: #444'); - if (result.success === false) { - c.error(result.error); - } - else { - c.dir(JSON.stringify(result.data)); - } - c.groupEnd(); - } - else { - if (result.success === false) { - c.error('LOG FROM NATIVE', result.error); - } - else { - c.log('LOG FROM NATIVE', result.data); - } + } catch (error) { + this.status = 500; + this._headers = {}; + this.response = error; + this.responseText = error.toString(); + this.responseURL = this._url; + this.readyState = 4; + if (isProgressEventAvailable()) { + this.dispatchEvent( + new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + }), + ); } + setTimeout(() => { + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + console.timeEnd(tag); + } }; - const createLogToNative = (c) => (call) => { - if (isFullConsole(c)) { - c.groupCollapsed('%cnative %c' + call.pluginId + '.' + call.methodName + ' (#' + call.callbackId + ')', 'font-weight: lighter; color: gray', 'font-weight: bold; color: #000'); - c.dir(call); - c.groupEnd(); - } - else { - c.log('LOG TO NATIVE: ', call); + // XHR patch getAllResponseHeaders + prototype.getAllResponseHeaders = function () { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); + } + let returnString = ''; + for (const key in this._headers) { + if (key != 'Set-Cookie') { + returnString += key + ': ' + this._headers[key] + '\r\n'; } + } + return returnString; }; - const isFullConsole = (c) => { - if (!c) { - return false; - } - return typeof c.groupCollapsed === 'function' || typeof c.groupEnd === 'function' || typeof c.dir === 'function'; + // XHR patch getResponseHeader + prototype.getResponseHeader = function (name) { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); + } + return this._headers[name]; }; - const serializeConsoleMessage = (msg) => { - try { - if (typeof msg === 'object') { - msg = JSON.stringify(msg); - } - return String(msg); - } - catch (e) { - return ''; - } - }; - const platform = getPlatformId(win); - if (platform == 'android' && typeof win.CapacitorSystemBarsAndroidInterface !== 'undefined') { - // add DOM ready listener for System Bars - document.addEventListener('DOMContentLoaded', function () { - win.CapacitorSystemBarsAndroidInterface.onDOMReady(); + Object.setPrototypeOf(xhr, prototype); + return xhr; + }; + Object.assign(window.XMLHttpRequest, win.CapacitorWebXMLHttpRequest.fullObject); + } + } + // patch window.console on iOS and store original console fns + const isIos = getPlatformId(win) === 'ios'; + if (win.console && isIos) { + Object.defineProperties( + win.console, + BRIDGED_CONSOLE_METHODS.reduce((props, method) => { + const consoleMethod = win.console[method].bind(win.console); + props[method] = { + value: (...args) => { + const msgs = [...args]; + cap.toNative('Console', 'log', { + level: method, + message: msgs.map(serializeConsoleMessage).join(' '), }); - } - if (platform == 'android' || platform == 'ios') { - // patch document.cookie on Android/iOS - win.CapacitorCookiesDescriptor = - Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') || - Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie'); - let doPatchCookies = false; - // check if capacitor cookies is disabled before patching - if (platform === 'ios') { - // Use prompt to synchronously get capacitor cookies config. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorCookies.isEnabled', - }; - const isCookiesEnabled = prompt(JSON.stringify(payload)); - if (isCookiesEnabled === 'true') { - doPatchCookies = true; - } - } - else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { - const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled(); - if (isCookiesEnabled === true) { - doPatchCookies = true; - } - } - if (doPatchCookies) { - Object.defineProperty(document, 'cookie', { - get: function () { - var _a, _b, _c; - if (platform === 'ios') { - // Use prompt to synchronously get cookies. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorCookies.get', - }; - const res = prompt(JSON.stringify(payload)); - return res; - } - else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { - // return original document.cookie since Android does not support filtering of `httpOnly` cookies - return (_c = (_b = (_a = win.CapacitorCookiesDescriptor) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(document)) !== null && _c !== void 0 ? _c : ''; - } - }, - set: function (val) { - const cookiePairs = val.split(';'); - const domainSection = val.toLowerCase().split('domain=')[1]; - const domain = cookiePairs.length > 1 && domainSection != null && domainSection.length > 0 - ? domainSection.split(';')[0].trim() - : ''; - if (platform === 'ios') { - // Use prompt to synchronously set cookies. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorCookies.set', - action: val, - domain, - }; - prompt(JSON.stringify(payload)); - } - else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { - win.CapacitorCookiesAndroidInterface.setCookie(domain, val); - } - }, - }); - } - // patch fetch / XHR on Android/iOS - // store original fetch & XHR functions - win.CapacitorWebFetch = window.fetch; - win.CapacitorWebXMLHttpRequest = { - abort: window.XMLHttpRequest.prototype.abort, - constructor: window.XMLHttpRequest.prototype.constructor, - fullObject: window.XMLHttpRequest, - getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, - getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, - open: window.XMLHttpRequest.prototype.open, - prototype: window.XMLHttpRequest.prototype, - send: window.XMLHttpRequest.prototype.send, - setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, - }; - let doPatchHttp = false; - // check if capacitor http is disabled before patching - if (platform === 'ios') { - // Use prompt to synchronously get capacitor http config. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorHttp', - }; - const isHttpEnabled = prompt(JSON.stringify(payload)); - if (isHttpEnabled === 'true') { - doPatchHttp = true; - } - } - else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') { - const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled(); - if (isHttpEnabled === true) { - doPatchHttp = true; - } - } - if (doPatchHttp) { - // fetch patch - window.fetch = async (resource, options) => { - const headers = new Headers(options === null || options === void 0 ? void 0 : options.headers); - const contentType = headers.get('Content-Type') || headers.get('content-type'); - if ((options === null || options === void 0 ? void 0 : options.body) instanceof FormData && - (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/form-data')) && - !contentType.includes('boundary')) { - headers.delete('Content-Type'); - headers.delete('content-type'); - options.headers = headers; - } - const request = new Request(resource, options); - if (request.url.startsWith(`${cap.getServerUrl()}/`)) { - return win.CapacitorWebFetch(resource, options); - } - const { method } = request; - if (method.toLocaleUpperCase() === 'GET' || - method.toLocaleUpperCase() === 'HEAD' || - method.toLocaleUpperCase() === 'OPTIONS' || - method.toLocaleUpperCase() === 'TRACE') { - // a workaround for following android webview issue: - // https://issues.chromium.org/issues/40450316 - // Sets the user-agent header to a custom value so that its not stripped - // on its way to the native layer - if (platform === 'android' && (options === null || options === void 0 ? void 0 : options.headers)) { - const userAgent = headers.get('User-Agent') || headers.get('user-agent'); - if (userAgent !== null) { - headers.set('x-cap-user-agent', userAgent); - options.headers = headers; - } - } - if (typeof resource === 'string') { - return await win.CapacitorWebFetch(createProxyUrl(resource, win), options); - } - else if (resource instanceof URL) { - const modifiedURL = new URL(createProxyUrl(resource.toString(), win)); - return await win.CapacitorWebFetch(modifiedURL, options); - } - else if (resource instanceof Request) { - const modifiedRequest = new Request(createProxyUrl(resource.url, win), resource); - return await win.CapacitorWebFetch(modifiedRequest, options); - } - } - const tag = `CapacitorHttp fetch ${Date.now()} ${resource}`; - console.time(tag); - try { - const { body } = request; - const optionHeaders = Object.fromEntries(request.headers.entries()); - const { data: requestData, type, headers: requestHeaders, } = await convertBody((options === null || options === void 0 ? void 0 : options.body) || body || undefined, optionHeaders['Content-Type'] || optionHeaders['content-type']); - const nativeHeaders = Object.assign(Object.assign({}, requestHeaders), optionHeaders); - if (platform === 'android') { - if (headers.has('User-Agent')) { - nativeHeaders['User-Agent'] = headers.get('User-Agent'); - } - if (headers.has('user-agent')) { - nativeHeaders['user-agent'] = headers.get('user-agent'); - } - } - const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', { - url: request.url, - method: method, - data: requestData, - dataType: type, - headers: nativeHeaders, - }); - const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type']; - let data = (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json')) - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - // use null data for 204 No Content HTTP response - if (nativeResponse.status === 204) { - data = null; - } - // intercept & parse response before returning - const response = new Response(data, { - headers: nativeResponse.headers, - status: nativeResponse.status, - }); - /* - * copy url to response, `cordova-plugin-ionic` uses this url from the response - * we need `Object.defineProperty` because url is an inherited getter on the Response - * see: https://stackoverflow.com/a/57382543 - * */ - Object.defineProperty(response, 'url', { - value: nativeResponse.url, - }); - console.timeEnd(tag); - return response; - } - catch (error) { - console.timeEnd(tag); - return Promise.reject(error); - } - }; - window.XMLHttpRequest = function () { - const xhr = new win.CapacitorWebXMLHttpRequest.constructor(); - Object.defineProperties(xhr, { - _headers: { - value: {}, - writable: true, - }, - _method: { - value: xhr.method, - writable: true, - }, - }); - const prototype = win.CapacitorWebXMLHttpRequest.prototype; - const isProgressEventAvailable = () => typeof ProgressEvent !== 'undefined' && ProgressEvent.prototype instanceof Event; - // XHR patch abort - prototype.abort = function () { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.abort.call(this); - } - this.readyState = 0; - setTimeout(() => { - this.dispatchEvent(new Event('abort')); - this.dispatchEvent(new Event('loadend')); - }); - }; - // XHR patch open - prototype.open = function (method, url) { - this._method = method.toLocaleUpperCase(); - this._url = url; - if (!this._method || - this._method === 'GET' || - this._method === 'HEAD' || - this._method === 'OPTIONS' || - this._method === 'TRACE') { - if (isRelativeOrProxyUrl(url)) { - return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); - } - this._url = createProxyUrl(this._url, win); - return win.CapacitorWebXMLHttpRequest.open.call(this, method, this._url); - } - Object.defineProperties(this, { - readyState: { - get: function () { - var _a; - return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0; - }, - set: function (val) { - this._readyState = val; - setTimeout(() => { - this.dispatchEvent(new Event('readystatechange')); - }); - }, - }, - }); - setTimeout(() => { - this.dispatchEvent(new Event('loadstart')); - }); - this.readyState = 1; - }; - // XHR patch set request header - prototype.setRequestHeader = function (header, value) { - // a workaround for the following android web view issue: - // https://issues.chromium.org/issues/40450316 - // Sets the user-agent header to a custom value so that its not stripped - // on its way to the native layer - if (platform === 'android' && (header === 'User-Agent' || header === 'user-agent')) { - header = 'x-cap-user-agent'; - } - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); - } - this._headers[header] = value; - }; - // XHR patch send - prototype.send = function (body) { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.send.call(this, body); - } - const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; - console.time(tag); - try { - this.readyState = 2; - Object.defineProperties(this, { - response: { - value: '', - writable: true, - }, - responseText: { - value: '', - writable: true, - }, - responseURL: { - value: '', - writable: true, - }, - status: { - value: 0, - writable: true, - }, - }); - convertBody(body).then(({ data, type, headers }) => { - let otherHeaders = this._headers != null && Object.keys(this._headers).length > 0 ? this._headers : undefined; - if (body instanceof FormData) { - if (!this._headers['Content-Type'] && !this._headers['content-type']) { - otherHeaders = Object.assign(Object.assign({}, otherHeaders), { 'Content-Type': `multipart/form-data; boundary=----WebKitFormBoundary${Math.random().toString(36).substring(2, 15)}` }); - } - } - // intercept request & pass to the bridge - cap - .nativePromise('CapacitorHttp', 'request', { - url: this._url, - method: this._method, - data: data !== null ? data : undefined, - headers: Object.assign(Object.assign({}, headers), otherHeaders), - dataType: type, - }) - .then((nativeResponse) => { - var _a; - // intercept & parse response before returning - if (this.readyState == 2) { - //TODO: Add progress event emission on native side - if (isProgressEventAvailable()) { - this.dispatchEvent(new ProgressEvent('progress', { - lengthComputable: true, - loaded: nativeResponse.data.length, - total: nativeResponse.data.length, - })); - } - this._headers = nativeResponse.headers; - this.status = nativeResponse.status; - if (this.responseType === '' || this.responseType === 'text') { - this.response = - typeof nativeResponse.data !== 'string' - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - } - else { - this.response = nativeResponse.data; - } - this.responseText = ((_a = (nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type'])) === null || _a === void 0 ? void 0 : _a.startsWith('application/json')) - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - this.responseURL = nativeResponse.url; - this.readyState = 4; - setTimeout(() => { - this.dispatchEvent(new Event('load')); - this.dispatchEvent(new Event('loadend')); - }); - } - console.timeEnd(tag); - }) - .catch((error) => { - this.status = error.status; - this._headers = error.headers; - this.response = error.data; - this.responseText = JSON.stringify(error.data); - this.responseURL = error.url; - this.readyState = 4; - if (isProgressEventAvailable()) { - this.dispatchEvent(new ProgressEvent('progress', { - lengthComputable: false, - loaded: 0, - total: 0, - })); - } - setTimeout(() => { - this.dispatchEvent(new Event('error')); - this.dispatchEvent(new Event('loadend')); - }); - console.timeEnd(tag); - }); - }); - } - catch (error) { - this.status = 500; - this._headers = {}; - this.response = error; - this.responseText = error.toString(); - this.responseURL = this._url; - this.readyState = 4; - if (isProgressEventAvailable()) { - this.dispatchEvent(new ProgressEvent('progress', { - lengthComputable: false, - loaded: 0, - total: 0, - })); - } - setTimeout(() => { - this.dispatchEvent(new Event('error')); - this.dispatchEvent(new Event('loadend')); - }); - console.timeEnd(tag); - } - }; - // XHR patch getAllResponseHeaders - prototype.getAllResponseHeaders = function () { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); - } - let returnString = ''; - for (const key in this._headers) { - if (key != 'Set-Cookie') { - returnString += key + ': ' + this._headers[key] + '\r\n'; - } - } - return returnString; - }; - // XHR patch getResponseHeader - prototype.getResponseHeader = function (name) { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); - } - return this._headers[name]; - }; - Object.setPrototypeOf(xhr, prototype); - return xhr; - }; - Object.assign(window.XMLHttpRequest, win.CapacitorWebXMLHttpRequest.fullObject); - } - } - // patch window.console on iOS and store original console fns - const isIos = getPlatformId(win) === 'ios'; - if (win.console && isIos) { - Object.defineProperties(win.console, BRIDGED_CONSOLE_METHODS.reduce((props, method) => { - const consoleMethod = win.console[method].bind(win.console); - props[method] = { - value: (...args) => { - const msgs = [...args]; - cap.toNative('Console', 'log', { - level: method, - message: msgs.map(serializeConsoleMessage).join(' '), - }); - return consoleMethod(...args); - }, - }; - return props; - }, {})); - } - cap.logJs = (msg, level) => { - switch (level) { - case 'error': - win.console.error(msg); - break; - case 'warn': - win.console.warn(msg); - break; - case 'info': - win.console.info(msg); - break; - default: - win.console.log(msg); - } + return consoleMethod(...args); + }, }; - cap.logToNative = createLogToNative(win.console); - cap.logFromNative = createLogFromNative(win.console); - cap.handleError = (err) => win.console.error(err); - win.Capacitor = cap; + return props; + }, {}), + ); + } + cap.logJs = (msg, level) => { + switch (level) { + case 'error': + win.console.error(msg); + break; + case 'warn': + win.console.warn(msg); + break; + case 'info': + win.console.info(msg); + break; + default: + win.console.log(msg); + } + }; + cap.logToNative = createLogToNative(win.console); + cap.logFromNative = createLogFromNative(win.console); + cap.handleError = (err) => win.console.error(err); + win.Capacitor = cap; + }; + function initNativeBridge(win) { + const cap = win.Capacitor || {}; + // keep a collection of callbacks for native response data + const callbacks = new Map(); + const webviewServerUrl = typeof win.WEBVIEW_SERVER_URL === 'string' ? win.WEBVIEW_SERVER_URL : ''; + cap.getServerUrl = () => webviewServerUrl; + cap.convertFileSrc = (filePath) => convertFileSrcServerUrl(webviewServerUrl, filePath); + // Counter of callback ids, randomized to avoid + // any issues during reloads if a call comes back with + // an existing callback id from an old session + let callbackIdCount = Math.floor(Math.random() * 134217728); + let postToNative = null; + const isNativePlatform = () => true; + const getPlatform = () => getPlatformId(win); + cap.getPlatform = getPlatform; + cap.isPluginAvailable = (name) => Object.prototype.hasOwnProperty.call(cap.Plugins, name); + cap.isNativePlatform = isNativePlatform; + // create the postToNative() fn if needed + if (getPlatformId(win) === 'android') { + // android platform + postToNative = (data) => { + var _a; + try { + win.androidBridge.postMessage(JSON.stringify(data)); + } catch (e) { + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 + ? void 0 + : _a.error(e); + } }; - function initNativeBridge(win) { - const cap = win.Capacitor || {}; - // keep a collection of callbacks for native response data - const callbacks = new Map(); - const webviewServerUrl = typeof win.WEBVIEW_SERVER_URL === 'string' ? win.WEBVIEW_SERVER_URL : ''; - cap.getServerUrl = () => webviewServerUrl; - cap.convertFileSrc = (filePath) => convertFileSrcServerUrl(webviewServerUrl, filePath); - // Counter of callback ids, randomized to avoid - // any issues during reloads if a call comes back with - // an existing callback id from an old session - let callbackIdCount = Math.floor(Math.random() * 134217728); - let postToNative = null; - const isNativePlatform = () => true; - const getPlatform = () => getPlatformId(win); - cap.getPlatform = getPlatform; - cap.isPluginAvailable = (name) => Object.prototype.hasOwnProperty.call(cap.Plugins, name); - cap.isNativePlatform = isNativePlatform; - // create the postToNative() fn if needed - if (getPlatformId(win) === 'android') { - // android platform - postToNative = (data) => { - var _a; - try { - win.androidBridge.postMessage(JSON.stringify(data)); - } - catch (e) { - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e); - } - }; - } - else if (getPlatformId(win) === 'ios') { - // ios platform - postToNative = (data) => { - var _a; - try { - data.type = data.type ? data.type : 'message'; - win.webkit.messageHandlers.bridge.postMessage(data); - } - catch (e) { - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e); - } - }; + } else if (getPlatformId(win) === 'ios') { + // ios platform + postToNative = (data) => { + var _a; + try { + data.type = data.type ? data.type : 'message'; + win.webkit.messageHandlers.bridge.postMessage(data); + } catch (e) { + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 + ? void 0 + : _a.error(e); + } + }; + } + cap.handleWindowError = (msg, url, lineNo, columnNo, err) => { + const str = msg.toLowerCase(); + if (str.indexOf('script error') > -1); + else { + const errObj = { + type: 'js.error', + error: { + message: msg, + url: url, + line: lineNo, + col: columnNo, + errorObject: JSON.stringify(err), + }, + }; + if (err !== null) { + cap.handleError(err); + } + postToNative(errObj); + } + return false; + }; + if (cap.DEBUG) { + window.onerror = cap.handleWindowError; + } + initLogger(win, cap); + /** + * Send a plugin method call to the native layer + */ + cap.toNative = (pluginName, methodName, options, storedCallback) => { + var _a, _b; + try { + if (typeof postToNative === 'function') { + let callbackId = '-1'; + if ( + storedCallback && + (typeof storedCallback.callback === 'function' || typeof storedCallback.resolve === 'function') + ) { + // store the call for later lookup + callbackId = String(++callbackIdCount); + callbacks.set(callbackId, storedCallback); } - cap.handleWindowError = (msg, url, lineNo, columnNo, err) => { - const str = msg.toLowerCase(); - if (str.indexOf('script error') > -1) ; - else { - const errObj = { - type: 'js.error', - error: { - message: msg, - url: url, - line: lineNo, - col: columnNo, - errorObject: JSON.stringify(err), - }, - }; - if (err !== null) { - cap.handleError(err); - } - postToNative(errObj); - } - return false; + const callData = { + callbackId: callbackId, + pluginId: pluginName, + methodName: methodName, + options: options || {}, }; - if (cap.DEBUG) { - window.onerror = cap.handleWindowError; + if (cap.isLoggingEnabled && pluginName !== 'Console') { + cap.logToNative(callData); } - initLogger(win, cap); - /** - * Send a plugin method call to the native layer - */ - cap.toNative = (pluginName, methodName, options, storedCallback) => { - var _a, _b; - try { - if (typeof postToNative === 'function') { - let callbackId = '-1'; - if (storedCallback && - (typeof storedCallback.callback === 'function' || typeof storedCallback.resolve === 'function')) { - // store the call for later lookup - callbackId = String(++callbackIdCount); - callbacks.set(callbackId, storedCallback); - } - const callData = { - callbackId: callbackId, - pluginId: pluginName, - methodName: methodName, - options: options || {}, - }; - if (cap.isLoggingEnabled && pluginName !== 'Console') { - cap.logToNative(callData); - } - // post the call data to native - postToNative(callData); - return callbackId; - } - else { - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(`implementation unavailable for: ${pluginName}`); - } - } - catch (e) { - (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); - } - return null; - }; - if (win === null || win === void 0 ? void 0 : win.androidBridge) { - win.androidBridge.onmessage = function (event) { - returnResult(JSON.parse(event.data)); - }; + // post the call data to native + postToNative(callData); + return callbackId; + } else { + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 + ? void 0 + : _a.warn(`implementation unavailable for: ${pluginName}`); + } + } catch (e) { + (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); + } + return null; + }; + if (win === null || win === void 0 ? void 0 : win.androidBridge) { + win.androidBridge.onmessage = function (event) { + returnResult(JSON.parse(event.data)); + }; + } + /** + * Process a response from the native layer. + */ + cap.fromNative = (result) => { + returnResult(result); + }; + const returnResult = (result) => { + var _a, _b; + if (cap.isLoggingEnabled && result.pluginId !== 'Console') { + cap.logFromNative(result); + } + // get the stored call, if it exists + try { + const storedCall = callbacks.get(result.callbackId); + if (storedCall) { + // looks like we've got a stored call + if (result.error) { + // ensure stacktraces by copying error properties to an Error + result.error = Object.keys(result.error).reduce((err, key) => { + // use any type to avoid importing util and compiling most of .ts files + err[key] = result.error[key]; + return err; + }, new cap.Exception('')); } - /** - * Process a response from the native layer. - */ - cap.fromNative = (result) => { - returnResult(result); - }; - const returnResult = (result) => { - var _a, _b; - if (cap.isLoggingEnabled && result.pluginId !== 'Console') { - cap.logFromNative(result); - } - // get the stored call, if it exists - try { - const storedCall = callbacks.get(result.callbackId); - if (storedCall) { - // looks like we've got a stored call - if (result.error) { - // ensure stacktraces by copying error properties to an Error - result.error = Object.keys(result.error).reduce((err, key) => { - // use any type to avoid importing util and compiling most of .ts files - err[key] = result.error[key]; - return err; - }, new cap.Exception('')); - } - if (typeof storedCall.callback === 'function') { - // callback - if (result.success) { - storedCall.callback(result.data); - } - else { - storedCall.callback(null, result.error); - } - } - else if (typeof storedCall.resolve === 'function') { - // promise - if (result.success) { - storedCall.resolve(result.data); - } - else { - storedCall.reject(result.error); - } - // no need to keep this stored callback - // around for a one time resolve promise - callbacks.delete(result.callbackId); - } - } - else if (!result.success && result.error) { - // no stored callback, but if there was an error let's log it - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(result.error); - } - if (result.save === false) { - callbacks.delete(result.callbackId); - } - } - catch (e) { - (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); - } - // always delete to prevent memory leaks - // overkill but we're not sure what apps will do with this data - delete result.data; - delete result.error; - }; - cap.nativeCallback = (pluginName, methodName, options, callback) => { - if (typeof options === 'function') { - console.warn(`Using a callback as the 'options' parameter of 'nativeCallback()' is deprecated.`); - callback = options; - options = null; - } - return cap.toNative(pluginName, methodName, options, { callback }); - }; - cap.nativePromise = (pluginName, methodName, options) => { - return new Promise((resolve, reject) => { - cap.toNative(pluginName, methodName, options, { - resolve: resolve, - reject: reject, - }); - }); - }; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - cap.withPlugin = (_pluginId, _fn) => dummy; - cap.Exception = CapacitorException; - initEvents(win, cap); - initLegacyHandlers(win, cap); - initVendor(win, cap); - win.Capacitor = cap; + if (typeof storedCall.callback === 'function') { + // callback + if (result.success) { + storedCall.callback(result.data); + } else { + storedCall.callback(null, result.error); + } + } else if (typeof storedCall.resolve === 'function') { + // promise + if (result.success) { + storedCall.resolve(result.data); + } else { + storedCall.reject(result.error); + } + // no need to keep this stored callback + // around for a one time resolve promise + callbacks.delete(result.callbackId); + } + } else if (!result.success && result.error) { + // no stored callback, but if there was an error let's log it + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 + ? void 0 + : _a.warn(result.error); + } + if (result.save === false) { + callbacks.delete(result.callbackId); + } + } catch (e) { + (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); } - initNativeBridge(w); - }; - initBridge(typeof globalThis !== 'undefined' - ? globalThis - : typeof self !== 'undefined' - ? self - : typeof window !== 'undefined' - ? window - : typeof global !== 'undefined' - ? global - : {}); - - dummy = initBridge; + // always delete to prevent memory leaks + // overkill but we're not sure what apps will do with this data + delete result.data; + delete result.error; + }; + cap.nativeCallback = (pluginName, methodName, options, callback) => { + if (typeof options === 'function') { + console.warn(`Using a callback as the 'options' parameter of 'nativeCallback()' is deprecated.`); + callback = options; + options = null; + } + return cap.toNative(pluginName, methodName, options, { callback }); + }; + cap.nativePromise = (pluginName, methodName, options) => { + return new Promise((resolve, reject) => { + cap.toNative(pluginName, methodName, options, { + resolve: resolve, + reject: reject, + }); + }); + }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + cap.withPlugin = (_pluginId, _fn) => dummy; + cap.Exception = CapacitorException; + initEvents(win, cap); + initLegacyHandlers(win, cap); + initVendor(win, cap); + win.Capacitor = cap; + } + initNativeBridge(w); + }; + initBridge( + typeof globalThis !== 'undefined' + ? globalThis + : typeof self !== 'undefined' + ? self + : typeof window !== 'undefined' + ? window + : typeof global !== 'undefined' + ? global + : {}, + ); - Object.defineProperty(exports, '__esModule', { value: true }); + dummy = initBridge; - return exports; + Object.defineProperty(exports, '__esModule', { value: true }); + return exports; })({}); diff --git a/ios/Tests/CapacitorTests/BridgedTypesTests.swift b/ios/Tests/CapacitorTests/BridgedTypesTests.swift index 36e7bbbbef..96afab539c 100644 --- a/ios/Tests/CapacitorTests/BridgedTypesTests.swift +++ b/ios/Tests/CapacitorTests/BridgedTypesTests.swift @@ -5,11 +5,11 @@ import Testing private class TestContainer: NSObject, JSValueContainer { var coercedDictionary: [AnyHashable: Any] = [:] - public static var jsDateFormatter: ISO8601DateFormatter = { + static var jsDateFormatter: ISO8601DateFormatter = { return ISO8601DateFormatter() }() - public var jsObjectRepresentation: JSObject { + var jsObjectRepresentation: JSObject { return coercedDictionary as? JSObject ?? [:] } } diff --git a/ios/Tests/CapacitorTests/ConfigurationTests.swift b/ios/Tests/CapacitorTests/ConfigurationTests.swift index f87be78e14..6e9055beb7 100644 --- a/ios/Tests/CapacitorTests/ConfigurationTests.swift +++ b/ios/Tests/CapacitorTests/ConfigurationTests.swift @@ -27,7 +27,7 @@ struct ConfigurationTests { private func getConfigURL() -> URL { Bundle.module.resourceURL?.appendingPathComponent("configurations") ?? - Bundle.module.resourceURL ?? Bundle.main.resourceURL ?? URL(fileURLWithPath: "/") + Bundle.module.resourceURL ?? Bundle.main.resourceURL ?? URL(fileURLWithPath: "/") } @Test func defaultErrors() throws { From 9a3f9ff56f389a7396c2e158033b0fa1bdee6788 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 19 Aug 2026 18:48:41 -0500 Subject: [PATCH 16/42] Update native-bridge.js path for SPM directory layout --- .eslintignore | 2 +- .prettierignore | 2 +- core/rollup.bridge.config.js | 2 +- ios/Sources/Capacitor/assets/native-bridge.js | 2027 ++++++++--------- 4 files changed, 993 insertions(+), 1040 deletions(-) diff --git a/.eslintignore b/.eslintignore index 4a1df4e690..abf24cdbf6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,6 +3,6 @@ cli/assets dist types android/capacitor/src/main/assets/native-bridge.js -ios/Capacitor/Capacitor/assets/native-bridge.js +ios/Sources/Capacitor/assets/native-bridge.js ios/Frameworks/Capacitor.xcframework/ios-arm64_x86_64-simulator/Capacitor.framework/native-bridge.js ios/Frameworks/Capacitor.xcframework/ios-arm64/Capacitor.framework/native-bridge.js diff --git a/.prettierignore b/.prettierignore index 143c857005..bc26e51947 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,6 +3,6 @@ core/types cli/assets dist android/capacitor/src/main/assets/native-bridge.js -ios/Capacitor/Capacitor/assets/native-bridge.js +ios/Sources/Capacitor/assets/native-bridge.js ios/Frameworks/Capacitor.xcframework/ios-arm64_x86_64-simulator/Capacitor.framework/native-bridge.js ios/Frameworks/Capacitor.xcframework/ios-arm64/Capacitor.framework/native-bridge.js diff --git a/core/rollup.bridge.config.js b/core/rollup.bridge.config.js index 9f8d90e97d..d741e5dcc5 100644 --- a/core/rollup.bridge.config.js +++ b/core/rollup.bridge.config.js @@ -17,7 +17,7 @@ export default { sourcemap: false, }, { - file: '../ios/Capacitor/Capacitor/assets/native-bridge.js', + file: '../ios/Sources/Capacitor/assets/native-bridge.js', format: 'iife', name: 'nativeBridge', preferConst: true, diff --git a/ios/Sources/Capacitor/assets/native-bridge.js b/ios/Sources/Capacitor/assets/native-bridge.js index d16b9d5689..f5e7cc4403 100644 --- a/ios/Sources/Capacitor/assets/native-bridge.js +++ b/ios/Sources/Capacitor/assets/native-bridge.js @@ -1,1086 +1,1039 @@ + /*! Capacitor: https://capacitorjs.com/ - MIT License */ /* Generated File. Do not edit. */ var nativeBridge = (function (exports) { - 'use strict'; + 'use strict'; - var ExceptionCode; - (function (ExceptionCode) { - /** - * API is not implemented. - * - * This usually means the API can't be used because it is not implemented for - * the current platform. - */ - ExceptionCode['Unimplemented'] = 'UNIMPLEMENTED'; - /** - * API is not available. - * - * This means the API can't be used right now because: - * - it is currently missing a prerequisite, such as network connectivity - * - it requires a particular platform or browser version - */ - ExceptionCode['Unavailable'] = 'UNAVAILABLE'; - })(ExceptionCode || (ExceptionCode = {})); - class CapacitorException extends Error { - constructor(message, code, data) { - super(message); - this.message = message; - this.code = code; - this.data = data; + var ExceptionCode; + (function (ExceptionCode) { + /** + * API is not implemented. + * + * This usually means the API can't be used because it is not implemented for + * the current platform. + */ + ExceptionCode["Unimplemented"] = "UNIMPLEMENTED"; + /** + * API is not available. + * + * This means the API can't be used right now because: + * - it is currently missing a prerequisite, such as network connectivity + * - it requires a particular platform or browser version + */ + ExceptionCode["Unavailable"] = "UNAVAILABLE"; + })(ExceptionCode || (ExceptionCode = {})); + class CapacitorException extends Error { + constructor(message, code, data) { + super(message); + this.message = message; + this.code = code; + this.data = data; + } } - } - // For removing exports for iOS/Android, keep let for reassignment - // eslint-disable-next-line - let dummy = {}; - const readFileAsBase64 = (file) => - new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onloadend = () => { - const data = reader.result; - resolve(btoa(data)); - }; - reader.onerror = reject; - reader.readAsBinaryString(file); + // For removing exports for iOS/Android, keep let for reassignment + // eslint-disable-next-line + let dummy = {}; + const readFileAsBase64 = (file) => new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => { + const data = reader.result; + resolve(btoa(data)); + }; + reader.onerror = reject; + reader.readAsBinaryString(file); }); - const convertFormData = async (formData) => { - const newFormData = []; - for (const pair of formData.entries()) { - const [key, value] = pair; - if (value instanceof File) { - const base64File = await readFileAsBase64(value); - newFormData.push({ - key, - value: base64File, - type: 'base64File', - contentType: value.type, - fileName: value.name, - }); - } else { - newFormData.push({ key, value, type: 'string' }); - } - } - return newFormData; - }; - const convertBody = async (body, contentType) => { - if (body instanceof ReadableStream || body instanceof Uint8Array) { - let encodedData; - if (body instanceof ReadableStream) { - const reader = body.getReader(); - const chunks = []; - while (true) { - const { done, value } = await reader.read(); - if (done) break; - chunks.push(value); - } - const concatenated = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0)); - let position = 0; - for (const chunk of chunks) { - concatenated.set(chunk, position); - position += chunk.length; - } - encodedData = concatenated; - } else { - encodedData = body; - } - let data = new TextDecoder().decode(encodedData); - let type; - if (contentType === 'application/json') { - try { - data = JSON.parse(data); - } catch (ignored) { - // ignore - } - type = 'json'; - } else if (contentType === 'multipart/form-data') { - type = 'formData'; - } else if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) { - type = 'image'; - } else if (contentType === 'application/octet-stream') { - type = 'binary'; - } else { - type = 'text'; - } - return { - data, - type, - headers: { 'Content-Type': contentType || 'application/octet-stream' }, - }; - } else if (body instanceof URLSearchParams) { - return { - data: body.toString(), - type: 'text', - }; - } else if (body instanceof FormData) { - return { - data: await convertFormData(body), - type: 'formData', - }; - } else if (body instanceof File) { - const fileData = await readFileAsBase64(body); - return { - data: fileData, - type: 'file', - headers: { 'Content-Type': body.type }, - }; - } - return { data: body, type: 'json' }; - }; - const CAPACITOR_HTTP_INTERCEPTOR = '/_capacitor_http_interceptor_'; - const CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM = 'u'; - // TODO: export as Cap function - const isRelativeOrProxyUrl = (url) => - !url || !(url.startsWith('http:') || url.startsWith('https:')) || url.indexOf(CAPACITOR_HTTP_INTERCEPTOR) > -1; - // TODO: export as Cap function - const createProxyUrl = (url, win) => { - var _a, _b; - if (isRelativeOrProxyUrl(url)) return url; - const bridgeUrl = new URL( - (_b = (_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.getServerUrl()) !== null && _b !== void 0 - ? _b - : '', - ); - bridgeUrl.pathname = CAPACITOR_HTTP_INTERCEPTOR; - bridgeUrl.searchParams.append(CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM, url); - return bridgeUrl.toString(); - }; - const initBridge = (w) => { - const getPlatformId = (win) => { - var _a, _b; - if (win === null || win === void 0 ? void 0 : win.androidBridge) { - return 'android'; - } else if ( - (_b = - (_a = win === null || win === void 0 ? void 0 : win.webkit) === null || _a === void 0 - ? void 0 - : _a.messageHandlers) === null || _b === void 0 - ? void 0 - : _b.bridge - ) { - return 'ios'; - } else { - return 'web'; - } - }; - const convertFileSrcServerUrl = (webviewServerUrl, filePath) => { - if (typeof filePath === 'string') { - if (filePath.startsWith('/')) { - return webviewServerUrl + '/_capacitor_file_' + filePath; - } else if (filePath.startsWith('file://')) { - return webviewServerUrl + filePath.replace('file://', '/_capacitor_file_'); - } else if (filePath.startsWith('content://')) { - return webviewServerUrl + filePath.replace('content:/', '/_capacitor_content_'); + const convertFormData = async (formData) => { + const newFormData = []; + for (const pair of formData.entries()) { + const [key, value] = pair; + if (value instanceof File) { + const base64File = await readFileAsBase64(value); + newFormData.push({ + key, + value: base64File, + type: 'base64File', + contentType: value.type, + fileName: value.name, + }); + } + else { + newFormData.push({ key, value, type: 'string' }); + } } - } - return filePath; + return newFormData; }; - const initEvents = (win, cap) => { - cap.addListener = (pluginName, eventName, callback) => { - const callbackId = cap.nativeCallback( - pluginName, - 'addListener', - { - eventName: eventName, - }, - callback, - ); - return { - remove: async () => { - var _a; - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 - ? void 0 - : _a.debug('Removing listener', pluginName, eventName); - cap.removeListener(pluginName, callbackId, eventName, callback); - }, - }; - }; - cap.removeListener = (pluginName, callbackId, eventName, callback) => { - cap.nativeCallback( - pluginName, - 'removeListener', - { - callbackId: callbackId, - eventName: eventName, - }, - callback, - ); - }; - cap.createEvent = (eventName, eventData) => { - const doc = win.document; - if (doc) { - const ev = doc.createEvent('Events'); - ev.initEvent(eventName, false, false); - if (eventData && typeof eventData === 'object') { - for (const i in eventData) { - // eslint-disable-next-line no-prototype-builtins - if (eventData.hasOwnProperty(i)) { - ev[i] = eventData[i]; - } + const convertBody = async (body, contentType) => { + if (body instanceof ReadableStream || body instanceof Uint8Array) { + let encodedData; + if (body instanceof ReadableStream) { + const reader = body.getReader(); + const chunks = []; + while (true) { + const { done, value } = await reader.read(); + if (done) + break; + chunks.push(value); + } + const concatenated = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0)); + let position = 0; + for (const chunk of chunks) { + concatenated.set(chunk, position); + position += chunk.length; + } + encodedData = concatenated; } - } - return ev; - } - return null; - }; - cap.triggerEvent = (eventName, target, eventData) => { - const doc = win.document; - const cordova = win.cordova; - eventData = eventData || {}; - const ev = cap.createEvent(eventName, eventData); - if (ev) { - if (target === 'document') { - if (cordova === null || cordova === void 0 ? void 0 : cordova.fireDocumentEvent) { - cordova.fireDocumentEvent(eventName, eventData); - return true; - } else if (doc === null || doc === void 0 ? void 0 : doc.dispatchEvent) { - return doc.dispatchEvent(ev); + else { + encodedData = body; } - } else if (target === 'window' && win.dispatchEvent) { - return win.dispatchEvent(ev); - } else if (doc === null || doc === void 0 ? void 0 : doc.querySelector) { - const targetEl = doc.querySelector(target); - if (targetEl) { - return targetEl.dispatchEvent(ev); + let data = new TextDecoder().decode(encodedData); + let type; + if (contentType === 'application/json') { + try { + data = JSON.parse(data); + } + catch (ignored) { + // ignore + } + type = 'json'; } - } - } - return false; - }; - win.Capacitor = cap; - }; - const initLegacyHandlers = (win, cap) => { - // define cordova if it's not there already - win.cordova = win.cordova || {}; - const doc = win.document; - const nav = win.navigator; - if (nav) { - nav.app = nav.app || {}; - nav.app.exitApp = () => { - var _a; - if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { - win.console.warn('App plugin not installed'); - } else { - cap.nativeCallback('App', 'exitApp', {}); - } - }; - } - if (doc) { - const docAddEventListener = doc.addEventListener; - doc.addEventListener = (...args) => { - var _a; - const eventName = args[0]; - const handler = args[1]; - if (eventName === 'deviceready' && handler) { - Promise.resolve().then(handler); - } else if (eventName === 'backbutton' && cap.Plugins.App) { - // Add a dummy listener so Capacitor doesn't do the default - // back button action - if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { - win.console.warn('App plugin not installed'); - } else { - cap.Plugins.App.addListener('backButton', () => { - // ignore - }); + else if (contentType === 'multipart/form-data') { + type = 'formData'; } - } - return docAddEventListener.apply(doc, args); - }; - } - win.Capacitor = cap; - }; - const initVendor = (win, cap) => { - const Ionic = (win.Ionic = win.Ionic || {}); - const IonicWebView = (Ionic.WebView = Ionic.WebView || {}); - const Plugins = cap.Plugins; - IonicWebView.getServerBasePath = (callback) => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 - ? void 0 - : _a.getServerBasePath().then((result) => { - callback(result.path); - }); - }; - IonicWebView.setServerAssetPath = (path) => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 - ? void 0 - : _a.setServerAssetPath({ path }); - }; - IonicWebView.setServerBasePath = (path) => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 - ? void 0 - : _a.setServerBasePath({ path }); - }; - IonicWebView.persistServerBasePath = () => { - var _a; - (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 - ? void 0 - : _a.persistServerBasePath(); - }; - IonicWebView.convertFileSrc = (url) => cap.convertFileSrc(url); - win.Capacitor = cap; - win.Ionic.WebView = IonicWebView; - }; - const initLogger = (win, cap) => { - const BRIDGED_CONSOLE_METHODS = ['debug', 'error', 'info', 'log', 'trace', 'warn']; - const createLogFromNative = (c) => (result) => { - if (isFullConsole(c)) { - const success = result.success === true; - const tagStyles = success - ? 'font-style: italic; font-weight: lighter; color: gray' - : 'font-style: italic; font-weight: lighter; color: red'; - c.groupCollapsed( - '%cresult %c' + result.pluginId + '.' + result.methodName + ' (#' + result.callbackId + ')', - tagStyles, - 'font-style: italic; font-weight: bold; color: #444', - ); - if (result.success === false) { - c.error(result.error); - } else { - c.dir(JSON.stringify(result.data)); - } - c.groupEnd(); - } else { - if (result.success === false) { - c.error('LOG FROM NATIVE', result.error); - } else { - c.log('LOG FROM NATIVE', result.data); - } - } - }; - const createLogToNative = (c) => (call) => { - if (isFullConsole(c)) { - c.groupCollapsed( - '%cnative %c' + call.pluginId + '.' + call.methodName + ' (#' + call.callbackId + ')', - 'font-weight: lighter; color: gray', - 'font-weight: bold; color: #000', - ); - c.dir(call); - c.groupEnd(); - } else { - c.log('LOG TO NATIVE: ', call); - } - }; - const isFullConsole = (c) => { - if (!c) { - return false; - } - return ( - typeof c.groupCollapsed === 'function' || typeof c.groupEnd === 'function' || typeof c.dir === 'function' - ); - }; - const serializeConsoleMessage = (msg) => { - try { - if (typeof msg === 'object') { - msg = JSON.stringify(msg); - } - return String(msg); - } catch (e) { - return ''; + else if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) { + type = 'image'; + } + else if (contentType === 'application/octet-stream') { + type = 'binary'; + } + else { + type = 'text'; + } + return { + data, + type, + headers: { 'Content-Type': contentType || 'application/octet-stream' }, + }; } - }; - const platform = getPlatformId(win); - if (platform == 'android' && typeof win.CapacitorSystemBarsAndroidInterface !== 'undefined') { - // add DOM ready listener for System Bars - document.addEventListener('DOMContentLoaded', function () { - win.CapacitorSystemBarsAndroidInterface.onDOMReady(); - }); - } - if (platform == 'android' || platform == 'ios') { - // patch document.cookie on Android/iOS - win.CapacitorCookiesDescriptor = - Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') || - Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie'); - let doPatchCookies = false; - // check if capacitor cookies is disabled before patching - if (platform === 'ios') { - // Use prompt to synchronously get capacitor cookies config. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorCookies.isEnabled', - }; - const isCookiesEnabled = prompt(JSON.stringify(payload)); - if (isCookiesEnabled === 'true') { - doPatchCookies = true; - } - } else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { - const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled(); - if (isCookiesEnabled === true) { - doPatchCookies = true; - } + else if (body instanceof URLSearchParams) { + return { + data: body.toString(), + type: 'text', + }; } - if (doPatchCookies) { - Object.defineProperty(document, 'cookie', { - get: function () { - var _a, _b, _c; - if (platform === 'ios') { - // Use prompt to synchronously get cookies. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorCookies.get', - }; - const res = prompt(JSON.stringify(payload)); - return res; - } else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { - // return original document.cookie since Android does not support filtering of `httpOnly` cookies - return (_c = - (_b = (_a = win.CapacitorCookiesDescriptor) === null || _a === void 0 ? void 0 : _a.get) === null || - _b === void 0 - ? void 0 - : _b.call(document)) !== null && _c !== void 0 - ? _c - : ''; - } - }, - set: function (val) { - const cookiePairs = val.split(';'); - const domainSection = val.toLowerCase().split('domain=')[1]; - const domain = - cookiePairs.length > 1 && domainSection != null && domainSection.length > 0 - ? domainSection.split(';')[0].trim() - : ''; - if (platform === 'ios') { - // Use prompt to synchronously set cookies. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorCookies.set', - action: val, - domain, - }; - prompt(JSON.stringify(payload)); - } else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { - win.CapacitorCookiesAndroidInterface.setCookie(domain, val); - } - }, - }); + else if (body instanceof FormData) { + return { + data: await convertFormData(body), + type: 'formData', + }; } - // patch fetch / XHR on Android/iOS - // store original fetch & XHR functions - win.CapacitorWebFetch = window.fetch; - win.CapacitorWebXMLHttpRequest = { - abort: window.XMLHttpRequest.prototype.abort, - constructor: window.XMLHttpRequest.prototype.constructor, - fullObject: window.XMLHttpRequest, - getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, - getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, - open: window.XMLHttpRequest.prototype.open, - prototype: window.XMLHttpRequest.prototype, - send: window.XMLHttpRequest.prototype.send, - setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, - }; - let doPatchHttp = false; - // check if capacitor http is disabled before patching - if (platform === 'ios') { - // Use prompt to synchronously get capacitor http config. - // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 - const payload = { - type: 'CapacitorHttp', - }; - const isHttpEnabled = prompt(JSON.stringify(payload)); - if (isHttpEnabled === 'true') { - doPatchHttp = true; - } - } else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') { - const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled(); - if (isHttpEnabled === true) { - doPatchHttp = true; - } + else if (body instanceof File) { + const fileData = await readFileAsBase64(body); + return { + data: fileData, + type: 'file', + headers: { 'Content-Type': body.type }, + }; } - if (doPatchHttp) { - // fetch patch - window.fetch = async (resource, options) => { - const headers = new Headers(options === null || options === void 0 ? void 0 : options.headers); - const contentType = headers.get('Content-Type') || headers.get('content-type'); - if ( - (options === null || options === void 0 ? void 0 : options.body) instanceof FormData && - (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/form-data')) && - !contentType.includes('boundary') - ) { - headers.delete('Content-Type'); - headers.delete('content-type'); - options.headers = headers; + return { data: body, type: 'json' }; + }; + const CAPACITOR_HTTP_INTERCEPTOR = '/_capacitor_http_interceptor_'; + const CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM = 'u'; + // TODO: export as Cap function + const isRelativeOrProxyUrl = (url) => !url || !(url.startsWith('http:') || url.startsWith('https:')) || url.indexOf(CAPACITOR_HTTP_INTERCEPTOR) > -1; + // TODO: export as Cap function + const createProxyUrl = (url, win) => { + var _a, _b; + if (isRelativeOrProxyUrl(url)) + return url; + const bridgeUrl = new URL((_b = (_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.getServerUrl()) !== null && _b !== void 0 ? _b : ''); + bridgeUrl.pathname = CAPACITOR_HTTP_INTERCEPTOR; + bridgeUrl.searchParams.append(CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM, url); + return bridgeUrl.toString(); + }; + const initBridge = (w) => { + const getPlatformId = (win) => { + var _a, _b; + if (win === null || win === void 0 ? void 0 : win.androidBridge) { + return 'android'; } - const request = new Request(resource, options); - if (request.url.startsWith(`${cap.getServerUrl()}/`)) { - return win.CapacitorWebFetch(resource, options); + else if ((_b = (_a = win === null || win === void 0 ? void 0 : win.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge) { + return 'ios'; } - const { method } = request; - if ( - method.toLocaleUpperCase() === 'GET' || - method.toLocaleUpperCase() === 'HEAD' || - method.toLocaleUpperCase() === 'OPTIONS' || - method.toLocaleUpperCase() === 'TRACE' - ) { - // a workaround for following android webview issue: - // https://issues.chromium.org/issues/40450316 - // Sets the user-agent header to a custom value so that its not stripped - // on its way to the native layer - if (platform === 'android' && (options === null || options === void 0 ? void 0 : options.headers)) { - const userAgent = headers.get('User-Agent') || headers.get('user-agent'); - if (userAgent !== null) { - headers.set('x-cap-user-agent', userAgent); - options.headers = headers; - } - } - if (typeof resource === 'string') { - return await win.CapacitorWebFetch(createProxyUrl(resource, win), options); - } else if (resource instanceof URL) { - const modifiedURL = new URL(createProxyUrl(resource.toString(), win)); - return await win.CapacitorWebFetch(modifiedURL, options); - } else if (resource instanceof Request) { - const modifiedRequest = new Request(createProxyUrl(resource.url, win), resource); - return await win.CapacitorWebFetch(modifiedRequest, options); - } + else { + return 'web'; } - const tag = `CapacitorHttp fetch ${Date.now()} ${resource}`; - console.time(tag); - try { - const { body } = request; - const optionHeaders = Object.fromEntries(request.headers.entries()); - const { - data: requestData, - type, - headers: requestHeaders, - } = await convertBody( - (options === null || options === void 0 ? void 0 : options.body) || body || undefined, - optionHeaders['Content-Type'] || optionHeaders['content-type'], - ); - const nativeHeaders = Object.assign(Object.assign({}, requestHeaders), optionHeaders); - if (platform === 'android') { - if (headers.has('User-Agent')) { - nativeHeaders['User-Agent'] = headers.get('User-Agent'); + }; + const convertFileSrcServerUrl = (webviewServerUrl, filePath) => { + if (typeof filePath === 'string') { + if (filePath.startsWith('/')) { + return webviewServerUrl + '/_capacitor_file_' + filePath; } - if (headers.has('user-agent')) { - nativeHeaders['user-agent'] = headers.get('user-agent'); + else if (filePath.startsWith('file://')) { + return webviewServerUrl + filePath.replace('file://', '/_capacitor_file_'); + } + else if (filePath.startsWith('content://')) { + return webviewServerUrl + filePath.replace('content:/', '/_capacitor_content_'); } - } - const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', { - url: request.url, - method: method, - data: requestData, - dataType: type, - headers: nativeHeaders, - }); - const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type']; - let data = ( - contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json') - ) - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - // use null data for 204 No Content HTTP response - if (nativeResponse.status === 204) { - data = null; - } - // intercept & parse response before returning - const response = new Response(data, { - headers: nativeResponse.headers, - status: nativeResponse.status, - }); - /* - * copy url to response, `cordova-plugin-ionic` uses this url from the response - * we need `Object.defineProperty` because url is an inherited getter on the Response - * see: https://stackoverflow.com/a/57382543 - * */ - Object.defineProperty(response, 'url', { - value: nativeResponse.url, - }); - console.timeEnd(tag); - return response; - } catch (error) { - console.timeEnd(tag); - return Promise.reject(error); } - }; - window.XMLHttpRequest = function () { - const xhr = new win.CapacitorWebXMLHttpRequest.constructor(); - Object.defineProperties(xhr, { - _headers: { - value: {}, - writable: true, - }, - _method: { - value: xhr.method, - writable: true, - }, - }); - const prototype = win.CapacitorWebXMLHttpRequest.prototype; - const isProgressEventAvailable = () => - typeof ProgressEvent !== 'undefined' && ProgressEvent.prototype instanceof Event; - // XHR patch abort - prototype.abort = function () { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.abort.call(this); - } - this.readyState = 0; - setTimeout(() => { - this.dispatchEvent(new Event('abort')); - this.dispatchEvent(new Event('loadend')); - }); + return filePath; + }; + const initEvents = (win, cap) => { + cap.addListener = (pluginName, eventName, callback) => { + const callbackId = cap.nativeCallback(pluginName, 'addListener', { + eventName: eventName, + }, callback); + return { + remove: async () => { + var _a; + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.debug('Removing listener', pluginName, eventName); + cap.removeListener(pluginName, callbackId, eventName, callback); + }, + }; + }; + cap.removeListener = (pluginName, callbackId, eventName, callback) => { + cap.nativeCallback(pluginName, 'removeListener', { + callbackId: callbackId, + eventName: eventName, + }, callback); }; - // XHR patch open - prototype.open = function (method, url) { - this._method = method.toLocaleUpperCase(); - this._url = url; - if ( - !this._method || - this._method === 'GET' || - this._method === 'HEAD' || - this._method === 'OPTIONS' || - this._method === 'TRACE' - ) { - if (isRelativeOrProxyUrl(url)) { - return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); + cap.createEvent = (eventName, eventData) => { + const doc = win.document; + if (doc) { + const ev = doc.createEvent('Events'); + ev.initEvent(eventName, false, false); + if (eventData && typeof eventData === 'object') { + for (const i in eventData) { + // eslint-disable-next-line no-prototype-builtins + if (eventData.hasOwnProperty(i)) { + ev[i] = eventData[i]; + } + } + } + return ev; } - this._url = createProxyUrl(this._url, win); - return win.CapacitorWebXMLHttpRequest.open.call(this, method, this._url); - } - Object.defineProperties(this, { - readyState: { - get: function () { - var _a; - return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0; - }, - set: function (val) { - this._readyState = val; - setTimeout(() => { - this.dispatchEvent(new Event('readystatechange')); - }); - }, - }, - }); - setTimeout(() => { - this.dispatchEvent(new Event('loadstart')); - }); - this.readyState = 1; + return null; }; - // XHR patch set request header - prototype.setRequestHeader = function (header, value) { - // a workaround for the following android web view issue: - // https://issues.chromium.org/issues/40450316 - // Sets the user-agent header to a custom value so that its not stripped - // on its way to the native layer - if (platform === 'android' && (header === 'User-Agent' || header === 'user-agent')) { - header = 'x-cap-user-agent'; - } - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); - } - this._headers[header] = value; + cap.triggerEvent = (eventName, target, eventData) => { + const doc = win.document; + const cordova = win.cordova; + eventData = eventData || {}; + const ev = cap.createEvent(eventName, eventData); + if (ev) { + if (target === 'document') { + if (cordova === null || cordova === void 0 ? void 0 : cordova.fireDocumentEvent) { + cordova.fireDocumentEvent(eventName, eventData); + return true; + } + else if (doc === null || doc === void 0 ? void 0 : doc.dispatchEvent) { + return doc.dispatchEvent(ev); + } + } + else if (target === 'window' && win.dispatchEvent) { + return win.dispatchEvent(ev); + } + else if (doc === null || doc === void 0 ? void 0 : doc.querySelector) { + const targetEl = doc.querySelector(target); + if (targetEl) { + return targetEl.dispatchEvent(ev); + } + } + } + return false; }; - // XHR patch send - prototype.send = function (body) { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.send.call(this, body); - } - const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; - console.time(tag); - try { - this.readyState = 2; - Object.defineProperties(this, { - response: { - value: '', - writable: true, - }, - responseText: { - value: '', - writable: true, - }, - responseURL: { - value: '', - writable: true, - }, - status: { - value: 0, - writable: true, - }, - }); - convertBody(body).then(({ data, type, headers }) => { - let otherHeaders = - this._headers != null && Object.keys(this._headers).length > 0 ? this._headers : undefined; - if (body instanceof FormData) { - if (!this._headers['Content-Type'] && !this._headers['content-type']) { - otherHeaders = Object.assign(Object.assign({}, otherHeaders), { - 'Content-Type': `multipart/form-data; boundary=----WebKitFormBoundary${Math.random().toString(36).substring(2, 15)}`, - }); + win.Capacitor = cap; + }; + const initLegacyHandlers = (win, cap) => { + // define cordova if it's not there already + win.cordova = win.cordova || {}; + const doc = win.document; + const nav = win.navigator; + if (nav) { + nav.app = nav.app || {}; + nav.app.exitApp = () => { + var _a; + if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { + win.console.warn('App plugin not installed'); + } + else { + cap.nativeCallback('App', 'exitApp', {}); + } + }; + } + if (doc) { + const docAddEventListener = doc.addEventListener; + doc.addEventListener = (...args) => { + var _a; + const eventName = args[0]; + const handler = args[1]; + if (eventName === 'deviceready' && handler) { + Promise.resolve().then(handler); } - } - // intercept request & pass to the bridge - cap - .nativePromise('CapacitorHttp', 'request', { - url: this._url, - method: this._method, - data: data !== null ? data : undefined, - headers: Object.assign(Object.assign({}, headers), otherHeaders), - dataType: type, - }) - .then((nativeResponse) => { - var _a; - // intercept & parse response before returning - if (this.readyState == 2) { - //TODO: Add progress event emission on native side - if (isProgressEventAvailable()) { - this.dispatchEvent( - new ProgressEvent('progress', { - lengthComputable: true, - loaded: nativeResponse.data.length, - total: nativeResponse.data.length, - }), - ); + else if (eventName === 'backbutton' && cap.Plugins.App) { + // Add a dummy listener so Capacitor doesn't do the default + // back button action + if (!((_a = cap.Plugins) === null || _a === void 0 ? void 0 : _a.App)) { + win.console.warn('App plugin not installed'); } - this._headers = nativeResponse.headers; - this.status = nativeResponse.status; - if (this.responseType === '' || this.responseType === 'text') { - this.response = - typeof nativeResponse.data !== 'string' - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - } else { - this.response = nativeResponse.data; + else { + cap.Plugins.App.addListener('backButton', () => { + // ignore + }); } - this.responseText = ( - (_a = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type']) === - null || _a === void 0 - ? void 0 - : _a.startsWith('application/json') - ) - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - this.responseURL = nativeResponse.url; - this.readyState = 4; - setTimeout(() => { - this.dispatchEvent(new Event('load')); - this.dispatchEvent(new Event('loadend')); - }); - } - console.timeEnd(tag); - }) - .catch((error) => { - this.status = error.status; - this._headers = error.headers; - this.response = error.data; - this.responseText = JSON.stringify(error.data); - this.responseURL = error.url; - this.readyState = 4; - if (isProgressEventAvailable()) { - this.dispatchEvent( - new ProgressEvent('progress', { - lengthComputable: false, - loaded: 0, - total: 0, - }), - ); - } - setTimeout(() => { - this.dispatchEvent(new Event('error')); - this.dispatchEvent(new Event('loadend')); - }); - console.timeEnd(tag); - }); + } + return docAddEventListener.apply(doc, args); + }; + } + win.Capacitor = cap; + }; + const initVendor = (win, cap) => { + const Ionic = (win.Ionic = win.Ionic || {}); + const IonicWebView = (Ionic.WebView = Ionic.WebView || {}); + const Plugins = cap.Plugins; + IonicWebView.getServerBasePath = (callback) => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.getServerBasePath().then((result) => { + callback(result.path); }); - } catch (error) { - this.status = 500; - this._headers = {}; - this.response = error; - this.responseText = error.toString(); - this.responseURL = this._url; - this.readyState = 4; - if (isProgressEventAvailable()) { - this.dispatchEvent( - new ProgressEvent('progress', { - lengthComputable: false, - loaded: 0, - total: 0, - }), - ); + }; + IonicWebView.setServerAssetPath = (path) => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerAssetPath({ path }); + }; + IonicWebView.setServerBasePath = (path) => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.setServerBasePath({ path }); + }; + IonicWebView.persistServerBasePath = () => { + var _a; + (_a = Plugins === null || Plugins === void 0 ? void 0 : Plugins.WebView) === null || _a === void 0 ? void 0 : _a.persistServerBasePath(); + }; + IonicWebView.convertFileSrc = (url) => cap.convertFileSrc(url); + win.Capacitor = cap; + win.Ionic.WebView = IonicWebView; + }; + const initLogger = (win, cap) => { + const BRIDGED_CONSOLE_METHODS = ['debug', 'error', 'info', 'log', 'trace', 'warn']; + const createLogFromNative = (c) => (result) => { + if (isFullConsole(c)) { + const success = result.success === true; + const tagStyles = success + ? 'font-style: italic; font-weight: lighter; color: gray' + : 'font-style: italic; font-weight: lighter; color: red'; + c.groupCollapsed('%cresult %c' + result.pluginId + '.' + result.methodName + ' (#' + result.callbackId + ')', tagStyles, 'font-style: italic; font-weight: bold; color: #444'); + if (result.success === false) { + c.error(result.error); + } + else { + c.dir(JSON.stringify(result.data)); + } + c.groupEnd(); + } + else { + if (result.success === false) { + c.error('LOG FROM NATIVE', result.error); + } + else { + c.log('LOG FROM NATIVE', result.data); + } } - setTimeout(() => { - this.dispatchEvent(new Event('error')); - this.dispatchEvent(new Event('loadend')); - }); - console.timeEnd(tag); - } }; - // XHR patch getAllResponseHeaders - prototype.getAllResponseHeaders = function () { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); - } - let returnString = ''; - for (const key in this._headers) { - if (key != 'Set-Cookie') { - returnString += key + ': ' + this._headers[key] + '\r\n'; + const createLogToNative = (c) => (call) => { + if (isFullConsole(c)) { + c.groupCollapsed('%cnative %c' + call.pluginId + '.' + call.methodName + ' (#' + call.callbackId + ')', 'font-weight: lighter; color: gray', 'font-weight: bold; color: #000'); + c.dir(call); + c.groupEnd(); + } + else { + c.log('LOG TO NATIVE: ', call); } - } - return returnString; }; - // XHR patch getResponseHeader - prototype.getResponseHeader = function (name) { - if (isRelativeOrProxyUrl(this._url)) { - return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); - } - return this._headers[name]; + const isFullConsole = (c) => { + if (!c) { + return false; + } + return typeof c.groupCollapsed === 'function' || typeof c.groupEnd === 'function' || typeof c.dir === 'function'; }; - Object.setPrototypeOf(xhr, prototype); - return xhr; - }; - Object.assign(window.XMLHttpRequest, win.CapacitorWebXMLHttpRequest.fullObject); - } - } - // patch window.console on iOS and store original console fns - const isIos = getPlatformId(win) === 'ios'; - if (win.console && isIos) { - Object.defineProperties( - win.console, - BRIDGED_CONSOLE_METHODS.reduce((props, method) => { - const consoleMethod = win.console[method].bind(win.console); - props[method] = { - value: (...args) => { - const msgs = [...args]; - cap.toNative('Console', 'log', { - level: method, - message: msgs.map(serializeConsoleMessage).join(' '), + const serializeConsoleMessage = (msg) => { + try { + if (typeof msg === 'object') { + msg = JSON.stringify(msg); + } + return String(msg); + } + catch (e) { + return ''; + } + }; + const platform = getPlatformId(win); + if (platform == 'android' && typeof win.CapacitorSystemBarsAndroidInterface !== 'undefined') { + // add DOM ready listener for System Bars + document.addEventListener('DOMContentLoaded', function () { + win.CapacitorSystemBarsAndroidInterface.onDOMReady(); }); - return consoleMethod(...args); - }, + } + if (platform == 'android' || platform == 'ios') { + // patch document.cookie on Android/iOS + win.CapacitorCookiesDescriptor = + Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') || + Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie'); + let doPatchCookies = false; + // check if capacitor cookies is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor cookies config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.isEnabled', + }; + const isCookiesEnabled = prompt(JSON.stringify(payload)); + if (isCookiesEnabled === 'true') { + doPatchCookies = true; + } + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled(); + if (isCookiesEnabled === true) { + doPatchCookies = true; + } + } + if (doPatchCookies) { + Object.defineProperty(document, 'cookie', { + get: function () { + var _a, _b, _c; + if (platform === 'ios') { + // Use prompt to synchronously get cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.get', + }; + const res = prompt(JSON.stringify(payload)); + return res; + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + // return original document.cookie since Android does not support filtering of `httpOnly` cookies + return (_c = (_b = (_a = win.CapacitorCookiesDescriptor) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(document)) !== null && _c !== void 0 ? _c : ''; + } + }, + set: function (val) { + const cookiePairs = val.split(';'); + const domainSection = val.toLowerCase().split('domain=')[1]; + const domain = cookiePairs.length > 1 && domainSection != null && domainSection.length > 0 + ? domainSection.split(';')[0].trim() + : ''; + if (platform === 'ios') { + // Use prompt to synchronously set cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.set', + action: val, + domain, + }; + prompt(JSON.stringify(payload)); + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + win.CapacitorCookiesAndroidInterface.setCookie(domain, val); + } + }, + }); + } + // patch fetch / XHR on Android/iOS + // store original fetch & XHR functions + win.CapacitorWebFetch = window.fetch; + win.CapacitorWebXMLHttpRequest = { + abort: window.XMLHttpRequest.prototype.abort, + constructor: window.XMLHttpRequest.prototype.constructor, + fullObject: window.XMLHttpRequest, + getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, + getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, + open: window.XMLHttpRequest.prototype.open, + prototype: window.XMLHttpRequest.prototype, + send: window.XMLHttpRequest.prototype.send, + setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, + }; + let doPatchHttp = false; + // check if capacitor http is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor http config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorHttp', + }; + const isHttpEnabled = prompt(JSON.stringify(payload)); + if (isHttpEnabled === 'true') { + doPatchHttp = true; + } + } + else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') { + const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled(); + if (isHttpEnabled === true) { + doPatchHttp = true; + } + } + if (doPatchHttp) { + // fetch patch + window.fetch = async (resource, options) => { + const headers = new Headers(options === null || options === void 0 ? void 0 : options.headers); + const contentType = headers.get('Content-Type') || headers.get('content-type'); + if ((options === null || options === void 0 ? void 0 : options.body) instanceof FormData && + (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/form-data')) && + !contentType.includes('boundary')) { + headers.delete('Content-Type'); + headers.delete('content-type'); + options.headers = headers; + } + const request = new Request(resource, options); + if (request.url.startsWith(`${cap.getServerUrl()}/`)) { + return win.CapacitorWebFetch(resource, options); + } + const { method } = request; + if (method.toLocaleUpperCase() === 'GET' || + method.toLocaleUpperCase() === 'HEAD' || + method.toLocaleUpperCase() === 'OPTIONS' || + method.toLocaleUpperCase() === 'TRACE') { + // a workaround for following android webview issue: + // https://issues.chromium.org/issues/40450316 + // Sets the user-agent header to a custom value so that its not stripped + // on its way to the native layer + if (platform === 'android' && (options === null || options === void 0 ? void 0 : options.headers)) { + const userAgent = headers.get('User-Agent') || headers.get('user-agent'); + if (userAgent !== null) { + headers.set('x-cap-user-agent', userAgent); + options.headers = headers; + } + } + if (typeof resource === 'string') { + return await win.CapacitorWebFetch(createProxyUrl(resource, win), options); + } + else if (resource instanceof URL) { + const modifiedURL = new URL(createProxyUrl(resource.toString(), win)); + return await win.CapacitorWebFetch(modifiedURL, options); + } + else if (resource instanceof Request) { + const modifiedRequest = new Request(createProxyUrl(resource.url, win), resource); + return await win.CapacitorWebFetch(modifiedRequest, options); + } + } + const tag = `CapacitorHttp fetch ${Date.now()} ${resource}`; + console.time(tag); + try { + const { body } = request; + const optionHeaders = Object.fromEntries(request.headers.entries()); + const { data: requestData, type, headers: requestHeaders, } = await convertBody((options === null || options === void 0 ? void 0 : options.body) || body || undefined, optionHeaders['Content-Type'] || optionHeaders['content-type']); + const nativeHeaders = Object.assign(Object.assign({}, requestHeaders), optionHeaders); + if (platform === 'android') { + if (headers.has('User-Agent')) { + nativeHeaders['User-Agent'] = headers.get('User-Agent'); + } + if (headers.has('user-agent')) { + nativeHeaders['user-agent'] = headers.get('user-agent'); + } + } + const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', { + url: request.url, + method: method, + data: requestData, + dataType: type, + headers: nativeHeaders, + }); + const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type']; + let data = (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json')) + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + // use null data for 204 No Content HTTP response + if (nativeResponse.status === 204) { + data = null; + } + // intercept & parse response before returning + const response = new Response(data, { + headers: nativeResponse.headers, + status: nativeResponse.status, + }); + /* + * copy url to response, `cordova-plugin-ionic` uses this url from the response + * we need `Object.defineProperty` because url is an inherited getter on the Response + * see: https://stackoverflow.com/a/57382543 + * */ + Object.defineProperty(response, 'url', { + value: nativeResponse.url, + }); + console.timeEnd(tag); + return response; + } + catch (error) { + console.timeEnd(tag); + return Promise.reject(error); + } + }; + window.XMLHttpRequest = function () { + const xhr = new win.CapacitorWebXMLHttpRequest.constructor(); + Object.defineProperties(xhr, { + _headers: { + value: {}, + writable: true, + }, + _method: { + value: xhr.method, + writable: true, + }, + }); + const prototype = win.CapacitorWebXMLHttpRequest.prototype; + const isProgressEventAvailable = () => typeof ProgressEvent !== 'undefined' && ProgressEvent.prototype instanceof Event; + // XHR patch abort + prototype.abort = function () { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.abort.call(this); + } + this.readyState = 0; + setTimeout(() => { + this.dispatchEvent(new Event('abort')); + this.dispatchEvent(new Event('loadend')); + }); + }; + // XHR patch open + prototype.open = function (method, url) { + this._method = method.toLocaleUpperCase(); + this._url = url; + if (!this._method || + this._method === 'GET' || + this._method === 'HEAD' || + this._method === 'OPTIONS' || + this._method === 'TRACE') { + if (isRelativeOrProxyUrl(url)) { + return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); + } + this._url = createProxyUrl(this._url, win); + return win.CapacitorWebXMLHttpRequest.open.call(this, method, this._url); + } + Object.defineProperties(this, { + readyState: { + get: function () { + var _a; + return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0; + }, + set: function (val) { + this._readyState = val; + setTimeout(() => { + this.dispatchEvent(new Event('readystatechange')); + }); + }, + }, + }); + setTimeout(() => { + this.dispatchEvent(new Event('loadstart')); + }); + this.readyState = 1; + }; + // XHR patch set request header + prototype.setRequestHeader = function (header, value) { + // a workaround for the following android web view issue: + // https://issues.chromium.org/issues/40450316 + // Sets the user-agent header to a custom value so that its not stripped + // on its way to the native layer + if (platform === 'android' && (header === 'User-Agent' || header === 'user-agent')) { + header = 'x-cap-user-agent'; + } + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); + } + this._headers[header] = value; + }; + // XHR patch send + prototype.send = function (body) { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.send.call(this, body); + } + const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; + console.time(tag); + try { + this.readyState = 2; + Object.defineProperties(this, { + response: { + value: '', + writable: true, + }, + responseText: { + value: '', + writable: true, + }, + responseURL: { + value: '', + writable: true, + }, + status: { + value: 0, + writable: true, + }, + }); + convertBody(body).then(({ data, type, headers }) => { + let otherHeaders = this._headers != null && Object.keys(this._headers).length > 0 ? this._headers : undefined; + if (body instanceof FormData) { + if (!this._headers['Content-Type'] && !this._headers['content-type']) { + otherHeaders = Object.assign(Object.assign({}, otherHeaders), { 'Content-Type': `multipart/form-data; boundary=----WebKitFormBoundary${Math.random().toString(36).substring(2, 15)}` }); + } + } + // intercept request & pass to the bridge + cap + .nativePromise('CapacitorHttp', 'request', { + url: this._url, + method: this._method, + data: data !== null ? data : undefined, + headers: Object.assign(Object.assign({}, headers), otherHeaders), + dataType: type, + }) + .then((nativeResponse) => { + var _a; + // intercept & parse response before returning + if (this.readyState == 2) { + //TODO: Add progress event emission on native side + if (isProgressEventAvailable()) { + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: true, + loaded: nativeResponse.data.length, + total: nativeResponse.data.length, + })); + } + this._headers = nativeResponse.headers; + this.status = nativeResponse.status; + if (this.responseType === '' || this.responseType === 'text') { + this.response = + typeof nativeResponse.data !== 'string' + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + } + else { + this.response = nativeResponse.data; + } + this.responseText = ((_a = (nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type'])) === null || _a === void 0 ? void 0 : _a.startsWith('application/json')) + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + this.responseURL = nativeResponse.url; + this.readyState = 4; + setTimeout(() => { + this.dispatchEvent(new Event('load')); + this.dispatchEvent(new Event('loadend')); + }); + } + console.timeEnd(tag); + }) + .catch((error) => { + this.status = error.status; + this._headers = error.headers; + this.response = error.data; + this.responseText = JSON.stringify(error.data); + this.responseURL = error.url; + this.readyState = 4; + if (isProgressEventAvailable()) { + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + })); + } + setTimeout(() => { + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + console.timeEnd(tag); + }); + }); + } + catch (error) { + this.status = 500; + this._headers = {}; + this.response = error; + this.responseText = error.toString(); + this.responseURL = this._url; + this.readyState = 4; + if (isProgressEventAvailable()) { + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + })); + } + setTimeout(() => { + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + console.timeEnd(tag); + } + }; + // XHR patch getAllResponseHeaders + prototype.getAllResponseHeaders = function () { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); + } + let returnString = ''; + for (const key in this._headers) { + if (key != 'Set-Cookie') { + returnString += key + ': ' + this._headers[key] + '\r\n'; + } + } + return returnString; + }; + // XHR patch getResponseHeader + prototype.getResponseHeader = function (name) { + if (isRelativeOrProxyUrl(this._url)) { + return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); + } + return this._headers[name]; + }; + Object.setPrototypeOf(xhr, prototype); + return xhr; + }; + Object.assign(window.XMLHttpRequest, win.CapacitorWebXMLHttpRequest.fullObject); + } + } + // patch window.console on iOS and store original console fns + const isIos = getPlatformId(win) === 'ios'; + if (win.console && isIos) { + Object.defineProperties(win.console, BRIDGED_CONSOLE_METHODS.reduce((props, method) => { + const consoleMethod = win.console[method].bind(win.console); + props[method] = { + value: (...args) => { + const msgs = [...args]; + cap.toNative('Console', 'log', { + level: method, + message: msgs.map(serializeConsoleMessage).join(' '), + }); + return consoleMethod(...args); + }, + }; + return props; + }, {})); + } + cap.logJs = (msg, level) => { + switch (level) { + case 'error': + win.console.error(msg); + break; + case 'warn': + win.console.warn(msg); + break; + case 'info': + win.console.info(msg); + break; + default: + win.console.log(msg); + } }; - return props; - }, {}), - ); - } - cap.logJs = (msg, level) => { - switch (level) { - case 'error': - win.console.error(msg); - break; - case 'warn': - win.console.warn(msg); - break; - case 'info': - win.console.info(msg); - break; - default: - win.console.log(msg); - } - }; - cap.logToNative = createLogToNative(win.console); - cap.logFromNative = createLogFromNative(win.console); - cap.handleError = (err) => win.console.error(err); - win.Capacitor = cap; - }; - function initNativeBridge(win) { - const cap = win.Capacitor || {}; - // keep a collection of callbacks for native response data - const callbacks = new Map(); - const webviewServerUrl = typeof win.WEBVIEW_SERVER_URL === 'string' ? win.WEBVIEW_SERVER_URL : ''; - cap.getServerUrl = () => webviewServerUrl; - cap.convertFileSrc = (filePath) => convertFileSrcServerUrl(webviewServerUrl, filePath); - // Counter of callback ids, randomized to avoid - // any issues during reloads if a call comes back with - // an existing callback id from an old session - let callbackIdCount = Math.floor(Math.random() * 134217728); - let postToNative = null; - const isNativePlatform = () => true; - const getPlatform = () => getPlatformId(win); - cap.getPlatform = getPlatform; - cap.isPluginAvailable = (name) => Object.prototype.hasOwnProperty.call(cap.Plugins, name); - cap.isNativePlatform = isNativePlatform; - // create the postToNative() fn if needed - if (getPlatformId(win) === 'android') { - // android platform - postToNative = (data) => { - var _a; - try { - win.androidBridge.postMessage(JSON.stringify(data)); - } catch (e) { - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 - ? void 0 - : _a.error(e); - } + cap.logToNative = createLogToNative(win.console); + cap.logFromNative = createLogFromNative(win.console); + cap.handleError = (err) => win.console.error(err); + win.Capacitor = cap; }; - } else if (getPlatformId(win) === 'ios') { - // ios platform - postToNative = (data) => { - var _a; - try { - data.type = data.type ? data.type : 'message'; - win.webkit.messageHandlers.bridge.postMessage(data); - } catch (e) { - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 - ? void 0 - : _a.error(e); - } - }; - } - cap.handleWindowError = (msg, url, lineNo, columnNo, err) => { - const str = msg.toLowerCase(); - if (str.indexOf('script error') > -1); - else { - const errObj = { - type: 'js.error', - error: { - message: msg, - url: url, - line: lineNo, - col: columnNo, - errorObject: JSON.stringify(err), - }, - }; - if (err !== null) { - cap.handleError(err); - } - postToNative(errObj); - } - return false; - }; - if (cap.DEBUG) { - window.onerror = cap.handleWindowError; - } - initLogger(win, cap); - /** - * Send a plugin method call to the native layer - */ - cap.toNative = (pluginName, methodName, options, storedCallback) => { - var _a, _b; - try { - if (typeof postToNative === 'function') { - let callbackId = '-1'; - if ( - storedCallback && - (typeof storedCallback.callback === 'function' || typeof storedCallback.resolve === 'function') - ) { - // store the call for later lookup - callbackId = String(++callbackIdCount); - callbacks.set(callbackId, storedCallback); + function initNativeBridge(win) { + const cap = win.Capacitor || {}; + // keep a collection of callbacks for native response data + const callbacks = new Map(); + const webviewServerUrl = typeof win.WEBVIEW_SERVER_URL === 'string' ? win.WEBVIEW_SERVER_URL : ''; + cap.getServerUrl = () => webviewServerUrl; + cap.convertFileSrc = (filePath) => convertFileSrcServerUrl(webviewServerUrl, filePath); + // Counter of callback ids, randomized to avoid + // any issues during reloads if a call comes back with + // an existing callback id from an old session + let callbackIdCount = Math.floor(Math.random() * 134217728); + let postToNative = null; + const isNativePlatform = () => true; + const getPlatform = () => getPlatformId(win); + cap.getPlatform = getPlatform; + cap.isPluginAvailable = (name) => Object.prototype.hasOwnProperty.call(cap.Plugins, name); + cap.isNativePlatform = isNativePlatform; + // create the postToNative() fn if needed + if (getPlatformId(win) === 'android') { + // android platform + postToNative = (data) => { + var _a; + try { + win.androidBridge.postMessage(JSON.stringify(data)); + } + catch (e) { + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e); + } + }; } - const callData = { - callbackId: callbackId, - pluginId: pluginName, - methodName: methodName, - options: options || {}, - }; - if (cap.isLoggingEnabled && pluginName !== 'Console') { - cap.logToNative(callData); + else if (getPlatformId(win) === 'ios') { + // ios platform + postToNative = (data) => { + var _a; + try { + data.type = data.type ? data.type : 'message'; + win.webkit.messageHandlers.bridge.postMessage(data); + } + catch (e) { + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.error(e); + } + }; } - // post the call data to native - postToNative(callData); - return callbackId; - } else { - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 - ? void 0 - : _a.warn(`implementation unavailable for: ${pluginName}`); - } - } catch (e) { - (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); - } - return null; - }; - if (win === null || win === void 0 ? void 0 : win.androidBridge) { - win.androidBridge.onmessage = function (event) { - returnResult(JSON.parse(event.data)); - }; - } - /** - * Process a response from the native layer. - */ - cap.fromNative = (result) => { - returnResult(result); - }; - const returnResult = (result) => { - var _a, _b; - if (cap.isLoggingEnabled && result.pluginId !== 'Console') { - cap.logFromNative(result); - } - // get the stored call, if it exists - try { - const storedCall = callbacks.get(result.callbackId); - if (storedCall) { - // looks like we've got a stored call - if (result.error) { - // ensure stacktraces by copying error properties to an Error - result.error = Object.keys(result.error).reduce((err, key) => { - // use any type to avoid importing util and compiling most of .ts files - err[key] = result.error[key]; - return err; - }, new cap.Exception('')); + cap.handleWindowError = (msg, url, lineNo, columnNo, err) => { + const str = msg.toLowerCase(); + if (str.indexOf('script error') > -1) ; + else { + const errObj = { + type: 'js.error', + error: { + message: msg, + url: url, + line: lineNo, + col: columnNo, + errorObject: JSON.stringify(err), + }, + }; + if (err !== null) { + cap.handleError(err); + } + postToNative(errObj); + } + return false; + }; + if (cap.DEBUG) { + window.onerror = cap.handleWindowError; } - if (typeof storedCall.callback === 'function') { - // callback - if (result.success) { - storedCall.callback(result.data); - } else { - storedCall.callback(null, result.error); - } - } else if (typeof storedCall.resolve === 'function') { - // promise - if (result.success) { - storedCall.resolve(result.data); - } else { - storedCall.reject(result.error); - } - // no need to keep this stored callback - // around for a one time resolve promise - callbacks.delete(result.callbackId); + initLogger(win, cap); + /** + * Send a plugin method call to the native layer + */ + cap.toNative = (pluginName, methodName, options, storedCallback) => { + var _a, _b; + try { + if (typeof postToNative === 'function') { + let callbackId = '-1'; + if (storedCallback && + (typeof storedCallback.callback === 'function' || typeof storedCallback.resolve === 'function')) { + // store the call for later lookup + callbackId = String(++callbackIdCount); + callbacks.set(callbackId, storedCallback); + } + const callData = { + callbackId: callbackId, + pluginId: pluginName, + methodName: methodName, + options: options || {}, + }; + if (cap.isLoggingEnabled && pluginName !== 'Console') { + cap.logToNative(callData); + } + // post the call data to native + postToNative(callData); + return callbackId; + } + else { + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(`implementation unavailable for: ${pluginName}`); + } + } + catch (e) { + (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); + } + return null; + }; + if (win === null || win === void 0 ? void 0 : win.androidBridge) { + win.androidBridge.onmessage = function (event) { + returnResult(JSON.parse(event.data)); + }; } - } else if (!result.success && result.error) { - // no stored callback, but if there was an error let's log it - (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 - ? void 0 - : _a.warn(result.error); - } - if (result.save === false) { - callbacks.delete(result.callbackId); - } - } catch (e) { - (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); - } - // always delete to prevent memory leaks - // overkill but we're not sure what apps will do with this data - delete result.data; - delete result.error; - }; - cap.nativeCallback = (pluginName, methodName, options, callback) => { - if (typeof options === 'function') { - console.warn(`Using a callback as the 'options' parameter of 'nativeCallback()' is deprecated.`); - callback = options; - options = null; + /** + * Process a response from the native layer. + */ + cap.fromNative = (result) => { + returnResult(result); + }; + const returnResult = (result) => { + var _a, _b; + if (cap.isLoggingEnabled && result.pluginId !== 'Console') { + cap.logFromNative(result); + } + // get the stored call, if it exists + try { + const storedCall = callbacks.get(result.callbackId); + if (storedCall) { + // looks like we've got a stored call + if (result.error) { + // ensure stacktraces by copying error properties to an Error + result.error = Object.keys(result.error).reduce((err, key) => { + // use any type to avoid importing util and compiling most of .ts files + err[key] = result.error[key]; + return err; + }, new cap.Exception('')); + } + if (typeof storedCall.callback === 'function') { + // callback + if (result.success) { + storedCall.callback(result.data); + } + else { + storedCall.callback(null, result.error); + } + } + else if (typeof storedCall.resolve === 'function') { + // promise + if (result.success) { + storedCall.resolve(result.data); + } + else { + storedCall.reject(result.error); + } + // no need to keep this stored callback + // around for a one time resolve promise + callbacks.delete(result.callbackId); + } + } + else if (!result.success && result.error) { + // no stored callback, but if there was an error let's log it + (_a = win === null || win === void 0 ? void 0 : win.console) === null || _a === void 0 ? void 0 : _a.warn(result.error); + } + if (result.save === false) { + callbacks.delete(result.callbackId); + } + } + catch (e) { + (_b = win === null || win === void 0 ? void 0 : win.console) === null || _b === void 0 ? void 0 : _b.error(e); + } + // always delete to prevent memory leaks + // overkill but we're not sure what apps will do with this data + delete result.data; + delete result.error; + }; + cap.nativeCallback = (pluginName, methodName, options, callback) => { + if (typeof options === 'function') { + console.warn(`Using a callback as the 'options' parameter of 'nativeCallback()' is deprecated.`); + callback = options; + options = null; + } + return cap.toNative(pluginName, methodName, options, { callback }); + }; + cap.nativePromise = (pluginName, methodName, options) => { + return new Promise((resolve, reject) => { + cap.toNative(pluginName, methodName, options, { + resolve: resolve, + reject: reject, + }); + }); + }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + cap.withPlugin = (_pluginId, _fn) => dummy; + cap.Exception = CapacitorException; + initEvents(win, cap); + initLegacyHandlers(win, cap); + initVendor(win, cap); + win.Capacitor = cap; } - return cap.toNative(pluginName, methodName, options, { callback }); - }; - cap.nativePromise = (pluginName, methodName, options) => { - return new Promise((resolve, reject) => { - cap.toNative(pluginName, methodName, options, { - resolve: resolve, - reject: reject, - }); - }); - }; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - cap.withPlugin = (_pluginId, _fn) => dummy; - cap.Exception = CapacitorException; - initEvents(win, cap); - initLegacyHandlers(win, cap); - initVendor(win, cap); - win.Capacitor = cap; - } - initNativeBridge(w); - }; - initBridge( - typeof globalThis !== 'undefined' - ? globalThis - : typeof self !== 'undefined' - ? self - : typeof window !== 'undefined' - ? window - : typeof global !== 'undefined' - ? global - : {}, - ); + initNativeBridge(w); + }; + initBridge(typeof globalThis !== 'undefined' + ? globalThis + : typeof self !== 'undefined' + ? self + : typeof window !== 'undefined' + ? window + : typeof global !== 'undefined' + ? global + : {}); + + dummy = initBridge; - dummy = initBridge; + Object.defineProperty(exports, '__esModule', { value: true }); - Object.defineProperty(exports, '__esModule', { value: true }); + return exports; - return exports; })({}); From e00d310dcfa87cc179f634fd1e0335458c56bb19 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 19 Aug 2026 19:02:41 -0500 Subject: [PATCH 17/42] Update swiftlint excluded paths for restructured iOS test dirs --- swiftlint.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swiftlint.config.js b/swiftlint.config.js index 59e40b8907..6c51f7e913 100644 --- a/swiftlint.config.js +++ b/swiftlint.config.js @@ -1,5 +1,5 @@ module.exports = { ...require('@ionic/swiftlint-config'), included: ['${PWD}/ios', '${PWD}/ios-pods-template', '${PWD}/ios-spm-template'], - excluded: ['${PWD}/ios/Capacitor/CapacitorTests', '${PWD}/ios/Capacitor/TestsHostApp', '${PWD}/ios/Frameworks'], + excluded: ['${PWD}/ios/Tests/CapacitorTests', '${PWD}/ios/Frameworks'], }; From 3c3abb0d837aa93653c660f19808bd16e8404004 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 19 Aug 2026 19:21:58 -0500 Subject: [PATCH 18/42] Rename class_ to classRef in UIStatusBarManager swizzle --- .../UIStatusBarManager+CAPHandleTapAction.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ios/Sources/Capacitor/UIStatusBarManager+CAPHandleTapAction.swift b/ios/Sources/Capacitor/UIStatusBarManager+CAPHandleTapAction.swift index 08d3858189..663bbd9054 100644 --- a/ios/Sources/Capacitor/UIStatusBarManager+CAPHandleTapAction.swift +++ b/ios/Sources/Capacitor/UIStatusBarManager+CAPHandleTapAction.swift @@ -11,17 +11,17 @@ import ObjectiveC extension UIStatusBarManager { private static let swizzle: Void = { - let class_ = UIStatusBarManager.self + let classRef = UIStatusBarManager.self let originalSelector = Selector(("handleTapAction:")) let swizzledSelector = #selector(UIStatusBarManager.nofity_handleTapAction(_:)) - guard let originalMethod = class_getInstanceMethod(class_, originalSelector), - let swizzledMethod = class_getInstanceMethod(class_, swizzledSelector) else { + guard let originalMethod = class_getInstanceMethod(classRef, originalSelector), + let swizzledMethod = class_getInstanceMethod(classRef, swizzledSelector) else { return } let didAddMethod = class_addMethod( - class_, + classRef, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod) @@ -29,7 +29,7 @@ extension UIStatusBarManager { if didAddMethod { class_replaceMethod( - class_, + classRef, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod) From ffb8fa90632442c10bb7a4a1ec35e5fb9e76a83a Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 19 Aug 2026 20:24:07 -0500 Subject: [PATCH 19/42] Update iOS CI to macos-26-intel runner and Xcode 26.6 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 471e939ec8..3d77607173 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,7 +89,7 @@ jobs: - run: npm test working-directory: ./core test-ios: - runs-on: macos-15 + runs-on: macos-26-intel timeout-minutes: 30 needs: - setup @@ -97,7 +97,7 @@ jobs: strategy: matrix: xcode: - - /Applications/Xcode_26.0.app + - /Applications/Xcode_26.6.app steps: - run: sudo xcode-select --switch ${{ matrix.xcode }} - run: xcrun simctl list > /dev/null From 35c4b209d53734a0e93697d481a2ae9e3a66257c Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 19 Aug 2026 21:18:31 -0500 Subject: [PATCH 20/42] Update iOS simulator OS version to 26.5 in test script --- ios/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/package.json b/ios/package.json index 6283736249..711ecf080a 100644 --- a/ios/package.json +++ b/ios/package.json @@ -21,7 +21,7 @@ "scripts": { "verify": "npm run xc:build && npm run xc:test", "xc:build": "cd .. && xcodebuild build -scheme Capacitor-Package -destination 'generic/platform=iOS Simulator'", - "xc:test": "cd .. && xcodebuild test -scheme Capacitor-Package -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.0.1' -collect-test-diagnostics never" + "xc:test": "cd .. && xcodebuild test -scheme Capacitor-Package -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.5' -collect-test-diagnostics never" }, "peerDependencies": { "@capacitor/core": "^9.0.0-alpha.6" From 5b02e3b53821b7377e6c2da78b79c866c841552a Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 24 Aug 2026 09:41:34 -0500 Subject: [PATCH 21/42] Switch iOS CI runner from macos-26-intel to macos-26 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d77607173..a3d35a1919 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,7 +89,7 @@ jobs: - run: npm test working-directory: ./core test-ios: - runs-on: macos-26-intel + runs-on: macos-26 timeout-minutes: 30 needs: - setup From 9305c70e3632f3fb9b0b6b9b790c75fd057a4bee Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Fri, 12 Jun 2026 15:10:55 -0500 Subject: [PATCH 22/42] Starting on adding UIScene support From 9bcf38be9b6452aab3b627e2f0013122c384bbd0 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Fri, 12 Jun 2026 15:18:44 -0500 Subject: [PATCH 23/42] use UIScene lifecycle notifications From 383dce172fc8d989693c212c5c1c92c62a647554 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 15 Jun 2026 09:42:40 -0500 Subject: [PATCH 24/42] scene-aware openURL and universal link notifications From 6a26a6cabc8fd0442e57908dd0d12ac2fbf8694e Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 15 Jun 2026 09:56:41 -0500 Subject: [PATCH 25/42] remove TmpViewController From 2e14e5dc9ea283608edf9ecddbe0b2ff4173c2bd Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 22 Jun 2026 10:49:16 -0500 Subject: [PATCH 26/42] adding CapacitorView --- ios/Sources/Capacitor/CapacitorView.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 ios/Sources/Capacitor/CapacitorView.swift diff --git a/ios/Sources/Capacitor/CapacitorView.swift b/ios/Sources/Capacitor/CapacitorView.swift new file mode 100644 index 0000000000..fbbae46935 --- /dev/null +++ b/ios/Sources/Capacitor/CapacitorView.swift @@ -0,0 +1,16 @@ +// +// CapacitorView.swift +// Capacitor +// +// Created by Joseph Orlando Pender on 6/22/26. +// Copyright © 2026 Drifty Co. All rights reserved. +// +import SwiftUI + +public struct CapacitorView: UIViewControllerRepresentable { + public func makeUIViewController(context: Context) -> CAPBridgeViewController { + CAPBridgeViewController() + } + + public func updateUIViewController(_ vc: CAPBridgeViewController, context: Context) {} +} From d9cf1e11fc8a135aedc5b5d471856ea4cee83668 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 22 Jun 2026 11:19:17 -0500 Subject: [PATCH 27/42] updating templates for UIScene changes --- ios-pods-template/App/App/AppDelegate.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ios-pods-template/App/App/AppDelegate.swift b/ios-pods-template/App/App/AppDelegate.swift index 1dfbed0901..b0539b2b38 100644 --- a/ios-pods-template/App/App/AppDelegate.swift +++ b/ios-pods-template/App/App/AppDelegate.swift @@ -45,6 +45,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // tracking app url opens, make sure to keep this call return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler) } + + func application(_ application: UIApplication, + configurationForConnecting connectingSceneSession: UISceneSession, + options: UIScene.ConnectionOptions) -> UISceneConfiguration { + + let config = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + config.delegateClass = SceneDelegate.self + return config + } func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, From f5dbb1f14945c647284eea85777177c3473771f9 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 22 Jun 2026 12:20:02 -0500 Subject: [PATCH 28/42] remove CapacitorView from core --- ios/Sources/Capacitor/CapacitorView.swift | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 ios/Sources/Capacitor/CapacitorView.swift diff --git a/ios/Sources/Capacitor/CapacitorView.swift b/ios/Sources/Capacitor/CapacitorView.swift deleted file mode 100644 index fbbae46935..0000000000 --- a/ios/Sources/Capacitor/CapacitorView.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// CapacitorView.swift -// Capacitor -// -// Created by Joseph Orlando Pender on 6/22/26. -// Copyright © 2026 Drifty Co. All rights reserved. -// -import SwiftUI - -public struct CapacitorView: UIViewControllerRepresentable { - public func makeUIViewController(context: Context) -> CAPBridgeViewController { - CAPBridgeViewController() - } - - public func updateUIViewController(_ vc: CAPBridgeViewController, context: Context) {} -} From b3a1fe0b5371cfe9dba28742c63e60c35f25f50a Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 22 Jun 2026 12:25:54 -0500 Subject: [PATCH 29/42] Add SwiftUI App-struct support to SceneDelegateProxy and bridge --- ios-pods-template/App/App/AppDelegate.swift | 4 +- ios-spm-template/App/App/SceneDelegate.swift | 2 +- .../Capacitor/CAPSceneDelegateProxy.swift | 140 ++++++++++++++++-- 3 files changed, 128 insertions(+), 18 deletions(-) diff --git a/ios-pods-template/App/App/AppDelegate.swift b/ios-pods-template/App/App/AppDelegate.swift index b0539b2b38..c3f1ce4ece 100644 --- a/ios-pods-template/App/App/AppDelegate.swift +++ b/ios-pods-template/App/App/AppDelegate.swift @@ -45,11 +45,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // tracking app url opens, make sure to keep this call return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler) } - + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - + let config = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) config.delegateClass = SceneDelegate.self return config diff --git a/ios-spm-template/App/App/SceneDelegate.swift b/ios-spm-template/App/App/SceneDelegate.swift index f352e1e959..0a82aa3ce2 100644 --- a/ios-spm-template/App/App/SceneDelegate.swift +++ b/ios-spm-template/App/App/SceneDelegate.swift @@ -1,5 +1,5 @@ -import UIKit import Capacitor +import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? diff --git a/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift b/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift index 1c4514d5f6..3fd7e4f2f7 100644 --- a/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift +++ b/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift @@ -15,7 +15,10 @@ public class SceneDelegateProxy: NSObject, UISceneDelegate { public private(set) var lastURL: URL? - public func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + public func scene( + _ scene: UIScene, willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { NotificationCenter.default.post(name: .capacitorSceneWillConnect, object: scene) // Plugins haven't loaded yet on a cold start, so notifications posted here are @@ -42,34 +45,141 @@ public class SceneDelegateProxy: NSObject, UISceneDelegate { let options = Self.openURLOptions(from: context.options) // Capacitor 8 backwards compat - NotificationCenter.default.post(name: .capacitorOpenURL, object: [ - "url": context.url, + NotificationCenter.default.post( + name: .capacitorOpenURL, + object: [ + "url": context.url, + "options": options + ]) + + NotificationCenter.default.post( + name: .capacitorSceneOpenURL, object: scene, + userInfo: [ + "url": context.url, + "options": options + ]) + } + } + + public func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { + guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let url = userActivity.webpageURL + else { + return + } + lastURL = url + ApplicationDelegateProxy.shared.lastURL = url + + // Capacitor 8 backwards compat + NotificationCenter.default.post( + name: .capacitorOpenUniversalLink, + object: [ + "url": url + ]) + + NotificationCenter.default.post( + name: .capacitorSceneOpenUniversalLink, object: scene, + userInfo: [ + "url": url + ]) + } + + /// Routes a URL into Capacitor's open-URL handlers from a SwiftUI App-struct app. + /// + /// This is the recommended integration point for apps whose root is a `SwiftUI.App` + /// and which therefore do not declare an explicit `UISceneDelegate` subclass. + /// Call it from the `.onOpenURL` modifier inside the scene body: + /// + /// ```swift + /// WindowGroup { + /// CapacitorView() + /// .onOpenURL { url in + /// SceneDelegateProxy.shared.handle(openURL: url) + /// } + /// } + /// ``` + /// + /// Posts the same notifications as the `scene(_:openURLContexts:)` protocol path — + /// both `.capacitorOpenURL` (Capacitor 8 back-compat payload) and + /// `.capacitorSceneOpenURL` (scene-aware, with the resolved scene as the notification + /// object and the URL in `userInfo`). + /// + /// - Parameters: + /// - openURL: The URL to route. + /// - scene: The scene that received the URL. SwiftUI's `.onOpenURL` does not + /// surface a scene reference, so this defaults to `nil`; when `nil`, the active + /// foreground `UIWindowScene` is resolved from + /// `UIApplication.shared.connectedScenes`. For single-scene apps (the Phase 1 + /// default) this is unambiguous; multi-scene URL routing is Phase 2. + public func handle(openURL: URL, scene: UIScene? = nil) { + let targetScene = scene ?? Self.activeForegroundScene() + lastURL = openURL + let options: [UIApplication.OpenURLOptionsKey: Any] = [:] + + // Capacitor 8 backwards compat + NotificationCenter.default.post( + name: .capacitorOpenURL, + object: [ + "url": openURL, "options": options ]) - NotificationCenter.default.post(name: .capacitorSceneOpenURL, object: scene, userInfo: [ - "url": context.url, + NotificationCenter.default.post( + name: .capacitorSceneOpenURL, object: targetScene, + userInfo: [ + "url": openURL, "options": options ]) - } } - public func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { + /// Routes a browsing-web `NSUserActivity` into Capacitor's universal-link handlers + /// from a SwiftUI App-struct app. + /// + /// This is the recommended integration point for SwiftUI App-struct apps. Call it + /// from the `.onContinueUserActivity` modifier inside the scene body: + /// + /// ```swift + /// WindowGroup { + /// CapacitorView() + /// .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in + /// SceneDelegateProxy.shared.handle(userActivity: activity) + /// } + /// } + /// ``` + /// + /// Only activities with `activityType == NSUserActivityTypeBrowsingWeb` and a + /// non-nil `webpageURL` produce notifications; other activity types are silently + /// ignored, matching the `scene(_:continue:)` protocol path. When a notification is + /// produced, both `.capacitorOpenUniversalLink` (Capacitor 8 back-compat payload) + /// and `.capacitorSceneOpenUniversalLink` (scene-aware) are posted. + /// + /// - Parameters: + /// - userActivity: The activity to inspect. + /// - scene: The scene that received the activity. SwiftUI does not surface a + /// scene reference here either, so this defaults to `nil`; when `nil`, the + /// active foreground `UIWindowScene` is resolved from + /// `UIApplication.shared.connectedScenes`. + public func handle(userActivity: NSUserActivity, scene: UIScene? = nil) { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, - let url = userActivity.webpageURL else { + let url = userActivity.webpageURL + else { return } + let targetScene = scene ?? Self.activeForegroundScene() lastURL = url - ApplicationDelegateProxy.shared.lastURL = url // Capacitor 8 backwards compat - NotificationCenter.default.post(name: .capacitorOpenUniversalLink, object: [ - "url": url - ]) + NotificationCenter.default.post( + name: .capacitorOpenUniversalLink, + object: [ + "url": url + ]) - NotificationCenter.default.post(name: .capacitorSceneOpenUniversalLink, object: scene, userInfo: [ - "url": url - ]) + NotificationCenter.default.post( + name: .capacitorSceneOpenUniversalLink, object: targetScene, + userInfo: [ + "url": url + ]) } private static func openURLOptions(from sceneOptions: UIScene.OpenURLOptions) -> [UIApplication.OpenURLOptionsKey: Any] { From 61ce0a3f4e94f7164f95bc0bafd455dad62fb852 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of jcesarmobile)" Date: Fri, 19 Jun 2026 09:01:59 +0000 Subject: [PATCH 30/42] Release 8.4.1 --- CHANGELOG.md | 8 ++++++++ android/CHANGELOG.md | 4 ++++ ios/CHANGELOG.md | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23c60e63ed..865f96f3e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. # [9.0.0-alpha.6](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.5...9.0.0-alpha.6) (2026-07-14) +## [8.4.1](https://github.com/ionic-team/capacitor/compare/8.4.0...8.4.1) (2026-06-19) + +### Bug Fixes + +- **cli:** make SPM dependency patch work on prereleases ([#8508](https://github.com/ionic-team/capacitor/issues/8508)) ([6048e90](https://github.com/ionic-team/capacitor/commit/6048e90171afa0229a3c25b52a23c377c6bb804c)) +- **cli:** patch Capacitor SPM dependency version in plugins ([#8492](https://github.com/ionic-team/capacitor/issues/8492)) ([28bb2c6](https://github.com/ionic-team/capacitor/commit/28bb2c687069dfdd6aa7abc866004a1c6388d103)) + +# [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) ### Bug Fixes diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 0a30c257f2..b08ea428a1 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -31,6 +31,10 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.4.0...9.0.0-alpha.3) (2026-06-02) +## [8.4.1](https://github.com/ionic-team/capacitor/compare/8.4.0...8.4.1) (2026-06-19) + +**Note:** Version bump only for package @capacitor/android + # [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) ### Bug Fixes diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index c0eb3e1de5..ff21df1e13 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -27,6 +27,10 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.4.0...9.0.0-alpha.3) (2026-06-02) +## [8.4.1](https://github.com/ionic-team/capacitor/compare/8.4.0...8.4.1) (2026-06-19) + +**Note:** Version bump only for package @capacitor/ios + # [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) ### Features From e59bbefaf2fccdd50c2b3e0e0816abf26bab4225 Mon Sep 17 00:00:00 2001 From: Chace Daniels Date: Mon, 29 Jun 2026 11:00:49 -0500 Subject: [PATCH 31/42] fix(ios): mirror deep-link URL to ApplicationDelegateProxy for getLaunchUrl From 1644881f199d9d321866ada2810d2f64802d6a5d Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 22 Jul 2026 10:19:59 -0500 Subject: [PATCH 32/42] fmt --- .../cordova/MockCordovaWebViewImpl.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java b/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java index 7d6694866e..72136c496d 100644 --- a/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java +++ b/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java @@ -70,12 +70,14 @@ public CapacitorEvalBridgeMode(WebView webView, CordovaInterface cordova) { @Override public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) { - cordova.getActivity().runOnUiThread(() -> { - String js = queue.popAndEncodeAsJs(); - if (js != null) { - webView.evaluateJavascript(js, null); - } - }); + cordova + .getActivity() + .runOnUiThread(() -> { + String js = queue.popAndEncodeAsJs(); + if (js != null) { + webView.evaluateJavascript(js, null); + } + }); } } From 4522cdd4412873ce4a5f8806b57ed3e7014837d0 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 22 Jul 2026 11:44:28 -0500 Subject: [PATCH 33/42] add SceneDelegate to TestsHostApp From fa64ab89fd46961d477adc9214226f2df88db979 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Wed, 22 Jul 2026 11:44:55 -0500 Subject: [PATCH 34/42] fmt --- .../cordova/MockCordovaWebViewImpl.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java b/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java index 72136c496d..7d6694866e 100644 --- a/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java +++ b/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java @@ -70,14 +70,12 @@ public CapacitorEvalBridgeMode(WebView webView, CordovaInterface cordova) { @Override public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) { - cordova - .getActivity() - .runOnUiThread(() -> { - String js = queue.popAndEncodeAsJs(); - if (js != null) { - webView.evaluateJavascript(js, null); - } - }); + cordova.getActivity().runOnUiThread(() -> { + String js = queue.popAndEncodeAsJs(); + if (js != null) { + webView.evaluateJavascript(js, null); + } + }); } } From 12222a406b99041191da78c55b4ca12dd2272426 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 27 Jul 2026 12:47:18 -0500 Subject: [PATCH 35/42] Initialize window with CAPBridgeViewController in SceneDelegate From b6be36d614bbe1d24dc9cafa3d40d97f6a1f70c0 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 27 Jul 2026 12:54:37 -0500 Subject: [PATCH 36/42] Defer scene connect URL/activity delivery until plugins load From eba574dafd6377edfb58939fe5a1e87d43b7cf0e Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 27 Jul 2026 12:56:44 -0500 Subject: [PATCH 37/42] Remove Phase 2 multi-window TODO stubs from SceneDelegateProxy From fc93f592428863617e05151f088b3ecb8b5c1dbb Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Mon, 22 Jun 2026 13:05:21 -0500 Subject: [PATCH 38/42] Adding SwiftUI components to pods template --- .../App/App.xcodeproj/project.pbxproj | 28 +++----- ios-pods-template/App/App/App.swift | 18 +++++ ios-pods-template/App/App/AppDelegate.swift | 67 ------------------- .../App/App/Base.lproj/Main.storyboard | 19 ------ ios-pods-template/App/App/CapacitorView.swift | 10 +++ ios-pods-template/App/App/Info.plist | 18 +---- ios-pods-template/App/App/SceneDelegate.swift | 24 ------- 7 files changed, 37 insertions(+), 147 deletions(-) create mode 100644 ios-pods-template/App/App/App.swift delete mode 100644 ios-pods-template/App/App/AppDelegate.swift delete mode 100644 ios-pods-template/App/App/Base.lproj/Main.storyboard create mode 100644 ios-pods-template/App/App/CapacitorView.swift delete mode 100644 ios-pods-template/App/App/SceneDelegate.swift diff --git a/ios-pods-template/App/App.xcodeproj/project.pbxproj b/ios-pods-template/App/App.xcodeproj/project.pbxproj index 026cbd4583..2ebc501505 100644 --- a/ios-pods-template/App/App.xcodeproj/project.pbxproj +++ b/ios-pods-template/App/App.xcodeproj/project.pbxproj @@ -9,12 +9,11 @@ /* Begin PBXBuildFile section */ 2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; }; 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; }; - 504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; }; - 504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; }; 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; - 9582B6852FE996820072D4E8 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9582B6842FE996800072D4E8 /* SceneDelegate.swift */; }; + 9582B68A2FE9ABF30072D4E8 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9582B6892FE9ABF10072D4E8 /* App.swift */; }; + 9582B68C2FE9ACC30072D4E8 /* CapacitorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9582B68B2FE9ACBE0072D4E8 /* CapacitorView.swift */; }; A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; }; /* End PBXBuildFile section */ @@ -22,13 +21,12 @@ 2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = ""; }; 50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = ""; }; 504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; - 9582B6842FE996800072D4E8 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 9582B6892FE9ABF10072D4E8 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; + 9582B68B2FE9ACBE0072D4E8 /* CapacitorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorView.swift; sourceTree = ""; }; AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; }; AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = ""; }; FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = ""; }; @@ -75,10 +73,9 @@ 504EC3061FED79650016851F /* App */ = { isa = PBXGroup; children = ( - 9582B6842FE996800072D4E8 /* SceneDelegate.swift */, + 9582B68B2FE9ACBE0072D4E8 /* CapacitorView.swift */, + 9582B6892FE9ABF10072D4E8 /* App.swift */, 50379B222058CBB4000EE86E /* capacitor.config.json */, - 504EC3071FED79650016851F /* AppDelegate.swift */, - 504EC30B1FED79650016851F /* Main.storyboard */, 504EC30E1FED79650016851F /* Assets.xcassets */, 504EC3101FED79650016851F /* LaunchScreen.storyboard */, 504EC3131FED79650016851F /* Info.plist */, @@ -164,7 +161,6 @@ 50B271D11FEDC1A000F3C39B /* public in Resources */, 504EC30F1FED79650016851F /* Assets.xcassets in Resources */, 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */, - 504EC30D1FED79650016851F /* Main.storyboard in Resources */, 2FAD9763203C412B000D30F8 /* config.xml in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -212,22 +208,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, - 9582B6852FE996820072D4E8 /* SceneDelegate.swift in Sources */, + 9582B68A2FE9ABF30072D4E8 /* App.swift in Sources */, + 9582B68C2FE9ACC30072D4E8 /* CapacitorView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ - 504EC30B1FED79650016851F /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 504EC30C1FED79650016851F /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; 504EC3101FED79650016851F /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( diff --git a/ios-pods-template/App/App/App.swift b/ios-pods-template/App/App/App.swift new file mode 100644 index 0000000000..5255bdb611 --- /dev/null +++ b/ios-pods-template/App/App/App.swift @@ -0,0 +1,18 @@ +import SwiftUI +import Capacitor + +@main +struct CapacitorApp: App { + var body: some Scene { + WindowGroup { + CapacitorView() + .ignoresSafeArea() + .onOpenURL { url in + SceneDelegateProxy.shared.handle(openURL: url) + } + .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in + SceneDelegateProxy.shared.handle(userActivity: activity) + } + } + } +} diff --git a/ios-pods-template/App/App/AppDelegate.swift b/ios-pods-template/App/App/AppDelegate.swift deleted file mode 100644 index c3f1ce4ece..0000000000 --- a/ios-pods-template/App/App/AppDelegate.swift +++ /dev/null @@ -1,67 +0,0 @@ -import UIKit -import Capacitor - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } - - func applicationWillResignActive(_ application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. - } - - func applicationDidEnterBackground(_ application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - } - - func applicationWillEnterForeground(_ application: UIApplication) { - // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. - } - - func applicationDidBecomeActive(_ application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } - - func applicationWillTerminate(_ application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - } - - func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { - // Called when the app was launched with a url. Feel free to add additional processing here, - // but if you want the App API to support tracking app url opens, make sure to keep this call - return ApplicationDelegateProxy.shared.application(app, open: url, options: options) - } - - func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { - // Called when the app was launched with an activity, including Universal Links. - // Feel free to add additional processing here, but if you want the App API to support - // tracking app url opens, make sure to keep this call - return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler) - } - - func application(_ application: UIApplication, - configurationForConnecting connectingSceneSession: UISceneSession, - options: UIScene.ConnectionOptions) -> UISceneConfiguration { - - let config = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - config.delegateClass = SceneDelegate.self - return config - } - - func application(_ application: UIApplication, - configurationForConnecting connectingSceneSession: UISceneSession, - options: UIScene.ConnectionOptions) -> UISceneConfiguration { - - let config = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - config.delegateClass = SceneDelegate.self - return config - } - -} diff --git a/ios-pods-template/App/App/Base.lproj/Main.storyboard b/ios-pods-template/App/App/Base.lproj/Main.storyboard deleted file mode 100644 index b44df7be8f..0000000000 --- a/ios-pods-template/App/App/Base.lproj/Main.storyboard +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/ios-pods-template/App/App/CapacitorView.swift b/ios-pods-template/App/App/CapacitorView.swift new file mode 100644 index 0000000000..12575c875b --- /dev/null +++ b/ios-pods-template/App/App/CapacitorView.swift @@ -0,0 +1,10 @@ +import SwiftUI +import Capacitor + +public struct CapacitorView: UIViewControllerRepresentable { + public func makeUIViewController(context: Context) -> CAPBridgeViewController { + CAPBridgeViewController() + } + + public func updateUIViewController(_ vc: CAPBridgeViewController, context: Context) {} +} diff --git a/ios-pods-template/App/App/Info.plist b/ios-pods-template/App/App/Info.plist index f8c8a57659..23320dae5e 100644 --- a/ios-pods-template/App/App/Info.plist +++ b/ios-pods-template/App/App/Info.plist @@ -25,26 +25,10 @@ UIApplicationSceneManifest UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate - UISceneStoryboardFile - Main - - - + UILaunchStoryboardName LaunchScreen - UIMainStoryboardFile - Main UIRequiredDeviceCapabilities armv7 diff --git a/ios-pods-template/App/App/SceneDelegate.swift b/ios-pods-template/App/App/SceneDelegate.swift deleted file mode 100644 index f352e1e959..0000000000 --- a/ios-pods-template/App/App/SceneDelegate.swift +++ /dev/null @@ -1,24 +0,0 @@ -import UIKit -import Capacitor - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - guard let windowScene = scene as? UIWindowScene else { return } - - window = UIWindow(windowScene: windowScene) - window?.rootViewController = CAPBridgeViewController() - window?.makeKeyAndVisible() - - SceneDelegateProxy.shared.scene(scene, willConnectTo: session, options: connectionOptions) - } - - func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { - SceneDelegateProxy.shared.scene(scene, openURLContexts: URLContexts) - } - - func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { - SceneDelegateProxy.shared.scene(scene, continue: userActivity) - } -} From 48e01aa7325bedfd134220e9cd1a4f088e7eb897 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Fri, 7 Aug 2026 11:48:21 -0500 Subject: [PATCH 39/42] Add AppDelegate lifecycle integration to iOS pods template for legacy usage --- ios-pods-template/App/App.xcodeproj/project.pbxproj | 4 ++++ ios-pods-template/App/App/App.swift | 2 ++ ios-pods-template/App/App/AppDelegate.swift | 9 +++++++++ 3 files changed, 15 insertions(+) create mode 100644 ios-pods-template/App/App/AppDelegate.swift diff --git a/ios-pods-template/App/App.xcodeproj/project.pbxproj b/ios-pods-template/App/App.xcodeproj/project.pbxproj index 2ebc501505..88d1ad2041 100644 --- a/ios-pods-template/App/App.xcodeproj/project.pbxproj +++ b/ios-pods-template/App/App.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; + 952C1C64302642D6000D0FF6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952C1C63302642D3000D0FF6 /* AppDelegate.swift */; }; 9582B68A2FE9ABF30072D4E8 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9582B6892FE9ABF10072D4E8 /* App.swift */; }; 9582B68C2FE9ACC30072D4E8 /* CapacitorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9582B68B2FE9ACBE0072D4E8 /* CapacitorView.swift */; }; A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; }; @@ -25,6 +26,7 @@ 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; + 952C1C63302642D3000D0FF6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 9582B6892FE9ABF10072D4E8 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; 9582B68B2FE9ACBE0072D4E8 /* CapacitorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorView.swift; sourceTree = ""; }; AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -73,6 +75,7 @@ 504EC3061FED79650016851F /* App */ = { isa = PBXGroup; children = ( + 952C1C63302642D3000D0FF6 /* AppDelegate.swift */, 9582B68B2FE9ACBE0072D4E8 /* CapacitorView.swift */, 9582B6892FE9ABF10072D4E8 /* App.swift */, 50379B222058CBB4000EE86E /* capacitor.config.json */, @@ -208,6 +211,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 952C1C64302642D6000D0FF6 /* AppDelegate.swift in Sources */, 9582B68A2FE9ABF30072D4E8 /* App.swift in Sources */, 9582B68C2FE9ACC30072D4E8 /* CapacitorView.swift in Sources */, ); diff --git a/ios-pods-template/App/App/App.swift b/ios-pods-template/App/App/App.swift index 5255bdb611..23b0a8cc18 100644 --- a/ios-pods-template/App/App/App.swift +++ b/ios-pods-template/App/App/App.swift @@ -3,6 +3,8 @@ import Capacitor @main struct CapacitorApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + var body: some Scene { WindowGroup { CapacitorView() diff --git a/ios-pods-template/App/App/AppDelegate.swift b/ios-pods-template/App/App/AppDelegate.swift new file mode 100644 index 0000000000..99ba4ab96e --- /dev/null +++ b/ios-pods-template/App/App/AppDelegate.swift @@ -0,0 +1,9 @@ +import Foundation +import UIKit + +class AppDelegate: NSObject, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + return true + } +} + From 960d6bd9514e095a6035c27709b25077c7685011 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Fri, 7 Aug 2026 16:45:17 -0500 Subject: [PATCH 40/42] Adding SwiftUI to SPM template --- .../App/App.xcodeproj/project.pbxproj | 12 ++++-- ios-spm-template/App/App/App.swift | 21 ++++++++++ ios-spm-template/App/App/AppDelegate.swift | 39 +------------------ ios-spm-template/App/App/CapacitorView.swift | 11 ++++++ ios-spm-template/App/App/SceneDelegate.swift | 24 ------------ 5 files changed, 41 insertions(+), 66 deletions(-) create mode 100644 ios-spm-template/App/App/App.swift create mode 100644 ios-spm-template/App/App/CapacitorView.swift delete mode 100644 ios-spm-template/App/App/SceneDelegate.swift diff --git a/ios-spm-template/App/App.xcodeproj/project.pbxproj b/ios-spm-template/App/App.xcodeproj/project.pbxproj index 87225f719f..269623b05a 100644 --- a/ios-spm-template/App/App.xcodeproj/project.pbxproj +++ b/ios-spm-template/App/App.xcodeproj/project.pbxproj @@ -15,7 +15,8 @@ 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; - 9582B6832FE993A70072D4E8 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9582B6822FE993A50072D4E8 /* SceneDelegate.swift */; }; + 9575E845302683A8004C1ED7 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9575E844302683A6004C1ED7 /* App.swift */; }; + 9575E847302683C1004C1ED7 /* CapacitorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9575E846302683BC004C1ED7 /* CapacitorView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -28,7 +29,8 @@ 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; - 9582B6822FE993A50072D4E8 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 9575E844302683A6004C1ED7 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; + 9575E846302683BC004C1ED7 /* CapacitorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorView.swift; sourceTree = ""; }; 958DCC722DB07C7200EA8C5F /* debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = debug.xcconfig; path = ../debug.xcconfig; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ @@ -64,7 +66,8 @@ 504EC3061FED79650016851F /* App */ = { isa = PBXGroup; children = ( - 9582B6822FE993A50072D4E8 /* SceneDelegate.swift */, + 9575E846302683BC004C1ED7 /* CapacitorView.swift */, + 9575E844302683A6004C1ED7 /* App.swift */, 50379B222058CBB4000EE86E /* capacitor.config.json */, 504EC3071FED79650016851F /* AppDelegate.swift */, 504EC30B1FED79650016851F /* Main.storyboard */, @@ -158,8 +161,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9575E845302683A8004C1ED7 /* App.swift in Sources */, 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, - 9582B6832FE993A70072D4E8 /* SceneDelegate.swift in Sources */, + 9575E847302683C1004C1ED7 /* CapacitorView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios-spm-template/App/App/App.swift b/ios-spm-template/App/App/App.swift new file mode 100644 index 0000000000..fec128fcef --- /dev/null +++ b/ios-spm-template/App/App/App.swift @@ -0,0 +1,21 @@ +import SwiftUI +import Capacitor + +@main +struct CapacitorApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + + var body: some Scene { + WindowGroup { + CapacitorView() + .ignoresSafeArea() + .onOpenURL { url in + SceneDelegateProxy.shared.handle(openURL: url) + } + .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in + SceneDelegateProxy.shared.handle(userActivity: activity) + } + } + } +} + diff --git a/ios-spm-template/App/App/AppDelegate.swift b/ios-spm-template/App/App/AppDelegate.swift index 7fe69b516a..5fb4c09fbe 100644 --- a/ios-spm-template/App/App/AppDelegate.swift +++ b/ios-spm-template/App/App/AppDelegate.swift @@ -1,44 +1,7 @@ import UIKit -import Capacitor - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? +class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. return true } - - func applicationWillResignActive(_ application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. - } - - func applicationDidEnterBackground(_ application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - } - - func applicationWillEnterForeground(_ application: UIApplication) { - // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. - } - - func applicationDidBecomeActive(_ application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } - - func applicationWillTerminate(_ application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - } - - func application(_ application: UIApplication, - configurationForConnecting connectingSceneSession: UISceneSession, - options: UIScene.ConnectionOptions) -> UISceneConfiguration { - let config = UISceneConfiguration(name: "Default Configuration", - sessionRole: connectingSceneSession.role) - config.delegateClass = SceneDelegate.self - return config - } } diff --git a/ios-spm-template/App/App/CapacitorView.swift b/ios-spm-template/App/App/CapacitorView.swift new file mode 100644 index 0000000000..bae562243f --- /dev/null +++ b/ios-spm-template/App/App/CapacitorView.swift @@ -0,0 +1,11 @@ +import SwiftUI +import Capacitor + +public struct CapacitorView: UIViewControllerRepresentable { + public func makeUIViewController(context: Context) -> CAPBridgeViewController { + CAPBridgeViewController() + } + + public func updateUIViewController(_ vc: CAPBridgeViewController, context: Context) {} +} + diff --git a/ios-spm-template/App/App/SceneDelegate.swift b/ios-spm-template/App/App/SceneDelegate.swift deleted file mode 100644 index 0a82aa3ce2..0000000000 --- a/ios-spm-template/App/App/SceneDelegate.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Capacitor -import UIKit - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - guard let windowScene = scene as? UIWindowScene else { return } - - window = UIWindow(windowScene: windowScene) - window?.rootViewController = CAPBridgeViewController() - window?.makeKeyAndVisible() - - SceneDelegateProxy.shared.scene(scene, willConnectTo: session, options: connectionOptions) - } - - func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { - SceneDelegateProxy.shared.scene(scene, openURLContexts: URLContexts) - } - - func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { - SceneDelegateProxy.shared.scene(scene, continue: userActivity) - } -} From 5a44888774325f00a7169779b2760f8e8e1c7822 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 25 Aug 2026 09:31:36 -0500 Subject: [PATCH 41/42] Add helper to find the active foreground UIWindowScene --- ios/Sources/Capacitor/CAPSceneDelegateProxy.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift b/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift index 3fd7e4f2f7..f03fb5c887 100644 --- a/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift +++ b/ios/Sources/Capacitor/CAPSceneDelegateProxy.swift @@ -182,6 +182,15 @@ public class SceneDelegateProxy: NSObject, UISceneDelegate { ]) } + private static func activeForegroundScene() -> UIWindowScene? { + let scenes = UIApplication.shared.connectedScenes + if let active = scenes.first(where: { $0.activationState == .foregroundActive }) + as? UIWindowScene { + return active + } + return scenes.first(where: { $0.activationState == .foregroundInactive }) as? UIWindowScene + } + private static func openURLOptions(from sceneOptions: UIScene.OpenURLOptions) -> [UIApplication.OpenURLOptionsKey: Any] { var options: [UIApplication.OpenURLOptionsKey: Any] = [:] if let sourceApplication = sceneOptions.sourceApplication { From e6cf857dc6c439c4ba4fc77d685f1c32b10fefc3 Mon Sep 17 00:00:00 2001 From: Joseph Pender Date: Tue, 25 Aug 2026 10:35:37 -0500 Subject: [PATCH 42/42] Remove unused Capacitor-Bridging-Header.h --- .../Capacitor/Capacitor-Bridging-Header.h | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 ios/Sources/Capacitor/Capacitor-Bridging-Header.h diff --git a/ios/Sources/Capacitor/Capacitor-Bridging-Header.h b/ios/Sources/Capacitor/Capacitor-Bridging-Header.h deleted file mode 100644 index d70adea269..0000000000 --- a/ios/Sources/Capacitor/Capacitor-Bridging-Header.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Capacitor-Bridging-Header.h -// Capacitor -// -// Bridging header for Swift code to access Objective-C types from CapacitorC target -// - -#ifndef Capacitor_Bridging_Header_h -#define Capacitor_Bridging_Header_h - -#import -#import -#import -#import - -#import -#import - -#endif /* Capacitor_Bridging_Header_h */