From fd4f99ccbccd099bd5e39e4e4661281c32287c3f Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 23 Apr 2026 14:09:14 +0200 Subject: [PATCH 1/2] fix(ios): support Cordova plugins with Package.swift --- cli/src/ios/update.ts | 27 ++++++++++++++++++++++++--- cli/src/tasks/migrate.ts | 2 +- cli/src/util/spm.ts | 19 ++++++++++++++++--- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index cd9c82a00d..2bb7eade47 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -12,12 +12,14 @@ import { getAllElements, getFilePath, getPlatformElement, + getPluginPlatform, getPluginType, getPlugins, printPlugins, } from '../plugin'; import type { Plugin } from '../plugin'; import { copy as copyTask } from '../tasks/copy'; +import { setAllStringIn } from '../tasks/migrate'; import { convertToUnixPath } from '../util/fs'; import { generateIOSPackageJSON } from '../util/iosplugin'; import { resolveNode } from '../util/node'; @@ -83,7 +85,20 @@ async function generateCordovaPackageFile(p: Plugin, config: Config) { publicHeadersPath: "."`; } - const content = `// swift-tools-version: 5.9 + const platformTag = getPluginPlatform(p, platform); + if (platformTag.$.package) { + const packageSwiftPath = join(p.rootPath, 'Package.swift'); + let content = await readFile(packageSwiftPath, { encoding: 'utf-8' }); + content = content.replace(`apache`, `ionic-team`).replaceAll(`cordova-ios`, `capacitor-swift-pm`); + content = setAllStringIn( + content, + `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, + `)`, + ` from: "${iosPlatformVersion}"`, + ); + await writeFile(packageSwiftPath, content); + } else { + const content = `// swift-tools-version: 5.9 import PackageDescription @@ -109,7 +124,9 @@ let package = Package( ) ] )`; - await writeFile(join(config.ios.cordovaPluginsDirAbs, 'sources', p.name, 'Package.swift'), content); + + await writeFile(join(config.ios.cordovaPluginsDirAbs, 'sources', p.name, 'Package.swift'), content); + } } export async function installCocoaPodsPlugins(config: Config, plugins: Plugin[], deployment: boolean): Promise { @@ -388,12 +405,16 @@ function getLinkerFlags(config: Config) { async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) { for (const p of cordovaPlugins) { + const platformTag = getPluginPlatform(p, platform); + if (platformTag.$.package) { + continue; + } const sourceFiles = getPlatformElement(p, platform, 'source-file'); const headerFiles = getPlatformElement(p, platform, 'header-file'); const codeFiles = sourceFiles.concat(headerFiles); const frameworks = getPlatformElement(p, platform, 'framework'); let sourcesFolderName = 'sources'; - if (needsStaticPod(p)) { + if ((await config.ios.packageManager) !== 'SPM' && needsStaticPod(p)) { sourcesFolderName += 'static'; } const sourcesFolder = join(config.ios.cordovaPluginsDirAbs, sourcesFolderName, p.name); diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index b2c419c080..8524ad7337 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -579,7 +579,7 @@ async function updateFile( return false; } -function setAllStringIn(data: string, start: string, end: string, replacement: string): string { +export function setAllStringIn(data: string, start: string, end: string, replacement: string): string { let position = 0; let result = data; let replaced = true; diff --git a/cli/src/util/spm.ts b/cli/src/util/spm.ts index 3e2e515293..4bdc15bb88 100644 --- a/cli/src/util/spm.ts +++ b/cli/src/util/spm.ts @@ -11,7 +11,7 @@ import { fatal } from '../errors'; import { getMajoriOSVersion } from '../ios/common'; import { logger } from '../log'; import type { Plugin } from '../plugin'; -import { getPluginType, PluginType } from '../plugin'; +import { getPluginPlatform, getPluginType, PluginType } from '../plugin'; import { runCommand } from '../util/subprocess'; export interface SwiftPlugin { @@ -118,7 +118,13 @@ let package = Package( for (const plugin of plugins) { if (getPluginType(plugin, config.ios.name) === PluginType.Cordova) { - packageSwiftText += `,\n .package(name: "${plugin.name}", path: "../../capacitor-cordova-ios-plugins/sources/${plugin.name}")`; + const platformTag = getPluginPlatform(plugin, config.ios.name); + if (platformTag.$.package) { + const relPath = relative(config.ios.nativeXcodeProjDirAbs, plugin.rootPath); + packageSwiftText += `,\n .package(name: "${plugin.id}", path: "${relPath}")`; + } else { + packageSwiftText += `,\n .package(name: "${plugin.name}", path: "../../capacitor-cordova-ios-plugins/sources/${plugin.name}")`; + } } else { const relPath = relative(config.ios.nativeXcodeProjDirAbs, plugin.rootPath); const traits = packageTraits[plugin.id]; @@ -144,7 +150,14 @@ let package = Package( .product(name: "Cordova", package: "capacitor-swift-pm")`; for (const plugin of plugins) { - packageSwiftText += `,\n .product(name: "${plugin.ios?.name}", package: "${plugin.ios?.name}")`; + let pluginText = `,\n .product(name: "${plugin.ios?.name}", package: "${plugin.ios?.name}")`; + if (getPluginType(plugin, config.ios.name) === PluginType.Cordova) { + const platformTag = getPluginPlatform(plugin, config.ios.name); + if (platformTag.$.package) { + pluginText = `,\n .product(name: "${plugin.id}", package: "${plugin.id}")`; + } + } + packageSwiftText += pluginText; } packageSwiftText += ` From 1ecf8c7af0a820ee35b6f4729ca6070e11ef61e9 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 27 Apr 2026 13:20:28 +0200 Subject: [PATCH 2/2] check for empty platform tag --- cli/src/ios/update.ts | 4 ++-- cli/src/util/spm.ts | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 2bb7eade47..14bab0da67 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -86,7 +86,7 @@ async function generateCordovaPackageFile(p: Plugin, config: Config) { } const platformTag = getPluginPlatform(p, platform); - if (platformTag.$.package) { + if (platformTag.$?.package) { const packageSwiftPath = join(p.rootPath, 'Package.swift'); let content = await readFile(packageSwiftPath, { encoding: 'utf-8' }); content = content.replace(`apache`, `ionic-team`).replaceAll(`cordova-ios`, `capacitor-swift-pm`); @@ -406,7 +406,7 @@ function getLinkerFlags(config: Config) { async function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) { for (const p of cordovaPlugins) { const platformTag = getPluginPlatform(p, platform); - if (platformTag.$.package) { + if (platformTag.$?.package) { continue; } const sourceFiles = getPlatformElement(p, platform, 'source-file'); diff --git a/cli/src/util/spm.ts b/cli/src/util/spm.ts index 4bdc15bb88..b04d288216 100644 --- a/cli/src/util/spm.ts +++ b/cli/src/util/spm.ts @@ -52,7 +52,6 @@ export async function generatePackageFile(config: Config, plugins: Plugin[]): Pr export async function checkPluginsForPackageSwift(config: Config, plugins: Plugin[]): Promise { const iOSCapacitorPlugins = plugins.filter((p) => getPluginType(p, 'ios') === PluginType.Core); - const packageSwiftPluginList = await pluginsWithPackageSwift(iOSCapacitorPlugins); if (plugins.length == packageSwiftPluginList.length) { @@ -119,7 +118,7 @@ let package = Package( for (const plugin of plugins) { if (getPluginType(plugin, config.ios.name) === PluginType.Cordova) { const platformTag = getPluginPlatform(plugin, config.ios.name); - if (platformTag.$.package) { + if (platformTag.$?.package) { const relPath = relative(config.ios.nativeXcodeProjDirAbs, plugin.rootPath); packageSwiftText += `,\n .package(name: "${plugin.id}", path: "${relPath}")`; } else { @@ -153,7 +152,7 @@ let package = Package( let pluginText = `,\n .product(name: "${plugin.ios?.name}", package: "${plugin.ios?.name}")`; if (getPluginType(plugin, config.ios.name) === PluginType.Cordova) { const platformTag = getPluginPlatform(plugin, config.ios.name); - if (platformTag.$.package) { + if (platformTag.$?.package) { pluginText = `,\n .product(name: "${plugin.id}", package: "${plugin.id}")`; } }