插件作者適用的 Swift Package Manager 指南
如何為 iOS 和 macOS 插件新增 Swift Package Manager 相容性
如需了解如何開啟或關閉 SwiftPM,請參閱 應用程式開發者適用的 Swift Package Manager 指南。
如何為現有的 Flutter 插件新增 Swift Package Manager 支援
#本指南說明如何為已支援 CocoaPods 的插件新增 Swift Package Manager 支援, 以確保該插件可供所有 Flutter 專案使用。
Flutter 插件應在另行通知前同時支援 Swift Package Manager 和 CocoaPods。
Swift Package Manager 的採用將會是漸進式的。 自 Flutter 3.44 起,不支援 CocoaPods 的插件 無法在尚未遷移至 Swift Package Manager 的專案中使用。 不支援 Swift Package Manager 的插件 可能會對已完成遷移的專案造成問題。 請盡快遷移您的插件。
在本指南中,請將 plugin_name 替換為您的插件名稱。
以下範例使用 ios,請依實際情況替換為 macos 或 darwin。
確認您使用的是 Flutter 3.44 或更新版本,此版本預設啟用 SwiftPM。
-
首先在
ios、macos及/或darwin目錄下建立一個子目錄。 將這個新目錄命名為平台套件的名稱。plugin_name/
ios/
- …
- plugin_name/
-
在這個新目錄中,建立以下檔案/目錄:
Package.swift(檔案)Sources(目錄)Sources/plugin_name(目錄)
您的插件結構應如下所示:
plugin_name/
ios/
- …
plugin_name/
- Package.swift
Sources/
- plugin_name/
-
在
Package.swift檔案中使用以下範本:Package.swiftswift// swift-tools-version: 5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( // TODO: Update your plugin name. name: "plugin_name", platforms: [ // TODO: Update the platforms your plugin supports. // If your plugin only supports iOS, remove `.macOS(...)`. // If your plugin only supports macOS, remove `.iOS(...)`. .iOS("13.0"), .macOS("10.15") ], products: [ // TODO: Update your library and target names. // If the plugin name contains "_", replace with "-" for the library name. .library(name: "plugin-name", targets: ["plugin_name"]) ], dependencies: [ .package(name: "FlutterFramework", path: "../FlutterFramework") ], targets: [ .target( // TODO: Update your target name. name: "plugin_name", dependencies: [ .product(name: "FlutterFramework", package: "FlutterFramework") ], resources: [ // TODO: If your plugin requires a privacy manifest // (e.g. if it uses any required reason APIs), update the PrivacyInfo.xcprivacy file // to describe your plugin's privacy impact, and then uncomment this line. // For more information, visit: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files // .process("PrivacyInfo.xcprivacy"), // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package ] ) ] ) -
在
Package.swift檔案中更新支援的平台。Package.swiftswiftplatforms: [ // TODO: Update the platforms your plugin supports. // If your plugin only supports iOS, remove `.macOS(...)`. // If your plugin only supports macOS, remove `.iOS(...)`. .iOS("13.0"), .macOS("10.15") ], -
在
Package.swift檔案中更新套件、程式庫及目標名稱。Package.swiftswiftlet package = Package( // TODO: Update your plugin name. name: "plugin_name", platforms: [ .iOS("13.0"), .macOS("10.15") ], products: [ // TODO: Update your library and target names. // If the plugin name contains "_", replace with "-" for the library name .library(name: "plugin-name", targets: ["plugin_name"]) ], dependencies: [], targets: [ .target( // TODO: Update your target name. name: "plugin_name", dependencies: [], resources: [ // TODO: If your plugin requires a privacy manifest // (e.g. if it uses any required reason APIs), update the PrivacyInfo.xcprivacy file // to describe your plugin's privacy impact, and then uncomment this line. // For more information, visit: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files // .process("PrivacyInfo.xcprivacy"), // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package ] ) ] ) -
若您的插件有
PrivacyInfo.xcprivacy檔案,請將其移至ios/plugin_name/Sources/plugin_name/PrivacyInfo.xcprivacy,並在Package.swift檔案中取消對應資源的註解。Package.swiftswiftresources: [ // TODO: If your plugin requires a privacy manifest // (e.g. if it uses any required reason APIs), update the PrivacyInfo.xcprivacy file // to describe your plugin's privacy impact, and then uncomment this line. // For more information, visit: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files .process("PrivacyInfo.xcprivacy"), // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package ], -
將
ios/Assets中的資源檔移至ios/plugin_name/Sources/plugin_name(或其子目錄)。 如有必要,將資源檔加入Package.swift檔案。 更多操作說明請參考 使用 Swift 套件捆綁資源。
將
ios/Classes中的所有檔案移至ios/plugin_name/Sources/plugin_name。-
將
FlutterFramework新增為相依套件,並更新 Dart 和 Flutter 版本。更新
Package.swift以包含FlutterFramework:Package.swiftswiftdependencies: [ .package(name: "FlutterFramework", path: "../FlutterFramework") ], targets: [ .target( // TODO: Update your target name. name: "plugin_name", dependencies: [ .product(name: "FlutterFramework", package: "FlutterFramework") ],在
pubspec.yaml中,將版本更新為:pubspec.yamlyamlenvironment: sdk: ^3.11.0 flutter: ">=3.41.0" ios/Assets、ios/Resources和ios/Classes目錄現在應該是空的,可以刪除。-
若您的插件使用 Pigeon,請更新 Pigeon 輸入檔案。
pigeons/messages.dartdartkotlinOptions: KotlinOptions(), javaOut: 'android/app/src/main/java/io/flutter/plugins/Messages.java', javaOptions: JavaOptions(), swiftOut: 'ios/Classes/messages.g.swift', swiftOut: 'ios/plugin_name/Sources/plugin_name/messages.g.swift', swiftOptions: SwiftOptions(), -
依需求更新
Package.swift檔案中的自訂設定。在 Xcode 中,開啟
ios/plugin_name/目錄。在 Xcode 中,開啟您的
Package.swift檔案。 確認 Xcode 對此檔案不產生任何警告或錯誤。若您的
ios/plugin_name.podspec檔案有 CocoaPodsdependency, 請在Package.swift檔案中加入對應的 Swift Package Manager 相依套件。若您的套件必須明確指定為
static或dynamic連結 (Apple 不建議此做法),請更新 Product 以定義類型:Package.swiftswiftproducts: [ .library(name: "plugin-name", type: .static, targets: ["plugin_name"]) ],進行其他自訂設定。如需更多關於如何撰寫
Package.swift檔案的資訊, 請參考PackageDescription。
-
更新
ios/plugin_name.podspec以指向新路徑。ios/plugin_name.podspecrubys.source_files = 'Classes/**/*.swift' s.resource_bundles = {'plugin_name_privacy' => ['Resources/PrivacyInfo.xcprivacy']} s.source_files = 'plugin_name/Sources/plugin_name/**/*.swift' s.resource_bundles = {'plugin_name_privacy' => ['plugin_name/Sources/plugin_name/PrivacyInfo.xcprivacy']} -
更新從 bundle 載入資源的方式,改用
Bundle.module。swift#if SWIFT_PACKAGE let settingsURL = Bundle.module.url(forResource: "image", withExtension: "jpg") #else let settingsURL = Bundle(for: Self.self).url(forResource: "image", withExtension: "jpg") #endif -
若您的
.gitignore尚未包含.build/和.swiftpm/目錄, 請更新.gitignore加入以下內容:.gitignore.build/ .swiftpm/將插件的變更提交至您的版本控制系統。
-
確認插件仍可在 CocoaPods 下正常運作。
關閉 Swift Package Manager。
shflutter config --no-enable-swift-package-manager切換至插件的範例應用程式目錄。
shcd path/to/plugin/example/確認插件的範例應用程式可以建置並執行。
shflutter run切換至插件的頂層目錄。
shcd path/to/plugin/執行 CocoaPods 驗證 lint。
shpod lib lint ios/plugin_name.podspec --configuration=Debug --skip-tests --use-modular-headers --use-librariesshpod lib lint ios/plugin_name.podspec --configuration=Debug --skip-tests --use-modular-headers
-
確認插件可在 Swift Package Manager 下正常運作。
開啟 Swift Package Manager。
shflutter config --enable-swift-package-manager切換至插件的範例應用程式目錄。
shcd path/to/plugin/example/確認插件的範例應用程式可以建置並執行。
shflutter run在 Xcode 中,開啟插件的範例應用程式。 確認左側專案導覽器(Project Navigator)中顯示了 Package Dependencies。
-
確認測試通過。
若您的插件有原生單元測試(XCTest),請確保您也 更新了插件範例應用程式中的單元測試。
請依照測試插件的操作說明進行。
在整份指南中,請將 plugin_name 替換為你的插件名稱。
以下範例使用 ios,請依實際情況替換為 macos 或 darwin。
確認你目前執行的是 Flutter 3.44 或更新版本。此版本預設啟用 SwiftPM。
-
首先,在
ios、macos和/或darwin目錄下建立一個子目錄。 將這個新目錄命名為平台套件的名稱。plugin_name/
ios/
- …
- plugin_name/
-
在這個新目錄中,建立以下檔案/目錄:
Package.swift(檔案)Sources(目錄)Sources/plugin_name(目錄)Sources/plugin_name/include(目錄)Sources/plugin_name/include/plugin_name(目錄)Sources/plugin_name/include/plugin_name/.gitkeep(檔案)- 此檔案確保該目錄會被提交。
若目錄中已新增其他檔案,可移除
.gitkeep檔案。
- 此檔案確保該目錄會被提交。
若目錄中已新增其他檔案,可移除
你的插件結構應如下所示:
plugin_name/
ios/
- …
plugin_name/
- Package.swift
Sources/plugin_name/include/plugin_name/
- .gitkeep/
-
在
Package.swift檔案中使用以下範本:Package.swiftswift// swift-tools-version: 5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( // TODO: Update your plugin name. name: "plugin_name", platforms: [ // TODO: Update the platforms your plugin supports. // If your plugin only supports iOS, remove `.macOS(...)`. // If your plugin only supports macOS, remove `.iOS(...)`. .iOS("13.0"), .macOS("10.15") ], products: [ // TODO: Update your library and target names. // If the plugin name contains "_", replace with "-" for the library name .library(name: "plugin-name", targets: ["plugin_name"]) ], dependencies: [], targets: [ .target( // TODO: Update your target name. name: "plugin_name", dependencies: [], resources: [ // TODO: If your plugin requires a privacy manifest // (in other words, if it uses any required reason APIs), // update the PrivacyInfo.xcprivacy file // to describe your plugin's privacy impact, and then uncomment this line. // For more information, visit: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files // .process("PrivacyInfo.xcprivacy"), // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package ], cSettings: [ // TODO: Update your plugin name. .headerSearchPath("include/plugin_name") ] ) ] ) -
更新
Package.swift檔案中的支援平台。Package.swiftswiftplatforms: [ // TODO: Update the platforms your plugin supports. // If your plugin only supports iOS, remove `.macOS(...)`. // If your plugin only supports macOS, remove `.iOS(...)`. .iOS("13.0"), .macOS("10.15") ], -
更新
Package.swift檔案中的套件、程式庫與目標名稱。Package.swiftswiftlet package = Package( // TODO: Update your plugin name. name: "plugin_name", platforms: [ .iOS("13.0"), .macOS("10.15") ], products: [ // TODO: Update your library and target names. // If the plugin name contains "_", replace with "-" for the library name .library(name: "plugin-name", targets: ["plugin_name"]) ], dependencies: [], targets: [ .target( // TODO: Update your target name. name: "plugin_name", dependencies: [], resources: [ // TODO: If your plugin requires a privacy manifest // (for example, if it uses any required reason APIs), // update the PrivacyInfo.xcprivacy file // to describe your plugin's privacy impact, and then uncomment this line. // For more information, visit: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files // .process("PrivacyInfo.xcprivacy"), // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package ], cSettings: [ // TODO: Update your plugin name. .headerSearchPath("include/plugin_name") ] ) ] ) -
若你的插件有
PrivacyInfo.xcprivacy檔案, 請將其移至ios/plugin_name/Sources/plugin_name/PrivacyInfo.xcprivacy, 並取消Package.swift檔案中對應資源的註解。Package.swiftswiftresources: [ // TODO: If your plugin requires a privacy manifest // (for example, if it uses any required reason APIs), // update the PrivacyInfo.xcprivacy file // to describe your plugin's privacy impact, and then uncomment this line. // For more information, visit: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files .process("PrivacyInfo.xcprivacy"), // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package ], -
將
ios/Assets中的資源檔移至ios/plugin_name/Sources/plugin_name(或其子目錄)。 若適用,請將資源檔加入Package.swift檔案。 更多操作說明請參閱 https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package。 -
將
ios/Classes中的所有公開標頭移至ios/plugin_name/Sources/plugin_name/include/plugin_name。若不確定哪些標頭是公開的,請查看
podspec檔案的public_header_files屬性。 若未指定此屬性,則所有標頭均為公開。 建議你考慮是否真的需要將所有標頭設為公開。在
pubspec.yaml中定義的pluginClass必須為公開, 且須位於此目錄中。
-
處理
modulemap。若你的插件沒有
modulemap,請跳過此步驟。若你使用
modulemap讓 CocoaPods 建立 Test 子模組, 可考慮在 Swift Package Manager 中將其移除。 請注意,這會讓所有公開標頭都可透過模組存取。若要在 Swift Package Manager 中移除
modulemap但保留 CocoaPods 的設定, 請在插件的Package.swift檔案中排除modulemap和 umbrella header。以下範例假設
modulemap和 umbrella header 位於ios/plugin_name/Sources/plugin_name/include目錄中。Package.swiftswift.target( name: "plugin_name", dependencies: [], exclude: ["include/cocoapods_plugin_name.modulemap", "include/plugin_name-umbrella.h"],若你希望單元測試同時相容 CocoaPods 和 Swift Package Manager, 可嘗試以下做法:
Tests/TestFile.mobjc@import plugin_name; @import plugin_name.Test; #if __has_include(<plugin_name/plugin_name-umbrella.h>) @import plugin_name.Test; #endif若要在 Swift 套件中使用自訂
modulemap, 請參閱 Swift Package Manager 的說明文件。 -
將
ios/Classes中所有剩餘的檔案移至ios/plugin_name/Sources/plugin_name。 -
ios/Assets、ios/Resources和ios/Classes目錄現在應為空, 可以刪除。 -
若標頭檔案不再與實作檔案位於同一目錄, 請更新你的 import 陳述式。
舉例來說,假設有以下遷移情境:
遷移前:
ios/Classes/ ├── PublicHeaderFile.h └── ImplementationFile.m遷移後:
ios/plugin_name/Sources/plugin_name/ └── include/plugin_name/ └── PublicHeaderFile.h └── ImplementationFile.m
在此範例中,
ImplementationFile.m中的 import 陳述式應更新為:Sources/plugin_name/ImplementationFile.mobjc#import "PublicHeaderFile.h" #import "./include/plugin_name/PublicHeaderFile.h" -
若你的插件使用 Pigeon,請更新 Pigeon 輸入檔案。
pigeons/messages.dartdartjavaOptions: JavaOptions(), objcHeaderOut: 'ios/Classes/messages.g.h', objcSourceOut: 'ios/Classes/messages.g.m', objcHeaderOut: 'ios/plugin_name/Sources/plugin_name/messages.g.h', objcSourceOut: 'ios/plugin_name/Sources/plugin_name/messages.g.m', copyrightHeader: 'pigeons/copyright.txt',若
objcHeaderOut檔案不再與objcSourceOut位於同一目錄, 可透過ObjcOptions.headerIncludePath修改#import:pigeons/messages.dartdartjavaOptions: JavaOptions(), objcHeaderOut: 'ios/Classes/messages.g.h', objcSourceOut: 'ios/Classes/messages.g.m', objcHeaderOut: 'ios/plugin_name/Sources/plugin_name/include/plugin_name/messages.g.h', objcSourceOut: 'ios/plugin_name/Sources/plugin_name/messages.g.m', objcOptions: ObjcOptions( headerIncludePath: './include/plugin_name/messages.g.h', ), copyrightHeader: 'pigeons/copyright.txt',執行 Pigeon 以依最新設定重新產生程式碼。
-
依需求更新
Package.swift檔案中的自訂設定。在 Xcode 中開啟
ios/plugin_name/目錄。在 Xcode 中開啟
Package.swift檔案。 確認 Xcode 對此檔案不會產生任何警告或錯誤。若你的
ios/plugin_name.podspec檔案有 CocoaPodsdependency, 請將對應的 Swift Package Manager dependencies 加入Package.swift檔案。若套件必須明確指定連結方式為
static或dynamic(Apple 不建議此做法),請更新 Product 以定義類型:Package.swiftswiftproducts: [ .library(name: "plugin-name", type: .static, targets: ["plugin_name"]) ],進行其他自訂設定。如需了解如何撰寫
Package.swift檔案的詳細資訊,請參閱 https://developer.apple.com/documentation/packagedescription。
-
更新
ios/plugin_name.podspec以指向新路徑。ios/plugin_name.podspecrubys.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/cocoapods_plugin_name.modulemap' s.resource_bundles = {'plugin_name_privacy' => ['Resources/PrivacyInfo.xcprivacy']} s.source_files = 'plugin_name/Sources/plugin_name/**/*.{h,m}' s.public_header_files = 'plugin_name/Sources/plugin_name/include/**/*.h' s.module_map = 'plugin_name/Sources/plugin_name/include/cocoapods_plugin_name.modulemap' s.resource_bundles = {'plugin_name_privacy' => ['plugin_name/Sources/plugin_name/PrivacyInfo.xcprivacy']} -
更新從 bundle 載入資源的方式,改用
SWIFTPM_MODULE_BUNDLE:objc#if SWIFT_PACKAGE NSBundle *bundle = SWIFTPM_MODULE_BUNDLE; #else NSBundle *bundle = [NSBundle bundleForClass:[self class]]; #endif NSURL *imageURL = [bundle URLForResource:@"image" withExtension:@"jpg"]; -
若
ios/plugin_name/Sources/plugin_name/include目錄中 只包含.gitkeep,請更新.gitignore加入以下內容:.gitignore!.gitkeep執行
flutter pub publish --dry-run以確認include目錄 會被發布。 將插件的變更提交至版本控制系統。
-
驗證插件仍可與 CocoaPods 正常運作。
關閉 Swift Package Manager:
shflutter config --no-enable-swift-package-manager切換至插件的範例應用程式目錄。
shcd path/to/plugin/example/確認插件的範例應用程式可正常建置與執行。
shflutter run切換至插件的頂層目錄。
shcd path/to/plugin/執行 CocoaPods 驗證 lint:
shpod lib lint ios/plugin_name.podspec --configuration=Debug --skip-tests --use-modular-headers --use-librariesshpod lib lint ios/plugin_name.podspec --configuration=Debug --skip-tests --use-modular-headers
-
驗證插件可與 Swift Package Manager 正常運作。
開啟 Swift Package Manager:
shflutter config --enable-swift-package-manager切換至插件的範例應用程式目錄。
shcd path/to/plugin/example/確認插件的範例應用程式可正常建置與執行。
shflutter run在 Xcode 中開啟插件的範例應用程式。 確認左側 Project Navigator 中顯示 Package Dependencies。
-
驗證測試通過。
若你的插件有原生單元測試(XCTest),請務必同時 更新插件範例應用程式中的單元測試。
依照測試插件的說明操作。
(可選,但建議)將插件作為本機套件新增至範例應用程式
#若您的插件包含範例應用程式, 建議將插件作為本機套件新增至範例應用程式中。 雖然這並非必要,但在範例應用程式中編輯插件原始碼時, 可提供更好的 Xcode 支援。 請參閱 issue #179032。
將插件新增為本機套件
#在終端機中切換至
my_plugin目錄。-
在 Xcode 中,執行以下指令以開啟範例應用程式的工作區 (視需要將
ios替換為macos):
open example/ios/Runner.xcworkspace
-
右鍵點擊 Flutter > Add Files to "Runner"。

-
選取
my_plugin/ios/my_plugin(或視需要選macos或darwin)。 -
確認已選取 "Reference files in place" (這應為預設選項),然後點擊 Finish。

這會將插件新增為本機套件, 但它是以絕對路徑參照, 這在散佈時並不理想。 若要將其改為相對路徑,請依照以下說明操作。
改為相對路徑
#-
從 File Inspector 複製插件的「Full Path」。

-
在終端機中執行:
open -a Xcode example/ios/Runner.xcodeproj/project.pbxproj -
找到以下內容:
path = [COPIED FULL PATH]; sourceTree = "<absolute>"例如:
path = /Users/username/path/to/my_plugin/ios/my_plugin; sourceTree = "<absolute>" -
並將其替換為相對路徑:
path = ../../ios/my_plugin; sourceTree = "<group>"(視需要將
ios調整為macos或darwin)。
如何更新插件範例應用程式中的單元測試
#若您的插件含有原生 XCTests, 且以下任一條件成立, 您可能需要更新這些測試以配合 Swift Package Manager:
- 您在測試中使用了 CocoaPod 相依套件。
- 您在插件的
Package.swift檔案中明確將其設定為type: .dynamic。
若要更新單元測試:
在 Xcode 中開啟
example/ios/Runner.xcworkspace。-
若您在測試中使用了 CocoaPod 相依套件(例如
OCMock), 請將其從Podfile檔案中移除。ios/Podfilerubytarget 'RunnerTests' do inherit! :search_paths pod 'OCMock', '3.5' end接著在終端機中,於
plugin_name_ios/example/ios目錄下執行pod install。 -
在專案中切換至 Package Dependencies。

專案的套件相依性
-
點擊 + 按鈕,並在右上角的搜尋列中搜尋 僅供測試使用的相依套件並加以新增。

搜尋僅供測試使用的相依套件
-
確認相依套件已新增至
RunnerTests目標 (Target)。
確認相依套件已新增至
RunnerTests目標 點擊 Add Package 按鈕。
-
若您已在插件的
Package.swift檔案中明確將函式庫類型設定為.dynamic(Apple 不建議此做法), 您還需要將其作為相依套件新增至RunnerTests目標。確認
RunnerTests的 Build Phases 中包含 Link Binary With Libraries 建置階段:
RunnerTests目標中的Link Binary With Libraries建置階段若該建置階段尚不存在,請建立一個。 點擊 add 按鈕, 然後點擊 New Link Binary With Libraries Phase。

新增
Link Binary With Libraries建置階段在專案中切換至 Package Dependencies。
點擊 add 按鈕。
在開啟的對話框中,點擊 Add Local... 按鈕。
切換至
plugin_name/plugin_name_ios/ios/plugin_name_ios並點擊 Add Package 按鈕。確認已將其新增至
RunnerTests目標,然後點擊 Add Package 按鈕。
確認測試通過:Product > Test。
Unless stated otherwise, the documentation on this site reflects Flutter 3.44.0. Page last updated on 2026-06-14. View source or report an issue.