From 639d406789fb77f60cb355a5a9e791864580249b Mon Sep 17 00:00:00 2001 From: jworreth Date: Tue, 17 Feb 2026 21:52:36 +0100 Subject: [PATCH 01/69] fix!: replace deprecated `@UIApplicationMain` with `@main` (#8346) Co-authored-by: jwo Co-authored-by: Mark Anderson --- ios-pods-template/App/App/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios-pods-template/App/App/AppDelegate.swift b/ios-pods-template/App/App/AppDelegate.swift index c3cd83b5c0..0b31ad6866 100644 --- a/ios-pods-template/App/App/AppDelegate.swift +++ b/ios-pods-template/App/App/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Capacitor -@UIApplicationMain +@main class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? From d5c129ff938b4b798d9ce112630cf10b92ad293d Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 18 Feb 2026 15:13:52 +0100 Subject: [PATCH 02/69] fix(spm-template)!: replace deprecated @UIApplicationMain with @main (#8349) --- ios-spm-template/App/App/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios-spm-template/App/App/AppDelegate.swift b/ios-spm-template/App/App/AppDelegate.swift index c3cd83b5c0..0b31ad6866 100644 --- a/ios-spm-template/App/App/AppDelegate.swift +++ b/ios-spm-template/App/App/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Capacitor -@UIApplicationMain +@main class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? From 0594ac541ea07817e0aa298e847097d89dfd6094 Mon Sep 17 00:00:00 2001 From: Terence Honles Date: Mon, 23 Feb 2026 13:14:03 +0100 Subject: [PATCH 03/69] feat(android-template)!: update Android template to support AGP 9.0 (#8318) --- android-template/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-template/app/build.gradle b/android-template/app/build.gradle index df6c4d55a1..363eb2f77d 100644 --- a/android-template/app/build.gradle +++ b/android-template/app/build.gradle @@ -19,7 +19,7 @@ android { buildTypes { release { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } From bb5569848d305f6f05a2de69bdc7ce682a469031 Mon Sep 17 00:00:00 2001 From: Pedro Bilro Date: Tue, 24 Feb 2026 13:00:29 +0000 Subject: [PATCH 04/69] chore(cli)!: Remove unused livereload method and parameter (#8350) --- cli/src/util/livereload.ts | 40 ++------------------------------------ 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/cli/src/util/livereload.ts b/cli/src/util/livereload.ts index c3b15ee9fa..6aceb166ad 100644 --- a/cli/src/util/livereload.ts +++ b/cli/src/util/livereload.ts @@ -112,13 +112,7 @@ class CapLiveReload { return !all.length ? loopback(family) : all[0]; } - // TODO remove on next major as it's unused - async editExtConfigForLiveReload( - config: Config, - platformName: string, - options: RunCommandOptions, - rootConfigChange = false, - ): Promise { + async editCapConfigForLiveReload(config: Config, platformName: string, options: RunCommandOptions): Promise { const platformAbsPath = platformName == config.ios.name ? config.ios.nativeTargetDirAbs @@ -126,37 +120,7 @@ class CapLiveReload { ? config.android.assetsDirAbs : null; if (platformAbsPath == null) throw new Error('Platform not found.'); - const capConfigPath = rootConfigChange - ? config.app.extConfigFilePath - : join(platformAbsPath, 'capacitor.config.json'); - - const configJson = { ...config.app.extConfig }; - this.configJsonToRevertTo.json = JSON.stringify(configJson, null, 2); - this.configJsonToRevertTo.platformPath = capConfigPath; - const url = `http://${options.host}:${options.port}`; - configJson.server = { - url, - }; - return configJson; - } - - // TODO remove rootConfigChange param on next major as it's unused - async editCapConfigForLiveReload( - config: Config, - platformName: string, - options: RunCommandOptions, - rootConfigChange = false, - ): Promise { - const platformAbsPath = - platformName == config.ios.name - ? config.ios.nativeTargetDirAbs - : platformName == config.android.name - ? config.android.assetsDirAbs - : null; - if (platformAbsPath == null) throw new Error('Platform not found.'); - const capConfigPath = rootConfigChange - ? config.app.extConfigFilePath - : join(platformAbsPath, 'capacitor.config.json'); + const capConfigPath = join(platformAbsPath, 'capacitor.config.json'); const configJson = readJSONSync(capConfigPath); this.configJsonToRevertTo.json = JSON.stringify(configJson, null, 2); From 07439cf63cc6ecc366eba9a741e62fc252cf6596 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 25 Feb 2026 20:02:16 +0100 Subject: [PATCH 05/69] refactor(ios)!: remove deprecated tmpWindow/TmpViewController (#8362) --- ios/Capacitor/Capacitor.xcodeproj/project.pbxproj | 4 ---- ios/Capacitor/Capacitor/CapacitorBridge.swift | 7 ------- ios/Capacitor/Capacitor/TmpViewController.swift | 8 -------- 3 files changed, 19 deletions(-) delete mode 100644 ios/Capacitor/Capacitor/TmpViewController.swift diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index b060d9f7d0..ed784d5bdd 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -52,7 +52,6 @@ 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 */; }; - 62959B402524DA7800A3D7F1 /* TmpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B0E2524DA7700A3D7F1 /* TmpViewController.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 */; }; @@ -204,7 +203,6 @@ 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 = ""; }; - 62959B0E2524DA7700A3D7F1 /* TmpViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TmpViewController.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 = ""; }; @@ -395,7 +393,6 @@ 6214934625509C3F006C36F9 /* CAPInstanceConfiguration.swift */, 62959B082524DA7700A3D7F1 /* CAPLog.swift */, 62959AE72524DA7700A3D7F1 /* CAPFile.swift */, - 62959B0E2524DA7700A3D7F1 /* TmpViewController.swift */, 62959B102524DA7700A3D7F1 /* DocLinks.swift */, 62959B152524DA7700A3D7F1 /* CAPNotifications.swift */, 62959B072524DA7700A3D7F1 /* CapacitorExtension.swift */, @@ -710,7 +707,6 @@ A7D9312F2B23710300FF59A2 /* JSValueDecoder.swift in Sources */, 62959B362524DA7800A3D7F1 /* CAPBridgeViewController.swift in Sources */, 621ECCB72542045900D3D615 /* CAPBridgedJSTypes.m in Sources */, - 62959B402524DA7800A3D7F1 /* TmpViewController.swift in Sources */, 621ECCD6254205BD00D3D615 /* CAPBridgeProtocol.swift in Sources */, 62D43AF02581817500673C24 /* WKWebView+Capacitor.swift in Sources */, 62959B432524DA7800A3D7F1 /* Data+Capacitor.swift in Sources */, diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index 7b8b9b99dc..3a186d4e89 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -93,10 +93,6 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { } } } - @available(*, deprecated, message: "obsolete") - var tmpWindow: UIWindow? - @available(*, deprecated, message: "obsolete") - static let tmpVCAppeared = Notification(name: Notification.Name(rawValue: "tmpViewControllerAppeared")) public static let capacitorSite = "https://capacitorjs.com/" public static let fileStartIdentifier = "/_capacitor_file_" public static let httpInterceptorStartIdentifier = "/_capacitor_http_interceptor_" @@ -223,9 +219,6 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { setupCordovaCompatibility() exportMiscJS() canInjectJS = false - observers.append(NotificationCenter.default.addObserver(forName: type(of: self).tmpVCAppeared.name, object: .none, queue: .none) { [weak self] _ in - self?.tmpWindow = nil - }) self.setupWebDebugging(configuration: configuration) } diff --git a/ios/Capacitor/Capacitor/TmpViewController.swift b/ios/Capacitor/Capacitor/TmpViewController.swift deleted file mode 100644 index 3511d18aed..0000000000 --- a/ios/Capacitor/Capacitor/TmpViewController.swift +++ /dev/null @@ -1,8 +0,0 @@ -import UIKit - -internal class TmpViewController: UIViewController { - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - NotificationCenter.default.post(CapacitorBridge.tmpVCAppeared) - } -} From b5e61daecf67047ff76345841c14fb4a919a3db2 Mon Sep 17 00:00:00 2001 From: Pedro Bilro Date: Tue, 17 Mar 2026 17:46:51 +0000 Subject: [PATCH 06/69] refactor(android)!: Remove deprecated `PathHandler#getResponseHeaders` (#8374) --- .../java/com/getcapacitor/WebViewLocalServer.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java index a39483de91..1304770703 100755 --- a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java +++ b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java @@ -128,19 +128,6 @@ public String getReasonPhrase() { return reasonPhrase; } - /** - * @deprecated This method may return incorrect headers in concurrent range requests. - *

- * Use {@link #buildDefaultResponseHeaders()} instead, which returns a copy of the map. - *

- * This method will be removed in a future major version of Capacitor. - *

- */ - @Deprecated(forRemoval = true) // adjust version as appropriate - public Map getResponseHeaders() { - return responseHeaders; - } - public Map buildDefaultResponseHeaders() { return new HashMap<>(responseHeaders); } From 6881a4feb69c8b7ecfa8c37a62e081c44ef6d7e8 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 13 Mar 2026 09:54:59 +0100 Subject: [PATCH 07/69] chore(ci): close issues after 7 days (#8385) --- .github/workflows/needs-reply.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/needs-reply.yml b/.github/workflows/needs-reply.yml index 40d823c739..c4fd4a7444 100644 --- a/.github/workflows/needs-reply.yml +++ b/.github/workflows/needs-reply.yml @@ -13,7 +13,7 @@ jobs: with: repo-token: ${{ secrets.BOT_TOKEN }} issue-label: 'needs reply' - days-before-close: 15 + days-before-close: 7 close-message: | It looks like this issue didn't get the information it needed, so I'll close it for now. If I made a mistake, sorry! I am just a bot. From 0aac1f0aee8083dc5013f0d8e3c180eb02f21141 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 16 Mar 2026 17:46:06 +0100 Subject: [PATCH 08/69] fix(http): handle URL objects on fetch (#8386) --- android/capacitor/src/main/assets/native-bridge.js | 4 ++++ core/native-bridge.ts | 3 +++ ios/Capacitor/Capacitor/assets/native-bridge.js | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/android/capacitor/src/main/assets/native-bridge.js b/android/capacitor/src/main/assets/native-bridge.js index 40daf2a484..f5e7cc4403 100644 --- a/android/capacitor/src/main/assets/native-bridge.js +++ b/android/capacitor/src/main/assets/native-bridge.js @@ -505,6 +505,10 @@ var nativeBridge = (function (exports) { 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); diff --git a/core/native-bridge.ts b/core/native-bridge.ts index f988399be8..4fc028f379 100644 --- a/core/native-bridge.ts +++ b/core/native-bridge.ts @@ -532,6 +532,9 @@ const initBridge = (w: any): void => { 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); diff --git a/ios/Capacitor/Capacitor/assets/native-bridge.js b/ios/Capacitor/Capacitor/assets/native-bridge.js index 40daf2a484..f5e7cc4403 100644 --- a/ios/Capacitor/Capacitor/assets/native-bridge.js +++ b/ios/Capacitor/Capacitor/assets/native-bridge.js @@ -505,6 +505,10 @@ var nativeBridge = (function (exports) { 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); From d55f4f859bd88fa5617699e874c48c451bcb5e5f Mon Sep 17 00:00:00 2001 From: Joey Pender Date: Mon, 16 Mar 2026 16:38:42 -0500 Subject: [PATCH 09/69] fix(SystemBars): use native safe area insets on Android (#8384) --- .../com/getcapacitor/plugin/SystemBars.java | 96 +++++++++++++------ cli/src/declarations.ts | 2 +- core/system-bars.md | 2 +- 3 files changed, 67 insertions(+), 33 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java index 3f04adf976..c84923ca8b 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java @@ -7,7 +7,6 @@ import android.os.Build; import android.util.TypedValue; import android.view.View; -import android.view.ViewGroup; import android.view.Window; import android.webkit.JavascriptInterface; import android.webkit.WebView; @@ -36,6 +35,11 @@ public class SystemBars extends Plugin { static final String INSETS_HANDLING_CSS = "css"; static final String INSETS_HANDLING_DISABLE = "disable"; + // https://issues.chromium.org/issues/40699457 + private static final int WEBVIEW_VERSION_WITH_SAFE_AREA_FIX = 140; + // https://issues.chromium.org/issues/457682720 + private static final int WEBVIEW_VERSION_WITH_SAFE_AREA_KEYBOARD_FIX = 144; + static final String viewportMetaJSFunction = """ function capacitorSystemBarsCheckMetaViewport() { const meta = document.querySelectorAll("meta[name=viewport]"); @@ -94,7 +98,7 @@ private void initSystemBars() { } initWindowInsetsListener(); - initSafeAreaInsets(); + initSafeAreaCSSVariables(); getBridge().executeOnMainThread(() -> { setStyle(style, ""); @@ -157,7 +161,7 @@ private Insets calcSafeAreaInsets(WindowInsetsCompat insets) { return Insets.of(safeArea.left, safeArea.top, safeArea.right, safeArea.bottom); } - private void initSafeAreaInsets() { + private void initSafeAreaCSSVariables() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) { View v = (View) this.getBridge().getWebView().getParent(); WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v); @@ -169,41 +173,57 @@ private void initSafeAreaInsets() { } private void initWindowInsetsListener() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) { - ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> { - boolean hasBrokenWebViewVersion = getWebViewMajorVersion() <= 139; + ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> { + boolean shouldPassthroughInsets = getWebViewMajorVersion() >= WEBVIEW_VERSION_WITH_SAFE_AREA_FIX && hasViewportCover; + + Insets systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()); + Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()); + boolean keyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime()); - if (hasViewportCover) { + if (shouldPassthroughInsets) { + // We need to correct for a possible shown IME + v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) { Insets safeAreaInsets = calcSafeAreaInsets(insets); injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); } - if (hasBrokenWebViewVersion) { - if (hasViewportCover && v.hasWindowFocus() && v.isShown()) { - boolean keyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime()); - if (keyboardVisible) { - Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()); - setViewMargins(v, Insets.of(0, 0, 0, imeInsets.bottom)); - } else { - setViewMargins(v, Insets.NONE); - } - - return WindowInsetsCompat.CONSUMED; - } - } + return new WindowInsetsCompat.Builder(insets) + .setInsets( + WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), + Insets.of( + systemBarsInsets.left, + systemBarsInsets.top, + systemBarsInsets.right, + getBottomInset(systemBarsInsets, keyboardVisible) + ) + ) + .build(); + } - return insets; - }); - } - } + // We need to correct for a possible shown IME + v.setPadding( + systemBarsInsets.left, + systemBarsInsets.top, + systemBarsInsets.right, + keyboardVisible ? imeInsets.bottom : systemBarsInsets.bottom + ); + + // Returning `WindowInsetsCompat.CONSUMED` breaks recalculation of safe area insets + // So we have to explicitly set insets to `0` + // See: https://issues.chromium.org/issues/461332423 + WindowInsetsCompat newInsets = new WindowInsetsCompat.Builder(insets) + .setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), Insets.of(0, 0, 0, 0)) + .build(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) { + Insets safeAreaInsets = calcSafeAreaInsets(newInsets); + injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); + } - private void setViewMargins(View v, Insets insets) { - ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) v.getLayoutParams(); - mlp.leftMargin = insets.left; - mlp.bottomMargin = insets.bottom; - mlp.rightMargin = insets.right; - mlp.topMargin = insets.top; - v.setLayoutParams(mlp); + return newInsets; + }); } private void injectSafeAreaCSS(int top, int right, int bottom, int left) { @@ -305,4 +325,18 @@ private Integer getWebViewMajorVersion() { return 0; } + + private int getBottomInset(Insets systemBarsInsets, boolean keyboardVisible) { + if (getWebViewMajorVersion() < WEBVIEW_VERSION_WITH_SAFE_AREA_KEYBOARD_FIX) { + // This is a workaround for webview versions that have a bug + // that causes the bottom inset to be incorrect if the IME is visible + // See: https://issues.chromium.org/issues/457682720 + + if (keyboardVisible) { + return 0; + } + } + + return systemBarsInsets.bottom; + } } diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index 6612eec777..5eacf5e654 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -713,7 +713,7 @@ export interface PluginsConfig { * * `css` = Injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview. * - * `disable` = Disable all inset handling. + * `disable` = Disable CSS variables injection. * * @default "css" */ diff --git a/core/system-bars.md b/core/system-bars.md index c7654063bc..67275077af 100644 --- a/core/system-bars.md +++ b/core/system-bars.md @@ -73,7 +73,7 @@ const setStatusBarAnimation = async () => { ## Configuration | Prop | Type | Description | Default | | ------------- | -------------------- | ------------------------------------------------------------------------- | ------------------ | -| **`insetsHandling`** | string | Specifies how to handle problematic insets on Android. This option is only supported on Android.
`css` = Injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview.
`disable` = Disable all inset handling. | css | +| **`insetsHandling`** | string | Specifies how to handle problematic insets on Android. This option is only supported on Android.
`css` = Injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview.
`disable` = Disable CSS variables injection. | css | | **`style`** | string | The style of the text and icons of the system bars. | DEFAULT | | **`hidden`** | boolean | Hide the system bars on start. | false | | **`animation`** | string | The type of status bar animation used when showing or hiding. This option is only supported on iOS. | FADE | From a6b3e82bb62b4a5352e2c2c9a0cab0d8dd664284 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 17 Mar 2026 12:30:17 +0100 Subject: [PATCH 10/69] chore: change needs reproduction bot message (#8390) --- .github/ionic-issue-bot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ionic-issue-bot.yml b/.github/ionic-issue-bot.yml index fef86a40f0..7adf8c4573 100644 --- a/.github/ionic-issue-bot.yml +++ b/.github/ionic-issue-bot.yml @@ -20,7 +20,7 @@ comment: message: > This issue needs more information before it can be addressed. In particular, the reporter needs to provide a minimal sample app that demonstrates the issue. - If no sample app is provided within 15 days, the issue will be closed. + If no sample app is provided within 7 days, the issue will be closed. Please see the Contributing Guide for [how to create a Sample App](https://github.com/ionic-team/capacitor/blob/HEAD/CONTRIBUTING.md#creating-a-code-reproduction). From ef57c773e70af6b94685a2dc0f808db3ed4cda6e Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 18 Mar 2026 18:53:47 +0100 Subject: [PATCH 11/69] fix(ios): make getArray accesible on Objective-C plugins (#8392) --- ios/Capacitor/Capacitor/CAPBridgedJSTypes.h | 1 + ios/Capacitor/Capacitor/CAPBridgedJSTypes.m | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h b/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h index 516c8648b8..67f8a9501d 100644 --- a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h +++ b/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h @@ -10,6 +10,7 @@ - (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 diff --git a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m b/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m index fb6da4586f..c8ec07da7f 100644 --- a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m +++ b/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m @@ -29,6 +29,14 @@ - (NSDictionary * _Nullable)getObject:(NSString * _Nonnull)key defaultValue:(NSD 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]]) { From 9b0f1c8dc21ab7ed19c9c77294128eba6b94b06f Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Thu, 19 Mar 2026 08:51:53 -0400 Subject: [PATCH 12/69] feat!: make Cordova support optional (#8328) Co-authored-by: Steven Sherry Co-authored-by: jcesarmobile Co-authored-by: Joseph Pender --- .../main/java/com/getcapacitor/Bridge.java | 180 ++++---- .../java/com/getcapacitor/MessageHandler.java | 62 +-- .../getcapacitor/cordova/CordovaPlugin.java | 148 +++++++ .../cordova/MockCordovaInterfaceImpl.java | 2 +- .../cordova/MockCordovaWebViewImpl.java | 4 +- cli/src/android/update.ts | 20 +- cli/src/ios/update.ts | 419 +++--------------- cli/src/tasks/migrate.ts | 2 +- cli/src/util/cordova-ios.ts | 365 +++++++++++++++ cli/src/util/iosplugin.ts | 1 + cli/src/util/spm.ts | 10 +- ios/Capacitor.podspec | 1 - .../Capacitor.xcodeproj/project.pbxproj | 8 - .../CAPApplicationDelegateProxy.swift | 1 - .../Capacitor/CAPBridgeProtocol.swift | 3 + ...wController+CDVScreenOrientationDelegate.h | 6 - ...wController+CDVScreenOrientationDelegate.m | 5 - .../Capacitor/CAPBridgeViewController.swift | 18 +- .../Capacitor/CAPInstanceDescriptor.h | 4 +- .../Capacitor/CAPInstanceDescriptor.m | 1 - .../Capacitor/CAPInstanceDescriptor.swift | 19 +- ios/Capacitor/Capacitor/CapacitorBridge.swift | 126 ++---- ios/Capacitor/Capacitor/JS.swift | 10 +- ios/Capacitor/Capacitor/JSExport.swift | 40 -- .../Capacitor/WebViewDelegationHandler.swift | 13 +- .../CapacitorTests/ConfigurationTests.swift | 1 - .../CapacitorTests/PluginCallAccessorTests.m | 2 +- ios/CapacitorCordova.podspec | 3 +- .../Classes/Public/Plugin.swift | 126 ++++++ 29 files changed, 885 insertions(+), 715 deletions(-) create mode 100644 android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java create mode 100644 cli/src/util/cordova-ios.ts delete mode 100644 ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h delete mode 100644 ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m create mode 100644 ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 2a72a4304a..6008c961fc 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -35,8 +35,6 @@ import com.getcapacitor.android.R; import com.getcapacitor.annotation.CapacitorPlugin; import com.getcapacitor.annotation.Permission; -import com.getcapacitor.cordova.MockCordovaInterfaceImpl; -import com.getcapacitor.cordova.MockCordovaWebViewImpl; import com.getcapacitor.util.HostMask; import com.getcapacitor.util.InternalUtils; import com.getcapacitor.util.PermissionHelper; @@ -53,13 +51,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.cordova.ConfigXmlParser; -import org.apache.cordova.CordovaPreferences; -import org.apache.cordova.CordovaWebView; -import org.apache.cordova.PluginEntry; -import org.apache.cordova.PluginManager; import org.json.JSONException; /** @@ -125,9 +119,6 @@ public class Bridge { private Boolean canInjectJS = true; // A reference to the main WebView for the app private final WebView webView; - public final MockCordovaInterfaceImpl cordovaInterface; - private CordovaWebView cordovaWebView; - private CordovaPreferences preferences; private BridgeWebViewClient webViewClient; private App app; @@ -147,6 +138,8 @@ public class Bridge { // A map of Plugin Id's to PluginHandle's private Map plugins = new HashMap<>(); + private Map interceptors = new HashMap<>(); + // Stored plugin calls that we're keeping around to call again someday private Map savedCalls = new HashMap<>(); @@ -169,26 +162,6 @@ public class Bridge { // A pre-determined path to load the bridge private ServerPath serverPath; - /** - * Create the Bridge with a reference to the main {@link Activity} for the - * app, and a reference to the {@link WebView} our app will use. - * @param context - * @param webView - * @deprecated Use {@link Bridge.Builder} to create Bridge instances - */ - @Deprecated - public Bridge( - AppCompatActivity context, - WebView webView, - List> initialPlugins, - MockCordovaInterfaceImpl cordovaInterface, - PluginManager pluginManager, - CordovaPreferences preferences, - CapConfig config - ) { - this(context, null, null, webView, initialPlugins, new ArrayList<>(), cordovaInterface, pluginManager, preferences, config); - } - private Bridge( AppCompatActivity context, ServerPath serverPath, @@ -196,9 +169,6 @@ private Bridge( WebView webView, List> initialPlugins, List pluginInstances, - MockCordovaInterfaceImpl cordovaInterface, - PluginManager pluginManager, - CordovaPreferences preferences, CapConfig config ) { this.app = new App(); @@ -209,8 +179,6 @@ private Bridge( this.webViewClient = new BridgeWebViewClient(this); this.initialPlugins = initialPlugins; this.pluginInstances = pluginInstances; - this.cordovaInterface = cordovaInterface; - this.preferences = preferences; // Start our plugin execution threads and handlers handlerThread.start(); @@ -218,11 +186,10 @@ private Bridge( this.config = config != null ? config : CapConfig.loadDefault(getActivity()); Logger.init(this.config); - // Initialize web view and message handler for it this.initWebView(); this.setAllowedOriginRules(); - this.msgHandler = new MessageHandler(this, webView, pluginManager); + this.msgHandler = new MessageHandler(this, webView); // Grab any intent info that our app was launched with Intent intent = context.getIntent(); @@ -450,12 +417,30 @@ private boolean isNewBinary() { return false; } + private Plugin cordova() { + PluginHandle handle = getPlugin("__CordovaHandle"); + if (handle != null) { + return handle.getInstance(); + } + return null; + } + public boolean isDeployDisabled() { - return preferences.getBoolean("DisableDeploy", false); + Plugin cordova = this.cordova(); + if (cordova != null) { + return cordova.hasPermission("DisableDeploy"); + } else { + return false; + } } public boolean shouldKeepRunning() { - return preferences.getBoolean("KeepRunning", true); + Plugin cordova = this.cordova(); + if (cordova != null) { + return cordova.hasPermission("KeepRunning"); + } else { + return false; + } } public void handleAppUrlLoadError(Exception ex) { @@ -474,10 +459,6 @@ public boolean isDevMode() { return (getActivity().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; } - protected void setCordovaWebView(CordovaWebView cordovaWebView) { - this.cordovaWebView = cordovaWebView; - } - /** * Get the Context for the App * @return @@ -567,6 +548,14 @@ public CapConfig getConfig() { return this.config; } + public MessageHandler.Interceptor getCallInterceptor(String type) { + return this.interceptors.get(type); + } + + public void registerInterceptor(String type, MessageHandler.Interceptor interceptor) { + this.interceptors.put(type, interceptor); + } + public void reset() { savedCalls = new HashMap<>(); for (PluginHandle handle : this.plugins.values()) { @@ -1132,11 +1121,14 @@ boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] if (plugin == null) { boolean permissionHandled = false; Logger.debug("Unable to find a Capacitor plugin to handle permission requestCode, trying Cordova plugins " + requestCode); - try { - permissionHandled = cordovaInterface.handlePermissionResult(requestCode, permissions, grantResults); - } catch (JSONException e) { - Logger.debug("Error on Cordova plugin permissions request " + e.getMessage()); + PluginHandle cordovaHandle = getPlugin("__CordovaPlugin"); + + if (cordovaHandle != null) { + Plugin cordovaPlugin = cordovaHandle.getInstance(); + cordovaPlugin.handleRequestPermissionsResult(requestCode, permissions, grantResults); + permissionHandled = cordovaPlugin.hasDefinedRequiredPermissions(); } + return permissionHandled; } @@ -1271,7 +1263,13 @@ boolean onActivityResult(int requestCode, int resultCode, Intent data) { if (plugin == null || plugin.getInstance() == null) { Logger.debug("Unable to find a Capacitor plugin to handle requestCode, trying Cordova plugins " + requestCode); - return cordovaInterface.onActivityResult(requestCode, resultCode, data); + PluginHandle cordovaHandle = getPlugin("__CordovaPlugin"); + if (cordovaHandle != null) { + Plugin cordovaPlugin = cordovaHandle.getInstance(); + cordovaPlugin.handleOnActivityResult(requestCode, resultCode, data); + // This is a bit hacky but required to return the boolean out of the cordova interface + return cordovaPlugin.hasRequiredPermissions(); + } } // deprecated, to be removed @@ -1301,10 +1299,6 @@ public void onNewIntent(Intent intent) { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnNewIntent(intent); } - - if (cordovaWebView != null) { - cordovaWebView.onNewIntent(intent); - } } /** @@ -1333,10 +1327,6 @@ public void onStart() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnStart(); } - - if (cordovaWebView != null) { - cordovaWebView.handleStart(); - } } /** @@ -1346,10 +1336,6 @@ public void onResume() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnResume(); } - - if (cordovaWebView != null) { - cordovaWebView.handleResume(this.shouldKeepRunning()); - } } /** @@ -1359,11 +1345,6 @@ public void onPause() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnPause(); } - - if (cordovaWebView != null) { - boolean keepRunning = this.shouldKeepRunning() || cordovaInterface.getActivityResultCallback() != null; - cordovaWebView.handlePause(keepRunning); - } } /** @@ -1373,10 +1354,6 @@ public void onStop() { for (PluginHandle plugin : plugins.values()) { plugin.getInstance().handleOnStop(); } - - if (cordovaWebView != null) { - cordovaWebView.handleStop(); - } } /** @@ -1388,10 +1365,6 @@ public void onDestroy() { } handlerThread.quitSafely(); - - if (cordovaWebView != null) { - cordovaWebView.handleDestroy(); - } } /** @@ -1578,49 +1551,54 @@ public Builder setServerPath(ServerPath serverPath) { public Bridge create() { // Cordova initialization - ConfigXmlParser parser = new ConfigXmlParser(); - parser.parse(activity.getApplicationContext()); - CordovaPreferences preferences = parser.getPreferences(); - preferences.setPreferencesBundle(activity.getIntent().getExtras()); - List pluginEntries = parser.getPluginEntries(); - - MockCordovaInterfaceImpl cordovaInterface = new MockCordovaInterfaceImpl(activity); - if (instanceState != null) { - cordovaInterface.restoreInstanceState(instanceState); - } - WebView webView = this.fragment != null ? fragment.getView().findViewById(R.id.webview) : activity.findViewById(R.id.webview); - MockCordovaWebViewImpl mockWebView = new MockCordovaWebViewImpl(activity.getApplicationContext()); - mockWebView.init(cordovaInterface, pluginEntries, preferences, webView); - PluginManager pluginManager = mockWebView.getPluginManager(); - cordovaInterface.onCordovaInit(pluginManager); // Bridge initialization - Bridge bridge = new Bridge( - activity, - serverPath, - fragment, - webView, - plugins, - pluginInstances, - cordovaInterface, - pluginManager, - preferences, - config - ); + Bridge bridge = new Bridge(activity, serverPath, fragment, webView, plugins, pluginInstances, config); if (webView instanceof CapacitorWebView capacitorWebView) { capacitorWebView.setBridge(bridge); } - bridge.setCordovaWebView(mockWebView); bridge.setWebViewListeners(webViewListeners); bridge.setRouteProcessor(routeProcessor); if (instanceState != null) { + PluginHandle maybeCordova = bridge.getPlugin("__CordovaPlugin"); + if (maybeCordova != null) { + maybeCordova.getInstance().restoreState(instanceState); + } bridge.restoreInstanceState(instanceState); } + bridge.registerInterceptor("message", (postData) -> { + try { + String callbackId = postData.getString("callbackId"); + String pluginId = postData.getString("pluginId"); + String methodName = postData.getString("methodName"); + JSObject methodData = postData.getJSObject("options", new JSObject()); + + Logger.verbose( + Logger.tags("Plugin"), + "To native (Capacitor plugin): callbackId: " + + callbackId + + ", pluginId: " + + pluginId + + ", methodName: " + + methodName + ); + + PluginCall call = new PluginCall(bridge.msgHandler, pluginId, callbackId, methodName, methodData); + bridge.callPluginMethod(pluginId, methodName, call); + } catch (JSONException e) { + Logger.error(e.getMessage()); + } + }); + + bridge.registerInterceptor("js.error", (postData) -> { + Logger.error("JavaScript Error: " + postData.toString()); + }); + return bridge; } } diff --git a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java index dc91c9b01e..4de485b6fe 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java +++ b/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java @@ -5,7 +5,7 @@ import androidx.webkit.JavaScriptReplyProxy; import androidx.webkit.WebViewCompat; import androidx.webkit.WebViewFeature; -import org.apache.cordova.PluginManager; +import java.util.function.Function; /** * MessageHandler handles messages from the WebView, dispatching them @@ -13,15 +13,18 @@ */ public class MessageHandler { + @FunctionalInterface + public interface Interceptor { + void intercept(JSObject object); + } + private Bridge bridge; private WebView webView; - private PluginManager cordovaPluginManager; private JavaScriptReplyProxy javaScriptReplyProxy; - public MessageHandler(Bridge bridge, WebView webView, PluginManager cordovaPluginManager) { + public MessageHandler(Bridge bridge, WebView webView) { this.bridge = bridge; this.webView = webView; - this.cordovaPluginManager = cordovaPluginManager; if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER) && !bridge.getConfig().isUsingLegacyBridge()) { WebViewCompat.WebMessageListener capListener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> { @@ -42,6 +45,11 @@ public MessageHandler(Bridge bridge, WebView webView, PluginManager cordovaPlugi } } + @Deprecated + public MessageHandler(Bridge bridge, WebView webView, Object cordovaPluginManager) { + this(bridge, webView); + } + /** * The main message handler that will be called from JavaScript * to send a message to the native bridge. @@ -55,43 +63,11 @@ public void postMessage(String jsonStr) { String type = postData.getString("type"); - boolean typeIsNotNull = type != null; - boolean isCordovaPlugin = typeIsNotNull && type.equals("cordova"); - boolean isJavaScriptError = typeIsNotNull && type.equals("js.error"); - - String callbackId = postData.getString("callbackId"); - - if (isCordovaPlugin) { - String service = postData.getString("service"); - String action = postData.getString("action"); - String actionArgs = postData.getString("actionArgs"); - - Logger.verbose( - Logger.tags("Plugin"), - "To native (Cordova plugin): callbackId: " + - callbackId + - ", service: " + - service + - ", action: " + - action + - ", actionArgs: " + - actionArgs - ); - - this.callCordovaPluginMethod(callbackId, service, action, actionArgs); - } else if (isJavaScriptError) { - Logger.error("JavaScript Error: " + jsonStr); - } else { - String pluginId = postData.getString("pluginId"); - String methodName = postData.getString("methodName"); - JSObject methodData = postData.getJSObject("options", new JSObject()); - - Logger.verbose( - Logger.tags("Plugin"), - "To native (Capacitor plugin): callbackId: " + callbackId + ", pluginId: " + pluginId + ", methodName: " + methodName - ); + if (type == null) type = "message"; - this.callPluginMethod(callbackId, pluginId, methodName, methodData); + Interceptor interceptor = bridge.getCallInterceptor(type); + if (interceptor != null) { + interceptor.intercept(postData); } } catch (Exception ex) { Logger.error("Post message error:", ex); @@ -148,10 +124,4 @@ private void callPluginMethod(String callbackId, String pluginId, String methodN PluginCall call = new PluginCall(this, pluginId, callbackId, methodName, methodData); bridge.callPluginMethod(pluginId, methodName, call); } - - private void callCordovaPluginMethod(String callbackId, String service, String action, String actionArgs) { - bridge.execute(() -> { - cordovaPluginManager.exec(service, action, callbackId, actionArgs); - }); - } } diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java new file mode 100644 index 0000000000..efe101034d --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java @@ -0,0 +1,148 @@ +package com.getcapacitor.cordova; + +import android.content.Intent; +import android.os.Bundle; +import android.webkit.WebView; +import com.getcapacitor.Logger; +import com.getcapacitor.Plugin; +import com.getcapacitor.android.R; +import com.getcapacitor.annotation.CapacitorPlugin; +import java.util.List; +import org.apache.cordova.ConfigXmlParser; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPreferences; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginEntry; +import org.apache.cordova.PluginManager; +import org.json.JSONException; + +@CapacitorPlugin(name = "__CordovaPlugin") +public class CordovaPlugin extends Plugin { + + private MockCordovaInterfaceImpl cordovaInterface; + private CordovaWebView webView; + private CordovaPreferences preferences; + + private boolean pluginHadActivityResult = false; + private boolean pluginHadPermissionResult = false; + + @Override + public void load() { + ConfigXmlParser parser = new ConfigXmlParser(); + parser.parse(getActivity().getApplicationContext()); + preferences = parser.getPreferences(); + preferences.setPreferencesBundle(getActivity().getIntent().getExtras()); + List pluginEntries = parser.getPluginEntries(); + + cordovaInterface = new MockCordovaInterfaceImpl(getActivity()); + + MockCordovaWebViewImpl mockWebView = new MockCordovaWebViewImpl(getActivity().getApplicationContext()); + mockWebView.init(cordovaInterface, pluginEntries, preferences, bridge.getWebView()); + webView = mockWebView; + PluginManager pluginManager = mockWebView.getPluginManager(); + cordovaInterface.onCordovaInit(pluginManager); + + bridge.registerInterceptor("cordova", (postData) -> { + String callbackId = postData.getString("callbackId"); + + String service = postData.getString("service"); + String action = postData.getString("action"); + String actionArgs = postData.getString("actionArgs"); + + Logger.verbose( + Logger.tags("Plugin"), + "To native (Cordova plugin): callbackId: " + + callbackId + + ", service: " + + service + + ", action: " + + action + + ", actionArgs: " + + actionArgs + ); + + bridge.execute(() -> { + pluginManager.exec(service, action, callbackId, actionArgs); + }); + }); + } + + @Override + protected void restoreState(Bundle state) { + if (state != null) { + cordovaInterface.restoreInstanceState(state); + } + } + + @Override + protected void handleRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + try { + pluginHadPermissionResult = cordovaInterface.handlePermissionResult(requestCode, permissions, grantResults); + } catch (JSONException e) { + Logger.debug("Error on Cordova plugin permissions request " + e.getMessage()); + } + } + + @Override + public boolean hasPermission(String permission) { + if (permission.equals("DisableDeploy")) { + return preferences.getBoolean(permission, false); + } + + if (permission.equals("KeepRunning")) { + return preferences.getBoolean(permission, true); + } + + return false; + } + + @Override + public boolean hasDefinedRequiredPermissions() { + boolean currentPermissionResult = pluginHadPermissionResult; + pluginHadPermissionResult = false; + return currentPermissionResult; + } + + @Override + protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) { + pluginHadActivityResult = cordovaInterface.onActivityResult(requestCode, resultCode, data); + } + + @Override + public boolean hasRequiredPermissions() { + boolean currentActivityResult = pluginHadActivityResult; + pluginHadActivityResult = false; + return currentActivityResult; + } + + @Override + protected void handleOnNewIntent(Intent intent) { + webView.onNewIntent(intent); + } + + @Override + protected void handleOnStart() { + webView.handleStart(); + } + + @Override + protected void handleOnResume() { + webView.handleResume(bridge.shouldKeepRunning()); + } + + @Override + protected void handleOnPause() { + boolean keepRunning = bridge.shouldKeepRunning() || cordovaInterface.getActivityResultCallback() != null; + webView.handlePause(keepRunning); + } + + @Override + protected void handleOnStop() { + webView.handleStop(); + } + + @Override + protected void handleOnDestroy() { + webView.handleDestroy(); + } +} diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java index 7e8358da5e..11116bf131 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java @@ -7,7 +7,7 @@ import org.apache.cordova.CordovaPlugin; import org.json.JSONException; -public class MockCordovaInterfaceImpl extends CordovaInterfaceImpl { +class MockCordovaInterfaceImpl extends CordovaInterfaceImpl { public MockCordovaInterfaceImpl(AppCompatActivity activity) { super(activity, Executors.newCachedThreadPool()); diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java index 1115429d7b..72136c496d 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java @@ -20,7 +20,7 @@ import org.apache.cordova.PluginManager; import org.apache.cordova.PluginResult; -public class MockCordovaWebViewImpl implements CordovaWebView { +class MockCordovaWebViewImpl implements CordovaWebView { private Context context; private PluginManager pluginManager; @@ -32,7 +32,7 @@ public class MockCordovaWebViewImpl implements CordovaWebView { private WebView webView; private boolean hasPausedEver; - public MockCordovaWebViewImpl(Context context) { + MockCordovaWebViewImpl(Context context) { this.context = context; } diff --git a/cli/src/android/update.ts b/cli/src/android/update.ts index 16e8c4935f..fe22659b1a 100644 --- a/cli/src/android/update.ts +++ b/cli/src/android/update.ts @@ -7,6 +7,7 @@ import { checkPlatformVersions, runTask } from '../common'; import { checkPluginDependencies, handleCordovaPluginsJS, writeCordovaAndroidManifest } from '../cordova'; import type { Config } from '../definitions'; import { fatal } from '../errors'; +import { logger } from '../log'; import { PluginType, getAllElements, @@ -35,10 +36,13 @@ export async function updateAndroid(config: Config): Promise { printPlugins(capacitorPlugins, 'android'); - await writePluginsJson(config, capacitorPlugins); - await removePluginsNativeFiles(config); const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova); - if (cordovaPlugins.length > 0) { + const enableCordova = cordovaPlugins.length > 0; + + await writePluginsJson(config, capacitorPlugins, enableCordova); + await removePluginsNativeFiles(config); + if (enableCordova) { + logger.info('Found Cordova Plugins: Including Android Cordova Support'); await copyPluginsNativeFiles(config, cordovaPlugins); } if (!(await pathExists(config.android.webDirAbs))) { @@ -64,8 +68,16 @@ interface PluginsJsonEntry { classpath: string; } -async function writePluginsJson(config: Config, plugins: Plugin[]): Promise { +async function writePluginsJson(config: Config, plugins: Plugin[], includeCordova: boolean): Promise { const classes = await findAndroidPluginClasses(plugins); + + if (includeCordova) { + classes.push({ + pkg: '@capacitor/android', + classpath: 'com.getcapacitor.cordova.CordovaPlugin', + }); + } + const pluginsJsonPath = resolve(config.android.assetsDirAbs, 'capacitor.plugins.json'); await writeJSON(pluginsJsonPath, classes, { spaces: '\t' }); diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index cd9c82a00d..961f6ca922 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -1,31 +1,29 @@ -import { copy, remove, pathExists, readFile, realpath, writeFile } from 'fs-extra'; +import { pathExists, readFile, realpath, writeFile } from 'fs-extra'; import { basename, dirname, join, relative } from 'path'; import c from '../colors'; -import { checkPlatformVersions, getCapacitorPackageVersion, runTask } from '../common'; -import { checkPluginDependencies, handleCordovaPluginsJS, logCordovaManualSteps, needsStaticPod } from '../cordova'; +import { checkPlatformVersions, runTask } from '../common'; +import { checkPluginDependencies, handleCordovaPluginsJS, logCordovaManualSteps } from '../cordova'; import type { Config } from '../definitions'; import { fatal } from '../errors'; import { logger } from '../log'; -import { - PluginType, - getAllElements, - getFilePath, - getPlatformElement, - getPluginType, - getPlugins, - printPlugins, -} from '../plugin'; +import { PluginType, getPluginType, getPlugins, printPlugins } from '../plugin'; import type { Plugin } from '../plugin'; import { copy as copyTask } from '../tasks/copy'; +import { + generateCordovaPodspecs, + generateCordovaPackageFiles, + copyPluginsNativeFiles, + removePluginsNativeFiles, + cordovaPodfileLines, +} from '../util/cordova-ios'; import { convertToUnixPath } from '../util/fs'; import { generateIOSPackageJSON } from '../util/iosplugin'; import { resolveNode } from '../util/node'; import { generatePackageFile, checkPluginsForPackageSwift } from '../util/spm'; import { runCommand, isInstalled } from '../util/subprocess'; -import { extractTemplate } from '../util/template'; -import { getIOSPlugins, getMajoriOSVersion } from './common'; +import { getIOSPlugins } from './common'; const platform = 'ios'; @@ -43,89 +41,65 @@ export async function updateIOS(config: Config, deployment: boolean): Promise getPluginType(p, platform) === PluginType.Cordova); - if (cordovaPlugins.length > 0) { + const enableCordova = cordovaPlugins.length > 0; + + if (enableCordova) { + logger.info('Found Cordova Plugins: Including iOS Cordova Support'); await copyPluginsNativeFiles(config, cordovaPlugins); } + if (!(await pathExists(await config.ios.webDirAbs))) { await copyTask(config, platform); } + await handleCordovaPluginsJS(cordovaPlugins, config, platform); await checkPluginDependencies(plugins, platform, config.app.extConfig.cordova?.failOnUninstalledPlugins); if ((await config.ios.packageManager) === 'SPM') { - await generateCordovaPackageFiles(cordovaPlugins, config); + if (enableCordova) { + await generateCordovaPackageFiles(cordovaPlugins, config); + } const validSPMPackages = await checkPluginsForPackageSwift(config, plugins); await generatePackageFile(config, validSPMPackages.concat(cordovaPlugins)); } else { - await generateCordovaPodspecs(cordovaPlugins, config); - await installCocoaPodsPlugins(config, plugins, deployment); + await installCocoaPodsPlugins(config, plugins, deployment, enableCordova); } - await logCordovaManualSteps(cordovaPlugins, config, platform); - - const incompatibleCordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Incompatible); - printPlugins(incompatibleCordovaPlugins, platform, 'incompatible'); -} -async function generateCordovaPackageFiles(cordovaPlugins: Plugin[], config: Config) { - cordovaPlugins.map((plugin: any) => { - generateCordovaPackageFile(plugin, config); - }); + if (enableCordova) { + await logCordovaManualSteps(cordovaPlugins, config, platform); + } } -async function generateCordovaPackageFile(p: Plugin, config: Config) { - const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); - const iosVersion = getMajoriOSVersion(config); - const headerFiles = getPlatformElement(p, platform, 'header-file'); - let headersText = ''; - if (headerFiles.length > 0) { - headersText = `, - publicHeadersPath: "."`; +export async function installCocoaPodsPlugins( + config: Config, + plugins: Plugin[], + deployment: boolean, + enableCordova: boolean, +): Promise { + if (enableCordova) { + const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova); + await generateCordovaPodspecs(cordovaPlugins, config); } - const content = `// swift-tools-version: 5.9 - -import PackageDescription - -let package = Package( - name: "${p.name}", - platforms: [.iOS(.v${iosVersion})], - products: [ - .library( - name: "${p.name}", - targets: ["${p.name}"] - ) - ], - dependencies: [ - .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "${iosPlatformVersion}") - ], - targets: [ - .target( - name: "${p.name}", - dependencies: [ - .product(name: "Cordova", package: "capacitor-swift-pm") - ], - path: "."${headersText} - ) - ] -)`; - await writeFile(join(config.ios.cordovaPluginsDirAbs, 'sources', p.name, 'Package.swift'), content); -} - -export async function installCocoaPodsPlugins(config: Config, plugins: Plugin[], deployment: boolean): Promise { await runTask(`Updating iOS native dependencies with ${c.input(`${await config.ios.podPath} install`)}`, () => { - return updatePodfile(config, plugins, deployment); + return updatePodfile(config, plugins, deployment, enableCordova); }); } -async function updatePodfile(config: Config, plugins: Plugin[], deployment: boolean): Promise { - const dependenciesContent = await generatePodFile(config, plugins); +async function updatePodfile( + config: Config, + plugins: Plugin[], + deployment: boolean, + enableCordova: boolean, +): Promise { + const dependenciesContent = await generatePodFile(config, plugins, enableCordova); const relativeCapacitoriOSPath = await getRelativeCapacitoriOSPath(config); const podfilePath = join(config.ios.nativeProjectDirAbs, 'Podfile'); let podfileContent = await readFile(podfilePath, { encoding: 'utf-8' }); podfileContent = podfileContent.replace(/(def capacitor_pods)[\s\S]+?(\nend)/, `$1${dependenciesContent}$2`); podfileContent = podfileContent.replace( - /(require_relative)[\s\S]+?(@capacitor\/ios\/scripts\/pods_helpers')/, + /(require_relative)[\s\S]+?(ios\/scripts\/pods_helpers')/, `require_relative '${relativeCapacitoriOSPath}/scripts/pods_helpers'`, ); await writeFile(podfilePath, podfileContent, { encoding: 'utf-8' }); @@ -167,10 +141,12 @@ async function getRelativeCapacitoriOSPath(config: Config) { return convertToUnixPath(relative(config.ios.nativeProjectDirAbs, await realpath(dirname(capacitoriOSPath)))); } -async function generatePodFile(config: Config, plugins: Plugin[]): Promise { +async function generatePodFile(config: Config, plugins: Plugin[], enableCordova: boolean): Promise { const relativeCapacitoriOSPath = await getRelativeCapacitoriOSPath(config); const capacitorPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Core); + const cordovaPodlines = cordovaPodfileLines(config, plugins); + const pods = await Promise.all( capacitorPlugins.map(async (p) => { if (!p.ios) { @@ -182,297 +158,18 @@ async function generatePodFile(config: Config, plugins: Plugin[]): Promise getPluginType(p, platform) === PluginType.Cordova); - cordovaPlugins.map(async (p) => { - const podspecs = getPlatformElement(p, platform, 'podspec'); - podspecs.map((podspec: any) => { - podspec.pods.map((pPods: any) => { - pPods.pod.map((pod: any) => { - if (pod.$.git) { - let gitRef = ''; - if (pod.$.tag) { - gitRef = `, :tag => '${pod.$.tag}'`; - } else if (pod.$.branch) { - gitRef = `, :branch => '${pod.$.branch}'`; - } else if (pod.$.commit) { - gitRef = `, :commit => '${pod.$.commit}'`; - } - pods.push(` pod '${pod.$.name}', :git => '${pod.$.git}'${gitRef}\n`); - } - }); - }); - }); - }); - const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p)); - const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el)); - if (noStaticPlugins.length > 0) { - pods.push(` pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'\n`); - } - if (staticPlugins.length > 0) { - pods.push(` pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'\n`); - } - const resourcesPlugins = cordovaPlugins.filter(filterResources); - if (resourcesPlugins.length > 0) { - pods.push(` pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'\n`); - } - return ` - pod 'Capacitor', :path => '${relativeCapacitoriOSPath}' - pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}' -${pods.join('').trimRight()}`; -} - -function getFrameworkName(framework: any) { - if (isFramework(framework)) { - if (framework.$.custom && framework.$.custom === 'true') { - return framework.$.src; - } - return framework.$.src.substr(0, framework.$.src.indexOf('.')); - } - return framework.$.src.substr(0, framework.$.src.indexOf('.')).replace('lib', ''); -} - -function isFramework(framework: any) { - return framework.$.src.split('.').pop().includes('framework'); -} - -async function generateCordovaPodspecs(cordovaPlugins: Plugin[], config: Config) { - const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p)); - const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el)); - generateCordovaPodspec(noStaticPlugins, config, false); - generateCordovaPodspec(staticPlugins, config, true); -} - -async function generateCordovaPodspec(cordovaPlugins: Plugin[], config: Config, isStatic: boolean) { - const weakFrameworks: string[] = []; - const linkedFrameworks: string[] = []; - const customFrameworks: string[] = []; - const systemLibraries: string[] = []; - const sourceFrameworks: string[] = []; - const frameworkDeps: string[] = []; - const compilerFlags: string[] = []; - let prefsArray: any[] = []; - let name = 'CordovaPlugins'; - let sourcesFolderName = 'sources'; - if (isStatic) { - name += 'Static'; - frameworkDeps.push('s.static_framework = true'); - sourcesFolderName += 'static'; - } - cordovaPlugins.map((plugin: any) => { - const frameworks = getPlatformElement(plugin, platform, 'framework'); - frameworks.map((framework: any) => { - if (!framework.$.type) { - const name = getFrameworkName(framework); - if (isFramework(framework)) { - if (framework.$.weak && framework.$.weak === 'true') { - if (!weakFrameworks.includes(name)) { - weakFrameworks.push(name); - } - } else if (framework.$.custom && framework.$.custom === 'true') { - const frameworktPath = join(sourcesFolderName, plugin.name, name); - if (!customFrameworks.includes(frameworktPath)) { - customFrameworks.push(frameworktPath); - } - } else { - if (!linkedFrameworks.includes(name)) { - linkedFrameworks.push(name); - } - } - } else { - if (!systemLibraries.includes(name)) { - systemLibraries.push(name); - } - } - } else if (framework.$.type && framework.$.type === 'podspec') { - let depString = `s.dependency '${framework.$.src}'`; - if (framework.$.spec && framework.$.spec !== '') { - depString += `, '${framework.$.spec}'`; - } - if (!frameworkDeps.includes(depString)) { - frameworkDeps.push(depString); - } - } - }); - prefsArray = prefsArray.concat(getAllElements(plugin, platform, 'preference')); - const podspecs = getPlatformElement(plugin, platform, 'podspec'); - podspecs.map((podspec: any) => { - podspec.pods.map((pods: any) => { - pods.pod.map((pod: any) => { - let depString = `s.dependency '${pod.$.name}'`; - if (pod.$.spec && pod.$.spec !== '') { - depString += `, '${pod.$.spec}'`; - } - if (!frameworkDeps.includes(depString)) { - frameworkDeps.push(depString); - } - }); - }); - }); - const sourceFiles = getPlatformElement(plugin, platform, 'source-file'); - sourceFiles.map((sourceFile: any) => { - if (sourceFile.$.framework && sourceFile.$.framework === 'true') { - let fileName = sourceFile.$.src.split('/').pop(); - if (!fileName.startsWith('lib')) { - fileName = 'lib' + fileName; - } - const frameworktPath = join(sourcesFolderName, plugin.name, fileName); - if (!sourceFrameworks.includes(frameworktPath)) { - sourceFrameworks.push(frameworktPath); - } - } else if (sourceFile.$['compiler-flags']) { - const cFlag = sourceFile.$['compiler-flags']; - if (!compilerFlags.includes(cFlag)) { - compilerFlags.push(cFlag); - } - } - }); - }); - const onlySystemLibraries = systemLibraries.filter((library) => removeNoSystem(library, sourceFrameworks)); - if (weakFrameworks.length > 0) { - frameworkDeps.push(`s.weak_frameworks = '${weakFrameworks.join(`', '`)}'`); - } - if (linkedFrameworks.length > 0) { - frameworkDeps.push(`s.frameworks = '${linkedFrameworks.join(`', '`)}'`); - } - if (onlySystemLibraries.length > 0) { - frameworkDeps.push(`s.libraries = '${onlySystemLibraries.join(`', '`)}'`); - } - if (customFrameworks.length > 0) { - frameworkDeps.push(`s.vendored_frameworks = '${customFrameworks.join(`', '`)}'`); - frameworkDeps.push( - `s.exclude_files = 'sources/**/*.framework/Headers/*.h', 'sources/**/*.framework/PrivateHeaders/*.h'`, - ); - } - if (sourceFrameworks.length > 0) { - frameworkDeps.push(`s.vendored_libraries = '${sourceFrameworks.join(`', '`)}'`); - } - if (compilerFlags.length > 0) { - frameworkDeps.push(`s.compiler_flags = '${compilerFlags.join(' ')}'`); - } - const arcPlugins = cordovaPlugins.filter(filterARCFiles); - if (arcPlugins.length > 0) { - frameworkDeps.push(`s.subspec 'noarc' do |sna| - sna.requires_arc = false - sna.source_files = 'noarc/**/*.{swift,h,m,c,cc,mm,cpp}' - end`); - } - let frameworksString = frameworkDeps.join('\n '); - frameworksString = await replaceFrameworkVariables(config, prefsArray, frameworksString); - const content = ` - Pod::Spec.new do |s| - s.name = '${name}' - s.version = '${config.cli.package.version}' - s.summary = 'Autogenerated spec' - s.license = 'Unknown' - s.homepage = 'https://example.com' - s.authors = { 'Capacitor Generator' => 'hi@example.com' } - s.source = { :git => 'https://github.com/ionic-team/does-not-exist.git', :tag => '${config.cli.package.version}' } - s.source_files = '${sourcesFolderName}/**/*.{swift,h,m,c,cc,mm,cpp}' - s.ios.deployment_target = '${config.ios.minVersion}' - s.xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1 WK_WEB_VIEW_ONLY=1' } - s.dependency 'CapacitorCordova'${getLinkerFlags(config)} - s.swift_version = '5.1' - ${frameworksString} - end`; - await writeFile(join(config.ios.cordovaPluginsDirAbs, `${name}.podspec`), content); -} -function getLinkerFlags(config: Config) { - if (config.app.extConfig.ios?.cordovaLinkerFlags) { - return `\n s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '${config.app.extConfig.ios.cordovaLinkerFlags.join( - ' ', - )}' }`; - } - return ''; -} + let podfileString = '\n'; + podfileString += ` pod 'Capacitor', :path => '${relativeCapacitoriOSPath}'\n`; -async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) { - for (const p of cordovaPlugins) { - const sourceFiles = getPlatformElement(p, platform, 'source-file'); - const headerFiles = getPlatformElement(p, platform, 'header-file'); - const codeFiles = sourceFiles.concat(headerFiles); - const frameworks = getPlatformElement(p, platform, 'framework'); - let sourcesFolderName = 'sources'; - if (needsStaticPod(p)) { - sourcesFolderName += 'static'; - } - const sourcesFolder = join(config.ios.cordovaPluginsDirAbs, sourcesFolderName, p.name); - for (const codeFile of codeFiles) { - let fileName = codeFile.$.src.split('/').pop(); - const fileExt = codeFile.$.src.split('.').pop(); - if (fileExt === 'a' && !fileName.startsWith('lib')) { - fileName = 'lib' + fileName; - } - let destFolder = sourcesFolderName; - if (codeFile.$['compiler-flags'] && codeFile.$['compiler-flags'] === '-fno-objc-arc') { - destFolder = 'noarc'; - } - const filePath = getFilePath(config, p, codeFile.$.src); - const fileDest = join(config.ios.cordovaPluginsDirAbs, destFolder, p.name, fileName); - await copy(filePath, fileDest); - if (!codeFile.$.framework) { - let fileContent = await readFile(fileDest, { encoding: 'utf-8' }); - if (fileExt === 'swift') { - fileContent = 'import Cordova\n' + fileContent; - await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); - } else { - if (fileContent.includes('@import Firebase;')) { - fileContent = fileContent.replace('@import Firebase;', '#import '); - await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); - } - if ( - fileContent.includes('[NSBundle bundleForClass:[self class]]') || - fileContent.includes('[NSBundle bundleForClass:[CDVCapture class]]') - ) { - fileContent = fileContent.replace('[NSBundle bundleForClass:[self class]]', '[NSBundle mainBundle]'); - fileContent = fileContent.replace('[NSBundle bundleForClass:[CDVCapture class]]', '[NSBundle mainBundle]'); - await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); - } - if (fileContent.includes('[self.webView superview]') || fileContent.includes('self.webView.superview')) { - fileContent = fileContent.replace(/\[self.webView superview\]/g, 'self.viewController.view'); - fileContent = fileContent.replace(/self.webView.superview/g, 'self.viewController.view'); - await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); - } - } - } - } - const resourceFiles = getPlatformElement(p, platform, 'resource-file'); - for (const resourceFile of resourceFiles) { - const fileName = resourceFile.$.src.split('/').pop(); - await copy( - getFilePath(config, p, resourceFile.$.src), - join(config.ios.cordovaPluginsDirAbs, 'resources', fileName), - ); - } - for (const framework of frameworks) { - if (framework.$.custom && framework.$.custom === 'true') { - await copy(getFilePath(config, p, framework.$.src), join(sourcesFolder, framework.$.src)); - } - } + if (enableCordova) { + podfileString += ` pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}'\n`; + podfileString += cordovaPodlines.join('').trimEnd() + '\n'; } -} -async function removePluginsNativeFiles(config: Config) { - await remove(config.ios.cordovaPluginsDirAbs); - await extractTemplate(config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, config.ios.cordovaPluginsDirAbs); -} + podfileString += pods.join('').trimEnd(); -function filterResources(plugin: Plugin) { - const resources = getPlatformElement(plugin, platform, 'resource-file'); - return resources.length > 0; -} - -function filterARCFiles(plugin: Plugin) { - const sources = getPlatformElement(plugin, platform, 'source-file'); - const sourcesARC = sources.filter( - (sourceFile: any) => sourceFile.$['compiler-flags'] && sourceFile.$['compiler-flags'] === '-fno-objc-arc', - ); - return sourcesARC.length > 0; -} - -function removeNoSystem(library: string, sourceFrameworks: string[]) { - const libraries = sourceFrameworks.filter((framework) => framework.includes(library)); - return libraries.length === 0; + return podfileString; } async function getPluginsTask(config: Config) { @@ -482,13 +179,3 @@ async function getPluginsTask(config: Config) { return iosPlugins; }); } - -async function replaceFrameworkVariables(config: Config, prefsArray: any[], frameworkString: string) { - prefsArray.map((preference: any) => { - frameworkString = frameworkString.replace( - new RegExp(('$' + preference.$.name).replace('$', '\\$&'), 'g'), - preference.$.default, - ); - }); - return frameworkString; -} diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index b2c419c080..dbc09549a4 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -1,6 +1,6 @@ import { writeFileSync, readFileSync, existsSync } from 'fs-extra'; import { join } from 'path'; -import { rimraf } from 'rimraf'; +import rimraf from 'rimraf'; import { coerce, gte, lt } from 'semver'; import c from '../colors'; diff --git a/cli/src/util/cordova-ios.ts b/cli/src/util/cordova-ios.ts new file mode 100644 index 0000000000..52dee60147 --- /dev/null +++ b/cli/src/util/cordova-ios.ts @@ -0,0 +1,365 @@ +import { copy, readFile, writeFile, remove } from 'fs-extra'; +import { join } from 'path'; + +import { getCapacitorPackageVersion } from '../common'; +import { needsStaticPod } from '../cordova'; +import type { Config } from '../definitions'; +import { getMajoriOSVersion } from '../ios/common'; +import { PluginType, getPlatformElement, getPluginType, getAllElements, getFilePath } from '../plugin'; +import type { Plugin } from '../plugin'; +import { extractTemplate } from '../util/template'; + +const platform = 'ios'; + +export async function generateCordovaPodspecs(cordovaPlugins: Plugin[], config: Config): Promise { + const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p)); + const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el)); + generateCordovaPodspec(noStaticPlugins, config, false); + generateCordovaPodspec(staticPlugins, config, true); +} + +export async function generateCordovaPodspec( + cordovaPlugins: Plugin[], + config: Config, + isStatic: boolean, +): Promise { + const weakFrameworks: string[] = []; + const linkedFrameworks: string[] = []; + const customFrameworks: string[] = []; + const systemLibraries: string[] = []; + const sourceFrameworks: string[] = []; + const frameworkDeps: string[] = []; + const compilerFlags: string[] = []; + let prefsArray: any[] = []; + let name = 'CordovaPlugins'; + let sourcesFolderName = 'sources'; + if (isStatic) { + name += 'Static'; + frameworkDeps.push('s.static_framework = true'); + sourcesFolderName += 'static'; + } + cordovaPlugins.map((plugin: any) => { + const frameworks = getPlatformElement(plugin, platform, 'framework'); + frameworks.map((framework: any) => { + if (!framework.$.type) { + const name = getFrameworkName(framework); + if (isFramework(framework)) { + if (framework.$.weak && framework.$.weak === 'true') { + if (!weakFrameworks.includes(name)) { + weakFrameworks.push(name); + } + } else if (framework.$.custom && framework.$.custom === 'true') { + const frameworktPath = join(sourcesFolderName, plugin.name, name); + if (!customFrameworks.includes(frameworktPath)) { + customFrameworks.push(frameworktPath); + } + } else { + if (!linkedFrameworks.includes(name)) { + linkedFrameworks.push(name); + } + } + } else { + if (!systemLibraries.includes(name)) { + systemLibraries.push(name); + } + } + } else if (framework.$.type && framework.$.type === 'podspec') { + let depString = `s.dependency '${framework.$.src}'`; + if (framework.$.spec && framework.$.spec !== '') { + depString += `, '${framework.$.spec}'`; + } + if (!frameworkDeps.includes(depString)) { + frameworkDeps.push(depString); + } + } + }); + prefsArray = prefsArray.concat(getAllElements(plugin, platform, 'preference')); + const podspecs = getPlatformElement(plugin, platform, 'podspec'); + podspecs.map((podspec: any) => { + podspec.pods.map((pods: any) => { + pods.pod.map((pod: any) => { + let depString = `s.dependency '${pod.$.name}'`; + if (pod.$.spec && pod.$.spec !== '') { + depString += `, '${pod.$.spec}'`; + } + if (!frameworkDeps.includes(depString)) { + frameworkDeps.push(depString); + } + }); + }); + }); + const sourceFiles = getPlatformElement(plugin, platform, 'source-file'); + sourceFiles.map((sourceFile: any) => { + if (sourceFile.$.framework && sourceFile.$.framework === 'true') { + let fileName = sourceFile.$.src.split('/').pop(); + if (!fileName.startsWith('lib')) { + fileName = 'lib' + fileName; + } + const frameworktPath = join(sourcesFolderName, plugin.name, fileName); + if (!sourceFrameworks.includes(frameworktPath)) { + sourceFrameworks.push(frameworktPath); + } + } else if (sourceFile.$['compiler-flags']) { + const cFlag = sourceFile.$['compiler-flags']; + if (!compilerFlags.includes(cFlag)) { + compilerFlags.push(cFlag); + } + } + }); + }); + const onlySystemLibraries = systemLibraries.filter((library) => removeNoSystem(library, sourceFrameworks)); + if (weakFrameworks.length > 0) { + frameworkDeps.push(`s.weak_frameworks = '${weakFrameworks.join(`', '`)}'`); + } + if (linkedFrameworks.length > 0) { + frameworkDeps.push(`s.frameworks = '${linkedFrameworks.join(`', '`)}'`); + } + if (onlySystemLibraries.length > 0) { + frameworkDeps.push(`s.libraries = '${onlySystemLibraries.join(`', '`)}'`); + } + if (customFrameworks.length > 0) { + frameworkDeps.push(`s.vendored_frameworks = '${customFrameworks.join(`', '`)}'`); + frameworkDeps.push( + `s.exclude_files = 'sources/**/*.framework/Headers/*.h', 'sources/**/*.framework/PrivateHeaders/*.h'`, + ); + } + if (sourceFrameworks.length > 0) { + frameworkDeps.push(`s.vendored_libraries = '${sourceFrameworks.join(`', '`)}'`); + } + if (compilerFlags.length > 0) { + frameworkDeps.push(`s.compiler_flags = '${compilerFlags.join(' ')}'`); + } + const arcPlugins = cordovaPlugins.filter(filterARCFiles); + if (arcPlugins.length > 0) { + frameworkDeps.push(`s.subspec 'noarc' do |sna| + sna.requires_arc = false + sna.source_files = 'noarc/**/*.{swift,h,m,c,cc,mm,cpp}' + end`); + } + let frameworksString = frameworkDeps.join('\n '); + frameworksString = await replaceFrameworkVariables(config, prefsArray, frameworksString); + const content = ` + Pod::Spec.new do |s| + s.name = '${name}' + s.version = '${config.cli.package.version}' + s.summary = 'Autogenerated spec' + s.license = 'Unknown' + s.homepage = 'https://example.com' + s.authors = { 'Capacitor Generator' => 'hi@example.com' } + s.source = { :git => 'https://github.com/ionic-team/does-not-exist.git', :tag => '${config.cli.package.version}' } + s.source_files = '${sourcesFolderName}/**/*.{swift,h,m,c,cc,mm,cpp}' + s.ios.deployment_target = '${config.ios.minVersion}' + s.xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1 WK_WEB_VIEW_ONLY=1' } + s.dependency 'CapacitorCordova'${getLinkerFlags(config)} + s.swift_version = '5.1' + ${frameworksString} + end`; + await writeFile(join(config.ios.cordovaPluginsDirAbs, `${name}.podspec`), content); +} + +function getLinkerFlags(config: Config) { + if (config.app.extConfig.ios?.cordovaLinkerFlags) { + return `\n s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '${config.app.extConfig.ios.cordovaLinkerFlags.join( + ' ', + )}' }`; + } + return ''; +} + +export async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]): Promise { + for (const p of cordovaPlugins) { + const sourceFiles = getPlatformElement(p, platform, 'source-file'); + const headerFiles = getPlatformElement(p, platform, 'header-file'); + const codeFiles = sourceFiles.concat(headerFiles); + const frameworks = getPlatformElement(p, platform, 'framework'); + let sourcesFolderName = 'sources'; + if (needsStaticPod(p)) { + sourcesFolderName += 'static'; + } + const sourcesFolder = join(config.ios.cordovaPluginsDirAbs, sourcesFolderName, p.name); + for (const codeFile of codeFiles) { + let fileName = codeFile.$.src.split('/').pop(); + const fileExt = codeFile.$.src.split('.').pop(); + if (fileExt === 'a' && !fileName.startsWith('lib')) { + fileName = 'lib' + fileName; + } + let destFolder = sourcesFolderName; + if (codeFile.$['compiler-flags'] && codeFile.$['compiler-flags'] === '-fno-objc-arc') { + destFolder = 'noarc'; + } + const filePath = getFilePath(config, p, codeFile.$.src); + const fileDest = join(config.ios.cordovaPluginsDirAbs, destFolder, p.name, fileName); + await copy(filePath, fileDest); + if (!codeFile.$.framework) { + let fileContent = await readFile(fileDest, { encoding: 'utf-8' }); + if (fileExt === 'swift') { + fileContent = 'import Cordova\n' + fileContent; + await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); + } else { + if (fileContent.includes('@import Firebase;')) { + fileContent = fileContent.replace('@import Firebase;', '#import '); + await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); + } + if ( + fileContent.includes('[NSBundle bundleForClass:[self class]]') || + fileContent.includes('[NSBundle bundleForClass:[CDVCapture class]]') + ) { + fileContent = fileContent.replace('[NSBundle bundleForClass:[self class]]', '[NSBundle mainBundle]'); + fileContent = fileContent.replace('[NSBundle bundleForClass:[CDVCapture class]]', '[NSBundle mainBundle]'); + await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); + } + if (fileContent.includes('[self.webView superview]') || fileContent.includes('self.webView.superview')) { + fileContent = fileContent.replace(/\[self.webView superview\]/g, 'self.viewController.view'); + fileContent = fileContent.replace(/self.webView.superview/g, 'self.viewController.view'); + await writeFile(fileDest, fileContent, { encoding: 'utf-8' }); + } + } + } + } + const resourceFiles = getPlatformElement(p, platform, 'resource-file'); + for (const resourceFile of resourceFiles) { + const fileName = resourceFile.$.src.split('/').pop(); + await copy( + getFilePath(config, p, resourceFile.$.src), + join(config.ios.cordovaPluginsDirAbs, 'resources', fileName), + ); + } + for (const framework of frameworks) { + if (framework.$.custom && framework.$.custom === 'true') { + await copy(getFilePath(config, p, framework.$.src), join(sourcesFolder, framework.$.src)); + } + } + } +} + +export async function removePluginsNativeFiles(config: Config): Promise { + await remove(config.ios.cordovaPluginsDirAbs); + await extractTemplate(config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, config.ios.cordovaPluginsDirAbs); +} + +export function filterARCFiles(plugin: Plugin): boolean { + const sources = getPlatformElement(plugin, platform, 'source-file'); + const sourcesARC = sources.filter( + (sourceFile: any) => sourceFile.$['compiler-flags'] && sourceFile.$['compiler-flags'] === '-fno-objc-arc', + ); + return sourcesARC.length > 0; +} + +function removeNoSystem(library: string, sourceFrameworks: string[]): boolean { + const libraries = sourceFrameworks.filter((framework) => framework.includes(library)); + return libraries.length === 0; +} + +async function replaceFrameworkVariables(config: Config, prefsArray: any[], frameworkString: string): Promise { + prefsArray.map((preference: any) => { + frameworkString = frameworkString.replace( + new RegExp(('$' + preference.$.name).replace('$', '\\$&'), 'g'), + preference.$.default, + ); + }); + return frameworkString; +} + +function getFrameworkName(framework: any): string { + if (isFramework(framework)) { + if (framework.$.custom && framework.$.custom === 'true') { + return framework.$.src; + } + return framework.$.src.substr(0, framework.$.src.indexOf('.')); + } + return framework.$.src.substr(0, framework.$.src.indexOf('.')).replace('lib', ''); +} + +function isFramework(framework: any) { + return framework.$.src.split('.').pop().includes('framework'); +} + +export function cordovaPodfileLines(config: Config, plugins: Plugin[]): string[] { + const pods: string[] = []; + + const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova); + cordovaPlugins.map(async (p) => { + const podspecs = getPlatformElement(p, platform, 'podspec'); + podspecs.map((podspec: any) => { + podspec.pods.map((pPods: any) => { + pPods.pod.map((pod: any) => { + if (pod.$.git) { + let gitRef = ''; + if (pod.$.tag) { + gitRef = `, :tag => '${pod.$.tag}'`; + } else if (pod.$.branch) { + gitRef = `, :branch => '${pod.$.branch}'`; + } else if (pod.$.commit) { + gitRef = `, :commit => '${pod.$.commit}'`; + } + pods.push(` pod '${pod.$.name}', :git => '${pod.$.git}'${gitRef}\n`); + } + }); + }); + }); + }); + const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p)); + const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el)); + if (noStaticPlugins.length > 0) { + pods.push(` pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'\n`); + } + if (staticPlugins.length > 0) { + pods.push(` pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'\n`); + } + const resourcesPlugins = cordovaPlugins.filter(filterResources); + if (resourcesPlugins.length > 0) { + pods.push(` pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'\n`); + } + + return pods; +} + +function filterResources(plugin: Plugin) { + const resources = getPlatformElement(plugin, platform, 'resource-file'); + return resources.length > 0; +} + +export async function generateCordovaPackageFiles(cordovaPlugins: Plugin[], config: Config): Promise { + cordovaPlugins.map((plugin: any) => { + generateCordovaPackageFile(plugin, config); + }); +} + +export async function generateCordovaPackageFile(p: Plugin, config: Config): Promise { + const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); + const iosVersion = getMajoriOSVersion(config); + const headerFiles = getPlatformElement(p, platform, 'header-file'); + let headersText = ''; + if (headerFiles.length > 0) { + headersText = `, + publicHeadersPath: "."`; + } + + const content = `// swift-tools-version: 5.9 + +import PackageDescription + +let package = Package( + name: "${p.name}", + platforms: [.iOS(.v${iosVersion})], + products: [ + .library( + name: "${p.name}", + targets: ["${p.name}"] + ) + ], + dependencies: [ + .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "${iosPlatformVersion}") + ], + targets: [ + .target( + name: "${p.name}", + dependencies: [ + .product(name: "Cordova", package: "capacitor-swift-pm") + ], + path: "."${headersText} + ) + ] +)`; + await writeFile(join(config.ios.cordovaPluginsDirAbs, 'sources', p.name, 'Package.swift'), content); +} diff --git a/cli/src/util/iosplugin.ts b/cli/src/util/iosplugin.ts index 8ce42b35e7..ed4f4fd54b 100644 --- a/cli/src/util/iosplugin.ts +++ b/cli/src/util/iosplugin.ts @@ -68,6 +68,7 @@ export async function generateIOSPackageJSON(config: Config, plugins: Plugin[]): const cordovaPlugins = await getCordovaPlugins(config, 'ios'); if (cordovaPlugins.length > 0) { classList.push('CDVPlugin'); + classList.push('CordovaPlugin'); } writePluginJSON(config, classList); } diff --git a/cli/src/util/spm.ts b/cli/src/util/spm.ts index 30e51cf1a4..58cc7ecd31 100644 --- a/cli/src/util/spm.ts +++ b/cli/src/util/spm.ts @@ -6,6 +6,7 @@ import { build, parse } from 'plist'; import { extract } from 'tar'; import { getCapacitorPackageVersion } from '../common'; +import { getCordovaPlugins } from '../cordova'; import type { Config } from '../definitions'; import { fatal } from '../errors'; import { getMajoriOSVersion } from '../ios/common'; @@ -98,6 +99,8 @@ export async function removeCocoapodsFiles(config: Config): Promise { export async function generatePackageText(config: Config, plugins: Plugin[]): Promise { const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); const iosVersion = getMajoriOSVersion(config); + const cordovaPlugins = await getCordovaPlugins(config, 'ios'); + const enableCordova = cordovaPlugins.length > 0; let packageSwiftText = `// swift-tools-version: 5.9 import PackageDescription @@ -129,8 +132,11 @@ let package = Package( .target( name: "CapApp-SPM", dependencies: [ - .product(name: "Capacitor", package: "capacitor-swift-pm"), - .product(name: "Cordova", package: "capacitor-swift-pm")`; + .product(name: "Capacitor", package: "capacitor-swift-pm"),`; + + if (enableCordova) { + packageSwiftText += ` .product(name: "Cordova", package: "capacitor-swift-pm")`; + } for (const plugin of plugins) { packageSwiftText += `,\n .product(name: "${plugin.ios?.name}", package: "${plugin.ios?.name}")`; diff --git a/ios/Capacitor.podspec b/ios/Capacitor.podspec index 9d392728d2..1b92962750 100644 --- a/ios/Capacitor.podspec +++ b/ios/Capacitor.podspec @@ -19,6 +19,5 @@ Pod::Spec.new do |s| 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.dependency 'CapacitorCordova' s.swift_version = '5.1' end diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index ed784d5bdd..9e5ac570fe 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -9,8 +9,6 @@ /* Begin PBXBuildFile section */ 0F83E885285A332E006C43CB /* AppUUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F83E884285A332D006C43CB /* AppUUID.swift */; }; 0F8F33B327DA980A003F49D6 /* PluginConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F33B127DA980A003F49D6 /* PluginConfig.swift */; }; - 2F81F5C926FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */; }; - 2F81F5CA26FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */; }; 373A69C1255C9360000A6F44 /* NotificationHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */; }; 373A69F2255C95D0000A6F44 /* NotificationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */; }; 501CBAA71FC0A723009B0D4D /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 501CBAA61FC0A723009B0D4D /* WebKit.framework */; }; @@ -157,8 +155,6 @@ /* Begin PBXFileReference section */ 0F83E884285A332D006C43CB /* AppUUID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUUID.swift; sourceTree = ""; }; 0F8F33B127DA980A003F49D6 /* PluginConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PluginConfig.swift; sourceTree = ""; }; - 2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CAPBridgeViewController+CDVScreenOrientationDelegate.h"; sourceTree = ""; }; - 2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CAPBridgeViewController+CDVScreenOrientationDelegate.m"; sourceTree = ""; }; 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandlerProtocol.swift; sourceTree = ""; }; 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRouter.swift; sourceTree = ""; }; 501CBAA61FC0A723009B0D4D /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; @@ -400,8 +396,6 @@ 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */, 62D43AEF2581817500673C24 /* WKWebView+Capacitor.swift */, 62D43B642582A13D00673C24 /* WKWebView+Capacitor.m */, - 2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */, - 2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */, 62959AE92524DA7700A3D7F1 /* UIColor.swift */, 62959AFE2524DA7700A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m */, 62959B122524DA7700A3D7F1 /* Info.plist */, @@ -512,7 +506,6 @@ 623D6909254C6FDF002D01D1 /* CAPInstanceDescriptor.h in Headers */, 62959B192524DA7800A3D7F1 /* CAPBridgedPlugin.h in Headers */, 621ECCB82542045900D3D615 /* CAPBridgedJSTypes.h in Headers */, - 2F81F5C926FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -724,7 +717,6 @@ 62959B1D2524DA7800A3D7F1 /* UIColor.swift in Sources */, 62959B332524DA7800A3D7F1 /* CAPPlugin.m in Sources */, 62959B1C2524DA7800A3D7F1 /* CAPPluginMethod.m in Sources */, - 2F81F5CA26FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m in Sources */, 62ADC0CA25CB678000E914DE /* PluginCallResult.swift in Sources */, 62959B472524DA7800A3D7F1 /* CAPNotifications.swift in Sources */, 62D43B652582A13D00673C24 /* WKWebView+Capacitor.m in Sources */, diff --git a/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift b/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift index 7f510bc5ac..d687953fea 100644 --- a/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift +++ b/ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift @@ -11,7 +11,6 @@ public class ApplicationDelegateProxy: NSObject, UIApplicationDelegate { "url": url, "options": options ]) - NotificationCenter.default.post(name: NSNotification.Name.CDVPluginHandleOpenURL, object: url) lastURL = url return true } diff --git a/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift b/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift index 24ee52a75d..98ac66d965 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift @@ -79,6 +79,9 @@ import WebKit func registerPluginType(_ pluginType: CAPPlugin.Type) func registerPluginInstance(_ pluginInstance: CAPPlugin) + // MARK: - Interceptors + func registerCallInterceptor(_ name: String, handler: @escaping ([String: Any]) -> Void) + // MARK: - View Presentation func showAlertWith(title: String, message: String, buttonTitle: String) @available(*, deprecated, message: "Use self?.bridge?.viewController?.present") diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h b/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h deleted file mode 100644 index aeda727d5f..0000000000 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import - -@interface CAPBridgeViewController (CDVScreenOrientationDelegate) - -@end - diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m b/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m deleted file mode 100644 index 94c169a0cc..0000000000 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m +++ /dev/null @@ -1,5 +0,0 @@ -#import "CAPBridgeViewController+CDVScreenOrientationDelegate.h" - -@implementation CAPBridgeViewController (CDVScreenOrientationDelegate) - -@end diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index a7285213d8..567d6c69a9 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -1,6 +1,5 @@ import UIKit import WebKit -import Cordova @objc open class CAPBridgeViewController: UIViewController { private var capacitorBridge: CapacitorBridge? @@ -47,10 +46,10 @@ import Cordova // create the bridge capacitorBridge = CapacitorBridge(with: configuration, delegate: self, - cordovaConfiguration: configDescriptor.cordovaConfiguration, assetHandler: assetHandler, delegationHandler: delegationHandler) capacitorDidLoad() + updateAppLocationIfNeeded() if configDescriptor.instanceType == .fixed { updateBinaryVersion() @@ -89,18 +88,25 @@ import Cordova - Note: This is called early in the View Controller's lifecycle. Not all properties will be set at invocation. */ open func instanceDescriptor() -> InstanceDescriptor { - let descriptor = InstanceDescriptor.init() - if !isNewBinary && !descriptor.cordovaDeployDisabled { + return InstanceDescriptor() + } + + /// This function must be called after plugins are loaded or it will have no effect. + open func updateAppLocationIfNeeded() { + let cordovaPlugin = bridge?.plugin(withName: "__CordovaPlugin") + let cordovaDeployDisabled = cordovaPlugin?.perform(Selector(("cordovaDeployDisabled"))).takeUnretainedValue() as? Bool ?? false + + if !isNewBinary && !cordovaDeployDisabled { if let persistedPath = KeyValueStore.standard["serverBasePath", as: String.self], !persistedPath.isEmpty { if let libPath = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first { - descriptor.appLocation = URL(fileURLWithPath: libPath, isDirectory: true) + let serverBasePath = URL(fileURLWithPath: libPath, isDirectory: true) .appendingPathComponent("NoCloud") .appendingPathComponent("ionic_built_snapshots") .appendingPathComponent(URL(fileURLWithPath: persistedPath, isDirectory: true).lastPathComponent) + setServerBasePath(path: serverBasePath.path) } } } - return descriptor } open func router() -> Router { diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h index f477ef55be..3ca447e6f8 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h @@ -2,7 +2,7 @@ #define CAPInstanceDescriptor_h @import UIKit; -@import Cordova; + typedef NS_ENUM(NSInteger, CAPInstanceType) { CAPInstanceTypeFixed NS_SWIFT_NAME(fixed), @@ -137,7 +137,7 @@ NS_SWIFT_NAME(InstanceDescriptor) /** @brief The parser used to load the cofiguration for Cordova plugins. */ -@property (nonatomic, copy, nonnull) CDVConfigParser *cordovaConfiguration; +@property (nonatomic, copy, nonnull) NSObject *cordovaConfiguration; /** @brief Warnings generated during initialization. */ diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m index 2d7a4fba91..acdbc08caf 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m @@ -45,7 +45,6 @@ - (void)_setDefaultsWithAppLocation:(NSURL*)location { _contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; _appLocation = location; _limitsNavigationsToAppBoundDomains = FALSE; - _cordovaConfiguration = [[CDVConfigParser alloc] init]; _warnings = 0; if (location == nil) { _warnings |= CAPInstanceWarningMissingAppDir; diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift index a7eff5f623..495de0634d 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift @@ -52,23 +52,6 @@ internal extension InstanceDescriptor { warnings.update(with: .missingFile) } - // parse the cordova configuration - var configParser: XMLParser? - if let cordovaURL = cordovaURL, - FileManager.default.fileExists(atPath: cordovaURL.path, isDirectory: &isDirectory), - isDirectory.boolValue == false { - configParser = XMLParser(contentsOf: cordovaURL) - } else { - warnings.update(with: .missingCordovaFile) - // we don't want to break up string literals - // swiftlint:disable:next line_length - if let cordovaXML = "".data(using: .utf8) { - configParser = XMLParser(data: cordovaXML) - } - } - configParser?.delegate = cordovaConfiguration - configParser?.parse() - // extract our configuration values if let config = config { // to be removed @@ -160,7 +143,7 @@ internal extension InstanceDescriptor { extension InstanceDescriptor { @objc public var cordovaDeployDisabled: Bool { - return (cordovaConfiguration.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false + return false } @objc public func normalize() { diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index 3a186d4e89..de2beb22e3 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -1,12 +1,11 @@ import Foundation import Dispatch import WebKit -import Cordova internal typealias CapacitorPlugin = CAPPlugin & CAPBridgedPlugin struct RegistrationList: Codable { - let packageClassList: Set + var packageClassList: Set } /** @@ -113,18 +112,16 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { @objc public var config: InstanceConfiguration // Map of all loaded and instantiated plugins by pluginId -> instance var plugins = [String: CapacitorPlugin]() - // Manager for getting Cordova plugins - var cordovaPluginManager: CDVPluginManager? // Calls we are storing to resolve later var storedCalls = ConcurrentDictionary() // Whether to inject the Cordova files - private var injectCordovaFiles = false - private var cordovaParser: CDVConfigParser? + private var cordovaIsPresent = false private var injectMiscFiles: [String] = [] private var canInjectJS: Bool = true // Background dispatch queue for plugin calls open private(set) var dispatchQueue = DispatchQueue(label: "bridge") + internal private(set) var callInterceptors: [String: ([String: Any]) -> Void] = [:] // Array of block based observers var observers: [NSObjectProtocol] = [] @@ -201,12 +198,17 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { // MARK: - Initialization - public init(with configuration: InstanceConfiguration, delegate bridgeDelegate: CAPBridgeDelegate, cordovaConfiguration: CDVConfigParser, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler, autoRegisterPlugins: Bool = true) { + @available(*, deprecated, renamed: "init", message: "Use different init") + public convenience init(with configuration: InstanceConfiguration, delegate bridgeDelegate: CAPBridgeDelegate, cordovaConfiguration: Any, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler, autoRegisterPlugins: Bool = true) { + self.init(with: configuration, delegate: bridgeDelegate, assetHandler: assetHandler, delegationHandler: delegationHandler, autoRegisterPlugins: autoRegisterPlugins) + } + + public init(with configuration: InstanceConfiguration, delegate bridgeDelegate: CAPBridgeDelegate, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler, autoRegisterPlugins: Bool = true) { + self.bridgeDelegate = bridgeDelegate self.webViewAssetHandler = assetHandler self.webViewDelegationHandler = delegationHandler self.config = configuration - self.cordovaParser = cordovaConfiguration self.notificationRouter = NotificationRouter() self.notificationRouter.handleApplicationNotifications = configuration.handleApplicationNotifications self.autoRegisterPlugins = autoRegisterPlugins @@ -216,7 +218,7 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { exportCoreJS(localUrl: configuration.localURL.absoluteString) registerPlugins() - setupCordovaCompatibility() + setupListeners() exportMiscJS() canInjectJS = false @@ -255,31 +257,27 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { injectMiscFiles.removeAll() } - /** - Set up our Cordova compat by loading all known Cordova plugins and injecting their JS. - */ - func setupCordovaCompatibility() { - if injectCordovaFiles { - exportCordovaJS() - registerCordovaPlugins() - } else { - observers.append(NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in - self?.triggerDocumentJSEvent(eventName: "resume") - }) - observers.append(NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in - self?.triggerDocumentJSEvent(eventName: "pause") - }) - } - } - - /** - Export the core Cordova JS runtime - */ - func exportCordovaJS() { - do { - try JSExport.exportCordovaJS(userContentController: webViewDelegationHandler.contentController) - } catch { - type(of: self).fatalError(error, error) + func setupListeners() { + if !cordovaIsPresent { + observers.append( + NotificationCenter.default.addObserver( + forName: UIApplication.willEnterForegroundNotification, + object: nil, + queue: .main + ) { [weak self] _ in + self?.triggerDocumentJSEvent(eventName: "resume") + } + ) + + observers.append( + NotificationCenter.default.addObserver( + forName: UIApplication.didEnterBackgroundNotification, + object: nil, + queue: .main + ) { [weak self] _ in + self?.triggerDocumentJSEvent(eventName: "pause") + } + ) } } @@ -302,14 +300,13 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { do { if let pluginJSON = Bundle.main.url(forResource: "capacitor.config", withExtension: "json") { let pluginData = try Data(contentsOf: pluginJSON) - let registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData) + var registrationList = try JSONDecoder().decode(RegistrationList.self, from: pluginData) for plugin in registrationList.packageClassList { - if let pluginClass = NSClassFromString(plugin) { - if pluginClass == CDVPlugin.self { - injectCordovaFiles = true - } else { - pluginList.append(pluginClass) + if let pluginClass = NSClassFromString(plugin), pluginClass is CAPPlugin.Type { + pluginList.append(pluginClass) + if plugin == "CordovaPlugin" { + cordovaIsPresent = true } } } @@ -386,6 +383,10 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { // MARK: - CAPBridgeProtocol: Call Management + public func registerCallInterceptor(_ name: String, handler: @escaping ([String: Any]) -> Void) { + callInterceptors[name] = handler + } + @objc public func saveCall(_ call: CAPPluginCall) { storedCalls[call.callbackId] = call } @@ -418,23 +419,6 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { return self.dispatchQueue } - func registerCordovaPlugins() { - guard let cordovaParser = cordovaParser else { - return - } - cordovaPluginManager = CDVPluginManager.init(parser: cordovaParser, viewController: self.viewController, webView: self.getWebView()) - if cordovaParser.startupPluginNames.count > 0 { - for pluginName in cordovaParser.startupPluginNames { - _ = cordovaPluginManager?.getCommandInstance(pluginName as? String) - } - } - do { - try JSExport.exportCordovaPluginsJS(userContentController: webViewDelegationHandler.contentController) - } catch { - type(of: self).fatalError(error, error) - } - } - func reload() { self.getWebView()?.reload() } @@ -533,34 +517,6 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol { } } - /** - Handle a Cordova call from JavaScript. First, find the corresponding plugin, - construct a selector, and perform that selector on the plugin instance. - */ - func handleCordovaJSCall(call: JSCall) { - // Create a selector to send to the plugin - - if let plugin = self.cordovaPluginManager?.getCommandInstance(call.pluginId.lowercased()) { - let selector = NSSelectorFromString("\(call.method):") - if !plugin.responds(to: selector) { - CAPLog.print("Error: Plugin \(plugin.className ?? "") does not respond to method call \(selector).") - CAPLog.print("Ensure plugin method exists and uses @objc in its declaration") - return - } - - let arguments: [Any] = call.options["options"] as? [Any] ?? [] - let pluginCall = CDVInvokedUrlCommand(arguments: arguments, - callbackId: call.callbackId, - className: plugin.className, - methodName: call.method) - plugin.perform(selector, with: pluginCall) - - } else { - CAPLog.print("Error: Cordova Plugin mapping not found") - return - } - } - func removeAllPluginListeners() { for plugin in plugins.values { plugin.perform(#selector(CAPPlugin.removeAllListeners(_:)), with: nil) diff --git a/ios/Capacitor/Capacitor/JS.swift b/ios/Capacitor/Capacitor/JS.swift index 46972c48a5..b1198b1786 100644 --- a/ios/Capacitor/Capacitor/JS.swift +++ b/ios/Capacitor/Capacitor/JS.swift @@ -14,11 +14,11 @@ public typealias JSResultBody = [String: Any] /** * A call originating from JavaScript land */ -internal struct JSCall { - let options: [String: Any] - let pluginId: String - let method: String - let callbackId: String +public struct JSCall { + public let options: [String: Any] + public let pluginId: String + public let method: String + public let callbackId: String } internal protocol JSResultProtocol { diff --git a/ios/Capacitor/Capacitor/JSExport.swift b/ios/Capacitor/Capacitor/JSExport.swift index f6934c17a7..c51da733df 100644 --- a/ios/Capacitor/Capacitor/JSExport.swift +++ b/ios/Capacitor/Capacitor/JSExport.swift @@ -35,24 +35,6 @@ internal class JSExport { } } - static func exportCordovaJS(userContentController: WKUserContentController) throws { - guard let cordovaUrl = Bundle.main.url(forResource: "public/cordova", withExtension: "js") else { - CAPLog.print("ERROR: Required cordova.js file not found. Cordova plugins will not function!") - throw CapacitorBridgeError.errorExportingCoreJS - } - guard let cordovaPluginsUrl = Bundle.main.url(forResource: "public/cordova_plugins", withExtension: "js") else { - CAPLog.print("ERROR: Required cordova_plugins.js file not found. Cordova plugins will not function!") - throw CapacitorBridgeError.errorExportingCoreJS - } - do { - try self.injectFile(fileURL: cordovaUrl, userContentController: userContentController) - try self.injectFile(fileURL: cordovaPluginsUrl, userContentController: userContentController) - } catch { - CAPLog.print("ERROR: Unable to read required cordova files. Cordova plugins will not function!") - throw CapacitorBridgeError.errorExportingCoreJS - } - } - static func exportMiscFileJS(paths: [String], userContentController: WKUserContentController) { for path in paths { if let miscJSFilePath = Bundle.main.url(forResource: "public/\(path.replacingOccurrences(of: ".js", with: ""))", withExtension: "js") { @@ -184,28 +166,6 @@ internal class JSExport { return lines.joined(separator: "\n") } - static func exportCordovaPluginsJS(userContentController: WKUserContentController) throws { - if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) { - self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) - } - } - - static func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) { - let fileManager = FileManager.default - do { - let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: []) - for fileURL in fileURLs { - if fileURL.hasDirectoryPath { - injectFilesForFolder(folder: fileURL, userContentController: userContentController) - } else { - try self.injectFile(fileURL: fileURL, userContentController: userContentController) - } - } - } catch { - CAPLog.print("Error while enumerating files") - } - } - static func injectFile(fileURL: URL, userContentController: WKUserContentController) throws { do { let data = try String(contentsOf: fileURL, encoding: .utf8) diff --git a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift index 2b4a256d00..37deace97b 100644 --- a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift @@ -208,17 +208,8 @@ open class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDelegat } bridge.handleJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId)) - } else if type == "cordova" { - let pluginId = dict["service"] as? String ?? "" - let method = dict["action"] as? String ?? "" - let callbackId = dict["callbackId"] as? String ?? "" - - let args = dict["actionArgs"] as? Array ?? [] - let options = ["options": args] - - CAPLog.print("To Native Cordova -> ", pluginId, method, callbackId, options) - - bridge.handleCordovaJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId)) + } else if let handler = bridge.callInterceptors[type] { + handler(dict) } } } diff --git a/ios/Capacitor/CapacitorTests/ConfigurationTests.swift b/ios/Capacitor/CapacitorTests/ConfigurationTests.swift index 778bfd688d..177f211e7d 100644 --- a/ios/Capacitor/CapacitorTests/ConfigurationTests.swift +++ b/ios/Capacitor/CapacitorTests/ConfigurationTests.swift @@ -33,7 +33,6 @@ class ConfigurationTests: XCTestCase { let descriptor = InstanceDescriptor.init() XCTAssertTrue(descriptor.warnings.contains(.missingAppDir)) XCTAssertTrue(descriptor.warnings.contains(.missingFile)) - XCTAssertTrue(descriptor.warnings.contains(.missingCordovaFile)) } func testMissingAppDetection() throws { diff --git a/ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m b/ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m index 0295cfa822..4d665aeec1 100644 --- a/ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m +++ b/ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m @@ -21,7 +21,7 @@ - (void)setUp { @"testDateString": [formatter stringFromDate:date], @"testBoolTrue": @TRUE, @"testBoolFalse": @FALSE}; - [self setCall:[[CAPPluginCall alloc] initWithCallbackId:@"test" options:options success:NULL error:NULL]]; + [self setCall:[[CAPPluginCall alloc] initWithCallbackId:@"test" methodName:@"test" options:options success:NULL error:NULL]]; } - (void)testStringAccessor { diff --git a/ios/CapacitorCordova.podspec b/ios/CapacitorCordova.podspec index 65fcce86d7..b4aa014f30 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, 15.0 - s.source_files = "#{prefix}CapacitorCordova/CapacitorCordova/**/*.{h,m}" + s.source_files = "#{prefix}CapacitorCordova/CapacitorCordova/**/*.{h,m,swift}" s.public_header_files = "#{prefix}CapacitorCordova/CapacitorCordova/Classes/Public/*.h", "#{prefix}CapacitorCordova/CapacitorCordova/CapacitorCordova.h" s.module_map = "#{prefix}CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap" s.resource_bundles = { 'CapacitorCordova' => ["#{prefix}CapacitorCordova/CapacitorCordova/PrivacyInfo.xcprivacy"] } s.requires_arc = true + s.dependency 'Capacitor', s.version.to_s s.framework = 'WebKit' end diff --git a/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift new file mode 100644 index 0000000000..d58c4ca33f --- /dev/null +++ b/ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift @@ -0,0 +1,126 @@ +import Capacitor + +@objc(CordovaPlugin) +public class CordovaPlugin: CAPPlugin, CAPBridgedPlugin { + public let jsName = "__CordovaPlugin" + public let pluginMethods: [CAPPluginMethod] = [] + public var identifier: String { jsName } + + private var _cordovaDeployDisabled = false + + override public func load() { + injectJavascript() + configureRuntime() + } + + func configureRuntime() { + guard let configURL = Bundle.main.url(forResource: "config", withExtension: "xml") else { fatalError() } + guard let bridge, let webView = bridge.webView else { return } + + let cordovaConfigParser = CDVConfigParser() + + let xmlParser = XMLParser(contentsOf: configURL) + xmlParser?.delegate = cordovaConfigParser + xmlParser?.parse() + + guard let pluginManager = CDVPluginManager( + parser: cordovaConfigParser, + viewController: bridge.viewController, + webView: bridge.webView + ) else { return } + + for plugin in cordovaConfigParser.startupPluginNames.compactMap({ $0 as? String }) { + _ = pluginManager.getCommandInstance(plugin) + } + + exportCordovaPluginsJS(userContentController: webView.configuration.userContentController) + + bridge.registerCallInterceptor("cordova") { [pluginManager] dict in + let pluginId = dict["service"] as? String ?? "" + let method = dict["action"] as? String ?? "" + let callbackId = dict["callbackId"] as? String ?? "" + + let args = dict["actionArgs"] as? Array ?? [] + let options = ["options": args] + + CAPLog.print("To Native Cordova -> ", pluginId, method, callbackId, options) + + if let plugin = pluginManager.getCommandInstance(pluginId.lowercased()) { + let selector = NSSelectorFromString("\(method):") + if !plugin.responds(to: selector) { + CAPLog.print("Error: Plugin \(plugin.className ?? "") does not respond to method call \(selector).") + CAPLog.print("Ensure plugin method exists and uses @objc in its declaration") + return + } + + let arguments = options["options"] ?? [] + let pluginCall = CDVInvokedUrlCommand( + arguments: arguments, + callbackId: callbackId, + className: plugin.className, + methodName: method + ) + + plugin.perform(selector, with: pluginCall) + + } else { + CAPLog.print("Error: Cordova Plugin mapping not found") + return + } + } + + _cordovaDeployDisabled = (cordovaConfigParser.settings?["DisableDeploy".lowercased()] as? NSString)?.boolValue ?? false + } + + @objc + func cordovaDeployDisabled() -> NSNumber { + return _cordovaDeployDisabled as NSNumber + } + + func injectJavascript() { + guard let cordovaUrl = Bundle.main.url(forResource: "public/cordova", withExtension: "js") else { + fatalError("ERROR: Required cordova.js file not found. Cordova plugins will not function!") + } + + guard let cordovaPluginsUrl = Bundle.main.url(forResource: "public/cordova_plugins", withExtension: "js") else { + fatalError("ERROR: Required cordova_plugins.js file not found. Cordova plugins will not function!") + } + + guard let webView = bridge?.webView else { return } + + injectFile(fileURL: cordovaUrl, userContentController: webView.configuration.userContentController) + injectFile(fileURL: cordovaPluginsUrl, userContentController: webView.configuration.userContentController) + } + + func exportCordovaPluginsJS(userContentController: WKUserContentController) { + if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) { + injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) + } + } + + func injectFile(fileURL: URL, userContentController: WKUserContentController) { + do { + let data = try String(contentsOf: fileURL, encoding: .utf8) + let userScript = WKUserScript(source: data, injectionTime: .atDocumentStart, forMainFrameOnly: true) + userContentController.addUserScript(userScript) + } catch { + fatalError("Unable to inject js file") + } + } + + func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) { + let fileManager = FileManager.default + do { + let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: []) + for fileURL in fileURLs { + if fileURL.hasDirectoryPath { + injectFilesForFolder(folder: fileURL, userContentController: userContentController) + } else { + injectFile(fileURL: fileURL, userContentController: userContentController) + } + } + } catch { + CAPLog.print("Error while enumerating files") + } + } +} From dd83b9920788d7c976475263e2befce95a7fbb54 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Thu, 19 Mar 2026 12:27:24 -0400 Subject: [PATCH 13/69] Upgrade to major version pre-release --- android/package.json | 4 ++-- cli/package.json | 2 +- core/package.json | 2 +- ios/package.json | 4 ++-- lerna.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/package.json b/android/package.json index 9555484901..d1f6155219 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "8.2.0", + "version": "9.0.0-dev.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -23,7 +23,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^8.2.0" + "@capacitor/core": "^9.0.0-dev.0" }, "publishConfig": { "access": "public" diff --git a/cli/package.json b/cli/package.json index d488db7dbc..9f9d89eed9 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "8.2.0", + "version": "9.0.0-dev.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/package.json b/core/package.json index 98e5b1b7e0..674c151eff 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "8.2.0", + "version": "9.0.0-dev.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/package.json b/ios/package.json index 86e5c661d6..477e3c881b 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "8.2.0", + "version": "9.0.0-dev.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^8.2.0" + "@capacitor/core": "^9.0.0-dev.0" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index 1b708a45da..cac40342b5 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "8.2.0", + "version": "9.0.0-dev.0", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From 05730dedb2283d7a4d03ece908711e822e91dde4 Mon Sep 17 00:00:00 2001 From: Pedro Bilro Date: Mon, 23 Mar 2026 18:16:46 +0000 Subject: [PATCH 14/69] fix(cli)!: Remove default port for --live-reload (#8376) --- cli/src/tasks/run.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/cli/src/tasks/run.ts b/cli/src/tasks/run.ts index 2abdb9cd2c..8abe6fa47e 100644 --- a/cli/src/tasks/run.ts +++ b/cli/src/tasks/run.ts @@ -43,9 +43,6 @@ export async function runCommand( options: RunCommandOptions, ): Promise { options.host = options.host ?? CapLiveReloadHelper.getIpAddress() ?? 'localhost'; - if (!options.https && !options.port) { - options.port = '3000'; - } if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) { const platformDir = resolvePlatform(config, selectedPlatformName); if (platformDir) { From 65ad1fd55b9793a3e078e4a837a00e2f28c75c17 Mon Sep 17 00:00:00 2001 From: Pedro Bilro Date: Fri, 24 Apr 2026 10:36:02 +0100 Subject: [PATCH 15/69] fix(android): Handle activity result without plugin handle (#8436) --- android/capacitor/src/main/java/com/getcapacitor/Bridge.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 4868db3d0e..0a787ffe29 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -1270,6 +1270,7 @@ boolean onActivityResult(int requestCode, int resultCode, Intent data) { // This is a bit hacky but required to return the boolean out of the cordova interface return cordovaPlugin.hasRequiredPermissions(); } + return false; } // deprecated, to be removed From e37aad87bb22afc6d974190e0a59221dcc863b5b Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 29 Apr 2026 19:52:12 +0200 Subject: [PATCH 16/69] refactor(android-template)!: remove capacitor-cordova-android-plugins module (#8446) --- android-template/app/build.gradle | 7 ------- android-template/settings.gradle | 2 -- 2 files changed, 9 deletions(-) diff --git a/android-template/app/build.gradle b/android-template/app/build.gradle index 363eb2f77d..b701096d4b 100644 --- a/android-template/app/build.gradle +++ b/android-template/app/build.gradle @@ -24,12 +24,6 @@ android { } } -repositories { - flatDir{ - dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' - } -} - dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" @@ -39,7 +33,6 @@ dependencies { testImplementation "junit:junit:$junitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" - implementation project(':capacitor-cordova-android-plugins') } apply from: 'capacitor.build.gradle' diff --git a/android-template/settings.gradle b/android-template/settings.gradle index 3b4431d772..fb68af1dfb 100644 --- a/android-template/settings.gradle +++ b/android-template/settings.gradle @@ -1,5 +1,3 @@ include ':app' -include ':capacitor-cordova-android-plugins' -project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') apply from: 'capacitor.settings.gradle' \ No newline at end of file From 9e85905bf36c1e2300994e231cf10e829f2074ed Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 30 Apr 2026 10:11:41 +0200 Subject: [PATCH 17/69] fix(cli): conditionally add capacitor-cordova-android-plugins module (#8449) --- cli/src/android/update.ts | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/cli/src/android/update.ts b/cli/src/android/update.ts index fe22659b1a..d2e5fcabd1 100644 --- a/cli/src/android/update.ts +++ b/cli/src/android/update.ts @@ -51,8 +51,10 @@ export async function updateAndroid(config: Config): Promise { await handleCordovaPluginsJS(cordovaPlugins, config, platform); await checkPluginDependencies(plugins, platform, config.app.extConfig.cordova?.failOnUninstalledPlugins); await installGradlePlugins(config, capacitorPlugins, cordovaPlugins); - await handleCordovaPluginsGradle(config, cordovaPlugins); - await writeCordovaAndroidManifest(cordovaPlugins, config, platform); + if (enableCordova) { + await handleCordovaPluginsGradle(config, cordovaPlugins); + await writeCordovaAndroidManifest(cordovaPlugins, config, platform); + } const incompatibleCordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Incompatible); printPlugins(incompatibleCordovaPlugins, platform, 'incompatible'); @@ -155,6 +157,13 @@ export async function installGradlePlugins( ); } + const enableCordova = cordovaPlugins.length > 0; + const cdvIncludeText = enableCordova + ? ` +include ':capacitor-cordova-android-plugins' +project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')` + : ''; + const capacitorAndroidPath = resolve(dirname(capacitorAndroidPackagePath), 'capacitor'); const settingsPath = config.android.platformDirAbs; @@ -163,6 +172,7 @@ export async function installGradlePlugins( const settingsLines = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN include ':capacitor-android' project(':capacitor-android').projectDir = new File('${relativeCapcitorAndroidPath}') +${cdvIncludeText} ${capacitorPlugins .map((p) => { if (!p.android) { @@ -202,6 +212,18 @@ project(':${getGradlePackageName(p.id)}').projectDir = new File('${relativePlugi }); prefsArray = prefsArray.concat(getAllElements(p, platform, 'preference')); }); + const cdvRepoApplyText = enableCordova + ? ` +repositories { + flatDir{ + dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' + } +} + +apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" +` + : ''; + const cdvDependencyText = enableCordova ? ` implementation project(':capacitor-cordova-android-plugins')\n` : ''; let frameworkString = frameworksArray.join('\n'); frameworkString = await replaceFrameworkVariables(config, prefsArray, frameworkString); const dependencyLines = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN @@ -212,14 +234,13 @@ android { targetCompatibility JavaVersion.VERSION_21 } } - -apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" +${cdvRepoApplyText} dependencies { -${capacitorPlugins - .map((p) => { - return ` implementation project(':${getGradlePackageName(p.id)}')`; - }) - .join('\n')} +${cdvDependencyText}${capacitorPlugins + .map((p) => { + return ` implementation project(':${getGradlePackageName(p.id)}')`; + }) + .join('\n')} ${frameworkString} } ${applyArray.join('\n')} From e136b3f0cdd1437cac5234576301ed388edf85e7 Mon Sep 17 00:00:00 2001 From: "Alex J." Date: Tue, 5 May 2026 10:22:40 -0400 Subject: [PATCH 18/69] fix(android): conditionally add Cordova (#8453) --- android/capacitor-cordova/build.gradle | 45 +++++++++++++++++++ android/capacitor-cordova/proguard-rules.pro | 4 ++ .../src/main/AndroidManifest.xml | 3 ++ .../CapacitorCordovaCookieManager.java | 0 .../getcapacitor/cordova/CordovaPlugin.java | 1 - .../cordova/MockCordovaInterfaceImpl.java | 0 .../cordova/MockCordovaWebViewImpl.java | 0 android/capacitor/build.gradle | 4 +- android/capacitor/proguard-rules.pro | 6 --- .../main/java/com/getcapacitor/Bridge.java | 2 +- android/package.json | 5 ++- android/settings.gradle | 5 +++ cli/src/android/update.ts | 19 +++++--- 13 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 android/capacitor-cordova/build.gradle create mode 100644 android/capacitor-cordova/proguard-rules.pro create mode 100644 android/capacitor-cordova/src/main/AndroidManifest.xml rename android/{capacitor => capacitor-cordova}/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java (100%) rename android/{capacitor => capacitor-cordova}/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java (99%) rename android/{capacitor => capacitor-cordova}/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java (100%) rename android/{capacitor => capacitor-cordova}/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java (100%) diff --git a/android/capacitor-cordova/build.gradle b/android/capacitor-cordova/build.gradle new file mode 100644 index 0000000000..717c6b1aa2 --- /dev/null +++ b/android/capacitor-cordova/build.gradle @@ -0,0 +1,45 @@ +ext { + androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1' + cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '14.0.1' +} + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.13.0' + } +} + +apply plugin: 'com.android.library' + +android { + namespace = "com.getcapacitor.cordova" + compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36 + defaultConfig { + minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36 + versionCode 1 + versionName "1.0" + consumerProguardFiles 'proguard-rules.pro' + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_21 + targetCompatibility JavaVersion.VERSION_21 + } + publishing { + singleVariant("release") + } +} + +repositories { + google() + mavenCentral() +} + +dependencies { + implementation project(':capacitor-android') + implementation "org.apache.cordova:framework:$cordovaAndroidVersion" +} diff --git a/android/capacitor-cordova/proguard-rules.pro b/android/capacitor-cordova/proguard-rules.pro new file mode 100644 index 0000000000..9a21635529 --- /dev/null +++ b/android/capacitor-cordova/proguard-rules.pro @@ -0,0 +1,4 @@ +-keep public class * extends org.apache.cordova.* { + public ; + public ; +} diff --git a/android/capacitor-cordova/src/main/AndroidManifest.xml b/android/capacitor-cordova/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..9a40236b94 --- /dev/null +++ b/android/capacitor-cordova/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java b/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java similarity index 100% rename from android/capacitor/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java rename to android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java b/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java similarity index 99% rename from android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java rename to android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java index efe101034d..5fe4e74b5b 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java +++ b/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/CordovaPlugin.java @@ -5,7 +5,6 @@ import android.webkit.WebView; import com.getcapacitor.Logger; import com.getcapacitor.Plugin; -import com.getcapacitor.android.R; import com.getcapacitor.annotation.CapacitorPlugin; import java.util.List; import org.apache.cordova.ConfigXmlParser; diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java b/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java similarity index 100% rename from android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java rename to android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java b/android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java similarity index 100% rename from android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java rename to android/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index 6ece3eda05..5841ded35d 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -8,7 +8,6 @@ ext { junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2' androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0' androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0' - cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '14.0.1' } @@ -87,8 +86,7 @@ dependencies { testImplementation "junit:junit:$junitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" - implementation "org.apache.cordova:framework:$cordovaAndroidVersion" - testImplementation 'org.json:json:20250517' +testImplementation 'org.json:json:20250517' testImplementation 'org.mockito:mockito-core:5.20.0' } diff --git a/android/capacitor/proguard-rules.pro b/android/capacitor/proguard-rules.pro index 96db065b39..d34e7df58e 100644 --- a/android/capacitor/proguard-rules.pro +++ b/android/capacitor/proguard-rules.pro @@ -20,9 +20,3 @@ -keep @com.getcapacitor.NativePlugin public class * { @com.getcapacitor.PluginMethod public ; } - -# Rules for Cordova plugins --keep public class * extends org.apache.cordova.* { - public ; - public ; -} \ No newline at end of file diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 0a787ffe29..0ca600aff6 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -418,7 +418,7 @@ private boolean isNewBinary() { } private Plugin cordova() { - PluginHandle handle = getPlugin("__CordovaHandle"); + PluginHandle handle = getPlugin("__CordovaPlugin"); if (handle != null) { return handle.getInstance(); } diff --git a/android/package.json b/android/package.json index d1f6155219..483d629955 100644 --- a/android/package.json +++ b/android/package.json @@ -17,7 +17,10 @@ "capacitor/lint-baseline.xml", "capacitor/lint.xml", "capacitor/proguard-rules.pro", - "capacitor/src/main/" + "capacitor/src/main/", + "capacitor-cordova/build.gradle", + "capacitor-cordova/proguard-rules.pro", + "capacitor-cordova/src/main/" ], "scripts": { "verify": "./gradlew clean lint build test -b capacitor/build.gradle" diff --git a/android/settings.gradle b/android/settings.gradle index e69de29bb2..85956d0c11 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -0,0 +1,5 @@ +include ':capacitor-android' +project(':capacitor-android').projectDir = new File('capacitor') + +include ':capacitor-cordova-android' +project(':capacitor-cordova-android').projectDir = new File('capacitor-cordova') diff --git a/cli/src/android/update.ts b/cli/src/android/update.ts index d2e5fcabd1..5053f639d5 100644 --- a/cli/src/android/update.ts +++ b/cli/src/android/update.ts @@ -158,17 +158,22 @@ export async function installGradlePlugins( } const enableCordova = cordovaPlugins.length > 0; - const cdvIncludeText = enableCordova - ? ` -include ':capacitor-cordova-android-plugins' -project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')` - : ''; const capacitorAndroidPath = resolve(dirname(capacitorAndroidPackagePath), 'capacitor'); + const capacitorCordovaAndroidPath = resolve(dirname(capacitorAndroidPackagePath), 'capacitor-cordova'); const settingsPath = config.android.platformDirAbs; const dependencyPath = config.android.appDirAbs; const relativeCapcitorAndroidPath = convertToUnixPath(relative(settingsPath, capacitorAndroidPath)); + const relativeCapacitorCordovaAndroidPath = convertToUnixPath(relative(settingsPath, capacitorCordovaAndroidPath)); + const cdvIncludeText = enableCordova + ? ` +include ':capacitor-cordova-android' +project(':capacitor-cordova-android').projectDir = new File('${relativeCapacitorCordovaAndroidPath}') + +include ':capacitor-cordova-android-plugins' +project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')` + : ''; const settingsLines = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN include ':capacitor-android' project(':capacitor-android').projectDir = new File('${relativeCapcitorAndroidPath}') @@ -223,7 +228,9 @@ repositories { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" ` : ''; - const cdvDependencyText = enableCordova ? ` implementation project(':capacitor-cordova-android-plugins')\n` : ''; + const cdvDependencyText = enableCordova + ? ` implementation project(':capacitor-cordova-android')\n implementation project(':capacitor-cordova-android-plugins')\n` + : ''; let frameworkString = frameworksArray.join('\n'); frameworkString = await replaceFrameworkVariables(config, prefsArray, frameworkString); const dependencyLines = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN From f1e9e15e0474f807513ca6cf793df7a283f311b5 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Thu, 7 May 2026 09:56:41 -0400 Subject: [PATCH 19/69] chore: fixed errors introduced by merge --- cli/src/ios/update.ts | 11 +---------- cli/src/util/cordova-ios.ts | 5 +++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index afdd389c61..4bce181464 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -8,12 +8,7 @@ import type { Config } from '../definitions'; import { fatal } from '../errors'; import { logger } from '../log'; import type { Plugin } from '../plugin'; -import { - PluginType, - getPluginType, - getPlugins, - printPlugins, -} from '../plugin'; +import { PluginType, getPluginType, getPlugins, printPlugins } from '../plugin'; import { copy as copyTask } from '../tasks/copy'; import { generateCordovaPodspecs, @@ -22,8 +17,6 @@ import { removePluginsNativeFiles, cordovaPodfileLines, } from '../util/cordova-ios'; - -import { setAllStringIn } from '../tasks/migrate'; import { convertToUnixPath } from '../util/fs'; import { generateIOSPackageJSON } from '../util/iosplugin'; import { resolveNode } from '../util/node'; @@ -186,5 +179,3 @@ async function getPluginsTask(config: Config) { return iosPlugins; }); } - - diff --git a/cli/src/util/cordova-ios.ts b/cli/src/util/cordova-ios.ts index f16bffee98..5fc3c1a8c4 100644 --- a/cli/src/util/cordova-ios.ts +++ b/cli/src/util/cordova-ios.ts @@ -169,10 +169,10 @@ function getLinkerFlags(config: Config) { return ''; } -export async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) { +export async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]): Promise { const isSPM = (await config.ios.packageManager) === 'SPM'; for (const p of cordovaPlugins) { - const platformTag = getPluginPla tform(p, platform); + const platformTag = getPluginPlatform(p, platform); if (platformTag.$?.package) { continue; } @@ -245,6 +245,7 @@ export async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plu } } } + export async function removePluginsNativeFiles(config: Config): Promise { await remove(config.ios.cordovaPluginsDirAbs); await extractTemplate(config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, config.ios.cordovaPluginsDirAbs); From 23ad7f8a4231a3b78abec32bb67bbd0c415b5011 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of markemer)" Date: Thu, 7 May 2026 16:55:19 +0000 Subject: [PATCH 20/69] Release 8.3.2 --- CHANGELOG.md | 14 ++++++++++++++ android/CHANGELOG.md | 6 ++++++ android/package.json | 2 +- cli/CHANGELOG.md | 13 +++++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 4 ++++ core/package.json | 2 +- ios/CHANGELOG.md | 4 ++++ ios/package.json | 2 +- lerna.json | 2 +- 10 files changed, 46 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95ee704f66..3851a6663d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) + +### Bug Fixes + +- **cli:** add cSettings support for compiler flags in generated Package.swift ([#8448](https://github.com/ionic-team/capacitor/issues/8448)) ([0bd0676](https://github.com/ionic-team/capacitor/commit/0bd0676315c5fd77e50312dd7b5bf4990dcbd7d0)) +- **cli:** add system framework and weak framework support in SPM Package.swift ([#8447](https://github.com/ionic-team/capacitor/issues/8447)) ([3232f0f](https://github.com/ionic-team/capacitor/commit/3232f0fe1d9811b0b5c500e3dc05cb8a250177f8)) +- **cli:** correct Capacitor plugin SPM compat check ([#8440](https://github.com/ionic-team/capacitor/issues/8440)) ([e5ccc45](https://github.com/ionic-team/capacitor/commit/e5ccc451dda27d56bca824ed644bd20fe4d988cb)) +- **cli:** generate binaryTarget entries for custom xcframeworks in Package.swift ([#8445](https://github.com/ionic-team/capacitor/issues/8445)) ([1f7e33f](https://github.com/ionic-team/capacitor/commit/1f7e33fca43d183332ec19d22b0d75ef81d8cc6d)) +- **cli:** generate resource entries in Package.swift ([#8455](https://github.com/ionic-team/capacitor/issues/8455)) ([790bd27](https://github.com/ionic-team/capacitor/commit/790bd27123497111984227010c3162cec94a108e)) +- **cli:** handle Cordova plugins without iOS source files ([#8443](https://github.com/ionic-team/capacitor/issues/8443)) ([0da130e](https://github.com/ionic-team/capacitor/commit/0da130eb7a861bee4e2c35bc0aac53ba9c983fc3)) +- **cli:** link plugin dependencies in Package.swift ([#8457](https://github.com/ionic-team/capacitor/issues/8457)) ([b3c769e](https://github.com/ionic-team/capacitor/commit/b3c769e856c826b1174518877cf86ac7ce73bf09)) +- **ios:** support Cordova plugins with Package.swift ([#8438](https://github.com/ionic-team/capacitor/issues/8438)) ([139943b](https://github.com/ionic-team/capacitor/commit/139943b0c05fddb2d1ce2d6f468800fddf17b4cf)) +- **SystemBars:** avoid extra view padding on API <= 34 ([#8439](https://github.com/ionic-team/capacitor/issues/8439)) ([5b135a7](https://github.com/ionic-team/capacitor/commit/5b135a70217be560e7176c8d5b514cc92ed3e4e4)) + ## [8.3.1](https://github.com/ionic-team/capacitor/compare/8.3.0...8.3.1) (2026-04-16) ### Bug Fixes diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 5812a48328..ca3b6f0ef1 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) + +### Bug Fixes + +- **SystemBars:** avoid extra view padding on API <= 34 ([#8439](https://github.com/ionic-team/capacitor/issues/8439)) ([5b135a7](https://github.com/ionic-team/capacitor/commit/5b135a70217be560e7176c8d5b514cc92ed3e4e4)) + ## [8.3.1](https://github.com/ionic-team/capacitor/compare/8.3.0...8.3.1) (2026-04-16) ### Bug Fixes diff --git a/android/package.json b/android/package.json index f55229c313..5ecf01e071 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "8.3.1", + "version": "8.3.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index ac66f222fc..1cc5720a18 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) + +### Bug Fixes + +- **cli:** add cSettings support for compiler flags in generated Package.swift ([#8448](https://github.com/ionic-team/capacitor/issues/8448)) ([0bd0676](https://github.com/ionic-team/capacitor/commit/0bd0676315c5fd77e50312dd7b5bf4990dcbd7d0)) +- **cli:** add system framework and weak framework support in SPM Package.swift ([#8447](https://github.com/ionic-team/capacitor/issues/8447)) ([3232f0f](https://github.com/ionic-team/capacitor/commit/3232f0fe1d9811b0b5c500e3dc05cb8a250177f8)) +- **cli:** correct Capacitor plugin SPM compat check ([#8440](https://github.com/ionic-team/capacitor/issues/8440)) ([e5ccc45](https://github.com/ionic-team/capacitor/commit/e5ccc451dda27d56bca824ed644bd20fe4d988cb)) +- **cli:** generate binaryTarget entries for custom xcframeworks in Package.swift ([#8445](https://github.com/ionic-team/capacitor/issues/8445)) ([1f7e33f](https://github.com/ionic-team/capacitor/commit/1f7e33fca43d183332ec19d22b0d75ef81d8cc6d)) +- **cli:** generate resource entries in Package.swift ([#8455](https://github.com/ionic-team/capacitor/issues/8455)) ([790bd27](https://github.com/ionic-team/capacitor/commit/790bd27123497111984227010c3162cec94a108e)) +- **cli:** handle Cordova plugins without iOS source files ([#8443](https://github.com/ionic-team/capacitor/issues/8443)) ([0da130e](https://github.com/ionic-team/capacitor/commit/0da130eb7a861bee4e2c35bc0aac53ba9c983fc3)) +- **cli:** link plugin dependencies in Package.swift ([#8457](https://github.com/ionic-team/capacitor/issues/8457)) ([b3c769e](https://github.com/ionic-team/capacitor/commit/b3c769e856c826b1174518877cf86ac7ce73bf09)) +- **ios:** support Cordova plugins with Package.swift ([#8438](https://github.com/ionic-team/capacitor/issues/8438)) ([139943b](https://github.com/ionic-team/capacitor/commit/139943b0c05fddb2d1ce2d6f468800fddf17b4cf)) + ## [8.3.1](https://github.com/ionic-team/capacitor/compare/8.3.0...8.3.1) (2026-04-16) ### Bug Fixes diff --git a/cli/package.json b/cli/package.json index b9f5d1cccc..abc3774df3 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "8.3.1", + "version": "8.3.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 60a9ca82bf..08c8815c37 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) + +**Note:** Version bump only for package @capacitor/core + ## [8.3.1](https://github.com/ionic-team/capacitor/compare/8.3.0...8.3.1) (2026-04-16) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index 54e8c71108..bc3bebc0f0 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "8.3.1", + "version": "8.3.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index a16975c795..111b642acd 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) + +**Note:** Version bump only for package @capacitor/ios + ## [8.3.1](https://github.com/ionic-team/capacitor/compare/8.3.0...8.3.1) (2026-04-16) ### Bug Fixes diff --git a/ios/package.json b/ios/package.json index 4f7200cada..0972ed9467 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "8.3.1", + "version": "8.3.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/lerna.json b/lerna.json index 5f513f6567..d0a1ebc027 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "8.3.1", + "version": "8.3.2", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From 3f18cb70f9e8d0c3d5c68acb91640fdc1792f566 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Thu, 7 May 2026 13:13:44 -0400 Subject: [PATCH 21/69] chore: update lerna.json for alpha publish --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index cac40342b5..0a1a0886fe 100644 --- a/lerna.json +++ b/lerna.json @@ -4,7 +4,7 @@ "hoist": true }, "version": { - "allowBranch": "main", + "allowBranch": "next", "conventionalCommits": true, "createRelease": "github", "message": "Release %s", From 52e0cd7160e895273e30f45f177a3d42f00c9c83 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of markemer)" Date: Thu, 7 May 2026 17:15:50 +0000 Subject: [PATCH 22/69] Release 9.0.0-alpha.0 --- CHANGELOG.md | 11 +++++++++++ android/CHANGELOG.md | 9 +++++++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 8 ++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 7 +++++++ core/package.json | 2 +- ios/CHANGELOG.md | 7 +++++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 49 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3851a6663d..802047e8f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ 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.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) + +### Bug Fixes + +- **android:** conditionally add Cordova ([#8453](https://github.com/ionic-team/capacitor/issues/8453)) ([e136b3f](https://github.com/ionic-team/capacitor/commit/e136b3f0cdd1437cac5234576301ed388edf85e7)) +- **android:** Handle activity result without plugin handle ([#8436](https://github.com/ionic-team/capacitor/issues/8436)) ([65ad1fd](https://github.com/ionic-team/capacitor/commit/65ad1fd55b9793a3e078e4a837a00e2f28c75c17)) +- **cli:** conditionally add capacitor-cordova-android-plugins module ([#8449](https://github.com/ionic-team/capacitor/issues/8449)) ([9e85905](https://github.com/ionic-team/capacitor/commit/9e85905bf36c1e2300994e231cf10e829f2074ed)) +- **http:** handle URL objects on fetch ([#8386](https://github.com/ionic-team/capacitor/issues/8386)) ([0aac1f0](https://github.com/ionic-team/capacitor/commit/0aac1f0aee8083dc5013f0d8e3c180eb02f21141)) +- **ios:** make getArray accesible on Objective-C plugins ([#8392](https://github.com/ionic-team/capacitor/issues/8392)) ([ef57c77](https://github.com/ionic-team/capacitor/commit/ef57c773e70af6b94685a2dc0f808db3ed4cda6e)) +- **SystemBars:** use native safe area insets on Android ([#8384](https://github.com/ionic-team/capacitor/issues/8384)) ([d55f4f8](https://github.com/ionic-team/capacitor/commit/d55f4f859bd88fa5617699e874c48c451bcb5e5f)) + ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) ### Bug Fixes diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index ca3b6f0ef1..4abb7c931b 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,15 @@ 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.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) + +### Bug Fixes + +- **android:** conditionally add Cordova ([#8453](https://github.com/ionic-team/capacitor/issues/8453)) ([e136b3f](https://github.com/ionic-team/capacitor/commit/e136b3f0cdd1437cac5234576301ed388edf85e7)) +- **android:** Handle activity result without plugin handle ([#8436](https://github.com/ionic-team/capacitor/issues/8436)) ([65ad1fd](https://github.com/ionic-team/capacitor/commit/65ad1fd55b9793a3e078e4a837a00e2f28c75c17)) +- **http:** handle URL objects on fetch ([#8386](https://github.com/ionic-team/capacitor/issues/8386)) ([0aac1f0](https://github.com/ionic-team/capacitor/commit/0aac1f0aee8083dc5013f0d8e3c180eb02f21141)) +- **SystemBars:** use native safe area insets on Android ([#8384](https://github.com/ionic-team/capacitor/issues/8384)) ([d55f4f8](https://github.com/ionic-team/capacitor/commit/d55f4f859bd88fa5617699e874c48c451bcb5e5f)) + ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) ### Bug Fixes diff --git a/android/package.json b/android/package.json index 483d629955..794b343d38 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "9.0.0-dev.0", + "version": "9.0.0-alpha.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -26,7 +26,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^9.0.0-dev.0" + "@capacitor/core": "^9.0.0-alpha.0" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 1cc5720a18..b90561f8c2 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,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.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) + +### Bug Fixes + +- **android:** conditionally add Cordova ([#8453](https://github.com/ionic-team/capacitor/issues/8453)) ([e136b3f](https://github.com/ionic-team/capacitor/commit/e136b3f0cdd1437cac5234576301ed388edf85e7)) +- **cli:** conditionally add capacitor-cordova-android-plugins module ([#8449](https://github.com/ionic-team/capacitor/issues/8449)) ([9e85905](https://github.com/ionic-team/capacitor/commit/9e85905bf36c1e2300994e231cf10e829f2074ed)) +- **SystemBars:** use native safe area insets on Android ([#8384](https://github.com/ionic-team/capacitor/issues/8384)) ([d55f4f8](https://github.com/ionic-team/capacitor/commit/d55f4f859bd88fa5617699e874c48c451bcb5e5f)) + ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) ### Bug Fixes diff --git a/cli/package.json b/cli/package.json index 9f9d89eed9..5341b2bb1d 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "9.0.0-dev.0", + "version": "9.0.0-alpha.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 08c8815c37..4448742a8f 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,13 @@ 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.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) + +### Bug Fixes + +- **http:** handle URL objects on fetch ([#8386](https://github.com/ionic-team/capacitor/issues/8386)) ([0aac1f0](https://github.com/ionic-team/capacitor/commit/0aac1f0aee8083dc5013f0d8e3c180eb02f21141)) +- **SystemBars:** use native safe area insets on Android ([#8384](https://github.com/ionic-team/capacitor/issues/8384)) ([d55f4f8](https://github.com/ionic-team/capacitor/commit/d55f4f859bd88fa5617699e874c48c451bcb5e5f)) + ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index 674c151eff..e462f243da 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "9.0.0-dev.0", + "version": "9.0.0-alpha.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 111b642acd..20ea880360 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,13 @@ 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.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) + +### Bug Fixes + +- **http:** handle URL objects on fetch ([#8386](https://github.com/ionic-team/capacitor/issues/8386)) ([0aac1f0](https://github.com/ionic-team/capacitor/commit/0aac1f0aee8083dc5013f0d8e3c180eb02f21141)) +- **ios:** make getArray accesible on Objective-C plugins ([#8392](https://github.com/ionic-team/capacitor/issues/8392)) ([ef57c77](https://github.com/ionic-team/capacitor/commit/ef57c773e70af6b94685a2dc0f808db3ed4cda6e)) + ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) **Note:** Version bump only for package @capacitor/ios diff --git a/ios/package.json b/ios/package.json index 477e3c881b..f3dca61381 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "9.0.0-dev.0", + "version": "9.0.0-alpha.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^9.0.0-dev.0" + "@capacitor/core": "^9.0.0-alpha.0" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index 0a1a0886fe..fdc0f9716e 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "9.0.0-dev.0", + "version": "9.0.0-alpha.0", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From b2d771926a180e60deea31992d7d4abcd5ca3bc7 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 8 May 2026 18:44:22 +0200 Subject: [PATCH 23/69] fix(cli): copy plugin files in CocoaPods projects (#8467) --- cli/src/ios/update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 9fcd1c7184..82ca85a88f 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -564,7 +564,7 @@ async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) const isSPM = (await config.ios.packageManager) === 'SPM'; for (const p of cordovaPlugins) { const platformTag = getPluginPlatform(p, platform); - if (platformTag.$?.package) { + if (isSPM && platformTag.$?.package) { continue; } const sourceFiles = getPlatformElement(p, platform, 'source-file'); From 0d5848d1669a95da25a77e233044678986dc30bb Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Fri, 8 May 2026 12:57:41 -0400 Subject: [PATCH 24/69] Fix from main to copy plugin files in CocoaPods found in: 5c63a58c88cefa7ec882536975ccaf86c6b0d7e2 --- cli/src/util/cordova-ios.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/util/cordova-ios.ts b/cli/src/util/cordova-ios.ts index 5fc3c1a8c4..a54c059aaa 100644 --- a/cli/src/util/cordova-ios.ts +++ b/cli/src/util/cordova-ios.ts @@ -173,7 +173,7 @@ export async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plu const isSPM = (await config.ios.packageManager) === 'SPM'; for (const p of cordovaPlugins) { const platformTag = getPluginPlatform(p, platform); - if (platformTag.$?.package) { + if (isSPM && platformTag.$?.package) { continue; } const sourceFiles = getPlatformElement(p, platform, 'source-file'); From 76ff70e0e40bbbca63c5195c572f166e5ce82089 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of markemer)" Date: Fri, 8 May 2026 17:08:40 +0000 Subject: [PATCH 25/69] Release 8.3.3 --- CHANGELOG.md | 6 ++++++ android/CHANGELOG.md | 4 ++++ android/package.json | 2 +- cli/CHANGELOG.md | 6 ++++++ cli/package.json | 2 +- core/CHANGELOG.md | 4 ++++ core/package.json | 2 +- ios/CHANGELOG.md | 4 ++++ ios/package.json | 2 +- lerna.json | 2 +- 10 files changed, 29 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3851a6663d..d4ad41ff2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) + +### Bug Fixes + +- **cli:** copy plugin files in CocoaPods projects ([#8467](https://github.com/ionic-team/capacitor/issues/8467)) ([b2d7719](https://github.com/ionic-team/capacitor/commit/b2d771926a180e60deea31992d7d4abcd5ca3bc7)) + ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) ### Bug Fixes diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index ca3b6f0ef1..d4b2fc5346 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) + +**Note:** Version bump only for package @capacitor/android + ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) ### Bug Fixes diff --git a/android/package.json b/android/package.json index 5ecf01e071..07db8a26e4 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "8.3.2", + "version": "8.3.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 1cc5720a18..452ba4cc62 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) + +### Bug Fixes + +- **cli:** copy plugin files in CocoaPods projects ([#8467](https://github.com/ionic-team/capacitor/issues/8467)) ([b2d7719](https://github.com/ionic-team/capacitor/commit/b2d771926a180e60deea31992d7d4abcd5ca3bc7)) + ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) ### Bug Fixes diff --git a/cli/package.json b/cli/package.json index abc3774df3..cd17f5d2ff 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "8.3.2", + "version": "8.3.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 08c8815c37..26f5723fe9 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) + +**Note:** Version bump only for package @capacitor/core + ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index bc3bebc0f0..373249f982 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "8.3.2", + "version": "8.3.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 111b642acd..b9b7863b06 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) + +**Note:** Version bump only for package @capacitor/ios + ## [8.3.2](https://github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) **Note:** Version bump only for package @capacitor/ios diff --git a/ios/package.json b/ios/package.json index 0972ed9467..bd4c0a320b 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "8.3.2", + "version": "8.3.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/lerna.json b/lerna.json index d0a1ebc027..d64bfcd44a 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "8.3.2", + "version": "8.3.3", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From 676b8643aed45432cf36abc41261e543119a83a8 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of markemer)" Date: Fri, 8 May 2026 17:21:39 +0000 Subject: [PATCH 26/69] Release 9.0.0-alpha.1 --- CHANGELOG.md | 4 ++++ android/CHANGELOG.md | 4 ++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 4 ++++ cli/package.json | 2 +- core/CHANGELOG.md | 4 ++++ core/package.json | 2 +- ios/CHANGELOG.md | 4 ++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 27 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 802047e8f5..336a85b2a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ 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.1](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...9.0.0-alpha.1) (2026-05-08) + +**Note:** Version bump only for package capacitor + # [9.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) ### Bug Fixes diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 4abb7c931b..a84db5cd3a 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,10 @@ 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.1](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...9.0.0-alpha.1) (2026-05-08) + +**Note:** Version bump only for package @capacitor/android + # [9.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) ### Bug Fixes diff --git a/android/package.json b/android/package.json index 794b343d38..aec3fc4025 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "9.0.0-alpha.0", + "version": "9.0.0-alpha.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -26,7 +26,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^9.0.0-alpha.0" + "@capacitor/core": "^9.0.0-alpha.1" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index b90561f8c2..66216f9348 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,10 @@ 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.1](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...9.0.0-alpha.1) (2026-05-08) + +**Note:** Version bump only for package @capacitor/cli + # [9.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) ### Bug Fixes diff --git a/cli/package.json b/cli/package.json index 5341b2bb1d..239857cc9a 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "9.0.0-alpha.0", + "version": "9.0.0-alpha.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 4448742a8f..b8468911ef 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,10 @@ 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.1](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...9.0.0-alpha.1) (2026-05-08) + +**Note:** Version bump only for package @capacitor/core + # [9.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) ### Bug Fixes diff --git a/core/package.json b/core/package.json index e462f243da..dc1c0f967c 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "9.0.0-alpha.0", + "version": "9.0.0-alpha.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 20ea880360..083f685c6c 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,10 @@ 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.1](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...9.0.0-alpha.1) (2026-05-08) + +**Note:** Version bump only for package @capacitor/ios + # [9.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) ### Bug Fixes diff --git a/ios/package.json b/ios/package.json index f3dca61381..243ef00cea 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "9.0.0-alpha.0", + "version": "9.0.0-alpha.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^9.0.0-alpha.0" + "@capacitor/core": "^9.0.0-alpha.1" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index fdc0f9716e..5189de7572 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "9.0.0-alpha.0", + "version": "9.0.0-alpha.1", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From b7e167bbab47c57a5f72ff9e76a726ab7d8a1c1f Mon Sep 17 00:00:00 2001 From: "Alex J." Date: Tue, 12 May 2026 15:19:58 -0400 Subject: [PATCH 27/69] chore(cli): update telemetry endpoint (#8464) --- cli/src/ipc.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/ipc.ts b/cli/src/ipc.ts index 41894e53a7..37c38110bb 100644 --- a/cli/src/ipc.ts +++ b/cli/src/ipc.ts @@ -49,9 +49,9 @@ export async function receive(msg: IPCMessage): Promise { // This request is only made if telemetry is on. const req = request( { - hostname: 'api.ionicjs.com', + hostname: 'metrics-capacitor.outsystems.com', port: 443, - path: '/events/metrics', + path: '/metrics', method: 'POST', headers: { 'Content-Type': 'application/json', @@ -60,7 +60,7 @@ export async function receive(msg: IPCMessage): Promise { (response) => { debug('Sent %O metric to events service (status: %O)', data.name, response.statusCode); - if (response.statusCode !== 204) { + if (response.statusCode !== 202) { response.on('data', (chunk) => { debug('Bad response from events service. Request body: %O', chunk.toString()); }); From 6b44e781c4370c865da7b370aec85a01492d5728 Mon Sep 17 00:00:00 2001 From: "Alex J." Date: Tue, 12 May 2026 15:19:58 -0400 Subject: [PATCH 28/69] chore(cli): update telemetry endpoint (#8464) --- cli/src/ipc.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/ipc.ts b/cli/src/ipc.ts index 41894e53a7..37c38110bb 100644 --- a/cli/src/ipc.ts +++ b/cli/src/ipc.ts @@ -49,9 +49,9 @@ export async function receive(msg: IPCMessage): Promise { // This request is only made if telemetry is on. const req = request( { - hostname: 'api.ionicjs.com', + hostname: 'metrics-capacitor.outsystems.com', port: 443, - path: '/events/metrics', + path: '/metrics', method: 'POST', headers: { 'Content-Type': 'application/json', @@ -60,7 +60,7 @@ export async function receive(msg: IPCMessage): Promise { (response) => { debug('Sent %O metric to events service (status: %O)', data.name, response.statusCode); - if (response.statusCode !== 204) { + if (response.statusCode !== 202) { response.on('data', (chunk) => { debug('Bad response from events service. Request body: %O', chunk.toString()); }); From de1e506e48204f3d9a51bc911fafc341fd81fc36 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of alexgerardojacinto)" Date: Tue, 12 May 2026 20:38:58 +0000 Subject: [PATCH 29/69] Release 8.3.4 --- CHANGELOG.md | 4 ++++ android/CHANGELOG.md | 4 ++++ android/package.json | 2 +- cli/CHANGELOG.md | 4 ++++ cli/package.json | 2 +- core/CHANGELOG.md | 4 ++++ core/package.json | 2 +- ios/CHANGELOG.md | 4 ++++ ios/package.json | 2 +- lerna.json | 2 +- 10 files changed, 25 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4ad41ff2a..a7f2b77136 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) + +**Note:** Version bump only for package capacitor + ## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) ### Bug Fixes diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index d4b2fc5346..74d538d9fe 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) + +**Note:** Version bump only for package @capacitor/android + ## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index 07db8a26e4..8ae93f3f2b 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "8.3.3", + "version": "8.3.4", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 452ba4cc62..bba24688f6 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) + +**Note:** Version bump only for package @capacitor/cli + ## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) ### Bug Fixes diff --git a/cli/package.json b/cli/package.json index cd17f5d2ff..9df346b143 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "8.3.3", + "version": "8.3.4", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 26f5723fe9..017293794f 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) + +**Note:** Version bump only for package @capacitor/core + ## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index 373249f982..eef113a50a 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "8.3.3", + "version": "8.3.4", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index b9b7863b06..87ee73bb4f 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) + +**Note:** Version bump only for package @capacitor/ios + ## [8.3.3](https://github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) **Note:** Version bump only for package @capacitor/ios diff --git a/ios/package.json b/ios/package.json index bd4c0a320b..a7b8d31335 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "8.3.3", + "version": "8.3.4", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/lerna.json b/lerna.json index d64bfcd44a..1e53d6bdca 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "8.3.3", + "version": "8.3.4", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From faf895efde7f200442c6396edadb51decb714dff Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of alexgerardojacinto)" Date: Tue, 12 May 2026 21:10:26 +0000 Subject: [PATCH 30/69] Release 9.0.0-alpha.2 --- CHANGELOG.md | 4 ++++ android/CHANGELOG.md | 4 ++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 4 ++++ cli/package.json | 2 +- core/CHANGELOG.md | 4 ++++ core/package.json | 2 +- ios/CHANGELOG.md | 4 ++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 27 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 336a85b2a3..814970df10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ 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.2](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...9.0.0-alpha.2) (2026-05-12) + +**Note:** Version bump only for package capacitor + # [9.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...9.0.0-alpha.1) (2026-05-08) **Note:** Version bump only for package capacitor diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index a84db5cd3a..6bcf12438a 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,10 @@ 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.2](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...9.0.0-alpha.2) (2026-05-12) + +**Note:** Version bump only for package @capacitor/android + # [9.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...9.0.0-alpha.1) (2026-05-08) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index aec3fc4025..8022d692c8 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "9.0.0-alpha.1", + "version": "9.0.0-alpha.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -26,7 +26,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^9.0.0-alpha.1" + "@capacitor/core": "^9.0.0-alpha.2" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 66216f9348..8781458412 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,10 @@ 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.2](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...9.0.0-alpha.2) (2026-05-12) + +**Note:** Version bump only for package @capacitor/cli + # [9.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...9.0.0-alpha.1) (2026-05-08) **Note:** Version bump only for package @capacitor/cli diff --git a/cli/package.json b/cli/package.json index 239857cc9a..38c0c01b39 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "9.0.0-alpha.1", + "version": "9.0.0-alpha.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index b8468911ef..afde81bcfd 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,10 @@ 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.2](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...9.0.0-alpha.2) (2026-05-12) + +**Note:** Version bump only for package @capacitor/core + # [9.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...9.0.0-alpha.1) (2026-05-08) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index dc1c0f967c..8567c8be97 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "9.0.0-alpha.1", + "version": "9.0.0-alpha.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 083f685c6c..22acaa2c88 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,10 @@ 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.2](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...9.0.0-alpha.2) (2026-05-12) + +**Note:** Version bump only for package @capacitor/ios + # [9.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...9.0.0-alpha.1) (2026-05-08) **Note:** Version bump only for package @capacitor/ios diff --git a/ios/package.json b/ios/package.json index 243ef00cea..a57c13889e 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "9.0.0-alpha.1", + "version": "9.0.0-alpha.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^9.0.0-alpha.1" + "@capacitor/core": "^9.0.0-alpha.2" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index 5189de7572..cfcbca64cb 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "9.0.0-alpha.1", + "version": "9.0.0-alpha.2", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From 731a82e554d9c1270241897228e934c38759bbbb Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 21 May 2026 17:39:31 +0200 Subject: [PATCH 31/69] chore: format java code (#8475) --- .../com/getcapacitor/BridgeWebChromeClient.java | 12 ++++++------ .../src/main/java/com/getcapacitor/FileUtils.java | 6 +++--- .../src/main/java/com/getcapacitor/Plugin.java | 4 ++-- .../cordova/MockCordovaWebViewImpl.java | 14 ++++++-------- .../com/getcapacitor/plugin/CapacitorHttp.java | 4 ++-- .../java/com/getcapacitor/ConfigBuildingTest.java | 6 +++--- 6 files changed, 22 insertions(+), 24 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java b/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java index 6ca1c8a838..58371a9ff3 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java +++ b/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java @@ -246,14 +246,14 @@ public boolean onJsPrompt(WebView view, String url, String message, String defau public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { super.onGeolocationPermissionsShowPrompt(origin, callback); Logger.debug("onGeolocationPermissionsShowPrompt: DOING IT HERE FOR ORIGIN: " + origin); - final String[] geoPermissions = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }; + final String[] geoPermissions = {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}; if (!PermissionHelper.hasPermissions(bridge.getContext(), geoPermissions)) { permissionListener = (isGranted) -> { if (isGranted) { callback.invoke(origin, true, false); } else { - final String[] coarsePermission = { Manifest.permission.ACCESS_COARSE_LOCATION }; + final String[] coarsePermission = {Manifest.permission.ACCESS_COARSE_LOCATION}; if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && PermissionHelper.hasPermissions(bridge.getContext(), coarsePermission) @@ -294,7 +294,7 @@ public boolean onShowFileChooser( filePathCallback.onReceiveValue(null); } }; - final String[] camPermission = { Manifest.permission.CAMERA }; + final String[] camPermission = {Manifest.permission.CAMERA}; permissionLauncher.launch(camPermission); } } else { @@ -305,7 +305,7 @@ public boolean onShowFileChooser( } private boolean isMediaCaptureSupported() { - String[] permissions = { Manifest.permission.CAMERA }; + String[] permissions = {Manifest.permission.CAMERA}; return ( PermissionHelper.hasPermissions(bridge.getContext(), permissions) || !PermissionHelper.hasDefinedPermission(bridge.getContext(), Manifest.permission.CAMERA) @@ -343,7 +343,7 @@ private boolean showImageCapturePicker(final ValueCallback filePathCallba activityListener = (activityResult) -> { Uri[] result = null; if (activityResult.getResultCode() == Activity.RESULT_OK) { - result = new Uri[] { imageFileUri }; + result = new Uri[] {imageFileUri}; } filePathCallback.onReceiveValue(result); }; @@ -362,7 +362,7 @@ private boolean showVideoCapturePicker(final ValueCallback filePathCallba activityListener = (activityResult) -> { Uri[] result = null; if (activityResult.getResultCode() == Activity.RESULT_OK) { - result = new Uri[] { activityResult.getData().getData() }; + result = new Uri[] {activityResult.getData().getData()}; } filePathCallback.onReceiveValue(result); }; diff --git a/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java b/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java index ce09e7c2f0..0fdf64feb3 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java +++ b/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java @@ -118,7 +118,7 @@ else if (isMediaDocument(uri)) { } final String selection = "_id=?"; - final String[] selectionArgs = new String[] { split[1] }; + final String[] selectionArgs = new String[] {split[1]}; return getDataColumn(context, contentUri, selection, selectionArgs); } @@ -195,7 +195,7 @@ private static String getDataColumn(Context context, Uri uri, String selection, String path = null; Cursor cursor = null; final String column = "_data"; - final String[] projection = { column }; + final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); @@ -292,7 +292,7 @@ private static String getPathToNonPrimaryVolume(Context context, String tag) { } private static String sanitizeFilename(String displayName) { - String[] badCharacters = new String[] { "..", "/" }; + String[] badCharacters = new String[] {"..", "/"}; String[] segments = displayName.split("/"); String fileName = segments[segments.length - 1]; for (String suspString : badCharacters) { diff --git a/android/capacitor/src/main/java/com/getcapacitor/Plugin.java b/android/capacitor/src/main/java/com/getcapacitor/Plugin.java index 19c8b8b3a6..e55d933ccb 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Plugin.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Plugin.java @@ -465,7 +465,7 @@ protected void requestAllPermissions(@NonNull PluginCall call, @NonNull String c * @param callbackName the name of the callback to run when the permission request is complete */ protected void requestPermissionForAlias(@NonNull String alias, @NonNull PluginCall call, @NonNull String callbackName) { - requestPermissionForAliases(new String[] { alias }, call, callbackName); + requestPermissionForAliases(new String[] {alias}, call, callbackName); } /** @@ -583,7 +583,7 @@ public void pluginRequestAllPermissions() { */ @Deprecated public void pluginRequestPermission(String permission, int requestCode) { - ActivityCompat.requestPermissions(getActivity(), new String[] { permission }, requestCode); + ActivityCompat.requestPermissions(getActivity(), new String[] {permission}, requestCode); } /** diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java index 1115429d7b..020c1457a1 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java +++ b/android/capacitor/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); + } + }); } } diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java index 46bc17417f..7d5ab337e3 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java @@ -18,8 +18,8 @@ @CapacitorPlugin( permissions = { - @Permission(strings = { Manifest.permission.WRITE_EXTERNAL_STORAGE }, alias = "HttpWrite"), - @Permission(strings = { Manifest.permission.READ_EXTERNAL_STORAGE }, alias = "HttpRead") + @Permission(strings = {Manifest.permission.WRITE_EXTERNAL_STORAGE}, alias = "HttpWrite"), + @Permission(strings = {Manifest.permission.READ_EXTERNAL_STORAGE}, alias = "HttpRead") } ) public class CapacitorHttp extends Plugin { diff --git a/android/capacitor/src/test/java/com/getcapacitor/ConfigBuildingTest.java b/android/capacitor/src/test/java/com/getcapacitor/ConfigBuildingTest.java index 1af51039da..ae12edb02b 100644 --- a/android/capacitor/src/test/java/com/getcapacitor/ConfigBuildingTest.java +++ b/android/capacitor/src/test/java/com/getcapacitor/ConfigBuildingTest.java @@ -42,7 +42,7 @@ public void setup() { config = new CapConfig.Builder(context) .setAllowMixedContent(true) - .setAllowNavigation(new String[] { "http://www.google.com" }) + .setAllowNavigation(new String[] {"http://www.google.com"}) .setAndroidScheme("test") .setCaptureInput(true) .setLoggingEnabled(true) @@ -64,7 +64,7 @@ public void setup() { @Test public void getCoreConfigValues() { assertTrue(config.isMixedContentAllowed()); - assertArrayEquals(new String[] { "http://www.google.com" }, config.getAllowNavigation()); + assertArrayEquals(new String[] {"http://www.google.com"}, config.getAllowNavigation()); assertEquals("test", config.getAndroidScheme()); assertTrue(config.isInputCaptured()); assertTrue(config.isLoggingEnabled()); @@ -97,7 +97,7 @@ public void getPluginInt() { @Test public void getPluginArray() { - String[] comparison = new String[] { "5", "6", "7", "8" }; + String[] comparison = new String[] {"5", "6", "7", "8"}; String[] testArray = config.getPluginConfiguration(TEST_PLUGIN_NAME).getArray("var5"); assertArrayEquals(comparison, testArray); } From bc745364129853769ca2fcfc3cc2a6c7624ee513 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 21 May 2026 18:04:37 +0200 Subject: [PATCH 32/69] chore: format java code (#8477) --- .../java/com/getcapacitor/BridgeWebChromeClient.java | 12 ++++++------ .../src/main/java/com/getcapacitor/FileUtils.java | 6 +++--- .../src/main/java/com/getcapacitor/Plugin.java | 4 ++-- .../java/com/getcapacitor/plugin/CapacitorHttp.java | 4 ++-- .../java/com/getcapacitor/ConfigBuildingTest.java | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java b/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java index 58371a9ff3..6ca1c8a838 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java +++ b/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java @@ -246,14 +246,14 @@ public boolean onJsPrompt(WebView view, String url, String message, String defau public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { super.onGeolocationPermissionsShowPrompt(origin, callback); Logger.debug("onGeolocationPermissionsShowPrompt: DOING IT HERE FOR ORIGIN: " + origin); - final String[] geoPermissions = {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}; + final String[] geoPermissions = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }; if (!PermissionHelper.hasPermissions(bridge.getContext(), geoPermissions)) { permissionListener = (isGranted) -> { if (isGranted) { callback.invoke(origin, true, false); } else { - final String[] coarsePermission = {Manifest.permission.ACCESS_COARSE_LOCATION}; + final String[] coarsePermission = { Manifest.permission.ACCESS_COARSE_LOCATION }; if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && PermissionHelper.hasPermissions(bridge.getContext(), coarsePermission) @@ -294,7 +294,7 @@ public boolean onShowFileChooser( filePathCallback.onReceiveValue(null); } }; - final String[] camPermission = {Manifest.permission.CAMERA}; + final String[] camPermission = { Manifest.permission.CAMERA }; permissionLauncher.launch(camPermission); } } else { @@ -305,7 +305,7 @@ public boolean onShowFileChooser( } private boolean isMediaCaptureSupported() { - String[] permissions = {Manifest.permission.CAMERA}; + String[] permissions = { Manifest.permission.CAMERA }; return ( PermissionHelper.hasPermissions(bridge.getContext(), permissions) || !PermissionHelper.hasDefinedPermission(bridge.getContext(), Manifest.permission.CAMERA) @@ -343,7 +343,7 @@ private boolean showImageCapturePicker(final ValueCallback filePathCallba activityListener = (activityResult) -> { Uri[] result = null; if (activityResult.getResultCode() == Activity.RESULT_OK) { - result = new Uri[] {imageFileUri}; + result = new Uri[] { imageFileUri }; } filePathCallback.onReceiveValue(result); }; @@ -362,7 +362,7 @@ private boolean showVideoCapturePicker(final ValueCallback filePathCallba activityListener = (activityResult) -> { Uri[] result = null; if (activityResult.getResultCode() == Activity.RESULT_OK) { - result = new Uri[] {activityResult.getData().getData()}; + result = new Uri[] { activityResult.getData().getData() }; } filePathCallback.onReceiveValue(result); }; diff --git a/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java b/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java index 0fdf64feb3..ce09e7c2f0 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java +++ b/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java @@ -118,7 +118,7 @@ else if (isMediaDocument(uri)) { } final String selection = "_id=?"; - final String[] selectionArgs = new String[] {split[1]}; + final String[] selectionArgs = new String[] { split[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } @@ -195,7 +195,7 @@ private static String getDataColumn(Context context, Uri uri, String selection, String path = null; Cursor cursor = null; final String column = "_data"; - final String[] projection = {column}; + final String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); @@ -292,7 +292,7 @@ private static String getPathToNonPrimaryVolume(Context context, String tag) { } private static String sanitizeFilename(String displayName) { - String[] badCharacters = new String[] {"..", "/"}; + String[] badCharacters = new String[] { "..", "/" }; String[] segments = displayName.split("/"); String fileName = segments[segments.length - 1]; for (String suspString : badCharacters) { diff --git a/android/capacitor/src/main/java/com/getcapacitor/Plugin.java b/android/capacitor/src/main/java/com/getcapacitor/Plugin.java index e55d933ccb..19c8b8b3a6 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Plugin.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Plugin.java @@ -465,7 +465,7 @@ protected void requestAllPermissions(@NonNull PluginCall call, @NonNull String c * @param callbackName the name of the callback to run when the permission request is complete */ protected void requestPermissionForAlias(@NonNull String alias, @NonNull PluginCall call, @NonNull String callbackName) { - requestPermissionForAliases(new String[] {alias}, call, callbackName); + requestPermissionForAliases(new String[] { alias }, call, callbackName); } /** @@ -583,7 +583,7 @@ public void pluginRequestAllPermissions() { */ @Deprecated public void pluginRequestPermission(String permission, int requestCode) { - ActivityCompat.requestPermissions(getActivity(), new String[] {permission}, requestCode); + ActivityCompat.requestPermissions(getActivity(), new String[] { permission }, requestCode); } /** diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java index 7d5ab337e3..46bc17417f 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java @@ -18,8 +18,8 @@ @CapacitorPlugin( permissions = { - @Permission(strings = {Manifest.permission.WRITE_EXTERNAL_STORAGE}, alias = "HttpWrite"), - @Permission(strings = {Manifest.permission.READ_EXTERNAL_STORAGE}, alias = "HttpRead") + @Permission(strings = { Manifest.permission.WRITE_EXTERNAL_STORAGE }, alias = "HttpWrite"), + @Permission(strings = { Manifest.permission.READ_EXTERNAL_STORAGE }, alias = "HttpRead") } ) public class CapacitorHttp extends Plugin { diff --git a/android/capacitor/src/test/java/com/getcapacitor/ConfigBuildingTest.java b/android/capacitor/src/test/java/com/getcapacitor/ConfigBuildingTest.java index ae12edb02b..1af51039da 100644 --- a/android/capacitor/src/test/java/com/getcapacitor/ConfigBuildingTest.java +++ b/android/capacitor/src/test/java/com/getcapacitor/ConfigBuildingTest.java @@ -42,7 +42,7 @@ public void setup() { config = new CapConfig.Builder(context) .setAllowMixedContent(true) - .setAllowNavigation(new String[] {"http://www.google.com"}) + .setAllowNavigation(new String[] { "http://www.google.com" }) .setAndroidScheme("test") .setCaptureInput(true) .setLoggingEnabled(true) @@ -64,7 +64,7 @@ public void setup() { @Test public void getCoreConfigValues() { assertTrue(config.isMixedContentAllowed()); - assertArrayEquals(new String[] {"http://www.google.com"}, config.getAllowNavigation()); + assertArrayEquals(new String[] { "http://www.google.com" }, config.getAllowNavigation()); assertEquals("test", config.getAndroidScheme()); assertTrue(config.isInputCaptured()); assertTrue(config.isLoggingEnabled()); @@ -97,7 +97,7 @@ public void getPluginInt() { @Test public void getPluginArray() { - String[] comparison = new String[] {"5", "6", "7", "8"}; + String[] comparison = new String[] { "5", "6", "7", "8" }; String[] testArray = config.getPluginConfiguration(TEST_PLUGIN_NAME).getArray("var5"); assertArrayEquals(comparison, testArray); } From 93c72de40a2ec4c78b33659250cb08340083088e Mon Sep 17 00:00:00 2001 From: Robin Genz Date: Thu, 21 May 2026 18:54:21 +0200 Subject: [PATCH 33/69] feat: add method getDouble to plugin config (#7638) Co-authored-by: jcesarmobile --- .../java/com/getcapacitor/PluginConfig.java | 11 ++++++++++ .../java/com/getcapacitor/util/JSONUtils.java | 20 +++++++++++++++++++ ios/Capacitor/Capacitor/PluginConfig.swift | 7 +++++++ 3 files changed, 38 insertions(+) diff --git a/android/capacitor/src/main/java/com/getcapacitor/PluginConfig.java b/android/capacitor/src/main/java/com/getcapacitor/PluginConfig.java index 0f00fc530f..1d21ae90e1 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/PluginConfig.java +++ b/android/capacitor/src/main/java/com/getcapacitor/PluginConfig.java @@ -65,6 +65,17 @@ public int getInt(String configKey, int defaultValue) { return JSONUtils.getInt(config, configKey, defaultValue); } + /** + * Get a double value for a plugin in the Capacitor config. + * + * @param configKey The key of the value to retrieve + * @param defaultValue A default value to return if the key does not exist in the config + * @return The value from the config, if key exists. Default value returned if not + */ + public double getDouble(String configKey, double defaultValue) { + return JSONUtils.getDouble(config, configKey, defaultValue); + } + /** * Get a string array value for a plugin in the Capacitor config. * diff --git a/android/capacitor/src/main/java/com/getcapacitor/util/JSONUtils.java b/android/capacitor/src/main/java/com/getcapacitor/util/JSONUtils.java index 1d2fc20785..f24e5aa0b4 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/util/JSONUtils.java +++ b/android/capacitor/src/main/java/com/getcapacitor/util/JSONUtils.java @@ -75,6 +75,26 @@ public static int getInt(JSONObject jsonObject, String key, int defaultValue) { return defaultValue; } + /** + * Get a double value from the given JSON object. + * + * @param jsonObject A JSON object to search + * @param key A key to fetch from the JSON object + * @param defaultValue A default value to return if the key cannot be found + * @return The value at the given key in the JSON object, or the default value + */ + public static double getDouble(JSONObject jsonObject, String key, double defaultValue) { + String k = getDeepestKey(key); + try { + JSONObject o = getDeepestObject(jsonObject, key); + return o.getDouble(k); + } catch (JSONException ignore) { + // value was not found + } + + return defaultValue; + } + /** * Get a JSON object value from the given JSON object. * diff --git a/ios/Capacitor/Capacitor/PluginConfig.swift b/ios/Capacitor/Capacitor/PluginConfig.swift index f2461e102d..8d73bc1def 100644 --- a/ios/Capacitor/Capacitor/PluginConfig.swift +++ b/ios/Capacitor/Capacitor/PluginConfig.swift @@ -30,6 +30,13 @@ import Foundation return defaultValue } + @objc public func getDouble(_ configKey: String, _ defaultValue: Double) -> Double { + if let val = (self.config)[keyPath: KeyPath(configKey)] as? Double { + return val + } + return defaultValue + } + public func getArray(_ configKey: String, _ defaultValue: JSArray? = nil) -> JSArray? { if let val = (self.config)[keyPath: KeyPath(configKey)] as? JSArray { return val From e456de083e19644f484bec5a5359cb67960ac8bc Mon Sep 17 00:00:00 2001 From: Joey Pender Date: Fri, 22 May 2026 13:39:38 -0500 Subject: [PATCH 34/69] fix(SystemBars): make `safe-area-inset-x` available on API <= 34 (#8424) --- .../com/getcapacitor/plugin/SystemBars.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java index b335b4b1cf..76e74207cc 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java @@ -164,13 +164,18 @@ private Insets calcSafeAreaInsets(WindowInsetsCompat insets) { } private void initSafeAreaCSSVariables() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) { + WindowInsetsCompat insets; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { View v = (View) this.getBridge().getWebView().getParent(); - WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v); - if (insets != null) { - Insets safeAreaInsets = calcSafeAreaInsets(insets); - injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); - } + insets = ViewCompat.getRootWindowInsets(v); + } else { + insets = WindowInsetsCompat.CONSUMED; + } + + if (insets != null) { + Insets safeAreaInsets = calcSafeAreaInsets(insets); + injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); } } @@ -186,10 +191,8 @@ private void initWindowInsetsListener() { // We need to correct for a possible shown IME v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) { - Insets safeAreaInsets = calcSafeAreaInsets(insets); - injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); - } + Insets safeAreaInsets = calcSafeAreaInsets(insets); + injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); return new WindowInsetsCompat.Builder(insets) .setInsets( @@ -221,10 +224,8 @@ private void initWindowInsetsListener() { .setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), Insets.of(0, 0, 0, 0)) .build(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) { - Insets safeAreaInsets = calcSafeAreaInsets(newInsets); - injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); - } + Insets safeAreaInsets = calcSafeAreaInsets(newInsets); + injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); return newInsets; }); From 258867b7bf37b1837b99b02ec9638e5a6df08d97 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 26 May 2026 12:00:02 +0200 Subject: [PATCH 35/69] feat(cli): add experimental packageOptions (#8471) --- cli/src/declarations.ts | 20 ++++++++++++++++++++ cli/src/ios/update.ts | 3 +++ cli/src/util/spm.ts | 21 ++++++++++++++++++--- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index 6c8645406d..bda7ff0c03 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -559,6 +559,13 @@ export interface CapacitorConfig { * @since 8.3.0 */ packageTraits?: { [pluginId: string]: string[] }; + /** + * Define options to apply to the package. + * The key is the plugin ID (e.g. `@capacitor-community/device`) + * + * @since 8.4.0 + */ + packageOptions?: { [pluginId: string]: PackageOptions }; }; }; }; @@ -799,3 +806,16 @@ export interface PluginsConfig { animation?: 'FADE' | 'NONE'; }; } + +export interface PackageOptions { + /** + * Create a symlink to the plugin folder instead of pointing to the plugin path. + * Useful when plugin names conflict. + */ + symlink?: boolean; + /** + * Useful to avoid target name conflicts in dependencies + * [see](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/modulealiasing/) + */ + moduleAliases?: { [target: string]: string }; +} diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 82ca85a88f..b060e59a8f 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -639,6 +639,9 @@ async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) async function removePluginsNativeFiles(config: Config) { await remove(config.ios.cordovaPluginsDirAbs); + if ((await config.ios.packageManager) === 'SPM') { + await remove(join(config.ios.nativeProjectDirAbs, 'CapApp-SPM', 'symlinks')); + } await extractTemplate(config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, config.ios.cordovaPluginsDirAbs); } diff --git a/cli/src/util/spm.ts b/cli/src/util/spm.ts index 7d15787ce2..2ef26bcda0 100644 --- a/cli/src/util/spm.ts +++ b/cli/src/util/spm.ts @@ -1,4 +1,4 @@ -import { pathExists, existsSync, readFileSync, writeFileSync, remove, move, mkdtemp } from 'fs-extra'; +import { ensureSymlink, pathExists, existsSync, readFileSync, writeFileSync, remove, move, mkdtemp } from 'fs-extra'; import { tmpdir } from 'os'; import { join, relative, resolve } from 'path'; import type { PlistObject } from 'plist'; @@ -100,6 +100,7 @@ export async function generatePackageText(config: Config, plugins: Plugin[]): Pr const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); const iosVersion = getMajoriOSVersion(config); 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'; let packageSwiftText = `// swift-tools-version: ${swiftToolsVersion} @@ -132,7 +133,13 @@ let package = Package( packageSwiftText += `,\n .package(name: "${plugin.name}", path: "../../capacitor-cordova-ios-plugins/sources/${plugin.name}")`; } } else { - const relPath = relative(config.ios.nativeXcodeProjDirAbs, plugin.rootPath); + const options = packageOptions[plugin.id]; + const symlink = options?.symlink; + const symlinkFolder = join('symlinks', plugin.name); + const relPath = symlink ? symlinkFolder : relative(config.ios.nativeXcodeProjDirAbs, plugin.rootPath); + if (symlink) { + await ensureSymlink(plugin.rootPath, resolve(config.ios.nativeProjectDirAbs, 'CapApp-SPM', symlinkFolder)); + } const traits = packageTraits[plugin.id]; const traitsSuffix = traits?.length ? `, traits: [${traits @@ -156,7 +163,15 @@ let package = Package( .product(name: "Cordova", package: "capacitor-swift-pm")`; for (const plugin of plugins) { - let pluginText = `,\n .product(name: "${plugin.ios?.name}", package: "${plugin.ios?.name}")`; + const aliases = Object.entries(packageOptions[plugin.id]?.moduleAliases ?? {}); + const aliasText = aliases?.length + ? `, moduleAliases: [${aliases + .map(([target, replacement]) => { + return `"${target}": "${replacement}"`; + }) + .join(', ')}]` + : ''; + let pluginText = `,\n .product(name: "${plugin.ios?.name}", package: "${plugin.ios?.name}"${aliasText})`; if (getPluginType(plugin, config.ios.name) === PluginType.Cordova) { const platformTag = getPluginPlatform(plugin, config.ios.name); if (platformTag.$?.package) { From b4b297a52f8732659662d5e5aaeff81c0f7d9835 Mon Sep 17 00:00:00 2001 From: "Alex J." Date: Wed, 27 May 2026 14:49:32 -0400 Subject: [PATCH 36/69] feat(cli): capture ios_package_manager in telemetry (#8482) --- cli/src/telemetry.ts | 25 +++++++++++++++- cli/test/telemetry.spec.ts | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 cli/test/telemetry.spec.ts diff --git a/cli/src/telemetry.ts b/cli/src/telemetry.ts index 8e0d847ac3..593397502f 100644 --- a/cli/src/telemetry.ts +++ b/cli/src/telemetry.ts @@ -1,8 +1,9 @@ import { Command } from 'commander'; import Debug from 'debug'; +import { pathExists } from 'fs-extra'; import c from './colors'; -import type { Config } from './definitions'; +import type { Config, PackageManager } from './definitions'; import { send } from './ipc'; import { output } from './log'; import { readConfig, writeConfig } from './sysconfig'; @@ -26,6 +27,7 @@ export interface CommandMetricData { error: string | null; node_version: string; os: string; + ios_package_manager: PackageManager | 'unknown'; } export interface Metric { @@ -80,9 +82,12 @@ export function telemetryAction(config: Config, action: CommanderAction): Comman error: error ? (error.message ? error.message : String(error)) : null, node_version: process.version, os: config.cli.os, + ios_package_manager: await getIOSPackageManager(config), ...Object.fromEntries(versions), }; + debug('metric payload: %O', data); + if (isInteractive()) { let sysconfig = await readConfig(); @@ -126,6 +131,24 @@ export async function sendMetric( } } +/** + * Resolve the iOS dependency manager for telemetry. Returns 'unknown' for + * non-iOS projects or when detection throws, so callers never have to handle + * errors from this signal. + */ +export async function getIOSPackageManager(config: Config): Promise { + try { + if (!(await pathExists(config.ios.platformDirAbs))) { + return 'unknown'; + } + + return await config.ios.packageManager; + } catch (e) { + debug('Could not resolve iOS package manager for telemetry: %O', e); + return 'unknown'; + } +} + /** * Get a unique anonymous identifier for this app. */ diff --git a/cli/test/telemetry.spec.ts b/cli/test/telemetry.spec.ts new file mode 100644 index 0000000000..412048527c --- /dev/null +++ b/cli/test/telemetry.spec.ts @@ -0,0 +1,60 @@ +import { pathExists } from 'fs-extra'; + +import type { Config, IOSConfig } from '../src/definitions'; +import { getIOSPackageManager } from '../src/telemetry'; + +jest.mock('fs-extra', () => ({ + pathExists: jest.fn(), +})); + +const mockedPathExists = pathExists as unknown as jest.Mock; + +function makeConfig(ios: Partial): Config { + return { ios } as unknown as Config; +} + +describe('getIOSPackageManager', () => { + beforeEach(() => { + mockedPathExists.mockReset(); + }); + + it('returns "unknown" when the iOS platform directory does not exist', async () => { + mockedPathExists.mockResolvedValueOnce(false); + + const result = await getIOSPackageManager( + makeConfig({ + platformDirAbs: '/tmp/no-such-dir/ios', + packageManager: Promise.resolve('SPM'), + }), + ); + + expect(result).toBe('unknown'); + expect(mockedPathExists).toHaveBeenCalledWith('/tmp/no-such-dir/ios'); + }); + + it('returns the resolved package manager when iOS is present', async () => { + mockedPathExists.mockResolvedValueOnce(true); + + const result = await getIOSPackageManager( + makeConfig({ + platformDirAbs: '/tmp/app/ios', + packageManager: Promise.resolve('SPM'), + }), + ); + + expect(result).toBe('SPM'); + }); + + it('returns "unknown" when packageManager resolution throws', async () => { + mockedPathExists.mockResolvedValueOnce(true); + + const result = await getIOSPackageManager( + makeConfig({ + platformDirAbs: '/tmp/app/ios', + packageManager: Promise.reject(new Error('boom')), + }), + ); + + expect(result).toBe('unknown'); + }); +}); From 1d031a4abec2c793079ba8897ad2e40c4cc6c7f9 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 28 May 2026 15:00:43 +0200 Subject: [PATCH 37/69] fix(cli): revert live reload config on failure (#8485) --- cli/src/tasks/run.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/src/tasks/run.ts b/cli/src/tasks/run.ts index 2abdb9cd2c..439c292095 100644 --- a/cli/src/tasks/run.ts +++ b/cli/src/tasks/run.ts @@ -116,6 +116,9 @@ export async function runCommand( await sleepForever(); } } catch (e: any) { + if (options.liveReload) { + await CapLiveReloadHelper.revertCapConfigForLiveReload(); + } if (!isFatal(e)) { fatal(e.stack ?? e); } From f1077ef9645b1ada2dd2635e3c3220612bdb4da6 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 29 May 2026 11:02:23 +0200 Subject: [PATCH 38/69] chore: fix typo in declarations.ts (#8488) --- cli/src/declarations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index bda7ff0c03..1103b65850 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -604,7 +604,7 @@ export interface CapacitorConfig { * Configure the local scheme on Android. * * Custom schemes on Android are unable to change the URL path as of Webview 117. Changing this value from anything other than `http` or `https` can result in your - * application unable to resolve routing. If you must change this for some reason, consider using a hash-based url strategy, but there are no guarentees that this + * application unable to resolve routing. If you must change this for some reason, consider using a hash-based url strategy, but there are no guarantees that this * will continue to work long term as allowing non-standard schemes to modify query parameters and url fragments is only allowed for compatibility reasons. * https://ionic.io/blog/capacitor-android-customscheme-issue-with-chrome-117 * From e30773039c0ac249d13780be9d958104cc420cb5 Mon Sep 17 00:00:00 2001 From: Pedro Bilro Date: Fri, 29 May 2026 13:31:18 +0100 Subject: [PATCH 39/69] feat(cli)!: merge live-reload options into `--url` (#8468) Co-authored-by: jcesarmobile --- cli/src/index.ts | 15 ++---- cli/src/tasks/run.ts | 27 ++++++----- cli/src/util/livereload.ts | 98 +------------------------------------- 3 files changed, 20 insertions(+), 120 deletions(-) diff --git a/cli/src/index.ts b/cli/src/index.ts index fb70e1039b..185df15bac 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -249,11 +249,8 @@ export function runProgram(config: Config): void { ) .option('--no-sync', `do not run ${c.input('sync')}`) .option('--forwardPorts ', 'Automatically run "adb reverse" for better live-reloading support') - .option('-l, --live-reload', 'Set live-reload URL via CLI (uses defaults, overrides server.url config)') - .option('--host ', 'Configure host for live-reload URL (used with --live-reload)') - .option('--port ', 'Configure port for live-reload URL (used with --live-reload)') + .option('--url ', 'Load an external URL in the Web View, useful for live-reload (overrides server.url config)') .option('--configuration ', 'Configuration name of the iOS Scheme') - .option('--https', 'Use https:// instead of http:// for live-reload URL (used with --live-reload)') .action( wrapAction( telemetryAction( @@ -270,11 +267,8 @@ export function runProgram(config: Config): void { targetNameSdkVersion, sync, forwardPorts, - liveReload, - host, - port, + url, configuration, - https, }, ) => { const { runCommand } = await import('./tasks/run'); @@ -288,11 +282,8 @@ export function runProgram(config: Config): void { targetNameSdkVersion, sync, forwardPorts, - liveReload, - host, - port, + url, configuration, - https, }); }, ), diff --git a/cli/src/tasks/run.ts b/cli/src/tasks/run.ts index 923a335d1a..124422e6e9 100644 --- a/cli/src/tasks/run.ts +++ b/cli/src/tasks/run.ts @@ -30,11 +30,8 @@ export interface RunCommandOptions { targetNameSdkVersion?: string; sync?: boolean; forwardPorts?: string; - liveReload?: boolean; - host?: string; - port?: string; + url?: string; configuration?: string; - https?: boolean; } export async function runCommand( @@ -42,7 +39,17 @@ export async function runCommand( selectedPlatformName: string, options: RunCommandOptions, ): Promise { - options.host = options.host ?? CapLiveReloadHelper.getIpAddress() ?? 'localhost'; + if (options.url) { + let parsed: URL; + try { + parsed = new URL(options.url); + } catch { + fatal(`Invalid --url: "${options.url}" is not a valid URL`); + } + if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { + fatal(`Invalid --url: must use http:// or https:// (got "${parsed.protocol}//")`); + } + } if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) { const platformDir = resolvePlatform(config, selectedPlatformName); if (platformDir) { @@ -91,14 +98,14 @@ export async function runCommand( await sync(config, platformName, false, true); } const cordovaPlugins = await getCordovaPlugins(config, platformName); - if (options.liveReload) { + if (options.url) { await CapLiveReloadHelper.editCapConfigForLiveReload(config, platformName, options); if (platformName === config.android.name) { await await writeCordovaAndroidManifest(cordovaPlugins, config, platformName, true); } } await run(config, platformName, options); - if (options.liveReload) { + if (options.url) { new Promise((resolve) => process.on('SIGINT', resolve)) .then(async () => { await CapLiveReloadHelper.revertCapConfigForLiveReload(); @@ -107,13 +114,11 @@ export async function runCommand( } }) .then(() => process.exit()); - logger.info( - `App running with live reload listing for: ${options.https ? 'https' : 'http'}://${options.host}${options.port ? `:${options.port}` : ''}. Press Ctrl+C to quit.`, - ); + logger.info(`App running with live reload listening for: ${options.url}. Press Ctrl+C to quit.`); await sleepForever(); } } catch (e: any) { - if (options.liveReload) { + if (options.url) { await CapLiveReloadHelper.revertCapConfigForLiveReload(); } if (!isFatal(e)) { diff --git a/cli/src/util/livereload.ts b/cli/src/util/livereload.ts index c4e432b572..f447bec0a6 100644 --- a/cli/src/util/livereload.ts +++ b/cli/src/util/livereload.ts @@ -1,5 +1,4 @@ import { readJSONSync, writeJSONSync } from 'fs-extra'; -import { networkInterfaces } from 'os'; import { join } from 'path'; import type { Config } from '../definitions'; @@ -18,100 +17,6 @@ class CapLiveReload { // nothing to do } - getIpAddress(name?: string, family?: any) { - const interfaces: any = networkInterfaces() ?? {}; - - const _normalizeFamily = (family?: any) => { - if (family === 4) { - return 'ipv4'; - } - if (family === 6) { - return 'ipv6'; - } - return family ? family.toLowerCase() : 'ipv4'; - }; - const isLoopback = (addr: string) => { - return ( - /^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/.test(addr) || - /^fe80::1$/.test(addr) || - /^::1$/.test(addr) || - /^::$/.test(addr) - ); - }; - const isPrivate = (addr: string) => { - return ( - /^(::f{4}:)?10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) || - /^(::f{4}:)?192\.168\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) || - /^(::f{4}:)?172\.(1[6-9]|2\d|30|31)\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) || - /^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) || - /^(::f{4}:)?169\.254\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) || - /^f[cd][0-9a-f]{2}:/i.test(addr) || - /^fe80:/i.test(addr) || - /^::1$/.test(addr) || - /^::$/.test(addr) - ); - }; - const isPublic = (addr: string) => { - return !isPrivate(addr); - }; - const loopback = (family?: any) => { - // - // Default to `ipv4` - // - family = _normalizeFamily(family); - - if (family !== 'ipv4' && family !== 'ipv6') { - throw new Error('family must be ipv4 or ipv6'); - } - - return family === 'ipv4' ? '127.0.0.1' : 'fe80::1'; - }; - - // - // Default to `ipv4` - // - family = _normalizeFamily(family); - - // - // If a specific network interface has been named, - // return the address. - // - if (name && name !== 'private' && name !== 'public') { - const res = interfaces[name].filter((details: any) => { - const itemFamily = _normalizeFamily(details.family); - return itemFamily === family; - }); - if (res.length === 0) { - return undefined; - } - return res[0].address; - } - - const all = Object.keys(interfaces) - .map((nic) => { - // - // Note: name will only be `public` or `private` - // when this is called. - // - const addresses = interfaces[nic].filter((details: any) => { - details.family = _normalizeFamily(details.family); - if (details.family !== family || isLoopback(details.address)) { - return false; - } - if (!name) { - return true; - } - - return name === 'public' ? isPrivate(details.address) : isPublic(details.address); - }); - - return addresses.length ? addresses[0].address : undefined; - }) - .filter(Boolean); - - return !all.length ? loopback(family) : all[0]; - } - async editCapConfigForLiveReload(config: Config, platformName: string, options: RunCommandOptions): Promise { const platformAbsPath = platformName == config.ios.name @@ -125,10 +30,9 @@ class CapLiveReload { const configJson = readJSONSync(capConfigPath); this.configJsonToRevertTo.json = JSON.stringify(configJson, null, 2); this.configJsonToRevertTo.platformPath = capConfigPath; - const url = `${options.https ? 'https' : 'http'}://${options.host}${options.port ? `:${options.port}` : ''}`; configJson.server = { ...configJson.server, - url, + url: options.url, }; writeJSONSync(capConfigPath, configJson, { spaces: '\t' }); } From d4ad7ffe39daf66e0cfc63af9028d5c05543bde7 Mon Sep 17 00:00:00 2001 From: Joey Pender Date: Mon, 1 Jun 2026 10:31:48 -0500 Subject: [PATCH 40/69] fix(SystemBars): respect `insetsHandling` disable (#8481) --- .../com/getcapacitor/plugin/SystemBars.java | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java index 76e74207cc..29b76e2f28 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java @@ -16,6 +16,7 @@ import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsControllerCompat; import androidx.webkit.WebViewCompat; +import com.getcapacitor.Logger; import com.getcapacitor.Plugin; import com.getcapacitor.PluginCall; import com.getcapacitor.PluginMethod; @@ -32,6 +33,7 @@ public class SystemBars extends Plugin { static final String BAR_STATUS_BAR = "StatusBar"; static final String BAR_GESTURE_BAR = "NavigationBar"; + // TODO: In Cap 9, add an additional option "full" static final String INSETS_HANDLING_CSS = "css"; static final String INSETS_HANDLING_DISABLE = "disable"; @@ -53,7 +55,7 @@ function capacitorSystemBarsCheckMetaViewport() { capacitorSystemBarsCheckMetaViewport(); """; - private boolean insetHandlingEnabled = true; + private String insetsHandling = INSETS_HANDLING_CSS; private boolean hasViewportCover = false; private String currentStatusBarStyle = STYLE_DEFAULT; @@ -94,9 +96,15 @@ private void initSystemBars() { String style = getConfig().getString("style", STYLE_DEFAULT).toUpperCase(Locale.US); boolean hidden = getConfig().getBoolean("hidden", false); - String insetsHandling = getConfig().getString("insetsHandling", "css"); - if (insetsHandling.equals(INSETS_HANDLING_DISABLE)) { - insetHandlingEnabled = false; + String configuredInsetsHandling = getConfig().getString("insetsHandling", INSETS_HANDLING_CSS); + if (INSETS_HANDLING_CSS.equals(configuredInsetsHandling) || INSETS_HANDLING_DISABLE.equals(configuredInsetsHandling)) { + insetsHandling = configuredInsetsHandling; + } else { + Logger.warn( + "SystemBars", + "Unknown insetsHandling value '" + configuredInsetsHandling + "'. Falling back to '" + INSETS_HANDLING_CSS + "'." + ); + insetsHandling = INSETS_HANDLING_CSS; } initWindowInsetsListener(); @@ -146,13 +154,15 @@ public void setAnimation(final PluginCall call) { @JavascriptInterface public void onDOMReady() { - getActivity().runOnUiThread(() -> { - this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> { - hasViewportCover = res.equals("true"); + if (INSETS_HANDLING_CSS.equals(insetsHandling)) { + getActivity().runOnUiThread(() -> { + this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> { + hasViewportCover = res.equals("true"); - getBridge().getWebView().requestApplyInsets(); + getBridge().getWebView().requestApplyInsets(); + }); }); - }); + } } private Insets calcSafeAreaInsets(WindowInsetsCompat insets) { @@ -164,22 +174,28 @@ private Insets calcSafeAreaInsets(WindowInsetsCompat insets) { } private void initSafeAreaCSSVariables() { - WindowInsetsCompat insets; + if (INSETS_HANDLING_CSS.equals(insetsHandling)) { + WindowInsetsCompat insets; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { - View v = (View) this.getBridge().getWebView().getParent(); - insets = ViewCompat.getRootWindowInsets(v); - } else { - insets = WindowInsetsCompat.CONSUMED; - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + View v = (View) this.getBridge().getWebView().getParent(); + insets = ViewCompat.getRootWindowInsets(v); + } else { + insets = WindowInsetsCompat.CONSUMED; + } - if (insets != null) { - Insets safeAreaInsets = calcSafeAreaInsets(insets); - injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); + if (insets != null) { + Insets safeAreaInsets = calcSafeAreaInsets(insets); + injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left); + } } } private void initWindowInsetsListener() { + if (INSETS_HANDLING_DISABLE.equals(insetsHandling)) { + return; + } + ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> { boolean shouldPassthroughInsets = getWebViewMajorVersion() >= WEBVIEW_VERSION_WITH_SAFE_AREA_FIX && hasViewportCover; From 4c6c3219afb5223211e857457e46283c37eb9424 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Mon, 1 Jun 2026 21:04:08 +0200 Subject: [PATCH 41/69] fix(android): show only the requested system bar (#8480) Co-authored-by: Joey Pender Co-authored-by: Pedro Bilro Co-authored-by: Mark Anderson --- .../com/getcapacitor/plugin/SystemBars.java | 14 ++-- .../getcapacitor/plugin/SystemBarsTest.java | 73 +++++++++++++++++++ 2 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 android/capacitor/src/test/java/com/getcapacitor/plugin/SystemBarsTest.java diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java index 29b76e2f28..fd71370945 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java @@ -304,19 +304,21 @@ private void setHidden(boolean hide, String bar) { WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.getDecorView()); if (hide) { - if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) { + if (bar.isEmpty()) { + windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.systemBars()); + } else if (bar.equals(BAR_STATUS_BAR)) { windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.statusBars()); - } - if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) { + } else if (bar.equals(BAR_GESTURE_BAR)) { windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.navigationBars()); } return; } - if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) { + if (bar.isEmpty()) { windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars()); - } - if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) { + } else if (bar.equals(BAR_STATUS_BAR)) { + windowInsetsControllerCompat.show(WindowInsetsCompat.Type.statusBars()); + } else if (bar.equals(BAR_GESTURE_BAR)) { windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars()); } } diff --git a/android/capacitor/src/test/java/com/getcapacitor/plugin/SystemBarsTest.java b/android/capacitor/src/test/java/com/getcapacitor/plugin/SystemBarsTest.java new file mode 100644 index 0000000000..f72373a88e --- /dev/null +++ b/android/capacitor/src/test/java/com/getcapacitor/plugin/SystemBarsTest.java @@ -0,0 +1,73 @@ +package com.getcapacitor.plugin; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.view.View; +import android.view.Window; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.core.view.WindowInsetsControllerCompat; +import com.getcapacitor.Bridge; +import java.lang.reflect.Method; +import org.junit.Test; +import org.mockito.MockedStatic; + +public class SystemBarsTest { + + @Test + public void showWithEmptyBarShowsSystemBars() throws Exception { + WindowInsetsControllerCompat controller = invokeSetHidden(""); + + verify(controller).show(WindowInsetsCompat.Type.systemBars()); + verify(controller, never()).show(WindowInsetsCompat.Type.statusBars()); + verify(controller, never()).show(WindowInsetsCompat.Type.navigationBars()); + } + + @Test + public void showWithStatusBarShowsOnlyStatusBars() throws Exception { + WindowInsetsControllerCompat controller = invokeSetHidden("StatusBar"); + + verify(controller).show(WindowInsetsCompat.Type.statusBars()); + verify(controller, never()).show(WindowInsetsCompat.Type.systemBars()); + verify(controller, never()).show(WindowInsetsCompat.Type.navigationBars()); + } + + @Test + public void showWithNavigationBarShowsOnlyNavigationBars() throws Exception { + WindowInsetsControllerCompat controller = invokeSetHidden("NavigationBar"); + + verify(controller).show(WindowInsetsCompat.Type.navigationBars()); + verify(controller, never()).show(WindowInsetsCompat.Type.systemBars()); + verify(controller, never()).show(WindowInsetsCompat.Type.statusBars()); + } + + private WindowInsetsControllerCompat invokeSetHidden(String bar) throws Exception { + SystemBars plugin = new SystemBars(); + Bridge bridge = mock(Bridge.class); + AppCompatActivity activity = mock(AppCompatActivity.class); + Window window = mock(Window.class); + View decorView = mock(View.class); + WindowInsetsControllerCompat controller = mock(WindowInsetsControllerCompat.class); + + when(bridge.getActivity()).thenReturn(activity); + when(activity.getWindow()).thenReturn(window); + when(window.getDecorView()).thenReturn(decorView); + + plugin.setBridge(bridge); + + try (MockedStatic windowCompat = mockStatic(WindowCompat.class)) { + windowCompat.when(() -> WindowCompat.getInsetsController(window, decorView)).thenReturn(controller); + + Method setHidden = SystemBars.class.getDeclaredMethod("setHidden", boolean.class, String.class); + setHidden.setAccessible(true); + setHidden.invoke(plugin, false, bar); + } + + return controller; + } +} From 36b729d10380687bf5eab2cfa8affcbd1655541b Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 1 Jun 2026 15:11:51 -0400 Subject: [PATCH 42/69] chore: Decrease timeout for CI jobs from 60 to 30 minutes (#8476) Co-authored-by: jcesarmobile --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8664a402da..56fce53892 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ concurrency: jobs: setup: runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 30 steps: - name: Get Latest uses: actions/setup-node@v6 @@ -32,7 +32,7 @@ jobs: key: ${{ runner.OS }}-dependencies-cache-${{ hashFiles('**/package.json') }} lint: runs-on: macos-15 - timeout-minutes: 60 + timeout-minutes: 30 steps: - uses: actions/setup-node@v6 with: @@ -48,7 +48,7 @@ jobs: - run: npm run lint test-cli: runs-on: macos-15 - timeout-minutes: 60 + timeout-minutes: 30 needs: - setup - lint @@ -69,7 +69,7 @@ jobs: working-directory: ./cli test-core: runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 30 needs: - setup - lint @@ -90,7 +90,7 @@ jobs: working-directory: ./core test-ios: runs-on: macos-15 - timeout-minutes: 60 + timeout-minutes: 30 needs: - setup - lint @@ -118,7 +118,7 @@ jobs: run: sh ./scripts/native-podspec.sh lint test-android: runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 30 needs: - setup - lint From c4f4e4beb562ca9f2d05f4da3573018c9d3cb986 Mon Sep 17 00:00:00 2001 From: OS-ruimoreiramendes Date: Tue, 2 Jun 2026 16:53:35 +0100 Subject: [PATCH 43/69] feat(ios)!: bump minimum deployment target to 16 (#8491) --- cli/src/config.ts | 2 +- cli/src/tasks/migrate.ts | 2 +- .../App/App.xcodeproj/project.pbxproj | 8 ++++---- ios-pods-template/App/Podfile | 2 +- .../App/App.xcodeproj/project.pbxproj | 8 ++++---- ios-spm-template/App/CapApp-SPM/Package.swift | 2 +- ios/Capacitor.podspec | 2 +- .../Capacitor.xcodeproj/project.pbxproj | 16 ++++++++-------- .../Capacitor/CAPBridgeViewController.swift | 4 +--- ios/CapacitorCordova.podspec | 2 +- .../CapacitorCordova.xcodeproj/project.pbxproj | 4 ++-- ios/scripts/pods_helpers.rb | 6 +++--- 12 files changed, 28 insertions(+), 30 deletions(-) diff --git a/cli/src/config.ts b/cli/src/config.ts index 4e71fb01d1..2cb64e78d2 100644 --- a/cli/src/config.ts +++ b/cli/src/config.ts @@ -286,7 +286,7 @@ async function loadIOSConfig(rootDir: string, extConfig: ExternalConfig): Promis }; return { name, - minVersion: '15.0', + minVersion: '16.0', platformDir, platformDirAbs, scheme, diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index f1f792e431..47e14b4e4f 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -46,7 +46,7 @@ const plugins = [ const coreVersion = '^8.0.0'; const pluginVersion = '^8.0.0'; const gradleVersion = '8.14.3'; -const iOSVersion = '15'; +const iOSVersion = '16'; const kotlinVersion = '2.2.20'; let installFailed = false; diff --git a/ios-pods-template/App/App.xcodeproj/project.pbxproj b/ios-pods-template/App/App.xcodeproj/project.pbxproj index 092f574919..f1ec6501e8 100644 --- a/ios-pods-template/App/App.xcodeproj/project.pbxproj +++ b/ios-pods-template/App/App.xcodeproj/project.pbxproj @@ -283,7 +283,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -334,7 +334,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -350,7 +350,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.0; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; @@ -370,7 +370,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.App; diff --git a/ios-pods-template/App/Podfile b/ios-pods-template/App/Podfile index 25801ecef7..b50444d463 100644 --- a/ios-pods-template/App/Podfile +++ b/ios-pods-template/App/Podfile @@ -1,6 +1,6 @@ require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers' -platform :ios, '15.0' +platform :ios, '16.0' use_frameworks! # workaround to avoid Xcode caching of Pods that requires diff --git a/ios-spm-template/App/App.xcodeproj/project.pbxproj b/ios-spm-template/App/App.xcodeproj/project.pbxproj index 46f934efd5..06959b343b 100644 --- a/ios-spm-template/App/App.xcodeproj/project.pbxproj +++ b/ios-spm-template/App/App.xcodeproj/project.pbxproj @@ -230,7 +230,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -281,7 +281,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; @@ -298,7 +298,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -320,7 +320,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/ios-spm-template/App/CapApp-SPM/Package.swift b/ios-spm-template/App/CapApp-SPM/Package.swift index 5cde8b6645..e3309a275a 100644 --- a/ios-spm-template/App/CapApp-SPM/Package.swift +++ b/ios-spm-template/App/CapApp-SPM/Package.swift @@ -4,7 +4,7 @@ import PackageDescription // DO NOT MODIFY THIS FILE - managed by Capacitor CLI commands let package = Package( name: "CapApp-SPM", - platforms: [.iOS(.v15)], + platforms: [.iOS(.v16)], products: [ .library( name: "CapApp-SPM", diff --git a/ios/Capacitor.podspec b/ios/Capacitor.podspec index 1b92962750..01ce20591e 100644 --- a/ios/Capacitor.podspec +++ b/ios/Capacitor.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.summary = 'Capacitor for iOS' s.license = 'MIT' s.homepage = 'https://capacitorjs.com/' - s.ios.deployment_target = '15.0' + 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}" diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index 9e5ac570fe..9da29479dc 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -881,7 +881,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -939,7 +939,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -966,7 +966,7 @@ ); INFOPLIST_FILE = Capacitor/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + 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; @@ -996,7 +996,7 @@ ); INFOPLIST_FILE = Capacitor/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + 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; @@ -1060,7 +1060,7 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = TestsHostApp/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -1083,7 +1083,7 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = TestsHostApp/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.capacitorjs.ios.TestsHostApp; @@ -1106,7 +1106,7 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; @@ -1133,7 +1133,7 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index 567d6c69a9..33f17be66c 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -130,9 +130,7 @@ import WebKit webViewConfiguration.allowsAirPlayForMediaPlayback = true webViewConfiguration.mediaTypesRequiringUserActionForPlayback = [] webViewConfiguration.limitsNavigationsToAppBoundDomains = instanceConfiguration.limitsNavigationsToAppBoundDomains - if #available(iOS 15.4, *) { - webViewConfiguration.preferences.isElementFullscreenEnabled = true - } + webViewConfiguration.preferences.isElementFullscreenEnabled = true if let appendUserAgent = instanceConfiguration.appendedUserAgentString { if let appName = webViewConfiguration.applicationNameForUserAgent { webViewConfiguration.applicationNameForUserAgent = "\(appName) \(appendUserAgent)" diff --git a/ios/CapacitorCordova.podspec b/ios/CapacitorCordova.podspec index b4aa014f30..98f6bc3474 100644 --- a/ios/CapacitorCordova.podspec +++ b/ios/CapacitorCordova.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.license = 'MIT' s.authors = { 'Ionic Team' => 'hi@ionicframework.com' } s.source = { git: 'https://github.com/ionic-team/capacitor', tag: s.version.to_s } - s.platform = :ios, 15.0 + s.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" diff --git a/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj b/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj index 3f9ef4c65c..e95b03f613 100644 --- a/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj +++ b/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj @@ -328,7 +328,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -383,7 +383,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/ios/scripts/pods_helpers.rb b/ios/scripts/pods_helpers.rb index 7a240db461..42335c2072 100644 --- a/ios/scripts/pods_helpers.rb +++ b/ios/scripts/pods_helpers.rb @@ -1,11 +1,11 @@ def assertDeploymentTarget(installer) installer.pods_project.targets.each do |target| target.build_configurations.each do |config| - # ensure IPHONEOS_DEPLOYMENT_TARGET is at least 15.0 + # ensure IPHONEOS_DEPLOYMENT_TARGET is at least 16.0 deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f - should_upgrade = deployment_target < 15.0 && deployment_target != 0.0 + should_upgrade = deployment_target < 16.0 && deployment_target != 0.0 if should_upgrade - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '16.0' end end end From 41fd9deb6df07ef83ae390d16824e8bcbefef517 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of markemer)" Date: Tue, 2 Jun 2026 15:56:03 +0000 Subject: [PATCH 44/69] Release 8.4.0 --- CHANGELOG.md | 15 +++++++++++++++ android/CHANGELOG.md | 12 ++++++++++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 11 +++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 4 ++++ core/package.json | 2 +- ios/CHANGELOG.md | 6 ++++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 55 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7f2b77136..cd4fe5f5ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) + +### Bug Fixes + +- **android:** show only the requested system bar ([#8480](https://github.com/ionic-team/capacitor/issues/8480)) ([4c6c321](https://github.com/ionic-team/capacitor/commit/4c6c3219afb5223211e857457e46283c37eb9424)) +- **cli:** revert live reload config on failure ([#8485](https://github.com/ionic-team/capacitor/issues/8485)) ([1d031a4](https://github.com/ionic-team/capacitor/commit/1d031a4abec2c793079ba8897ad2e40c4cc6c7f9)) +- **SystemBars:** make `safe-area-inset-x` available on API <= 34 ([#8424](https://github.com/ionic-team/capacitor/issues/8424)) ([e456de0](https://github.com/ionic-team/capacitor/commit/e456de083e19644f484bec5a5359cb67960ac8bc)) +- **SystemBars:** respect `insetsHandling` disable ([#8481](https://github.com/ionic-team/capacitor/issues/8481)) ([d4ad7ff](https://github.com/ionic-team/capacitor/commit/d4ad7ffe39daf66e0cfc63af9028d5c05543bde7)) + +### Features + +- add method getDouble to plugin config ([#7638](https://github.com/ionic-team/capacitor/issues/7638)) ([93c72de](https://github.com/ionic-team/capacitor/commit/93c72de40a2ec4c78b33659250cb08340083088e)) +- **cli:** add experimental packageOptions ([#8471](https://github.com/ionic-team/capacitor/issues/8471)) ([258867b](https://github.com/ionic-team/capacitor/commit/258867b7bf37b1837b99b02ec9638e5a6df08d97)) +- **cli:** capture ios_package_manager in telemetry ([#8482](https://github.com/ionic-team/capacitor/issues/8482)) ([b4b297a](https://github.com/ionic-team/capacitor/commit/b4b297a52f8732659662d5e5aaeff81c0f7d9835)) + ## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) **Note:** Version bump only for package capacitor diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 74d538d9fe..df7ce545e4 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) + +### Bug Fixes + +- **android:** show only the requested system bar ([#8480](https://github.com/ionic-team/capacitor/issues/8480)) ([4c6c321](https://github.com/ionic-team/capacitor/commit/4c6c3219afb5223211e857457e46283c37eb9424)) +- **SystemBars:** make `safe-area-inset-x` available on API <= 34 ([#8424](https://github.com/ionic-team/capacitor/issues/8424)) ([e456de0](https://github.com/ionic-team/capacitor/commit/e456de083e19644f484bec5a5359cb67960ac8bc)) +- **SystemBars:** respect `insetsHandling` disable ([#8481](https://github.com/ionic-team/capacitor/issues/8481)) ([d4ad7ff](https://github.com/ionic-team/capacitor/commit/d4ad7ffe39daf66e0cfc63af9028d5c05543bde7)) + +### Features + +- add method getDouble to plugin config ([#7638](https://github.com/ionic-team/capacitor/issues/7638)) ([93c72de](https://github.com/ionic-team/capacitor/commit/93c72de40a2ec4c78b33659250cb08340083088e)) + ## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index 8ae93f3f2b..af3f4f556f 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "8.3.4", + "version": "8.4.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -23,7 +23,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^8.3.0" + "@capacitor/core": "^8.4.0" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index bba24688f6..ee48a8d5f4 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) + +### Bug Fixes + +- **cli:** revert live reload config on failure ([#8485](https://github.com/ionic-team/capacitor/issues/8485)) ([1d031a4](https://github.com/ionic-team/capacitor/commit/1d031a4abec2c793079ba8897ad2e40c4cc6c7f9)) + +### Features + +- **cli:** add experimental packageOptions ([#8471](https://github.com/ionic-team/capacitor/issues/8471)) ([258867b](https://github.com/ionic-team/capacitor/commit/258867b7bf37b1837b99b02ec9638e5a6df08d97)) +- **cli:** capture ios_package_manager in telemetry ([#8482](https://github.com/ionic-team/capacitor/issues/8482)) ([b4b297a](https://github.com/ionic-team/capacitor/commit/b4b297a52f8732659662d5e5aaeff81c0f7d9835)) + ## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) **Note:** Version bump only for package @capacitor/cli diff --git a/cli/package.json b/cli/package.json index 9df346b143..1f987d33ad 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "8.3.4", + "version": "8.4.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 017293794f..1de9522ff4 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) + +**Note:** Version bump only for package @capacitor/core + ## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index eef113a50a..b22aef174f 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "8.3.4", + "version": "8.4.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 87ee73bb4f..eac0a4a1ca 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) + +### Features + +- add method getDouble to plugin config ([#7638](https://github.com/ionic-team/capacitor/issues/7638)) ([93c72de](https://github.com/ionic-team/capacitor/commit/93c72de40a2ec4c78b33659250cb08340083088e)) + ## [8.3.4](https://github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) **Note:** Version bump only for package @capacitor/ios diff --git a/ios/package.json b/ios/package.json index a7b8d31335..a58c020903 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "8.3.4", + "version": "8.4.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^8.3.0" + "@capacitor/core": "^8.4.0" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index 1e53d6bdca..b2efb400d4 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "8.3.4", + "version": "8.4.0", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From 5bf135bc0741778db0c9b50477b971860f72878c Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of markemer)" Date: Tue, 2 Jun 2026 16:26:21 +0000 Subject: [PATCH 45/69] Release 9.0.0-alpha.3 --- CHANGELOG.md | 23 +++++++++++++++++++++++ android/CHANGELOG.md | 16 ++++++++++++++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 19 +++++++++++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 8 ++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 10 ++++++++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 83 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eb8f3a0e2..1b266d2040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,29 @@ 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.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...9.0.0-alpha.3) (2026-06-02) + +### Bug Fixes + +- **android:** show only the requested system bar ([#8480](https://github.com/ionic-team/capacitor/issues/8480)) ([4c6c321](https://github.com/ionic-team/capacitor/commit/4c6c3219afb5223211e857457e46283c37eb9424)) +- **cli:** revert live reload config on failure ([#8485](https://github.com/ionic-team/capacitor/issues/8485)) ([1d031a4](https://github.com/ionic-team/capacitor/commit/1d031a4abec2c793079ba8897ad2e40c4cc6c7f9)) +- **SystemBars:** make `safe-area-inset-x` available on API <= 34 ([#8424](https://github.com/ionic-team/capacitor/issues/8424)) ([e456de0](https://github.com/ionic-team/capacitor/commit/e456de083e19644f484bec5a5359cb67960ac8bc)) +- **SystemBars:** respect `insetsHandling` disable ([#8481](https://github.com/ionic-team/capacitor/issues/8481)) ([d4ad7ff](https://github.com/ionic-team/capacitor/commit/d4ad7ffe39daf66e0cfc63af9028d5c05543bde7)) + +### Features + +- add method getDouble to plugin config ([#7638](https://github.com/ionic-team/capacitor/issues/7638)) ([93c72de](https://github.com/ionic-team/capacitor/commit/93c72de40a2ec4c78b33659250cb08340083088e)) +- **cli:** add experimental packageOptions ([#8471](https://github.com/ionic-team/capacitor/issues/8471)) ([258867b](https://github.com/ionic-team/capacitor/commit/258867b7bf37b1837b99b02ec9638e5a6df08d97)) +- **cli:** capture ios_package_manager in telemetry ([#8482](https://github.com/ionic-team/capacitor/issues/8482)) ([b4b297a](https://github.com/ionic-team/capacitor/commit/b4b297a52f8732659662d5e5aaeff81c0f7d9835)) + +## [8.3.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...8.3.4) (2026-05-12) + +## [8.3.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...8.3.3) (2026-05-08) + +### Bug Fixes + +- **cli:** copy plugin files in CocoaPods projects ([#8467](https://github.com/ionic-team/capacitor/issues/8467)) ([b2d7719](https://github.com/ionic-team/capacitor/commit/b2d771926a180e60deea31992d7d4abcd5ca3bc7)) + # [9.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...9.0.0-alpha.2) (2026-05-12) **Note:** Version bump only for package capacitor diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 6b62e550ff..44df7be5e1 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,22 @@ 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.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...9.0.0-alpha.3) (2026-06-02) + +### Bug Fixes + +- **android:** show only the requested system bar ([#8480](https://github.com/ionic-team/capacitor/issues/8480)) ([4c6c321](https://github.com/ionic-team/capacitor/commit/4c6c3219afb5223211e857457e46283c37eb9424)) +- **SystemBars:** make `safe-area-inset-x` available on API <= 34 ([#8424](https://github.com/ionic-team/capacitor/issues/8424)) ([e456de0](https://github.com/ionic-team/capacitor/commit/e456de083e19644f484bec5a5359cb67960ac8bc)) +- **SystemBars:** respect `insetsHandling` disable ([#8481](https://github.com/ionic-team/capacitor/issues/8481)) ([d4ad7ff](https://github.com/ionic-team/capacitor/commit/d4ad7ffe39daf66e0cfc63af9028d5c05543bde7)) + +### Features + +- add method getDouble to plugin config ([#7638](https://github.com/ionic-team/capacitor/issues/7638)) ([93c72de](https://github.com/ionic-team/capacitor/commit/93c72de40a2ec4c78b33659250cb08340083088e)) + +## [8.3.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...8.3.4) (2026-05-12) + +## [8.3.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...8.3.3) (2026-05-08) + # [9.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...9.0.0-alpha.2) (2026-05-12) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index 8022d692c8..575e8ec18d 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "9.0.0-alpha.2", + "version": "9.0.0-alpha.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -26,7 +26,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^9.0.0-alpha.2" + "@capacitor/core": "^9.0.0-alpha.3" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 1fd1c69356..1c4dd6279e 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,25 @@ 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.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...9.0.0-alpha.3) (2026-06-02) + +### Bug Fixes + +- **cli:** revert live reload config on failure ([#8485](https://github.com/ionic-team/capacitor/issues/8485)) ([1d031a4](https://github.com/ionic-team/capacitor/commit/1d031a4abec2c793079ba8897ad2e40c4cc6c7f9)) + +### Features + +- **cli:** add experimental packageOptions ([#8471](https://github.com/ionic-team/capacitor/issues/8471)) ([258867b](https://github.com/ionic-team/capacitor/commit/258867b7bf37b1837b99b02ec9638e5a6df08d97)) +- **cli:** capture ios_package_manager in telemetry ([#8482](https://github.com/ionic-team/capacitor/issues/8482)) ([b4b297a](https://github.com/ionic-team/capacitor/commit/b4b297a52f8732659662d5e5aaeff81c0f7d9835)) + +## [8.3.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...8.3.4) (2026-05-12) + +## [8.3.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...8.3.3) (2026-05-08) + +### Bug Fixes + +- **cli:** copy plugin files in CocoaPods projects ([#8467](https://github.com/ionic-team/capacitor/issues/8467)) ([b2d7719](https://github.com/ionic-team/capacitor/commit/b2d771926a180e60deea31992d7d4abcd5ca3bc7)) + # [9.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...9.0.0-alpha.2) (2026-05-12) **Note:** Version bump only for package @capacitor/cli diff --git a/cli/package.json b/cli/package.json index 38c0c01b39..8f7de6b14d 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "9.0.0-alpha.2", + "version": "9.0.0-alpha.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 3285ca234c..cbcba0d990 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,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.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...9.0.0-alpha.3) (2026-06-02) + +## [8.3.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...8.3.4) (2026-05-12) + +## [8.3.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...8.3.3) (2026-05-08) + +**Note:** Version bump only for package @capacitor/core + # [9.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...9.0.0-alpha.2) (2026-05-12) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index 8567c8be97..2969b6130f 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "9.0.0-alpha.2", + "version": "9.0.0-alpha.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 0a6745fcf3..4a99bf4727 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,16 @@ 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.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...9.0.0-alpha.3) (2026-06-02) + +### Features + +- add method getDouble to plugin config ([#7638](https://github.com/ionic-team/capacitor/issues/7638)) ([93c72de](https://github.com/ionic-team/capacitor/commit/93c72de40a2ec4c78b33659250cb08340083088e)) + +## [8.3.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...8.3.4) (2026-05-12) + +## [8.3.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.0...8.3.3) (2026-05-08) + # [9.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.1...9.0.0-alpha.2) (2026-05-12) **Note:** Version bump only for package @capacitor/ios diff --git a/ios/package.json b/ios/package.json index a57c13889e..87c2b0b2e9 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "9.0.0-alpha.2", + "version": "9.0.0-alpha.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^9.0.0-alpha.2" + "@capacitor/core": "^9.0.0-alpha.3" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index cfcbca64cb..eab51ece20 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "9.0.0-alpha.2", + "version": "9.0.0-alpha.3", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From 28bb2c687069dfdd6aa7abc866004a1c6388d103 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 2 Jun 2026 21:31:41 +0200 Subject: [PATCH 46/69] fix(cli): patch Capacitor SPM dependency version in plugins (#8492) Co-authored-by: Mark Anderson --- cli/src/ios/update.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index b060e59a8f..1f4bc87d03 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -1,5 +1,6 @@ import { copy, remove, pathExists, readFile, realpath, writeFile } from 'fs-extra'; import { basename, dirname, join, relative } from 'path'; +import { major } from 'semver'; import c from '../colors'; import { checkPlatformVersions, getCapacitorPackageVersion, runTask } from '../common'; @@ -58,6 +59,28 @@ async function updatePluginFiles(config: Config, plugins: Plugin[], deployment: await generateCordovaPackageFiles(cordovaPlugins, config); const validSPMPackages = await checkPluginsForPackageSwift(config, plugins); + await Promise.all( + validSPMPackages.map(async (plugin) => { + const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); + 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) { + content = setAllStringIn( + content, + `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, + `)`, + ` from: "${majorCapVersion}.0.0"`, + ); + await writeFile(packageSwiftPath, content); + logger.warn(`${plugin.id} is built for Capacitor ${major(version)}, it might cause issues`); + } + }), + ); await generatePackageFile(config, validSPMPackages.concat(cordovaPlugins)); } else { From 16ca49dfcb26b761e35d5002b24710ba6e2150b2 Mon Sep 17 00:00:00 2001 From: OS-ruimoreiramendes Date: Wed, 3 Jun 2026 12:40:10 +0100 Subject: [PATCH 47/69] chore(cli): bump default Kotlin version to 2.4.0 (#8494) --- cli/src/android/update.ts | 2 +- cli/src/tasks/migrate.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/android/update.ts b/cli/src/android/update.ts index 5053f639d5..45275bddc3 100644 --- a/cli/src/android/update.ts +++ b/cli/src/android/update.ts @@ -264,7 +264,7 @@ if (hasProperty('postBuildExtras')) { export async function handleCordovaPluginsGradle(config: Config, cordovaPlugins: Plugin[]): Promise { const pluginsGradlePath = join(config.android.cordovaPluginsDirAbs, 'build.gradle'); const kotlinNeeded = await kotlinNeededCheck(config, cordovaPlugins); - const kotlinVersionString = config.app.extConfig.cordova?.preferences?.GradlePluginKotlinVersion ?? '2.2.20'; + const kotlinVersionString = config.app.extConfig.cordova?.preferences?.GradlePluginKotlinVersion ?? '2.4.0'; const frameworksArray: any[] = []; let prefsArray: any[] = []; const applyArray: any[] = []; diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 47e14b4e4f..addcd5bf4b 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -47,7 +47,7 @@ const coreVersion = '^8.0.0'; const pluginVersion = '^8.0.0'; const gradleVersion = '8.14.3'; const iOSVersion = '16'; -const kotlinVersion = '2.2.20'; +const kotlinVersion = '2.4.0'; let installFailed = false; export async function migrateCommand(config: Config, noprompt: boolean, packagemanager: string): Promise { From eaf8967594b490eb0393ee20db183219f5d5480d Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 3 Jun 2026 14:45:04 +0200 Subject: [PATCH 48/69] chore(android-template): bump compile/targetSdkVersion to 37 (#8495) --- android-template/variables.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android-template/variables.gradle b/android-template/variables.gradle index ee4ba41c46..445a4730d4 100644 --- a/android-template/variables.gradle +++ b/android-template/variables.gradle @@ -1,7 +1,7 @@ ext { minSdkVersion = 24 - compileSdkVersion = 36 - targetSdkVersion = 36 + compileSdkVersion = 37 + targetSdkVersion = 37 androidxActivityVersion = '1.11.0' androidxAppCompatVersion = '1.7.1' androidxCoordinatorLayoutVersion = '1.3.0' From 3d77cc20d94ee020f27ca224653121c791edf919 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 3 Jun 2026 14:45:18 +0200 Subject: [PATCH 49/69] chore(android)!: bump compile/targetSdkVersion to 37 (#8496) --- android/capacitor-cordova/build.gradle | 4 ++-- android/capacitor/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/capacitor-cordova/build.gradle b/android/capacitor-cordova/build.gradle index 717c6b1aa2..5987eb0e83 100644 --- a/android/capacitor-cordova/build.gradle +++ b/android/capacitor-cordova/build.gradle @@ -17,10 +17,10 @@ apply plugin: 'com.android.library' android { namespace = "com.getcapacitor.cordova" - compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36 + compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 37 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24 - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 37 versionCode 1 versionName "1.0" consumerProguardFiles 'proguard-rules.pro' diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index 5841ded35d..7421ce57f8 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -40,10 +40,10 @@ if (System.getenv("CAP_PUBLISH") == "true") { android { namespace = "com.getcapacitor.android" - compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36 + compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 37 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24 - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 37 versionCode 1 versionName "1.0" consumerProguardFiles 'proguard-rules.pro' From 1c3a28d480c4acc1ae2e2aba0a6a902aabf540a7 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 3 Jun 2026 15:15:22 +0200 Subject: [PATCH 50/69] chore(cli)!: bump compile/targetSdkVersion to 37 (#8497) --- capacitor-cordova-android-plugins/build.gradle | 4 ++-- cli/src/tasks/migrate.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index 5edcbfd2c2..96d1707a95 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -17,10 +17,10 @@ apply plugin: 'com.android.library' android { namespace = "capacitor.cordova.android.plugins" - compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36 + compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 37 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24 - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 37 versionCode 1 versionName "1.0" } diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index addcd5bf4b..a5fd06c7e6 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -90,7 +90,7 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem const { migrateconfirm } = noprompt ? { migrateconfirm: 'y' } - : await logPrompt(`Capacitor 8 sets a deployment target of iOS ${iOSVersion} and Android 16 (SDK 36). \n`, { + : await logPrompt(`Capacitor 8 sets a deployment target of iOS ${iOSVersion} and Android 17 (SDK 37). \n`, { type: 'text', name: 'migrateconfirm', message: `Are you sure you want to migrate? (Y/n)`, From fe6a83b4eb0b4ceacf3b566f3e262fc792001e58 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 4 Jun 2026 16:57:50 +0200 Subject: [PATCH 51/69] chore(android)!: bump minSdkVersion to 26 (#8500) --- android/capacitor-cordova/build.gradle | 2 +- android/capacitor/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/capacitor-cordova/build.gradle b/android/capacitor-cordova/build.gradle index 5987eb0e83..2f32501240 100644 --- a/android/capacitor-cordova/build.gradle +++ b/android/capacitor-cordova/build.gradle @@ -19,7 +19,7 @@ android { namespace = "com.getcapacitor.cordova" compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 37 defaultConfig { - minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24 + minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 26 targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 37 versionCode 1 versionName "1.0" diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index 7421ce57f8..82277e6b44 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -42,7 +42,7 @@ android { namespace = "com.getcapacitor.android" compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 37 defaultConfig { - minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24 + minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 26 targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 37 versionCode 1 versionName "1.0" From 753c97cac2c2b8ce862e5866b9feb03a47b62c27 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 4 Jun 2026 18:34:17 +0200 Subject: [PATCH 52/69] chore(cli)!: bump minSdkVersion to 26 (#8501) --- capacitor-cordova-android-plugins/build.gradle | 2 +- cli/src/config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index 96d1707a95..e2495759b1 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -19,7 +19,7 @@ android { namespace = "capacitor.cordova.android.plugins" compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 37 defaultConfig { - minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24 + minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 26 targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 37 versionCode 1 versionName "1.0" diff --git a/cli/src/config.ts b/cli/src/config.ts index 2cb64e78d2..9c6c5183d8 100644 --- a/cli/src/config.ts +++ b/cli/src/config.ts @@ -236,7 +236,7 @@ async function loadAndroidConfig( return { name, - minVersion: '24', + minVersion: '26', studioPath, platformDir, platformDirAbs, From c43cb8fb17aede606077fe46fc70990195d1cc53 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 5 Jun 2026 15:21:40 +0200 Subject: [PATCH 53/69] chore(android-template)!: bump minSdkVersion to 26 (#8502) --- android-template/variables.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-template/variables.gradle b/android-template/variables.gradle index 445a4730d4..36eff33927 100644 --- a/android-template/variables.gradle +++ b/android-template/variables.gradle @@ -1,5 +1,5 @@ ext { - minSdkVersion = 24 + minSdkVersion = 26 compileSdkVersion = 37 targetSdkVersion = 37 androidxActivityVersion = '1.11.0' From 42caab564d3687c1aa29acc6386d6943883adcb9 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 10 Jun 2026 15:24:44 +0200 Subject: [PATCH 54/69] refactor(android): remove unneeded code (#8506) --- .../main/java/com/getcapacitor/Bridge.java | 66 +++---------------- .../util/CapacitorHttpUrlConnection.java | 13 +--- 2 files changed, 12 insertions(+), 67 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 0ca600aff6..00dd89d60e 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -11,7 +11,6 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -51,7 +50,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.JSONException; @@ -294,63 +292,19 @@ public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) { @SuppressLint("WebViewApiAvailability") public boolean isMinimumWebViewInstalled() { - PackageManager pm = getContext().getPackageManager(); - - // Check getCurrentWebViewPackage() directly if above Android 8 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - PackageInfo info = WebView.getCurrentWebViewPackage(); - Pattern pattern = Pattern.compile("(\\d+)"); - Matcher matcher = pattern.matcher(info.versionName); - if (matcher.find()) { - String majorVersionStr = matcher.group(0); - int majorVersion = Integer.parseInt(majorVersionStr); - if (info.packageName.equals("com.huawei.webview")) { - return majorVersion >= config.getMinHuaweiWebViewVersion(); - } - return majorVersion >= config.getMinWebViewVersion(); - } else { - return false; - } - } - - // Otherwise manually check WebView versions - try { - PackageInfo info = InternalUtils.getPackageInfo(pm, "com.android.chrome"); - String majorVersionStr = info.versionName.split("\\.")[0]; - int majorVersion = Integer.parseInt(majorVersionStr); - return majorVersion >= config.getMinWebViewVersion(); - } catch (Exception ex) { - Logger.warn("Unable to get package info for 'com.google.android.webview'" + ex.toString()); - } - - try { - PackageInfo info = InternalUtils.getPackageInfo(pm, "com.android.webview"); - String majorVersionStr = info.versionName.split("\\.")[0]; + PackageInfo info = WebView.getCurrentWebViewPackage(); + Pattern pattern = Pattern.compile("(\\d+)"); + Matcher matcher = pattern.matcher(info.versionName); + if (matcher.find()) { + String majorVersionStr = matcher.group(0); int majorVersion = Integer.parseInt(majorVersionStr); + if (info.packageName.equals("com.huawei.webview")) { + return majorVersion >= config.getMinHuaweiWebViewVersion(); + } return majorVersion >= config.getMinWebViewVersion(); - } catch (Exception ex) { - Logger.warn("Unable to get package info for 'com.android.webview'" + ex.toString()); - } - - final int amazonFireMajorWebViewVersion = extractWebViewMajorVersion(pm, "com.amazon.webview.chromium"); - if (amazonFireMajorWebViewVersion >= config.getMinWebViewVersion()) { - return true; - } - - // Could not detect any webview, return false - return false; - } - - private int extractWebViewMajorVersion(final PackageManager pm, final String webViewPackageName) { - try { - final PackageInfo info = InternalUtils.getPackageInfo(pm, webViewPackageName); - final String majorVersionStr = info.versionName.split("\\.")[0]; - final int majorVersion = Integer.parseInt(majorVersionStr); - return majorVersion; - } catch (Exception ex) { - Logger.warn(String.format("Unable to get package info for '%s' with err '%s'", webViewPackageName, ex)); + } else { + return false; } - return 0; } public boolean launchIntent(Uri url) { diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java index 6180d64200..7cb0f34690 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java @@ -1,6 +1,5 @@ package com.getcapacitor.plugin.util; -import android.os.Build; import android.os.LocaleList; import android.text.TextUtils; import com.getcapacitor.Bridge; @@ -211,9 +210,7 @@ public void setRequestBody(PluginCall call, JSValue body, String bodyType) throw this.writeRequestBody(dataString != null ? dataString : ""); } else if (bodyType != null && bodyType.equals("file")) { try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - os.write(Base64.getDecoder().decode(body.toString())); - } + os.write(Base64.getDecoder().decode(body.toString())); os.flush(); } } else if (contentType.contains("application/x-www-form-urlencoded")) { @@ -295,13 +292,7 @@ private void writeFormDataRequestBody(String boundary, JSArray entries) throws I os.writeBytes("Content-Type: " + fileContentType + lineEnd); os.writeBytes("Content-Transfer-Encoding: binary" + lineEnd); os.writeBytes(lineEnd); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - os.write(Base64.getDecoder().decode(value)); - } else { - os.write(android.util.Base64.decode(value, android.util.Base64.DEFAULT)); - } - + os.write(Base64.getDecoder().decode(value)); os.writeBytes(lineEnd); } } From 1a5543fbfcf4ba911270002d573275342f680bde Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Wed, 10 Jun 2026 12:59:24 -0400 Subject: [PATCH 55/69] fix(ios): eliminate double comma in SPM generation (#8505) Co-authored-by: jcesarmobile --- cli/src/util/spm.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/util/spm.ts b/cli/src/util/spm.ts index d5491a20b0..3243c3efd3 100644 --- a/cli/src/util/spm.ts +++ b/cli/src/util/spm.ts @@ -162,10 +162,10 @@ let package = Package( .target( name: "CapApp-SPM", dependencies: [ - .product(name: "Capacitor", package: "capacitor-swift-pm"),`; + .product(name: "Capacitor", package: "capacitor-swift-pm")`; if (enableCordova) { - packageSwiftText += ` .product(name: "Cordova", package: "capacitor-swift-pm")`; + packageSwiftText += `,\n .product(name: "Cordova", package: "capacitor-swift-pm")`; } for (const plugin of plugins) { From 11f3c83f0883a1400e14c538f9040e13f7e0dbfc Mon Sep 17 00:00:00 2001 From: OS-ruimoreiramendes Date: Wed, 10 Jun 2026 18:00:06 +0100 Subject: [PATCH 56/69] chore(android)!: bump AGP to 9.2.1 and Gradle to 9.5.1 (#8498) --- android-template/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 48462 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 ++- android-template/gradlew | 7 ++-- android-template/gradlew.bat | 32 ++++++------------ android/capacitor-cordova/build.gradle | 2 +- android/capacitor/build.gradle | 2 +- android/gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 48462 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 ++- android/gradlew | 7 ++-- android/gradlew.bat | 32 ++++++------------ android/package.json | 2 +- .../build.gradle | 2 +- cli/src/tasks/migrate.ts | 2 +- scripts/publish-android.sh | 2 +- 15 files changed, 37 insertions(+), 63 deletions(-) diff --git a/android-template/build.gradle b/android-template/build.gradle index f8f0e43b6d..70bb56fc47 100644 --- a/android-template/build.gradle +++ b/android-template/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.13.0' + classpath 'com.android.tools.build:gradle:9.2.1' classpath 'com.google.gms:google-services:4.4.4' // NOTE: Do not place your application dependencies here; they belong diff --git a/android-template/gradle/wrapper/gradle-wrapper.jar b/android-template/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..b1b8ef56b44f16b14dc800fa8103a6d89abb526f 100644 GIT binary patch delta 40183 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 delta 35506 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 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,30 +65,18 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/android/capacitor-cordova/build.gradle b/android/capacitor-cordova/build.gradle index 2f32501240..7d2593190e 100644 --- a/android/capacitor-cordova/build.gradle +++ b/android/capacitor-cordova/build.gradle @@ -9,7 +9,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.13.0' + classpath 'com.android.tools.build:gradle:9.2.1' } } diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index 82277e6b44..873060946b 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -20,7 +20,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:8.13.0' + classpath 'com.android.tools.build:gradle:9.2.1' if (System.getenv("CAP_PUBLISH") == "true") { classpath 'io.github.gradle-nexus:publish-plugin:1.3.0' diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..b1b8ef56b44f16b14dc800fa8103a6d89abb526f 100644 GIT binary patch delta 40183 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 delta 35506 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 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,30 +65,18 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/android/package.json b/android/package.json index 575e8ec18d..d8773d0df9 100644 --- a/android/package.json +++ b/android/package.json @@ -23,7 +23,7 @@ "capacitor-cordova/src/main/" ], "scripts": { - "verify": "./gradlew clean lint build test -b capacitor/build.gradle" + "verify": "./gradlew :capacitor-android:clean :capacitor-android:lint :capacitor-android:build :capacitor-android:test" }, "peerDependencies": { "@capacitor/core": "^9.0.0-alpha.3" diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index e2495759b1..560ba3b0f9 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -9,7 +9,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.13.0' + classpath 'com.android.tools.build:gradle:9.2.1' } } diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index a5fd06c7e6..0bc891e499 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -45,7 +45,7 @@ const plugins = [ ]; const coreVersion = '^8.0.0'; const pluginVersion = '^8.0.0'; -const gradleVersion = '8.14.3'; +const gradleVersion = '9.5.1'; const iOSVersion = '16'; const kotlinVersion = '2.4.0'; let installFailed = false; diff --git a/scripts/publish-android.sh b/scripts/publish-android.sh index b9b39c9aa8..58c4ddfb94 100644 --- a/scripts/publish-android.sh +++ b/scripts/publish-android.sh @@ -24,7 +24,7 @@ else printf %"s\n" "Publishing $CAP_VERSION to MavenCentral production..." # Build and publish - $DIR/gradlew clean build publishReleasePublicationToSonatypeRepository closeAndReleaseSonatypeStagingRepository --max-workers 1 -b $DIR/capacitor/build.gradle -Pandroid.useAndroidX=true > $LOG_OUTPUT 2>&1 + $DIR/gradlew :capacitor-android:clean :capacitor-android:build :capacitor-android:publishReleasePublicationToSonatypeRepository closeAndReleaseSonatypeStagingRepository --max-workers 1 -Pandroid.useAndroidX=true > $LOG_OUTPUT 2>&1 echo $RESULT From 908bb1a507aeca77568ea65e276b8ef658382eae Mon Sep 17 00:00:00 2001 From: OS-ruimoreiramendes Date: Fri, 12 Jun 2026 10:42:52 +0100 Subject: [PATCH 57/69] chore(android): remove targetSdkVersion declarations (#8507) --- android-template/app/build.gradle | 1 - android/capacitor-cordova/build.gradle | 1 - android/capacitor/build.gradle | 1 - capacitor-cordova-android-plugins/build.gradle | 1 - 4 files changed, 4 deletions(-) diff --git a/android-template/app/build.gradle b/android-template/app/build.gradle index b701096d4b..5ac536dc7a 100644 --- a/android-template/app/build.gradle +++ b/android-template/app/build.gradle @@ -6,7 +6,6 @@ android { defaultConfig { applicationId "com.getcapacitor.app" minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/android/capacitor-cordova/build.gradle b/android/capacitor-cordova/build.gradle index 7d2593190e..4ce5f5eaaa 100644 --- a/android/capacitor-cordova/build.gradle +++ b/android/capacitor-cordova/build.gradle @@ -20,7 +20,6 @@ android { compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 37 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 26 - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 37 versionCode 1 versionName "1.0" consumerProguardFiles 'proguard-rules.pro' diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index 873060946b..ad5e9e5c3f 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -43,7 +43,6 @@ android { compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 37 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 26 - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 37 versionCode 1 versionName "1.0" consumerProguardFiles 'proguard-rules.pro' diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index 560ba3b0f9..8048084302 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -20,7 +20,6 @@ android { compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 37 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 26 - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 37 versionCode 1 versionName "1.0" } From 6048e90171afa0229a3c25b52a23c377c6bb804c Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 15 Jun 2026 12:11:19 +0200 Subject: [PATCH 58/69] fix(cli): make SPM dependency patch work on prereleases (#8508) --- cli/src/ios/update.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 1f4bc87d03..3ab793c250 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -1,6 +1,6 @@ import { copy, remove, pathExists, readFile, realpath, writeFile } from 'fs-extra'; import { basename, dirname, join, relative } from 'path'; -import { major } from 'semver'; +import { major, prerelease } from 'semver'; import c from '../colors'; import { checkPlatformVersions, getCapacitorPackageVersion, runTask } from '../common'; @@ -70,11 +70,13 @@ async function updatePluginFiles(config: Config, plugins: Plugin[], deployment: 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`; content = setAllStringIn( content, `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, `)`, - ` from: "${majorCapVersion}.0.0"`, + ` from: "${forceVersion}"`, ); await writeFile(packageSwiftPath, content); logger.warn(`${plugin.id} is built for Capacitor ${major(version)}, it might cause issues`); From 8b7fea003ffa8e1c5cea0db4a122130b939c282c Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of jcesarmobile)" Date: Fri, 19 Jun 2026 08:47:02 +0000 Subject: [PATCH 59/69] Release 9.0.0-alpha.4 --- CHANGELOG.md | 10 ++++++++++ android/CHANGELOG.md | 6 ++++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 10 ++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 6 ++++++ core/package.json | 2 +- ios/CHANGELOG.md | 6 ++++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 45 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d11e59757..a4aac96e58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ 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.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (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)) +- **ios:** eliminate double comma in SPM generation ([#8505](https://github.com/ionic-team/capacitor/issues/8505)) ([1a5543f](https://github.com/ionic-team/capacitor/commit/1a5543fbfcf4ba911270002d573275342f680bde)) + +# [8.4.0](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...8.4.0) (2026-06-02) + # [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...9.0.0-alpha.3) (2026-06-02) **Note:** Version bump only for package capacitor diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index ab038f6f72..fa517333df 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,12 @@ 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.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + +# [8.4.0](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...8.4.0) (2026-06-02) + +**Note:** Version bump only for package @capacitor/android + # [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...9.0.0-alpha.3) (2026-06-02) # [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) diff --git a/android/package.json b/android/package.json index 575e8ec18d..bec2650749 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "9.0.0-alpha.3", + "version": "9.0.0-alpha.4", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -26,7 +26,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^9.0.0-alpha.3" + "@capacitor/core": "^9.0.0-alpha.4" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 939d9b7218..2480e6e0cb 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,16 @@ 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.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (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)) +- **ios:** eliminate double comma in SPM generation ([#8505](https://github.com/ionic-team/capacitor/issues/8505)) ([1a5543f](https://github.com/ionic-team/capacitor/commit/1a5543fbfcf4ba911270002d573275342f680bde)) + +# [8.4.0](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...8.4.0) (2026-06-02) + # [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...9.0.0-alpha.3) (2026-06-02) # [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) diff --git a/cli/package.json b/cli/package.json index 8f7de6b14d..a6cd9023fe 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "9.0.0-alpha.3", + "version": "9.0.0-alpha.4", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 2657c6c9e5..c2b998cade 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,12 @@ 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.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + +# [8.4.0](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...8.4.0) (2026-06-02) + +**Note:** Version bump only for package @capacitor/core + # [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...9.0.0-alpha.3) (2026-06-02) # [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) diff --git a/core/package.json b/core/package.json index 2969b6130f..d0b42a41df 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "9.0.0-alpha.3", + "version": "9.0.0-alpha.4", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 54cc54a30c..8842ff0db1 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,12 @@ 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.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + +# [8.4.0](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...8.4.0) (2026-06-02) + +**Note:** Version bump only for package @capacitor/ios + # [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...9.0.0-alpha.3) (2026-06-02) # [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) diff --git a/ios/package.json b/ios/package.json index 87c2b0b2e9..5d8fb9a483 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "9.0.0-alpha.3", + "version": "9.0.0-alpha.4", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^9.0.0-alpha.3" + "@capacitor/core": "^9.0.0-alpha.4" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index eab51ece20..263fcadcd1 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "9.0.0-alpha.3", + "version": "9.0.0-alpha.4", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From 7217b5215a175eededf607a216e0cab2a8450a34 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 60/69] Release 8.4.1 --- CHANGELOG.md | 7 +++++++ android/CHANGELOG.md | 4 ++++ android/package.json | 2 +- cli/CHANGELOG.md | 7 +++++++ cli/package.json | 2 +- core/CHANGELOG.md | 4 ++++ core/package.json | 2 +- ios/CHANGELOG.md | 4 ++++ ios/package.json | 2 +- lerna.json | 2 +- 10 files changed, 31 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd4fe5f5ad..db39545625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [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 df7ce545e4..5bc9de9f61 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [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/android/package.json b/android/package.json index af3f4f556f..7354f3b2c6 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "8.4.0", + "version": "8.4.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index ee48a8d5f4..df61fcbaaf 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [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/cli/package.json b/cli/package.json index 1f987d33ad..7a77d11e96 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "8.4.0", + "version": "8.4.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 1de9522ff4..1003c8ceb1 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [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/core + # [8.4.0](https://github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) (2026-06-02) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index b22aef174f..f821cfd9d9 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "8.4.0", + "version": "8.4.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index eac0a4a1ca..dfcf383ff8 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [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 diff --git a/ios/package.json b/ios/package.json index a58c020903..a6a78c23df 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "8.4.0", + "version": "8.4.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/lerna.json b/lerna.json index b2efb400d4..be36669427 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "8.4.0", + "version": "8.4.1", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From 0585c180843064d4f704f38710bd0db5050b70b1 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 22 Jun 2026 15:59:27 +0200 Subject: [PATCH 61/69] fix(android): restore verify script (#8513) --- android/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/package.json b/android/package.json index bec2650749..800aec6172 100644 --- a/android/package.json +++ b/android/package.json @@ -23,7 +23,7 @@ "capacitor-cordova/src/main/" ], "scripts": { - "verify": "./gradlew clean lint build test -b capacitor/build.gradle" + "verify": "./gradlew :capacitor-android:clean :capacitor-android:lint :capacitor-android:build :capacitor-android:test" }, "peerDependencies": { "@capacitor/core": "^9.0.0-alpha.4" From 6df76da2752c5516eaf16e2e994cb333467b5613 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 22 Jun 2026 17:02:56 +0200 Subject: [PATCH 62/69] chore(ci): temporarily disable pod lib lint (#8514) --- .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 56fce53892..4e79030c54 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,8 +114,8 @@ jobs: - run: npm install - run: npm run verify working-directory: ./ios - - name: Validate native podspec - run: sh ./scripts/native-podspec.sh lint +# - name: Validate native podspec +# run: sh ./scripts/native-podspec.sh lint test-android: runs-on: ubuntu-latest timeout-minutes: 30 From 904b22e69f4f750ed0b2e006157baaa0c7fc2a7f Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 22 Jun 2026 17:16:01 +0200 Subject: [PATCH 63/69] fix(cli): Add missing imports (#8512) --- cli/src/ios/update.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 7b30e4f81c..93a1ad4b55 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -3,7 +3,7 @@ import { basename, dirname, join, relative } from 'path'; import { major, prerelease } from 'semver'; import c from '../colors'; -import { checkPlatformVersions, runTask } from '../common'; +import { checkPlatformVersions, getCapacitorPackageVersion, runTask } from '../common'; import { checkPluginDependencies, handleCordovaPluginsJS, logCordovaManualSteps } from '../cordova'; import type { Config } from '../definitions'; import { fatal } from '../errors'; @@ -11,6 +11,7 @@ import { logger } from '../log'; import type { Plugin } from '../plugin'; import { PluginType, getPluginType, getPlugins, printPlugins } from '../plugin'; import { copy as copyTask } from '../tasks/copy'; +import { setAllStringIn } from '../tasks/migrate'; import { generateCordovaPodspecs, generateCordovaPackageFiles, From 73d8b6da317a1f53802c975d52fe40c3ad0bf3c9 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Mon, 22 Jun 2026 14:06:32 -0400 Subject: [PATCH 64/69] chore: run `npm run fmt` to fix lint errors --- .../java/com/getcapacitor/BridgeWebChromeClient.java | 4 ++-- .../src/main/java/com/getcapacitor/FileUtils.java | 2 +- .../java/com/getcapacitor/WebViewLocalServer.java | 12 ++++++------ .../getcapacitor/plugin/CapacitorCookieManager.java | 6 +++--- .../getcapacitor/plugin/util/HttpRequestHandler.java | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java b/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java index 6ca1c8a838..ec7867369e 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java +++ b/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java @@ -282,7 +282,7 @@ public boolean onShowFileChooser( boolean captureEnabled = fileChooserParams.isCaptureEnabled(); boolean capturePhoto = captureEnabled && acceptTypes.contains("image/*"); final boolean captureVideo = captureEnabled && acceptTypes.contains("video/*"); - if ((capturePhoto || captureVideo)) { + if (capturePhoto || captureVideo) { if (isMediaCaptureSupported()) { showMediaCaptureOrFilePicker(filePathCallback, fileChooserParams, captureVideo); } else { @@ -447,7 +447,7 @@ public boolean onConsoleMessage(ConsoleMessage consoleMessage) { } public boolean isValidMsg(String msg) { - return !(msg.contains("%cresult %c") || (msg.contains("%cnative %c")) || msg.equalsIgnoreCase("console.groupEnd")); + return !(msg.contains("%cresult %c") || msg.contains("%cnative %c") || msg.equalsIgnoreCase("console.groupEnd")); } private Uri createImageFileUri() throws IOException { diff --git a/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java b/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java index ce09e7c2f0..cec171946c 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java +++ b/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java @@ -218,7 +218,7 @@ private static String getCopyFilePath(Uri uri, Context context) { Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); cursor.moveToFirst(); - String name = (cursor.getString(nameIndex)); + String name = cursor.getString(nameIndex); String fileName = sanitizeFilename(name); File file = new File(context.getFilesDir(), fileName); try { diff --git a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java index a39483de91..0c54800790 100755 --- a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java +++ b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java @@ -221,7 +221,7 @@ private boolean isErrorUrl(Uri uri) { } private boolean isMainUrl(Uri loadingUrl) { - return (bridge.getServerUrl() == null && loadingUrl.getHost().equalsIgnoreCase(bridge.getHost())); + return bridge.getServerUrl() == null && loadingUrl.getHost().equalsIgnoreCase(bridge.getHost()); } private boolean isAllowedUrl(Uri loadingUrl) { @@ -722,31 +722,31 @@ private InputStream getInputStream() { @Override public int available() throws IOException { InputStream is = getInputStream(); - return (is != null) ? is.available() : -1; + return is != null ? is.available() : -1; } @Override public int read() throws IOException { InputStream is = getInputStream(); - return (is != null) ? is.read() : -1; + return is != null ? is.read() : -1; } @Override public int read(byte[] b) throws IOException { InputStream is = getInputStream(); - return (is != null) ? is.read(b) : -1; + return is != null ? is.read(b) : -1; } @Override public int read(byte[] b, int off, int len) throws IOException { InputStream is = getInputStream(); - return (is != null) ? is.read(b, off, len) : -1; + return is != null ? is.read(b, off, len) : -1; } @Override public long skip(long n) throws IOException { InputStream is = getInputStream(); - return (is != null) ? is.skip(n) : 0; + return is != null ? is.skip(n) : 0; } } diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java index cf4ab63266..472323bbc4 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java @@ -192,12 +192,12 @@ public void flush() { @Override public void put(URI uri, Map> responseHeaders) { // make sure our args are valid - if ((uri == null) || (responseHeaders == null)) return; + if (uri == null || responseHeaders == null) return; // go over the headers for (String headerKey : responseHeaders.keySet()) { // ignore headers which aren't cookie related - if ((headerKey == null) || !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey.equalsIgnoreCase("Set-Cookie"))) continue; + if (headerKey == null || !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey.equalsIgnoreCase("Set-Cookie"))) continue; // process each of the headers for (String headerValue : Objects.requireNonNull(responseHeaders.get(headerKey))) { @@ -215,7 +215,7 @@ public void put(URI uri, Map> responseHeaders) { @Override public Map> get(URI uri, Map> requestHeaders) { // make sure our args are valid - if ((uri == null) || (requestHeaders == null)) throw new IllegalArgumentException("Argument is null"); + if (uri == null || requestHeaders == null) throw new IllegalArgumentException("Argument is null"); // save our url once String url = uri.toString(); diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java index b47d57cf10..69278bc63b 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java @@ -168,8 +168,8 @@ public HttpURLConnectionBuilder setUrlParams(JSObject params, boolean shouldEnco "://" + uri.getAuthority() + uri.getPath() + - ((!urlQuery.equals("")) ? "?" + urlQuery : "") + - ((uri.getFragment() != null) ? uri.getFragment() : ""); + (!urlQuery.equals("") ? "?" + urlQuery : "") + + (uri.getFragment() != null ? uri.getFragment() : ""); this.url = new URL(unEncodedUrlString); return this; From 356cafa7a06dd4a2b6d535f421ddc62132b34343 Mon Sep 17 00:00:00 2001 From: Mark Anderson Date: Tue, 23 Jun 2026 09:00:07 -0400 Subject: [PATCH 65/69] fix: add missing logic from merge (#8515) --- cli/src/util/cordova-ios.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/cli/src/util/cordova-ios.ts b/cli/src/util/cordova-ios.ts index 0401162576..371bd96d33 100644 --- a/cli/src/util/cordova-ios.ts +++ b/cli/src/util/cordova-ios.ts @@ -14,6 +14,7 @@ import { getPluginType, } from '../plugin'; import type { Plugin } from '../plugin'; +import { setAllStringIn } from '../tasks/migrate'; import { extractTemplate } from '../util/template'; const platform = 'ios'; @@ -352,7 +353,21 @@ export async function generateCordovaPackageFile(p: Plugin, config: Config): Pro publicHeadersPath: "."`; } - const content = `// swift-tools-version: 5.9 + 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}"`, + ); + await writeFile(packageSwiftPath, content); + } else { + const content = `// swift-tools-version: 5.9 import PackageDescription @@ -378,5 +393,6 @@ let package = Package( ) ] )`; - await writeFile(join(config.ios.cordovaPluginsDirAbs, 'sources', p.name, 'Package.swift'), content); + await writeFile(join(config.ios.cordovaPluginsDirAbs, 'sources', p.name, 'Package.swift'), content); + } } From 7f3708880fdc7cb8dc40bcbc74c838525e2fc5f9 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of markemer)" Date: Tue, 23 Jun 2026 15:59:38 +0000 Subject: [PATCH 66/69] Release 9.0.0-alpha.5 --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ android/CHANGELOG.md | 24 ++++++++++++++++++++++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 28 ++++++++++++++++++++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 18 ++++++++++++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 18 ++++++++++++++++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 127 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4f00acf50..e9477b313d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,39 @@ 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.5](https://github.com/ionic-team/capacitor/compare/8.4.1...9.0.0-alpha.5) (2026-06-23) + +### Bug Fixes + +- add missing logic from merge ([#8515](https://github.com/ionic-team/capacitor/issues/8515)) ([356cafa](https://github.com/ionic-team/capacitor/commit/356cafa7a06dd4a2b6d535f421ddc62132b34343)) +- **android:** restore verify script ([#8513](https://github.com/ionic-team/capacitor/issues/8513)) ([0585c18](https://github.com/ionic-team/capacitor/commit/0585c180843064d4f704f38710bd0db5050b70b1)) +- **cli:** Add missing imports ([#8512](https://github.com/ionic-team/capacitor/issues/8512)) ([904b22e](https://github.com/ionic-team/capacitor/commit/904b22e69f4f750ed0b2e006157baaa0c7fc2a7f)) + # [9.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + +### Bug Fixes + +- **ios:** eliminate double comma in SPM generation ([#8505](https://github.com/ionic-team/capacitor/issues/8505)) ([1a5543f](https://github.com/ionic-team/capacitor/commit/1a5543fbfcf4ba911270002d573275342f680bde)) + +# [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.4.0...9.0.0-alpha.3) (2026-06-02) + +# [9.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/8.3.4...9.0.0-alpha.2) (2026-05-12) + +# [9.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/8.3.3...9.0.0-alpha.1) (2026-05-08) + +# [9.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) + +### Bug Fixes + +- **android:** conditionally add Cordova ([#8453](https://github.com/ionic-team/capacitor/issues/8453)) ([e136b3f](https://github.com/ionic-team/capacitor/commit/e136b3f0cdd1437cac5234576301ed388edf85e7)) +- **android:** Handle activity result without plugin handle ([#8436](https://github.com/ionic-team/capacitor/issues/8436)) ([65ad1fd](https://github.com/ionic-team/capacitor/commit/65ad1fd55b9793a3e078e4a837a00e2f28c75c17)) +- **cli:** conditionally add capacitor-cordova-android-plugins module ([#8449](https://github.com/ionic-team/capacitor/issues/8449)) ([9e85905](https://github.com/ionic-team/capacitor/commit/9e85905bf36c1e2300994e231cf10e829f2074ed)) +- **http:** handle URL objects on fetch ([#8386](https://github.com/ionic-team/capacitor/issues/8386)) ([0aac1f0](https://github.com/ionic-team/capacitor/commit/0aac1f0aee8083dc5013f0d8e3c180eb02f21141)) +- **ios:** make getArray accesible on Objective-C plugins ([#8392](https://github.com/ionic-team/capacitor/issues/8392)) ([ef57c77](https://github.com/ionic-team/capacitor/commit/ef57c773e70af6b94685a2dc0f808db3ed4cda6e)) +- **SystemBars:** use native safe area insets on Android ([#8384](https://github.com/ionic-team/capacitor/issues/8384)) ([d55f4f8](https://github.com/ionic-team/capacitor/commit/d55f4f859bd88fa5617699e874c48c451bcb5e5f)) + +# [9.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + ## [8.4.1](https://github.com/ionic-team/capacitor/compare/8.4.0...8.4.1) (2026-06-19) ### Bug Fixes diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 463dd6f57b..16dda5d059 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,7 +3,31 @@ 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.5](https://github.com/ionic-team/capacitor/compare/8.4.1...9.0.0-alpha.5) (2026-06-23) + +### Bug Fixes + +- **android:** restore verify script ([#8513](https://github.com/ionic-team/capacitor/issues/8513)) ([0585c18](https://github.com/ionic-team/capacitor/commit/0585c180843064d4f704f38710bd0db5050b70b1)) + # [9.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + +# [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.4.0...9.0.0-alpha.3) (2026-06-02) + +# [9.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/8.3.4...9.0.0-alpha.2) (2026-05-12) + +# [9.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/8.3.3...9.0.0-alpha.1) (2026-05-08) + +# [9.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) + +### Bug Fixes + +- **android:** conditionally add Cordova ([#8453](https://github.com/ionic-team/capacitor/issues/8453)) ([e136b3f](https://github.com/ionic-team/capacitor/commit/e136b3f0cdd1437cac5234576301ed388edf85e7)) +- **android:** Handle activity result without plugin handle ([#8436](https://github.com/ionic-team/capacitor/issues/8436)) ([65ad1fd](https://github.com/ionic-team/capacitor/commit/65ad1fd55b9793a3e078e4a837a00e2f28c75c17)) +- **http:** handle URL objects on fetch ([#8386](https://github.com/ionic-team/capacitor/issues/8386)) ([0aac1f0](https://github.com/ionic-team/capacitor/commit/0aac1f0aee8083dc5013f0d8e3c180eb02f21141)) +- **SystemBars:** use native safe area insets on Android ([#8384](https://github.com/ionic-team/capacitor/issues/8384)) ([d55f4f8](https://github.com/ionic-team/capacitor/commit/d55f4f859bd88fa5617699e874c48c451bcb5e5f)) + +# [9.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + ## [8.4.1](https://github.com/ionic-team/capacitor/compare/8.4.0...8.4.1) (2026-06-19) # [8.4.0](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...8.4.0) (2026-06-02) diff --git a/android/package.json b/android/package.json index 800aec6172..1cc6e1df88 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "9.0.0-alpha.4", + "version": "9.0.0-alpha.5", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -26,7 +26,7 @@ "verify": "./gradlew :capacitor-android:clean :capacitor-android:lint :capacitor-android:build :capacitor-android:test" }, "peerDependencies": { - "@capacitor/core": "^9.0.0-alpha.4" + "@capacitor/core": "^9.0.0-alpha.5" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index c8d9f0be3b..55342ab8e7 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,7 +3,35 @@ 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.5](https://github.com/ionic-team/capacitor/compare/8.4.1...9.0.0-alpha.5) (2026-06-23) + +### Bug Fixes + +- add missing logic from merge ([#8515](https://github.com/ionic-team/capacitor/issues/8515)) ([356cafa](https://github.com/ionic-team/capacitor/commit/356cafa7a06dd4a2b6d535f421ddc62132b34343)) +- **cli:** Add missing imports ([#8512](https://github.com/ionic-team/capacitor/issues/8512)) ([904b22e](https://github.com/ionic-team/capacitor/commit/904b22e69f4f750ed0b2e006157baaa0c7fc2a7f)) + +# [9.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + +### Bug Fixes + +- **ios:** eliminate double comma in SPM generation ([#8505](https://github.com/ionic-team/capacitor/issues/8505)) ([1a5543f](https://github.com/ionic-team/capacitor/commit/1a5543fbfcf4ba911270002d573275342f680bde)) + +# [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.4.0...9.0.0-alpha.3) (2026-06-02) + +# [9.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/8.3.4...9.0.0-alpha.2) (2026-05-12) + +# [9.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/8.3.3...9.0.0-alpha.1) (2026-05-08) + +# [9.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) + +### Bug Fixes + +- **android:** conditionally add Cordova ([#8453](https://github.com/ionic-team/capacitor/issues/8453)) ([e136b3f](https://github.com/ionic-team/capacitor/commit/e136b3f0cdd1437cac5234576301ed388edf85e7)) +- **cli:** conditionally add capacitor-cordova-android-plugins module ([#8449](https://github.com/ionic-team/capacitor/issues/8449)) ([9e85905](https://github.com/ionic-team/capacitor/commit/9e85905bf36c1e2300994e231cf10e829f2074ed)) +- **SystemBars:** use native safe area insets on Android ([#8384](https://github.com/ionic-team/capacitor/issues/8384)) ([d55f4f8](https://github.com/ionic-team/capacitor/commit/d55f4f859bd88fa5617699e874c48c451bcb5e5f)) + # [9.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + ## [8.4.1](https://github.com/ionic-team/capacitor/compare/8.4.0...8.4.1) (2026-06-19) ### Bug Fixes diff --git a/cli/package.json b/cli/package.json index a6cd9023fe..c8283f06cc 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "9.0.0-alpha.4", + "version": "9.0.0-alpha.5", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 99a51f2a5e..98eb5e37d1 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,7 +3,25 @@ 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.5](https://github.com/ionic-team/capacitor/compare/8.4.1...9.0.0-alpha.5) (2026-06-23) + +# [9.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + +# [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.4.0...9.0.0-alpha.3) (2026-06-02) + +# [9.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/8.3.4...9.0.0-alpha.2) (2026-05-12) + +# [9.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/8.3.3...9.0.0-alpha.1) (2026-05-08) + +# [9.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) + +### Bug Fixes + +- **http:** handle URL objects on fetch ([#8386](https://github.com/ionic-team/capacitor/issues/8386)) ([0aac1f0](https://github.com/ionic-team/capacitor/commit/0aac1f0aee8083dc5013f0d8e3c180eb02f21141)) +- **SystemBars:** use native safe area insets on Android ([#8384](https://github.com/ionic-team/capacitor/issues/8384)) ([d55f4f8](https://github.com/ionic-team/capacitor/commit/d55f4f859bd88fa5617699e874c48c451bcb5e5f)) + # [9.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + ## [8.4.1](https://github.com/ionic-team/capacitor/compare/8.4.0...8.4.1) (2026-06-19) # [8.4.0](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...8.4.0) (2026-06-02) diff --git a/core/package.json b/core/package.json index d0b42a41df..3680220563 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "9.0.0-alpha.4", + "version": "9.0.0-alpha.5", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 255a0bf8e3..7c148548c4 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,7 +3,25 @@ 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.5](https://github.com/ionic-team/capacitor/compare/8.4.1...9.0.0-alpha.5) (2026-06-23) + +# [9.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + +# [9.0.0-alpha.3](https://github.com/ionic-team/capacitor/compare/8.4.0...9.0.0-alpha.3) (2026-06-02) + +# [9.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/8.3.4...9.0.0-alpha.2) (2026-05-12) + +# [9.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/8.3.3...9.0.0-alpha.1) (2026-05-08) + +# [9.0.0-alpha.0](https://github.com/ionic-team/capacitor/compare/8.3.2...9.0.0-alpha.0) (2026-05-07) + +### Bug Fixes + +- **http:** handle URL objects on fetch ([#8386](https://github.com/ionic-team/capacitor/issues/8386)) ([0aac1f0](https://github.com/ionic-team/capacitor/commit/0aac1f0aee8083dc5013f0d8e3c180eb02f21141)) +- **ios:** make getArray accesible on Objective-C plugins ([#8392](https://github.com/ionic-team/capacitor/issues/8392)) ([ef57c77](https://github.com/ionic-team/capacitor/commit/ef57c773e70af6b94685a2dc0f808db3ed4cda6e)) + # [9.0.0-alpha.4](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.3...9.0.0-alpha.4) (2026-06-19) + ## [8.4.1](https://github.com/ionic-team/capacitor/compare/8.4.0...8.4.1) (2026-06-19) # [8.4.0](https://github.com/ionic-team/capacitor/compare/9.0.0-alpha.2...8.4.0) (2026-06-02) diff --git a/ios/package.json b/ios/package.json index 5d8fb9a483..aa715c6bbe 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "9.0.0-alpha.4", + "version": "9.0.0-alpha.5", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^9.0.0-alpha.4" + "@capacitor/core": "^9.0.0-alpha.5" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index 263fcadcd1..3080b94447 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,6 @@ "tagVersionPrefix": "" } }, - "version": "9.0.0-alpha.4", + "version": "9.0.0-alpha.5", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } From 5e893cb0def4adc8111301186ffc9c4abfc501cb Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 24 Jun 2026 12:02:36 +0200 Subject: [PATCH 67/69] chore(android-template): Update variables and google services plugin (#8517) --- android-template/build.gradle | 2 +- android-template/variables.gradle | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/android-template/build.gradle b/android-template/build.gradle index 70bb56fc47..d72a753c67 100644 --- a/android-template/build.gradle +++ b/android-template/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:9.2.1' - classpath 'com.google.gms:google-services:4.4.4' + classpath 'com.google.gms:google-services:4.5.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/android-template/variables.gradle b/android-template/variables.gradle index 36eff33927..4f68607cae 100644 --- a/android-template/variables.gradle +++ b/android-template/variables.gradle @@ -2,15 +2,15 @@ ext { minSdkVersion = 26 compileSdkVersion = 37 targetSdkVersion = 37 - androidxActivityVersion = '1.11.0' + androidxActivityVersion = '1.13.0' androidxAppCompatVersion = '1.7.1' androidxCoordinatorLayoutVersion = '1.3.0' - androidxCoreVersion = '1.17.0' + androidxCoreVersion = '1.19.0' androidxFragmentVersion = '1.8.9' coreSplashScreenVersion = '1.2.0' - androidxWebkitVersion = '1.14.0' + androidxWebkitVersion = '1.16.0' junitVersion = '4.13.2' androidxJunitVersion = '1.3.0' androidxEspressoCoreVersion = '3.7.0' - cordovaAndroidVersion = '14.0.1' + cordovaAndroidVersion = '15.0.0' } \ No newline at end of file From 9a2d3e65a616366b42dbcc2ffa40790b9f102722 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 24 Jun 2026 18:18:40 +0200 Subject: [PATCH 68/69] fix(cli): Don't add kotlin plugin for Cordova plugins (#8518) --- cli/src/android/update.ts | 47 --------------------------------------- 1 file changed, 47 deletions(-) diff --git a/cli/src/android/update.ts b/cli/src/android/update.ts index 45275bddc3..8da8d8d9d2 100644 --- a/cli/src/android/update.ts +++ b/cli/src/android/update.ts @@ -263,8 +263,6 @@ if (hasProperty('postBuildExtras')) { export async function handleCordovaPluginsGradle(config: Config, cordovaPlugins: Plugin[]): Promise { const pluginsGradlePath = join(config.android.cordovaPluginsDirAbs, 'build.gradle'); - const kotlinNeeded = await kotlinNeededCheck(config, cordovaPlugins); - const kotlinVersionString = config.app.extConfig.cordova?.preferences?.GradlePluginKotlinVersion ?? '2.4.0'; const frameworksArray: any[] = []; let prefsArray: any[] = []; const applyArray: any[] = []; @@ -296,10 +294,6 @@ export async function handleCordovaPluginsGradle(config: Config, cordovaPlugins: }) .join('\n'); frameworkString = await replaceFrameworkVariables(config, prefsArray, frameworkString); - if (kotlinNeeded) { - frameworkString += `\n implementation "androidx.core:core-ktx:$androidxCoreKTXVersion"`; - frameworkString += `\n implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"`; - } const applyString = applyArray.join('\n'); let buildGradle = await readFile(pluginsGradlePath, { encoding: 'utf-8' }); buildGradle = buildGradle.replace( @@ -310,28 +304,6 @@ export async function handleCordovaPluginsGradle(config: Config, cordovaPlugins: /(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + applyString.concat('\n') + '$2', ); - if (kotlinNeeded) { - buildGradle = buildGradle.replace( - /(buildscript\s{\n(\t|\s{4})repositories\s{\n((\t{2}|\s{8}).+\n)+(\t|\s{4})}\n(\t|\s{4})dependencies\s{\n(\t{2}|\s{8}).+)\n((\t|\s{4})}\n}\n)/, - `$1\n classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"\n$8`, - ); - buildGradle = buildGradle.replace( - /(ext\s{)/, - `$1\n androidxCoreKTXVersion = project.hasProperty('androidxCoreKTXVersion') ? rootProject.ext.androidxCoreKTXVersion : '1.8.0'`, - ); - buildGradle = buildGradle.replace( - /(buildscript\s{)/, - `$1\n ext.kotlin_version = project.hasProperty('kotlin_version') ? rootProject.ext.kotlin_version : '${kotlinVersionString}'`, - ); - buildGradle = buildGradle.replace( - /(apply\splugin:\s'com\.android\.library')/, - `$1\napply plugin: 'kotlin-android'`, - ); - buildGradle = buildGradle.replace( - /(compileOptions\s{\n((\t{2}|\s{8}).+\n)+(\t|\s{4})})\n(})/, - `$1\n sourceSets {\n main.java.srcDirs += 'src/main/kotlin'\n }\n$5`, - ); - } await writeFile(pluginsGradlePath, buildGradle); const cordovaVariables = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN ext { @@ -343,25 +315,6 @@ ext { await writeFile(join(config.android.cordovaPluginsDirAbs, 'cordova.variables.gradle'), cordovaVariables); } -async function kotlinNeededCheck(config: Config, cordovaPlugins: Plugin[]) { - if (config.app.extConfig.cordova?.preferences?.GradlePluginKotlinEnabled !== 'true') { - for (const plugin of cordovaPlugins) { - const androidPlatform = getPluginPlatform(plugin, platform); - const sourceFiles = androidPlatform['source-file']; - if (sourceFiles) { - for (const srcFile of sourceFiles) { - if (/^.*\.kt$/.test(srcFile['$'].src)) { - return true; - } - } - } - } - return false; - } else { - return true; - } -} - async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) { const pluginsPath = join(config.android.cordovaPluginsDirAbs, 'src', 'main'); for (const p of cordovaPlugins) { From e5bb94a9527dfc644e080849bf92a57b4737e6ea Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 6 Jul 2026 16:45:03 +0200 Subject: [PATCH 69/69] fix(ios): remove Cordova.framework from Capacitor project --- ios/Capacitor/Capacitor.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index 9da29479dc..8c67100745 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -56,7 +56,6 @@ 62959B452524DA7800A3D7F1 /* CAPPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959B132524DA7700A3D7F1 /* CAPPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; 62959B462524DA7800A3D7F1 /* CAPBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B142524DA7700A3D7F1 /* CAPBridge.swift */; }; 62959B472524DA7800A3D7F1 /* CAPNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959B152524DA7700A3D7F1 /* CAPNotifications.swift */; }; - 62959BA52526475A00A3D7F1 /* Cordova.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62959BA02526474300A3D7F1 /* Cordova.framework */; }; 6296A77E253A2E49005A202A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6296A77D253A2E49005A202A /* AppDelegate.swift */; }; 6296A782253A2E49005A202A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6296A781253A2E49005A202A /* ViewController.swift */; }; 6296A785253A2E49005A202A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6296A783253A2E49005A202A /* Main.storyboard */; }; @@ -264,7 +263,6 @@ buildActionMask = 2147483647; files = ( 501CBAA71FC0A723009B0D4D /* WebKit.framework in Frameworks */, - 62959BA52526475A00A3D7F1 /* Cordova.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; };