---
title: "Capacitor 8: Swift Package Manager, Android 16 and the upgrade · vuejs.school"
description: "Capacitor 8 defaults to Swift Package Manager on iOS, adds a System Bars plugin for Android edge-to-edge and raises every minimum. Here is the upgrade."
canonical_url: "https://vuejs.school/community/blog/capacitor-8-swift-package-manager-and-the-upgrade"
meta:
  "og:description": "Capacitor 8 defaults to Swift Package Manager on iOS, adds a System Bars plugin for Android edge-to-edge and raises every minimum. Here is the upgrade."
  "og:image": "https://vuejs.school/_og/d/a_~Q2FwYWNpdG9yIDg6IFN3aWZ0IFBhY2thZ2UgTWFuYWdlciwgQW5kcm9pZCAxNiBhbmQgdGhlIHVwZ3JhZGUgwrcgdnVlanMuc2Nob29s%2Cc_OgCard.takumi%2Ctitle_~Q2FwYWNpdG9yIDg6IFN3aWZ0IFBhY2thZ2UgTWFuYWdlciwgQW5kcm9pZCAxNiBhbmQgdGhlIHVwZ3JhZGU%2Cdescription_~Q2FwYWNpdG9yIDggZGVmYXVsdHMgdG8gU3dpZnQgUGFja2FnZSBNYW5hZ2VyIG9uIGlPUywgYWRkcyBhIFN5c3RlbSBCYXJzIHBsdWdpbiBmb3IgQW5kcm9pZCBlZGdlLXRvLWVkZ2UgYW5kIHJhaXNlcyBldmVyeSBtaW5pbXVtLiBIZXJlIGlzIHRoZSB1cGdyYWRlLg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA4IG1pbiByZWFk%2Ctint_lilac%2Cp_Ii9jb21tdW5pdHkvYmxvZy9jYXBhY2l0b3ItOC1zd2lmdC1wYWNrYWdlLW1hbmFnZXItYW5kLXRoZS11cGdyYWRlIg%2Cs_ftZkJjxqQrcJQVga.png"
  "og:image:alt": "Capacitor 8: Swift Package Manager, Android 16 and the upgrade · vuejs.school"
  "og:image:height": "600"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": "Capacitor 8: Swift Package Manager, Android 16 and the upgrade · vuejs.school"
  "twitter:image": "https://vuejs.school/_og/d/a_~Q2FwYWNpdG9yIDg6IFN3aWZ0IFBhY2thZ2UgTWFuYWdlciwgQW5kcm9pZCAxNiBhbmQgdGhlIHVwZ3JhZGUgwrcgdnVlanMuc2Nob29s%2Cc_OgCard.takumi%2Ctitle_~Q2FwYWNpdG9yIDg6IFN3aWZ0IFBhY2thZ2UgTWFuYWdlciwgQW5kcm9pZCAxNiBhbmQgdGhlIHVwZ3JhZGU%2Cdescription_~Q2FwYWNpdG9yIDggZGVmYXVsdHMgdG8gU3dpZnQgUGFja2FnZSBNYW5hZ2VyIG9uIGlPUywgYWRkcyBhIFN5c3RlbSBCYXJzIHBsdWdpbiBmb3IgQW5kcm9pZCBlZGdlLXRvLWVkZ2UgYW5kIHJhaXNlcyBldmVyeSBtaW5pbXVtLiBIZXJlIGlzIHRoZSB1cGdyYWRlLg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA4IG1pbiByZWFk%2Ctint_lilac%2Cp_Ii9jb21tdW5pdHkvYmxvZy9jYXBhY2l0b3ItOC1zd2lmdC1wYWNrYWdlLW1hbmFnZXItYW5kLXRoZS11cGdyYWRlIg%2Cs_ftZkJjxqQrcJQVga.png"
  "twitter:image:alt": "Capacitor 8: Swift Package Manager, Android 16 and the upgrade · vuejs.school"
---

