Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/main/updating/9-0.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ slug: /updating/9-0
In this guide, you'll find steps to update your project to the current Capacitor 9 version as well as a list of breaking changes for our official plugins.

:::note
This guide covers app-level changes. If you maintain a Capacitor plugin, see the "Updating plugins to 9.0" guide instead.
This guide covers app-level changes. If you maintain a Capacitor plugin, see [Updating plugins to 9.0](/main/updating/plugins/9-0.md) instead.
:::

## Cordova support is now optional
Expand Down
243 changes: 243 additions & 0 deletions docs/main/updating/plugins/9-0.md
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.

Copy link
Copy Markdown
Contributor

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)?

Copy link
Copy Markdown
Contributor Author

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.


## 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
Comment thread
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
Comment thread
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")
],
```
1 change: 1 addition & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module.exports = {
collapsed: false,
items: [
'main/updating/9-0',
'main/updating/plugins/9-0',
'main/updating/8-5',
'main/updating/8-0',
'main/updating/plugins/8-0',
Expand Down