-
Notifications
You must be signed in to change notification settings - Fork 745
docs: add Capacitor 9 plugin upgrade guide #590
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
andredestro
wants to merge
3
commits into
chore/RMET-5320-cap9-app-upgrade-guide
from
chore/RMET-5321-cap9-plugin-upgrade-guide
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,243 @@ | ||
| --- | ||
| title: Updating plugins to 9.0 | ||
| description: Guide for updating Capacitor from v8 to v9 in your plugin | ||
| slug: /updating/plugins/9-0 | ||
| --- | ||
|
|
||
| # Breaking changes in code | ||
|
|
||
| # Updating Capacitor to 9.0 in your plugin | ||
|
|
||
| :::note | ||
| This guide covers plugin-author changes. If you're updating an app, see [Updating to 9.0](/main/updating/9-0.md) instead. | ||
| ::: | ||
|
|
||
| :::note | ||
| Capacitor 9 hasn't reached general availability yet. Dependency versions below point at the `next` dist-tag / current prerelease; once 9.0.0 ships, use a normal `^9.0.0` range instead. | ||
| ::: | ||
|
|
||
| ## Using @capacitor/plugin-migration-v8-to-v9 | ||
|
|
||
| From the plugin folder, run `npx @capacitor/plugin-migration-v8-to-v9@latest` and it will perform most of the file changes automatically. | ||
|
|
||
| ## Updating the files manually | ||
|
|
||
| ### Updating Capacitor dependencies | ||
|
|
||
| Update `@capacitor/cli`, `@capacitor/core`, `@capacitor/android` and `@capacitor/ios` in devDependencies to `next`. | ||
| Update `@capacitor/core` in peerDependencies to `>=9.0.0-alpha.6` (check npm for the current prerelease, since it moves forward regularly). | ||
|
|
||
| ### Cordova support is now optional | ||
|
|
||
| Capacitor's Cordova compatibility layer is now only wired into a consuming app when that app actually has a Cordova plugin installed, on both Android and iOS. If your plugin's own native code directly references symbols from Capacitor's Cordova compatibility layer (for example `com.getcapacitor.cordova.CordovaPlugin` on Android), be aware that layer may not be present in a consuming app that has no Cordova plugins. There is currently no configuration option to force it to be included. | ||
|
|
||
| If your plugin is SPM compatible, also remove the unconditional `Cordova` product dependency from your `Package.swift` — it's no longer guaranteed to be present in every consumer: | ||
|
|
||
| ```diff | ||
| dependencies: [ | ||
| .product(name: "Capacitor", package: "capacitor-swift-pm"), | ||
| - .product(name: "Cordova", package: "capacitor-swift-pm"), | ||
| ] | ||
| ``` | ||
|
|
||
| ### Update Android Plugin Variables | ||
|
|
||
| In your `build.gradle` file, update the following package versions: | ||
|
|
||
| ```diff | ||
| ext { | ||
| // Note: Some of the following dependencies are optional - only add/update the ones your plugin actually uses. | ||
| // If you use any of these dependencies, update them to the versions shown below. | ||
| junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2' | ||
| androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1' | ||
| androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0' | ||
| androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0' | ||
| - androidxActivityVersion = project.hasProperty('androidxActivityVersion') ? rootProject.ext.androidxActivityVersion : '1.11.0' | ||
| + androidxActivityVersion = project.hasProperty('androidxActivityVersion') ? rootProject.ext.androidxActivityVersion : '1.13.0' | ||
| androidxCoordinatorLayoutVersion = project.hasProperty('androidxCoordinatorLayoutVersion') ? rootProject.ext.androidxCoordinatorLayoutVersion : '1.3.0' | ||
| - androidxCoreVersion = project.hasProperty('androidxCoreVersion') ? rootProject.ext.androidxCoreVersion : '1.17.0' | ||
| + androidxCoreVersion = project.hasProperty('androidxCoreVersion') ? rootProject.ext.androidxCoreVersion : '1.19.0' | ||
| androidxFragmentVersion = project.hasProperty('androidxFragmentVersion') ? rootProject.ext.androidxFragmentVersion : '1.8.9' | ||
| firebaseMessagingVersion = project.hasProperty('firebaseMessagingVersion') ? rootProject.ext.firebaseMessagingVersion : '25.0.1' | ||
| - playServicesLocationVersion = project.hasProperty('playServicesLocationVersion') ? rootProject.ext.playServicesLocationVersion : '21.3.0' | ||
| + playServicesLocationVersion = project.hasProperty('playServicesLocationVersion') ? rootProject.ext.playServicesLocationVersion : '21.4.0' | ||
| - androidxBrowserVersion = project.hasProperty('androidxBrowserVersion') ? rootProject.ext.androidxBrowserVersion : '1.9.0' | ||
| + androidxBrowserVersion = project.hasProperty('androidxBrowserVersion') ? rootProject.ext.androidxBrowserVersion : '1.10.0' | ||
| - androidxMaterialVersion = project.hasProperty('androidxMaterialVersion') ? rootProject.ext.androidxMaterialVersion : '1.13.0' | ||
| + androidxMaterialVersion = project.hasProperty('androidxMaterialVersion') ? rootProject.ext.androidxMaterialVersion : '1.14.0' | ||
| - androidxExifInterfaceVersion = project.hasProperty('androidxExifInterfaceVersion') ? rootProject.ext.androidxExifInterfaceVersion : '1.4.1' | ||
| + androidxExifInterfaceVersion = project.hasProperty('androidxExifInterfaceVersion') ? rootProject.ext.androidxExifInterfaceVersion : '1.4.2' | ||
| coreSplashScreenVersion = project.hasProperty('coreSplashScreenVersion') ? rootProject.ext.coreSplashScreenVersion : '1.2.0' | ||
| - androidxWebkitVersion = project.hasProperty('androidxWebkitVersion') ? rootProject.ext.androidxWebkitVersion : '1.14.0' | ||
| + androidxWebkitVersion = project.hasProperty('androidxWebkitVersion') ? rootProject.ext.androidxWebkitVersion : '1.16.0' | ||
| - googleMapsPlayServicesVersion = project.hasProperty('googleMapsPlayServicesVersion') ? rootProject.ext.googleMapsPlayServicesVersion : '19.2.0' | ||
| + googleMapsPlayServicesVersion = project.hasProperty('googleMapsPlayServicesVersion') ? rootProject.ext.googleMapsPlayServicesVersion : '20.0.0' | ||
| - googleMapsUtilsVersion = project.hasProperty('googleMapsUtilsVersion') ? rootProject.ext.googleMapsUtilsVersion : '3.19.1' | ||
| + googleMapsUtilsVersion = project.hasProperty('googleMapsUtilsVersion') ? rootProject.ext.googleMapsUtilsVersion : '5.0.0' | ||
| - googleMapsKtxVersion = project.hasProperty('googleMapsKtxVersion') ? rootProject.ext.googleMapsKtxVersion : '5.2.1' | ||
| - googleMapsUtilsKtxVersion = project.hasProperty('googleMapsUtilsKtxVersion') ? rootProject.ext.googleMapsUtilsKtxVersion : '5.2.1' | ||
| + googleMapsKtxVersion = project.hasProperty('googleMapsKtxVersion') ? rootProject.ext.googleMapsKtxVersion : '6.0.1' | ||
| + googleMapsUtilsKtxVersion = project.hasProperty('googleMapsUtilsKtxVersion') ? rootProject.ext.googleMapsUtilsKtxVersion : '6.0.1' | ||
| - kotlinxCoroutinesVersion = project.hasProperty('kotlinxCoroutinesVersion') ? rootProject.ext.kotlinxCoroutinesVersion : '1.10.2' | ||
| + kotlinxCoroutinesVersion = project.hasProperty('kotlinxCoroutinesVersion') ? rootProject.ext.kotlinxCoroutinesVersion : '1.11.0' | ||
| } | ||
| ``` | ||
|
|
||
| `googleMapsUtilsVersion` 5.0.0 includes upstream breaking changes to the Google Maps Utils API; check your usage against the [Google Maps Android Utility Library release notes](https://github.com/googlemaps/android-maps-utils/releases) if your plugin depends on it directly. | ||
|
|
||
| ### Migrate core-ktx to core | ||
|
|
||
| `androidx.core:core` 1.19.0 merges every extension function previously shipped in `core-ktx` into `core` itself, turning `core-ktx` into an empty compatibility artifact. Drop the separate `androidxCoreKTXVersion` variable (reuse `androidxCoreVersion` instead) and depend on `core` rather than `core-ktx`: | ||
|
|
||
| ```diff | ||
| dependencies { | ||
| - implementation "androidx.core:core-ktx:$androidxCoreVersion" | ||
| + implementation "androidx.core:core:$androidxCoreVersion" | ||
| } | ||
| ``` | ||
|
|
||
| ### Remove targetSdkVersion, update minSdk / compileSdk | ||
|
|
||
| AGP 9 infers `targetSdkVersion` from `compileSdkVersion` when it isn't set, and on a library module (which is what your plugin's `android/build.gradle` is) `targetSdkVersion` has no runtime effect anyway, so drop it entirely: | ||
|
|
||
| ```diff | ||
| # build.gradle | ||
|
|
||
| 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 | ||
| + minSdkVersion = project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 26 | ||
| - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36 | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Rename the default ProGuard file | ||
|
|
||
| AGP 9 removed `proguard-android.txt` — any `build.gradle` that still references it fails at Gradle configuration time, even with `minifyEnabled false`. Most official plugins already switched to `proguard-android-optimize.txt` in Capacitor 8, but if yours (or a fork of one) hasn't yet: | ||
|
|
||
| ```diff | ||
| buildTypes { | ||
| release { | ||
| minifyEnabled false | ||
| - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||
| + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Remove the standalone Kotlin plugin | ||
|
|
||
| AGP 9 bundles the Kotlin Gradle Plugin natively (Kotlin `2.2.10`) instead of requiring it to be applied separately. If your plugin still applies `kotlin-android` (or `org.jetbrains.kotlin.android`) and declares its own `kotlin_version`/`kotlin-stdlib`/`kotlin-gradle-plugin`, remove them, otherwise you'll hit a duplicate-plugin build failure: | ||
|
|
||
| ```diff | ||
| buildscript { | ||
| - ext.kotlin_version = project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '2.2.20' | ||
| repositories { | ||
| google() | ||
| mavenCentral() | ||
| } | ||
| dependencies { | ||
| classpath 'com.android.tools.build:gradle:9.2.1' | ||
| - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | ||
| } | ||
| } | ||
| -apply plugin: 'kotlin-android' | ||
|
|
||
| dependencies { | ||
| - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" | ||
| } | ||
| ``` | ||
|
|
||
| ### Give your plugin a unique Android namespace | ||
|
|
||
| AGP 9 defaults `android.uniquePackageNames` to `true`: if your plugin's Android `namespace` collides with another library module in the same app, the build now fails instead of silently working. The most common cause is a leftover scaffold default (`create-capacitor-plugin` pre-fills the `namespace` prompt with `com.mycompany.plugins.example`, and pressing Enter accepts it). A fork that kept the original plugin's namespace unchanged can cause the same failure. Make sure your `namespace` in `build.gradle` is unique to your plugin. | ||
|
|
||
| ### Remove jcenter() | ||
|
|
||
| Gradle 9 fully removes the `jcenter()` repository helper (redirected to Maven Central since 2024). Any `build.gradle` that still calls it fails at Gradle configuration time: | ||
|
|
||
| ```diff | ||
| repositories { | ||
| google() | ||
| - jcenter() | ||
| + mavenCentral() | ||
| } | ||
| ``` | ||
|
|
||
| ### Update gradle plugin to 9.2.1 | ||
|
OS-pedrogustavobilro marked this conversation as resolved.
|
||
|
|
||
| ```diff | ||
| dependencies { | ||
| - classpath 'com.android.tools.build:gradle:8.13.0' | ||
| + classpath 'com.android.tools.build:gradle:9.2.1' | ||
| } | ||
| ``` | ||
|
|
||
| ### Update gradle wrapper to 9.5.1 | ||
|
|
||
| ```diff | ||
| # gradle-wrapper.properties | ||
|
|
||
| distributionBase=GRADLE_USER_HOME | ||
| distributionPath=wrapper/dists | ||
| - distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip | ||
| + distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-all.zip | ||
| zipStoreBase=GRADLE_USER_HOME | ||
| zipStorePath=wrapper/dists | ||
| ``` | ||
|
|
||
| ### Update google services plugin | ||
|
|
||
| ```diff | ||
| # build.gradle | ||
|
|
||
| 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' | ||
| ``` | ||
|
|
||
| ### Raise iOS Deployment Target to 16 | ||
|
OS-pedrogustavobilro marked this conversation as resolved.
|
||
|
|
||
| If your plugin observes app lifecycle or URL-opening notifications, also check it against the iOS UIScene lifecycle adopted in Capacitor 8.5 — see [Audit your custom code and plugins](/main/updating/8-5.md#audit-your-custom-code-and-plugins). | ||
|
|
||
| Update your plugin's `.podspec` file: | ||
|
|
||
| ```diff | ||
| - s.ios.deployment_target = '15.0' | ||
| + s.ios.deployment_target = '16.0' | ||
| ``` | ||
|
|
||
| #### SPM compatible plugins | ||
|
|
||
| Update `Package.swift` file: | ||
|
|
||
| ```diff | ||
| - platforms: [.iOS(.v15)], | ||
| + platforms: [.iOS(.v16)], | ||
| ``` | ||
|
|
||
| #### Plugins with old structure | ||
|
|
||
| Do the following for your Xcode project: select the **Project** within the project editor and open the **Build Settings** tab. Under the **Deployment** section, change **iOS Deployment Target** to **iOS 16.0**. Repeat the same steps for any app **Targets**. | ||
|
|
||
| Then, open `ios/Podfile` and update the iOS version to 16.0: | ||
|
|
||
| ```diff | ||
| -platform :ios, '15.0' | ||
| +platform :ios, '16.0' | ||
| ``` | ||
|
|
||
| ### Update Capacitor SPM dependency | ||
|
|
||
| In SPM compatible plugins, update `Package.swift` file to point at the current Capacitor 9 prerelease (Capacitor 9 hasn't shipped a stable release yet, so this isn't a plain `9.0.0` version). Use the latest alpha, since earlier ones had crashes related to Cordova Optionality: | ||
|
|
||
| ```diff | ||
| dependencies: [ | ||
| - .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0") | ||
| + .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "9.0.0-alpha.6") | ||
| ], | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't exist yet, and maybe would only exist when Capacitor 9 reaches GA? Or we could generate a prerelease for it (no prerelease exists yet)? Or, because the plugin-migration packages are in 0.x, perhaps it's fine to do a stable release?
Should we decide that (whether or not to release the plugin migrator for Cap 9 unstable version) before merging this PR (decision can potentially be done outside this PR)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed this can be decided outside this PR. Leaning towards a 0.x prerelease/prerelease-tag publish once we're closer to stabilizing the Cap 9 changes, rather than a stable release while Cap 9 itself is still alpha, but let's track that decision separately rather than block this guide on it.