> Index of every page on this site: [llms.txt](https://vuejs.school/llms.txt)

## What shipped

The release has two headline features and a long list of raised requirements:

- New iOS projects use Swift Package Manager (SPM) by default. CocoaPods projects keep working, and `npx cap add ios --packagemanager CocoaPods` still creates one.
- A System Bars plugin, bundled with `@capacitor/core`, replaces the old `android.adjustMarginsForEdgeToEdge` option and handles edge-to-edge layouts on Android with CSS variables for the safe areas.
- Minimums move to Node 22, Xcode 26, iOS 15, Android Studio Otter, `minSdkVersion` 24, and `compileSdkVersion` and `targetSdkVersion` 36 (Android 16).
- Every official plugin moves to 8.0.0, with behaviour changes in Geolocation, Screen Orientation, Barcode Scanner and Status Bar.
- Smaller items from the changelog: `npx cap run` can pick a device by target name, iOS plugins can hook into WebView authentication challenges, and `bridge_layout_main.xml` is renamed `capacitor_bridge_layout_main.xml`.

## Swift Package Manager becomes the iOS default

CocoaPods has been the way iOS projects pull in dependencies since 2011, and it is winding down: it entered maintenance mode in August 2024, and its package index becomes read-only on 2 December 2026. SPM is Apple's own tool, built into Xcode. Capacitor has offered it as an option since version 6; in 8 it becomes the default for projects created with `npx cap add ios`. The team's [October post on SPM](https://ionic.io/blog/swift-package-manager-and-capacitor) explains the plan: Ionic keeps publishing CocoaPods versions of its libraries until that December 2026 date. The [SPM guide](https://capacitorjs.com/docs/ios/spm) adds that almost all plugins in the official `capacitor-plugins` repository already support SPM.

An existing CocoaPods project stays on CocoaPods after the upgrade. The updating guide's only CocoaPods step is to raise `platform :ios` in `ios/App/Podfile` to `15.0`. You have a year to move, but check two limits first:

- You cannot mix SPM and CocoaPods in one project, so a single third-party plugin without SPM support keeps the whole app on CocoaPods. The migration assistant and `npx cap sync` warn about each one.
- A plugin that mixes Objective-C and Swift cannot be converted automatically, because an SPM target holds one language.

There are two ways to move. If nothing in the `ios` folder was ever edited by hand, delete the folder and recreate it: `npx cap add ios` now gives you the SPM template. Otherwise, use the migration assistant. The September app needs it, because recreating the folder would drop the camera permission strings it added to `ios/App/App/Info.plist`. Commit first, because the assistant deletes files:

```
npx cap spm-migration-assistant
npx cap open ios
```

The assistant runs `pod deintegrate`, deletes the `Podfile`, `Podfile.lock` and `App.xcworkspace`, creates a `CapApp-SPM` package that references all your plugins, generates its `Package.swift`, and adds a `debug.xcconfig`. Two steps remain in Xcode, and the SPM guide shows them with screenshots: add `CapApp-SPM` as a local package dependency, and select `debug.xcconfig` as a configuration file.

## Handle Android edge-to-edge with System Bars

Android 15 started drawing the apps that target it under the status bar and the navigation bar (a layout Android calls edge-to-edge), and an app could opt out with the theme attribute `windowOptOutEdgeToEdgeEnforcement`. Capacitor 8 targets Android 16, and for apps targeting API level 36 Android's own [behaviour changes](https://developer.android.com/about/versions/16/behavior-changes-16) say the opt-out is deprecated and disabled on Android 16 devices. Your web view will extend under the bars, so your layout has to know where they are.

Capacitor 7 tried to help with `android.adjustMarginsForEdgeToEdge`, which added margins around the web view. Capacitor 8 removes that option and moves the job into CSS, where it belongs.

The web view reports the bar heights through the standard `env(safe-area-inset-*)` values. Some Android WebView versions before 140 report them wrongly, so the System Bars plugin also injects corrected values into `--safe-area-inset-*` variables. The documented pattern goes in your global stylesheet and reads the variable first, with the `env()` value as a fallback:

*src/assets/main.css*

```
html {
  padding-top: var(--safe-area-inset-top, env(safe-area-inset-top, 0px));
  padding-bottom: var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));
  padding-left: var(--safe-area-inset-left, env(safe-area-inset-left, 0px));
  padding-right: var(--safe-area-inset-right, env(safe-area-inset-right, 0px));
}
```

On iOS the same `env()` values only work when the viewport meta tag in `index.html` includes `viewport-fit=cover`. The `index.html` that `create-vue` generates does not have it, so extend its viewport line to `<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">`.

The plugin also styles the bars. It is part of `@capacitor/core`, so there is nothing to install, and a dark mode toggle in a Vue component can keep the bar icons readable:

*src/components/ThemeToggle.vue*

```
<script setup lang="ts">
import { ref } from 'vue'
import { Capacitor, SystemBars, SystemBarsStyle } from '@capacitor/core'

const dark = ref(false)

async function toggle() {
  dark.value = !dark.value
  document.documentElement.classList.toggle('dark', dark.value)
  if (Capacitor.isNativePlatform()) {
    await SystemBars.setStyle({ style: dark.value ? SystemBarsStyle.Dark : SystemBarsStyle.Light })
  }
}
</script>

<template>
  <button type="button" @click="toggle">{{ dark ? 'Light theme' : 'Dark theme' }}</button>
</template>
```

`SystemBarsStyle.Dark` means light icons on a dark background, the same convention as the Status Bar plugin. `isNativePlatform()` skips the call in a browser, which has no system bars. The rest of the plugin, from the [System Bars docs](https://capacitorjs.com/docs/apis/system-bars):

- `show()` and `hide()`, which take a `bar` option to act on one bar only, such as the navigation bar.
- `setAnimation()`, on iOS only.
- Settings in `capacitor.config.ts` under `plugins.SystemBars`: the initial style, a `hidden` flag and the Android `insetsHandling` mode ( `css` by default, `disable` to turn the injection off).
- On iOS the plugin needs `UIViewControllerBasedStatusBarAppearance` set to `YES` in `Info.plist`.

## The new minimums

Check these before you upgrade:

- Node 22 or newer. The CLI checks its `engines` field and refuses to run on older versions; Node 24 is the current long-term support line.
- Xcode 26, which itself requires macOS 15.6 or later.
- An iOS deployment target of 15.0 for the project and every target.
- Android Studio Otter (2025.2.1) or newer, with the Android Gradle Plugin at 8.13.0, the Gradle wrapper at 8.14.3 and, if you use Kotlin, `kotlin_version` 2.2.20.
- In `variables.gradle`: `minSdkVersion` 24, `compileSdkVersion` 36 and `targetSdkVersion` 36, plus the AndroidX versions the guide lists.

## Upgrade the Vue app from September

The app from the Capacitor 7 post is a Vite project that builds into `dist`, with `android` and `ios` folders (iOS still on CocoaPods, Capacitor 7's default) and one plugin, Camera. Start on a branch with Node 22 or 24 active, then let the CLI do the work:

```
npm install -D @capacitor/cli@latest
npx cap migrate
```

`npx cap migrate` moves `@capacitor/core`, `@capacitor/android`, `@capacitor/ios` and the official plugins to 8.0.0, applies the Gradle and Xcode changes it can, and prints anything it could not do. For those, the guide lists the manual steps. On Android:

- Run the AGP Upgrade Assistant from the Tools menu in Android Studio and pick 8.13.0.
- In `app/build.gradle`, change `namespace "..."` and `compileSdk ...` to the `namespace = "..."` form.
- Update `google-services` to 4.4.4.
- Add `density` to the activity's `configChanges` in `AndroidManifest.xml`, so that the web view no longer reloads when the window is resized.

On iOS, set the deployment target to 15.0 in the project's build settings and in `ios/App/Podfile`.

Before you run the app, add the safe-area CSS and the `viewport-fit=cover` change from the edge-to-edge section, or the first thing you will see on Android 16 is your header under the status bar. Then rebuild the web app and push it into both shells:

```
npm run build
npx cap sync
npx cap run ios
npx cap run android
```

## Behaviour changes to check before you ship

Three changes can alter how an app that already works behaves:

- **Geolocation's `timeout`** now applies to every request on Android and iOS, not only on the web and to `getCurrentPosition()` on Android. An app that set a short timeout may start seeing timeouts; the guide suggests a higher value. `watchPosition()` on Android gains an `interval` option.
- **Screen Orientation's `lock()` and Barcode Scanner's `scanOrientation`** have no effect on tablets and other large screens running Android 16, because the system ignores orientation requests on displays whose smallest width is 600dp or more. Regular phones are unaffected. The manifest property `android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY` opts out temporarily and stops working at API level 37.
- **`appendUserAgent` on iOS** used to gain two extra spaces through a bug. The bug is fixed, so a server that matched on the old string sees a different one. To keep the old string, add a space to `ios.appendUserAgent`, not to the root `appendUserAgent`, which would change Android too.

## What to test on each platform

On iOS, build once in Xcode 26 and watch the build log for deprecated settings, then check the status bar in both themes and the safe areas on a device with a notch, in portrait and in landscape. If you migrated to SPM, test a clean clone: delete `DerivedData`, run `npx cap sync`, open the project and build, which proves the generated `Package.swift` is complete.

On Android, test on an Android 16 device or emulator and on an Android 14 one, because the bars behave differently under each. Scroll a long page to the bottom and confirm the last item clears the navigation bar; open the keyboard in a form and confirm the input stays visible; rotate a tablet; and if the app locks orientation, see what the lock now does on a large screen.

## Upgrade now or wait

Upgrade now if your app is in development or you can test both platforms this week. The requirements are the real work, and they are coming anyway: Google Play will ask for a current target SDK, Apple will ask for a current Xcode, and CocoaPods has a date on it. The migration command handles the mechanical part.

Wait if you depend on a third-party plugin that has not published an 8.0 version yet, or if your CI builds on an older macOS image: Xcode 26 needs macOS 15.6, and a build agent that cannot provide it cannot ship Capacitor 8.

**Check it stuck**3 questions from the Mobile apps with Capacitor quiz.

[**Practice now**](https://vuejs.school/learn/multiplatform/mobile-apps-with-capacitor?tab=quiz)

**Was this helpful?**

## **Keep reading**

[Tutorial · 7 min read**Tauri plugins, mobile builds and the updater**](https://vuejs.school/community/blog/tauri-plugins-mobile-builds-and-the-updater) [Tutorial · 7 min read**Mobile UI with Ionic Vue 8**](https://vuejs.school/community/blog/mobile-ui-with-ionic-vue-8)

```json
{"@context":"https://schema.org","@graph":[{"@id":"https://vuejs.school/#website","@type":"WebSite","description":"A free AI-instructed school for Vue.js. Short tutorials, runnable code and quizzes that adapt to you.","name":"vuejs.school","url":"https://vuejs.school/","publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/capacitor-8-swift-package-manager-and-the-upgrade#webpage","@type":"WebPage","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2025-12-09","description":"Capacitor 8 defaults to Swift Package Manager on iOS, adds a System Bars plugin for Android edge-to-edge and raises every minimum. Here is the upgrade.","name":"Capacitor 8: Swift Package Manager, Android 16 and the upgrade","url":"https://vuejs.school/community/blog/capacitor-8-swift-package-manager-and-the-upgrade","breadcrumb":{"@id":"https://vuejs.school/community/blog/capacitor-8-swift-package-manager-and-the-upgrade#breadcrumb"},"isPartOf":{"@id":"https://vuejs.school/#website"},"potentialAction":[{"@type":"ReadAction","target":["https://vuejs.school/community/blog/capacitor-8-swift-package-manager-and-the-upgrade"]}],"primaryImageOfPage":{"@id":"https://vuejs.school/#logo"}},{"@id":"https://vuejs.school/#identity","@type":"Organization","description":"A free AI-instructed school for Vue.js. Short tutorials, runnable code and quizzes that adapt to you.","name":"vuejs.school","url":"https://vuejs.school","logo":{"@id":"https://vuejs.school/#logo"}},{"@id":"https://vuejs.school/community/blog/capacitor-8-swift-package-manager-and-the-upgrade#article","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2025-12-09","description":"Capacitor 8 defaults to Swift Package Manager on iOS, adds a System Bars plugin for Android edge-to-edge and raises every minimum. Here is the upgrade.","headline":"Capacitor 8: Swift Package Manager, Android 16 and the upgrade","inLanguage":"en","isAccessibleForFree":true,"thumbnailUrl":"https://vuejs.school/_og/d/a_~Q2FwYWNpdG9yIDg6IFN3aWZ0IFBhY2thZ2UgTWFuYWdlciwgQW5kcm9pZCAxNiBhbmQgdGhlIHVwZ3JhZGUgwrcgdnVlanMuc2Nob29s%2Cc_OgCard.takumi%2Ctitle_~Q2FwYWNpdG9yIDg6IFN3aWZ0IFBhY2thZ2UgTWFuYWdlciwgQW5kcm9pZCAxNiBhbmQgdGhlIHVwZ3JhZGU%2Cdescription_~Q2FwYWNpdG9yIDggZGVmYXVsdHMgdG8gU3dpZnQgUGFja2FnZSBNYW5hZ2VyIG9uIGlPUywgYWRkcyBhIFN5c3RlbSBCYXJzIHBsdWdpbiBmb3IgQW5kcm9pZCBlZGdlLXRvLWVkZ2UgYW5kIHJhaXNlcyBldmVyeSBtaW5pbXVtLiBIZXJlIGlzIHRoZSB1cGdyYWRlLg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA4IG1pbiByZWFk%2Ctint_lilac%2Cp_Ii9jb21tdW5pdHkvYmxvZy9jYXBhY2l0b3ItOC1zd2lmdC1wYWNrYWdlLW1hbmFnZXItYW5kLXRoZS11cGdyYWRlIg%2Cs_ftZkJjxqQrcJQVga.png","@type":["Article","BlogPosting"],"articleSection":["Release notes"],"author":{"@id":"https://vuejs.school/#identity"},"image":{"@id":"https://vuejs.school/#/schema/image/1"},"isPartOf":{"@id":"https://vuejs.school/community/blog/capacitor-8-swift-package-manager-and-the-upgrade#webpage"},"mainEntityOfPage":{"@id":"https://vuejs.school/community/blog/capacitor-8-swift-package-manager-and-the-upgrade#webpage"},"publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/capacitor-8-swift-package-manager-and-the-upgrade#breadcrumb","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","name":"Blog","item":"https://vuejs.school/community/blog","position":1},{"@type":"ListItem","name":"Capacitor 8: Swift Package Manager, Android 16 and the upgrade","position":2}]},{"@id":"https://vuejs.school/#logo","@type":"ImageObject","caption":"vuejs.school","contentUrl":"https://vuejs.school/apple-touch-icon.png","inLanguage":"en","url":"https://vuejs.school/apple-touch-icon.png"},{"@id":"https://vuejs.school/#/schema/image/1","@type":"ImageObject","contentUrl":"https://vuejs.school/_og/d/a_~Q2FwYWNpdG9yIDg6IFN3aWZ0IFBhY2thZ2UgTWFuYWdlciwgQW5kcm9pZCAxNiBhbmQgdGhlIHVwZ3JhZGUgwrcgdnVlanMuc2Nob29s%2Cc_OgCard.takumi%2Ctitle_~Q2FwYWNpdG9yIDg6IFN3aWZ0IFBhY2thZ2UgTWFuYWdlciwgQW5kcm9pZCAxNiBhbmQgdGhlIHVwZ3JhZGU%2Cdescription_~Q2FwYWNpdG9yIDggZGVmYXVsdHMgdG8gU3dpZnQgUGFja2FnZSBNYW5hZ2VyIG9uIGlPUywgYWRkcyBhIFN5c3RlbSBCYXJzIHBsdWdpbiBmb3IgQW5kcm9pZCBlZGdlLXRvLWVkZ2UgYW5kIHJhaXNlcyBldmVyeSBtaW5pbXVtLiBIZXJlIGlzIHRoZSB1cGdyYWRlLg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA4IG1pbiByZWFk%2Ctint_lilac%2Cp_Ii9jb21tdW5pdHkvYmxvZy9jYXBhY2l0b3ItOC1zd2lmdC1wYWNrYWdlLW1hbmFnZXItYW5kLXRoZS11cGdyYWRlIg%2Cs_ftZkJjxqQrcJQVga.png","inLanguage":"en","url":"https://vuejs.school/_og/d/a_~Q2FwYWNpdG9yIDg6IFN3aWZ0IFBhY2thZ2UgTWFuYWdlciwgQW5kcm9pZCAxNiBhbmQgdGhlIHVwZ3JhZGUgwrcgdnVlanMuc2Nob29s%2Cc_OgCard.takumi%2Ctitle_~Q2FwYWNpdG9yIDg6IFN3aWZ0IFBhY2thZ2UgTWFuYWdlciwgQW5kcm9pZCAxNiBhbmQgdGhlIHVwZ3JhZGU%2Cdescription_~Q2FwYWNpdG9yIDggZGVmYXVsdHMgdG8gU3dpZnQgUGFja2FnZSBNYW5hZ2VyIG9uIGlPUywgYWRkcyBhIFN5c3RlbSBCYXJzIHBsdWdpbiBmb3IgQW5kcm9pZCBlZGdlLXRvLWVkZ2UgYW5kIHJhaXNlcyBldmVyeSBtaW5pbXVtLiBIZXJlIGlzIHRoZSB1cGdyYWRlLg%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA4IG1pbiByZWFk%2Ctint_lilac%2Cp_Ii9jb21tdW5pdHkvYmxvZy9jYXBhY2l0b3ItOC1zd2lmdC1wYWNrYWdlLW1hbmFnZXItYW5kLXRoZS11cGdyYWRlIg%2Cs_ftZkJjxqQrcJQVga.png"}]}
```