chore: sync upstream PR #8524 - fix(ios): remove Cordova.framework from Capacitor project - #82
chore: sync upstream PR #8524 - fix(ios): remove Cordova.framework from Capacitor project#82riderx wants to merge 84 commits into
Conversation
…m#8346) Co-authored-by: jwo <jonathan.worreth@protonmail.ch> Co-authored-by: Mark Anderson <mark.anderson@outsystems.com>
Co-authored-by: Steven Sherry <steven.r.sherry@gmail.com> Co-authored-by: jcesarmobile <jcesarmobile@gmail.com> Co-authored-by: Joseph Pender <joey.pender@outsystems.com>
Co-authored-by: jcesarmobile <jcesarmobile@gmail.com>
…preferred fallback)
|
Claude Code could not resolve this upstream sync completely. Please review the branch carefully before merging. |
Beta npm buildMaintainers can publish one Capacitor Plus workspace package from this PR to npm for fast testing. Comment Examples: /publish-beta core
/publish-beta cli
/publish-beta @capacitor-plus/coreIf exactly one workspace package changed, Packages:
The workflow will:
Security note: beta publish is only enabled for branches inside this repository. |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 53 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR decouples Cordova support from Capacitor core on Android and iOS into standalone CordovaPlugin implementations using a new generic call-interceptor mechanism, bumps Android/iOS SDK and toolchain versions, refactors CLI iOS Cordova podspec/SPM logic into shared utilities, replaces CLI live-reload flags with a single ChangesCordova Decoupling (Core)
Estimated code review effort: 4 (Complex) | ~75 minutes Toolchain/SDK Bumps
CLI Cordova/SPM and Run Command
Estimated code review effort: 4 (Complex) | ~60 minutes Metadata & Release
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant WebView
participant Bridge
participant CordovaPlugin
participant PluginManager
WebView->>Bridge: postMessage(type="cordova", data)
Bridge->>Bridge: getCallInterceptor("cordova")
Bridge->>CordovaPlugin: intercept(service, action, callbackId, actionArgs)
CordovaPlugin->>PluginManager: exec(...)
PluginManager-->>CordovaPlugin: result
CordovaPlugin-->>WebView: callback response
sequenceDiagram
participant CLI
participant runCommand
participant CapLiveReloadHelper
participant AppConfig
CLI->>runCommand: run --url http://host:port
runCommand->>runCommand: validate URL protocol
runCommand->>CapLiveReloadHelper: editCapConfigForLiveReload(config, platform, url)
CapLiveReloadHelper->>AppConfig: write server.url to capacitor.config.json
runCommand-->>CLI: log "listening for: url"
Related PRs: None identified in the provided context. Suggested labels: breaking-change, cordova, android, ios, cli, release Suggested reviewers: None specified. 🐰 Cordova hops away, a plugin now apart, 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Warning Review ran into problems🔥 ProblemsRepository analysis: Couldn't refresh Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java (1)
163-173: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winMissing
#delimiter corrupts URLs containing a fragment.
uri.getFragment()returns the fragment value without the leading#. Appending it directly after the query string produces a malformed URL (e.g....?foo=bar#sectionbecomes...?foo=barsection), silently corrupting any request whose URL contains a fragment.🐛 Proposed fix
String unEncodedUrlString = uri.getScheme() + "://" + uri.getAuthority() + uri.getPath() + (!urlQuery.equals("") ? "?" + urlQuery : "") + - (uri.getFragment() != null ? uri.getFragment() : ""); + (uri.getFragment() != null ? "#" + uri.getFragment() : "");🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java` around lines 163 - 173, The URL reconstruction in HttpRequestHandler is missing the fragment delimiter, so URLs with fragments are being concatenated incorrectly. Update the logic that builds unEncodedUrlString in the request setup path to append the fragment from uri.getFragment() with a leading # only when present, keeping the existing scheme, authority, path, and query handling intact. Use the URL/URI assembly block in HttpRequestHandler to locate the fix.android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java (1)
176-192: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winRemove the API 35 gate around root insets
android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java:176-192—ViewCompat.getRootWindowInsets(v)is compat-safe on older Android versions too; this branch sets the safe-area CSS vars to0on API 24-34 until the listener runs. Call it unconditionally here, or move the Android 15-specific check to the code that actually needs it.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java` around lines 176 - 192, The safe-area initialization in SystemBars.initSafeAreaCSSVariables() is unnecessarily gated by the Android 15 check, which forces zero insets on older versions before listeners update. Remove the API 35-specific branch around ViewCompat.getRootWindowInsets(v) and fetch root window insets unconditionally here; keep any Android 15-only logic only in the code path that truly depends on it.CHANGELOG.md (1)
6-119: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy liftCHANGELOG.md was manually edited, and merge conflict resolution left duplicated/inconsistent content.
Per repo guidelines,
CHANGELOG.mdshould not be manually edited since it's managed by CI/CD. This diff manually edits it as part of the merge sync, and the result contains clear artifacts of an unresolved conflict:
- The
9.0.0-alpha.4header/entry appears twice (Line 14 and Line 37).- The entire
9.0.0-alpha.0"Bug Fixes" section is duplicated verbatim (Lines 26-36 and Lines 61-71).- Lines 111-115 mix
ionic-team/capacitorcompare/issue/commit URLs withCap-go/capacitor-plusissue/commit URLs within the same entry, leaving a broken/inconsistent changelog history.This matches the PR's own note that "Claude Code could not fully resolve the upstream sync" and should be cleaned up (or regenerated via the normal CI/CD changelog process) before merging.
As per coding guidelines, "Do not manually edit
CHANGELOG.md; it is managed automatically by CI/CD."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@CHANGELOG.md` around lines 6 - 119, The CHANGELOG content has unresolved merge artifacts and inconsistent history that need to be removed. Clean up the duplicated 9.0.0-alpha.4 and 9.0.0-alpha.0 entries, then make the release notes consistent with the upstream Capacitor history by fixing the mixed compare/issue/commit references in the 8.3.1/8.3.2 area. Use the existing changelog section headers and release entries as the anchor points, and ensure the final file is either regenerated by the normal CI/CD changelog flow or left in a single, non-duplicated, internally consistent state.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@android/capacitor/src/main/java/com/getcapacitor/Bridge.java`:
- Around line 293-308: Guard the WebView package lookup in
isMinimumWebViewInstalled() by checking the result of
WebView.getCurrentWebViewPackage() before using it, and also handle a null
versionName safely. If the package info or version string is unavailable, return
false immediately so the version parsing logic only runs when info is present.
In `@android/CHANGELOG.md`:
- Around line 6-84: The android/CHANGELOG.md release history contains duplicate
entries for several versions, which creates conflicting compare links and
repeated release notes. Remove the repeated blocks for 9.0.0-alpha.4,
9.0.0-alpha.3, 9.0.0-alpha.0, 8.4.0, 8.3.4, and 8.3.3 so each release appears
only once, keeping the correct compare link and notes in the remaining canonical
entry.
In `@cli/src/ios/update.ts`:
- Around line 65-88: The `update` flow in `cli/src/ios/update.ts` is recomputing
`getCapacitorPackageVersion` for every plugin even though `iosPlatformVersion`
is independent of `plugin`; hoist that await out of the
`Promise.all`/`validSPMPackages.map` loop and reuse the single value inside the
per-plugin work. While updating the `Package.swift` parsing logic, make sure the
`major(version)` comparison in the `regex`/`content.match` path is guarded so
malformed or missing semver values do not throw during `update`.
In `@cli/src/tasks/migrate.ts`:
- Line 3: The migration task is importing rimraf as a default export, but rimraf
v6 only exposes named exports, so the current sync call will break at runtime.
Update the import in migrate.ts to use the named sync export (for example,
rimrafSync) and change the existing rimraf.sync(...) usage in the migration
logic to call that named function instead.
In `@cli/src/util/cordova-ios.ts`:
- Around line 299-318: In cordovaPodfileLines, remove the unnecessary async from
the cordovaPlugins.map callback because it does not await anything and the logic
is synchronous. Update the loop in cordova-ios.ts so the callback used to walk
cordovaPlugins, getPlatformElement, and podspec/pods/pod stays plain synchronous
code, preserving the current pods accumulation without implying async behavior.
- Around line 340-344: generateCordovaPackageFiles is returning before the async
per-plugin work completes because the cordovaPlugins.map callback does not await
generateCordovaPackageFile. Update generateCordovaPackageFiles to wait for each
generateCordovaPackageFile call to finish, so callers like ios/update.ts do not
continue into checkPluginsForPackageSwift and generatePackageFile until all
Package.swift files are written. Use the generateCordovaPackageFiles and
generateCordovaPackageFile symbols to locate the fix.
- Around line 22-27: The async podspec writes in generateCordovaPodspecs are not
awaited, so the function can finish before the .podspec files are written and
errors may be unhandled. Update generateCordovaPodspecs in cordova-ios.ts to
await both generateCordovaPodspec calls (or run them through a Promise-based
concurrency pattern) so the plugin podspec generation completes before
installCocoaPodsPlugins continues.
In `@ios/Capacitor/Capacitor/CAPBridgeProtocol.swift`:
- Around line 82-84: The new CAPBridgeProtocol requirement
registerCallInterceptor(_:handler:) is source-breaking for existing conformers.
Update the protocol so this API is not required for all adopters, either by
moving the behavior into a protocol extension with a default implementation or
by making the interceptor registration optional in CAPBridgeProtocol while
keeping CAPBridge-related call interception functionality available.
In `@ios/Capacitor/Capacitor/CAPBridgeViewController.swift`:
- Around line 94-110: In CAPBridgeViewController.updateAppLocationIfNeeded, stop
using perform(_:) on __CordovaPlugin for cordovaDeployDisabled because it is an
`@objc` Bool and the current call can misread the scalar value. Read the property
through KVC or a typed protocol/cast on the plugin instance instead, then keep
the existing conditional logic using cordovaDeployDisabled unchanged.
In `@ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift`:
- Line 43: The cast in Plugin should use a concrete array element type instead
of the untyped Array, since `as? Array` will not compile. Update the `args`
assignment in `Plugin` to cast to `[Any]` (or the correct known element type) so
the `actionArgs` value can be read safely.
---
Outside diff comments:
In `@android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java`:
- Around line 176-192: The safe-area initialization in
SystemBars.initSafeAreaCSSVariables() is unnecessarily gated by the Android 15
check, which forces zero insets on older versions before listeners update.
Remove the API 35-specific branch around ViewCompat.getRootWindowInsets(v) and
fetch root window insets unconditionally here; keep any Android 15-only logic
only in the code path that truly depends on it.
In
`@android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java`:
- Around line 163-173: The URL reconstruction in HttpRequestHandler is missing
the fragment delimiter, so URLs with fragments are being concatenated
incorrectly. Update the logic that builds unEncodedUrlString in the request
setup path to append the fragment from uri.getFragment() with a leading # only
when present, keeping the existing scheme, authority, path, and query handling
intact. Use the URL/URI assembly block in HttpRequestHandler to locate the fix.
In `@CHANGELOG.md`:
- Around line 6-119: The CHANGELOG content has unresolved merge artifacts and
inconsistent history that need to be removed. Clean up the duplicated
9.0.0-alpha.4 and 9.0.0-alpha.0 entries, then make the release notes consistent
with the upstream Capacitor history by fixing the mixed compare/issue/commit
references in the 8.3.1/8.3.2 area. Use the existing changelog section headers
and release entries as the anchor points, and ensure the final file is either
regenerated by the normal CI/CD changelog flow or left in a single,
non-duplicated, internally consistent state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3b6e7d24-8d64-4d89-832d-0d2cae0f09f9
⛔ Files ignored due to path filters (2)
android-template/gradle/wrapper/gradle-wrapper.jaris excluded by!**/*.jarandroid/gradle/wrapper/gradle-wrapper.jaris excluded by!**/*.jar
📒 Files selected for processing (87)
.github/workflows/test.ymlCHANGELOG.mdandroid-template/app/build.gradleandroid-template/build.gradleandroid-template/gradle/wrapper/gradle-wrapper.propertiesandroid-template/gradlewandroid-template/gradlew.batandroid-template/settings.gradleandroid-template/variables.gradleandroid/CHANGELOG.mdandroid/capacitor-cordova/build.gradleandroid/capacitor-cordova/proguard-rules.proandroid/capacitor-cordova/src/main/AndroidManifest.xmlandroid/capacitor-cordova/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.javaandroid/capacitor-cordova/src/main/java/com/getcapacitor/cordova/CordovaPlugin.javaandroid/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.javaandroid/capacitor-cordova/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.javaandroid/capacitor/build.gradleandroid/capacitor/proguard-rules.proandroid/capacitor/src/main/java/com/getcapacitor/Bridge.javaandroid/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.javaandroid/capacitor/src/main/java/com/getcapacitor/FileUtils.javaandroid/capacitor/src/main/java/com/getcapacitor/MessageHandler.javaandroid/capacitor/src/main/java/com/getcapacitor/PluginConfig.javaandroid/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.javaandroid/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.javaandroid/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.javaandroid/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.javaandroid/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.javaandroid/capacitor/src/main/java/com/getcapacitor/util/JSONUtils.javaandroid/capacitor/src/test/java/com/getcapacitor/plugin/SystemBarsTest.javaandroid/gradle/wrapper/gradle-wrapper.propertiesandroid/gradlewandroid/gradlew.batandroid/package.jsonandroid/settings.gradlecapacitor-cordova-android-plugins/build.gradlecli/CHANGELOG.mdcli/package.jsoncli/src/android/update.tscli/src/config.tscli/src/declarations.tscli/src/index.tscli/src/ios/update.tscli/src/ipc.tscli/src/tasks/migrate.tscli/src/tasks/run.tscli/src/telemetry.tscli/src/util/cordova-ios.tscli/src/util/iosplugin.tscli/src/util/livereload.tscli/src/util/spm.tscli/test/telemetry.spec.tscore/CHANGELOG.mdcore/package.jsonios-pods-template/App/App.xcodeproj/project.pbxprojios-pods-template/App/App/AppDelegate.swiftios-pods-template/App/Podfileios-spm-template/App/App.xcodeproj/project.pbxprojios-spm-template/App/App/AppDelegate.swiftios-spm-template/App/CapApp-SPM/Package.swiftios/CHANGELOG.mdios/Capacitor.podspecios/Capacitor/Capacitor.xcodeproj/project.pbxprojios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swiftios/Capacitor/Capacitor/CAPBridgeProtocol.swiftios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.hios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.mios/Capacitor/Capacitor/CAPBridgeViewController.swiftios/Capacitor/Capacitor/CAPInstanceDescriptor.hios/Capacitor/Capacitor/CAPInstanceDescriptor.mios/Capacitor/Capacitor/CAPInstanceDescriptor.swiftios/Capacitor/Capacitor/CapacitorBridge.swiftios/Capacitor/Capacitor/JS.swiftios/Capacitor/Capacitor/JSExport.swiftios/Capacitor/Capacitor/PluginConfig.swiftios/Capacitor/Capacitor/TmpViewController.swiftios/Capacitor/Capacitor/WebViewDelegationHandler.swiftios/Capacitor/CapacitorTests/ConfigurationTests.swiftios/Capacitor/CapacitorTests/PluginCallAccessorTests.mios/CapacitorCordova.podspecios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxprojios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swiftios/package.jsonios/scripts/pods_helpers.rblerna.jsonscripts/publish-android.sh
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
💤 Files with no reviewable changes (10)
- ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.h
- ios/Capacitor/Capacitor/CAPBridgeViewController+CDVScreenOrientationDelegate.m
- ios/Capacitor/Capacitor/JSExport.swift
- android-template/settings.gradle
- ios/Capacitor/Capacitor/CAPInstanceDescriptor.m
- android-template/app/build.gradle
- ios/Capacitor/CapacitorTests/ConfigurationTests.swift
- ios/Capacitor/Capacitor/TmpViewController.swift
- ios/Capacitor/Capacitor/CAPApplicationDelegateProxy.swift
- android/capacitor/proguard-rules.pro
| @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; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the target method and nearby constructor/load flow.
sed -n '250,340p' android/capacitor/src/main/java/com/getcapacitor/Bridge.java
printf '\n--- constructor/load flow ---\n'
sed -n '170,230p' android/capacitor/src/main/java/com/getcapacitor/Bridge.java
printf '\n--- search for current WebView package usage ---\n'
rg -n "getCurrentWebViewPackage|versionName|isMinimumWebViewInstalled" android/capacitor/src/main/java -S
printf '\n--- search for previous implementation or related guards ---\n'
git diff -- android/capacitor/src/main/java/com/getcapacitor/Bridge.java | sed -n '1,220p'Repository: Cap-go/capacitor-plus
Length of output: 1955
🏁 Script executed (no clone):
#!/bin/bash
set -euo pipefail
# Query Android documentation for nullability of WebView.getCurrentWebViewPackage()
python3 - <<'PY'
import urllib.request, urllib.parse, re
queries = [
"Android WebView.getCurrentWebViewPackage nullable PackageInfo versionName null",
]
for q in queries:
url = "https://www.google.com/search?q=" + urllib.parse.quote(q)
print("QUERY:", q)
try:
with urllib.request.urlopen(url, timeout=10) as r:
html = r.read().decode("utf-8", "ignore")
# very light extraction
for m in re.finditer(r"getCurrentWebViewPackage|PackageInfo|versionName|nullable", html, re.I):
start = max(0, m.start()-120)
end = min(len(html), m.end()+160)
print(html[start:end].replace("\n", " ")[:280])
print("---")
break
except Exception as e:
print("ERR:", e)
PY
Length of output: 146
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Look for any existing null handling or assumptions around WebView package info.
rg -n "WebView\.getCurrentWebViewPackage|PackageInfo info = WebView|getMinHuaweiWebViewVersion|getMinWebViewVersion" android -S
# Show the exact method with line numbers.
nl -ba android/capacitor/src/main/java/com/getcapacitor/Bridge.java | sed -n '292,312p'Repository: Cap-go/capacitor-plus
Length of output: 1955
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check the Android minSdk to see whether the WebView API call needs a version gate.
rg -n "minSdkVersion|minSdk" android -S
# Inspect the manifest / gradle file locations likely to define it.
fd -a "build.gradle*" android
fd -a "gradle.properties" android
fd -a "AndroidManifest.xml" android/capacitor/src/mainRepository: Cap-go/capacitor-plus
Length of output: 1955
Guard the WebView package lookup. WebView.getCurrentWebViewPackage() and info.versionName can be null here, which will crash before the fallback runs. Return false when package info isn’t available.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@android/capacitor/src/main/java/com/getcapacitor/Bridge.java` around lines
293 - 308, Guard the WebView package lookup in isMinimumWebViewInstalled() by
checking the result of WebView.getCurrentWebViewPackage() before using it, and
also handle a null versionName safely. If the package info or version string is
unavailable, return false immediately so the version parsing logic only runs
when info is present.
| 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) { | ||
| 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: "${forceVersion}"`, | ||
| ); | ||
| await writeFile(packageSwiftPath, content); | ||
| logger.warn(`${plugin.id} is built for Capacitor ${major(version)}, it might cause issues`); | ||
| } | ||
| }), | ||
| ); |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value
Hoist getCapacitorPackageVersion out of the per-plugin loop.
iosPlatformVersion does not depend on plugin, yet it is awaited once per package inside the Promise.all map. Compute it once before the loop. Also note major(version) will throw if the regex-captured version is not valid semver; consider guarding if plugin Package.swift contents can be malformed.
♻️ Proposed change
const validSPMPackages = await checkPluginsForPackageSwift(config, plugins);
+ const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name);
await Promise.all(
validSPMPackages.map(async (plugin) => {
- const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name);
const packageSwiftPath = join(plugin.rootPath, 'Package.swift');📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 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) { | |
| 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: "${forceVersion}"`, | |
| ); | |
| await writeFile(packageSwiftPath, content); | |
| logger.warn(`${plugin.id} is built for Capacitor ${major(version)}, it might cause issues`); | |
| } | |
| }), | |
| ); | |
| const validSPMPackages = await checkPluginsForPackageSwift(config, plugins); | |
| const iosPlatformVersion = await getCapacitorPackageVersion(config, config.ios.name); | |
| await Promise.all( | |
| validSPMPackages.map(async (plugin) => { | |
| const packageSwiftPath = join(plugin.rootPath, 'Package.swift'); | |
| let content = await readFile(packageSwiftPath, { encoding: 'utf-8' }); | |
| const regex = new RegExp( | |
| 'url:\\s*"https://github.com/ionic-team/capacitor-swift-pm\\.git",\\s*from:\\s*"([^"]+)"', | |
| ); | |
| const version = content.match(regex)?.[1]; | |
| const majorCapVersion = major(iosPlatformVersion); | |
| if (version && major(version) != majorCapVersion) { | |
| const preCapVersion = prerelease(iosPlatformVersion); | |
| const forceVersion = preCapVersion ? iosPlatformVersion : `${majorCapVersion}.0.0`; | |
| content = setAllStringIn( | |
| content, | |
| `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, | |
| `)`, | |
| ` from: "${forceVersion}"`, | |
| ); | |
| await writeFile(packageSwiftPath, content); | |
| logger.warn(`${plugin.id} is built for Capacitor ${major(version)}, it might cause issues`); | |
| } | |
| }), | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cli/src/ios/update.ts` around lines 65 - 88, The `update` flow in
`cli/src/ios/update.ts` is recomputing `getCapacitorPackageVersion` for every
plugin even though `iosPlatformVersion` is independent of `plugin`; hoist that
await out of the `Promise.all`/`validSPMPackages.map` loop and reuse the single
value inside the per-plugin work. While updating the `Package.swift` parsing
logic, make sure the `major(version)` comparison in the `regex`/`content.match`
path is guarded so malformed or missing semver values do not throw during
`update`.
| import { writeFileSync, readFileSync, existsSync } from 'fs-extra'; | ||
| import { join } from 'path'; | ||
| import { rimraf } from 'rimraf'; | ||
| import rimraf from 'rimraf'; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== package version references =="
rg -n '"rimraf"|rimraf@|from '\''rimraf'\''|from \"rimraf\"' cli package.json pnpm-lock.yaml package-lock.json yarn.lock --no-messages || true
echo
echo "== migrate.ts context =="
sed -n '1,380p' cli/src/tasks/migrate.tsRepository: Cap-go/capacitor-plus
Length of output: 1955
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate rimraf package metadata if present =="
fd -a "package.json" . | rg "rimraf|cli/package.json|package.json$" || true
echo
echo "== search for rimraf usages =="
rg -n "\brimraf\b" . --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' || true
echo
echo "== inspect relevant package file(s) =="
for f in cli/package.json package.json pnpm-lock.yaml package-lock.json yarn.lock; do
if [ -f "$f" ]; then
echo "--- $f ---"
sed -n '1,260p' "$f"
fi
doneRepository: Cap-go/capacitor-plus
Length of output: 1955
🌐 Web query:
rimraf v6 default export named exports rimrafSync sync import documentation
💡 Result:
In rimraf v6, there is no default export [1][2]. You must use named imports to access the library's functions [1][3]. To use the synchronous version of rimraf, import rimrafSync from the package [1][2]: import { rimrafSync } from 'rimraf'; You can also import other available functions, such as the primary async rimraf function or native implementations, using the same named export pattern [3][2]: import { rimraf, rimrafSync, native, nativeSync } from 'rimraf'; If you are using CommonJS, use destructuring assignment to access these named exports [3][2]: const { rimraf, rimrafSync } = require('rimraf');
Citations:
- 1: https://www.npmjs.com/package/rimraf
- 2: https://isaacs.github.io/rimraf/
- 3: https://registry.npmjs.org/rimraf
🌐 Web query:
site:npmjs.com rimraf 6.0.1 exports rimrafSync default export
💡 Result:
In rimraf version 6.0.1 and later, there is no default export [1]. You must import the functions directly using named imports [1]. For example, to use rimrafSync, you should use the following syntax: import { rimrafSync } from 'rimraf'; [1] Alternatively, if you are using CommonJS, you can import the functions from the package as follows: const { rimrafSync } = require('rimraf');
Citations:
Switch to the named rimraf export. rimraf v6 has no default export, so rimraf.sync(...) will fail at runtime; import rimrafSync and call that instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cli/src/tasks/migrate.ts` at line 3, The migration task is importing rimraf
as a default export, but rimraf v6 only exposes named exports, so the current
sync call will break at runtime. Update the import in migrate.ts to use the
named sync export (for example, rimrafSync) and change the existing
rimraf.sync(...) usage in the migration logic to call that named function
instead.
| export async function generateCordovaPodspecs(cordovaPlugins: Plugin[], config: Config): Promise<void> { | ||
| const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p)); | ||
| const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el)); | ||
| generateCordovaPodspec(noStaticPlugins, config, false); | ||
| generateCordovaPodspec(staticPlugins, config, true); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
Await the async podspec generation calls.
generateCordovaPodspec is async and performs writeFile, but both invocations are fire-and-forget. generateCordovaPodspecs resolves before the .podspec files are actually written, so the downstream pod install in installCocoaPodsPlugins can race against unfinished writes (and any write error becomes an unhandled rejection).
🐛 Proposed fix
export async function generateCordovaPodspecs(cordovaPlugins: Plugin[], config: Config): Promise<void> {
const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p));
const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el));
- generateCordovaPodspec(noStaticPlugins, config, false);
- generateCordovaPodspec(staticPlugins, config, true);
+ await Promise.all([
+ generateCordovaPodspec(noStaticPlugins, config, false),
+ generateCordovaPodspec(staticPlugins, config, true),
+ ]);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export async function generateCordovaPodspecs(cordovaPlugins: Plugin[], config: Config): Promise<void> { | |
| 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 generateCordovaPodspecs(cordovaPlugins: Plugin[], config: Config): Promise<void> { | |
| const staticPlugins = cordovaPlugins.filter((p) => needsStaticPod(p)); | |
| const noStaticPlugins = cordovaPlugins.filter((el) => !staticPlugins.includes(el)); | |
| await Promise.all([ | |
| generateCordovaPodspec(noStaticPlugins, config, false), | |
| generateCordovaPodspec(staticPlugins, config, true), | |
| ]); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cli/src/util/cordova-ios.ts` around lines 22 - 27, The async podspec writes
in generateCordovaPodspecs are not awaited, so the function can finish before
the .podspec files are written and errors may be unhandled. Update
generateCordovaPodspecs in cordova-ios.ts to await both generateCordovaPodspec
calls (or run them through a Promise-based concurrency pattern) so the plugin
podspec generation completes before installCocoaPodsPlugins continues.
| 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`); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Drop the unnecessary async callback in cordovaPodfileLines.
The .map(async (p) => {...}) body contains no await, so it currently runs synchronously and pods is populated before the function returns. But marking it async is misleading and will silently break the synchronous pods accumulation if anyone later adds an await inside. Since this function is synchronous, use a plain callback.
♻️ Proposed change
- cordovaPlugins.map(async (p) => {
+ cordovaPlugins.map((p) => {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 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`); | |
| } | |
| }); | |
| }); | |
| }); | |
| }); | |
| cordovaPlugins.map((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`); | |
| } | |
| }); | |
| }); | |
| }); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cli/src/util/cordova-ios.ts` around lines 299 - 318, In cordovaPodfileLines,
remove the unnecessary async from the cordovaPlugins.map callback because it
does not await anything and the logic is synchronous. Update the loop in
cordova-ios.ts so the callback used to walk cordovaPlugins, getPlatformElement,
and podspec/pods/pod stays plain synchronous code, preserving the current pods
accumulation without implying async behavior.
| export async function generateCordovaPackageFiles(cordovaPlugins: Plugin[], config: Config): Promise<void> { | ||
| cordovaPlugins.map((plugin: any) => { | ||
| generateCordovaPackageFile(plugin, config); | ||
| }); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
Await per-plugin package file generation.
generateCordovaPackageFile is async and writes Package.swift, but the .map callbacks are never awaited, so generateCordovaPackageFiles resolves before any file is written. The caller in ios/update.ts (Line 61) proceeds to checkPluginsForPackageSwift/generatePackageFile, which can observe missing or partially written files.
🐛 Proposed fix
export async function generateCordovaPackageFiles(cordovaPlugins: Plugin[], config: Config): Promise<void> {
- cordovaPlugins.map((plugin: any) => {
- generateCordovaPackageFile(plugin, config);
- });
+ await Promise.all(cordovaPlugins.map((plugin: Plugin) => generateCordovaPackageFile(plugin, config)));
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export async function generateCordovaPackageFiles(cordovaPlugins: Plugin[], config: Config): Promise<void> { | |
| cordovaPlugins.map((plugin: any) => { | |
| generateCordovaPackageFile(plugin, config); | |
| }); | |
| } | |
| export async function generateCordovaPackageFiles(cordovaPlugins: Plugin[], config: Config): Promise<void> { | |
| await Promise.all(cordovaPlugins.map((plugin: Plugin) => generateCordovaPackageFile(plugin, config))); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cli/src/util/cordova-ios.ts` around lines 340 - 344,
generateCordovaPackageFiles is returning before the async per-plugin work
completes because the cordovaPlugins.map callback does not await
generateCordovaPackageFile. Update generateCordovaPackageFiles to wait for each
generateCordovaPackageFile call to finish, so callers like ios/update.ts do not
continue into checkPluginsForPackageSwift and generatePackageFile until all
Package.swift files are written. Use the generateCordovaPackageFiles and
generateCordovaPackageFile symbols to locate the fix.
| let method = dict["action"] as? String ?? "" | ||
| let callbackId = dict["callbackId"] as? String ?? "" | ||
|
|
||
| let args = dict["actionArgs"] as? Array ?? [] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Swift conditional cast as? Array without element type compile error generic parameter Element
💡 Result:
In Swift, attempting to use the conditional cast operator as? on an Array without specifying the element type—such as writing myArray as? Array or myArray as? [Any] (where the generic structure itself is the target)—will result in a compile-time error because Array is a generic type that requires its Element parameter to be defined [1][2]. The syntax Array or [Element] is not a concrete type in Swift; it is a generic type constructor [3]. To perform a conditional cast, the compiler must know exactly what type of elements you are attempting to cast into, as the runtime performs an element-by-element conversion [1][2]. Key reasons for this behavior include: 1. Mandatory Generic Parameters: Swift collections like Array<Element>, Set<Element>, and Dictionary<Key, Value> cannot exist without their generic parameters [3][2]. Attempting to use them without these parameters creates an incomplete type definition, which the compiler rejects [3]. 2. Element-wise Casting Mechanism: The as? operator for arrays is not a simple pointer cast. The Swift runtime treats an array cast as an element-by-element transformation [1][4]. It effectively iterates through the source array, attempts to cast each individual element to the target Element type, and constructs a new array if all elements succeed [1][2]. Because this logic requires knowledge of the target Element type to perform the cast, you must provide it explicitly [3]. If you are dealing with arrays of unknown types (e.g., [Any]), you must cast to a specific concrete array type, such as myArray as? [String] or myArray as? [Int], so the compiler and runtime understand the target type for the individual elements [1][2]. If the element type is truly unknown at compile time, you cannot perform this type of downcast, as the generic requirement cannot be satisfied [3]. Note that empty arrays can sometimes exhibit "magic" behavior where they appear to cast successfully to any array type [5][6]. This occurs because there are no elements to inspect, but the underlying type requirement remains the same; you should avoid relying on this behavior for logic control, as it can lead to unexpected results or crashes when the array is later populated [7][5].
Citations:
- 1: https://github.com/apple/swift/blob/main/docs/DynamicCasting.md
- 2: https://forums.swift.org/t/rfc-dynamic-casting-specification/38874
- 3: https://stackoverflow.com/questions/28787009/swift-error-with-generic-array
- 4: https://github.com/apple/swift/blob/master/docs/DynamicCasting.md
- 5: [SR-7738] Empty array type casting always succeeds swiftlang/swift-corelibs-foundation#3699
- 6: https://forums.swift.org/t/understanding-type-comparison-of-collection-types/63909
- 7: https://stackoverflow.com/questions/46876445/swift-anyobject-as-array-casting-fail-if-the-array-is-empty-not-nil
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the target file around the reported line.
sed -n '1,140p' ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift | nl -ba
# Search for similar casts in the repo for context.
rg -n --no-heading 'as\?\s*Array\b|as\?\s*\[[^]]+\]' ios -g '*.swift' || trueRepository: Cap-go/capacitor-plus
Length of output: 1955
🌐 Web query:
Swift let args = dict["actionArgs"] as? Array ?? [] compile error generic parameter Element could not be inferred
💡 Result:
The compile error "Generic parameter 'Element' could not be inferred" occurs because Array in Swift is a generic type that must know what kind of elements it contains [1][2]. When you use as? Array, you are attempting to cast to a non-specific array type without defining its Element type [3][1]. The compiler cannot infer this type from the context, so it requires you to be explicit [4][1]. To fix this, provide the specific type the array is expected to hold [1]. If you expect an array of strings, for example, use: let args = dict["actionArgs"] as? [String]?? [] If you do not know the exact type but know it contains objects that can be treated as generic items, use [Any]: let args = dict["actionArgs"] as? [Any]?? [] The syntax [Type] is shorthand for Array and is generally preferred in Swift [3][5][2]. By specifying the type, you provide the compiler with the necessary information to satisfy the generic parameter requirement [1][6].
Citations:
- 1: https://stackoverflow.com/questions/30381490/argument-for-generic-parameter-could-not-be-inferred
- 2: https://stackoverflow.com/questions/51859628/swift-how-to-cast-any-to-array-dictionary
- 3: https://stackoverflow.com/questions/39799149/cant-cast-from-dictionary-value-to-array
- 4: https://stackoverflow.com/questions/46832056/error-generic-parameter-t-could-not-be-inferred-in-swift
- 5: https://stackoverflow.com/questions/42587629/swift-function-taking-generic-array
- 6: https://stackoverflow.com/questions/52507587/generic-parameter-t-could-not-be-inferred-with-optional-generic-array
Specify the array element type in ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift:43 Array needs a concrete Element; as? Array won’t compile. Use [Any] here, or the actual element type if it’s known.
Fix
- let args = dict["actionArgs"] as? Array ?? []
+ let args = dict["actionArgs"] as? [Any] ?? []📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let args = dict["actionArgs"] as? Array ?? [] | |
| let args = dict["actionArgs"] as? [Any] ?? [] |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@ios/CapacitorCordova/CapacitorCordova/Classes/Public/Plugin.swift` at line
43, The cast in Plugin should use a concrete array element type instead of the
untyped Array, since `as? Array` will not compile. Update the `args` assignment
in `Plugin` to cast to `[Any]` (or the correct known element type) so the
`actionArgs` value can be read safely.
RobinWitch
left a comment
There was a problem hiding this comment.
This upstream sync regresses the fork's package identity, which will break beta and release publishing for Capacitor Plus.
The PR changes all workspace package names from the fork namespace back to upstream names:
core/package.json @capacitor-plus/core -> @capacitor/core
cli/package.json @capacitor-plus/cli -> @capacitor/cli
android/package.json @capacitor-plus/android -> @capacitor/android
ios/package.json @capacitor-plus/ios -> @capacitor/ios
That is not just metadata. The PR beta publish workflow reads pkg.name from the selected workspace (.github/workflows/pr_beta_publish.yml lines 226-233 and 293), then runs npm publish in that package directory and adds a dist-tag using steps.version.outputs.package_name (lines 305-316). With these names, /publish-beta core would try to publish/tag @capacitor/core instead of @capacitor-plus/core, and the documented /publish-beta @capacitor-plus/core selector no longer matches any package name.
Please keep the @capacitor-plus/* package names while syncing the upstream code, otherwise the fork's beta publish path and downstream install commands point at the wrong npm namespace.
The sync branch had pulled in a large slice of upstream history with an upstream-preferred conflict resolution, which also reverted the fork's package metadata. Reset the tree to the plus tip and re-apply only this PR's own upstream commits. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
(cherry picked from commit e5bb94a)
Rebuilt, but blocked: this is a Capacitor 9 changeWhat I did: the branch was cut from upstream Why it is still blocked: Upstream ionic-team/capacitor#8524 was merged into upstream's This belongs to the same bucket as #81: land it as part of a deliberate Posted by an AI agent (Cursor). |
Merge Conflict Resolution Required
The sync of upstream PR ionic-team#8524 from @jcesarmobile encountered merge conflicts.
Original PR: ionic-team#8524
What happened
Synced from upstream by Capacitor+ Bot
Summary by CodeRabbit
New Features
run.Bug Fixes
Chores