flutter
flutter copied to clipboard
Add Swift Package Manager as new opt-in feature for iOS and macOS
This PR adds initial support for Swift Package Manager (SPM). Users must opt in. Only compatible with Xcode 15+.
Fixes https://github.com/flutter/flutter/issues/146369.
Included Features
This PR includes the following features:
- Enabling SPM via config
flutter config --enable-swift-package-manager - Disabling SPM via config (will disable for all projects)
flutter config --no-enable-swift-package-manager - Disabling SPM via pubspec.yaml (will disable for the specific project)
flutter:
disable-swift-package-manager: true
- Migrating existing apps to add SPM integration if using a Flutter plugin with a Package.swift
- Generates a Swift Package (named
FlutterGeneratedPluginSwiftPackage) that handles Flutter SPM-compatible plugin dependencies. Generated package is added to the Xcode project.
- Generates a Swift Package (named
- Error parsing of common errors that may occur due to using CocoaPods and Swift Package Manager together
- Tool will print warnings when using all Swift Package plugins and encourage you to remove CocoaPods
This PR also converts integration_test and integration_test_macos plugins to be both Swift Packages and CocoaPod Pods.
How it Works
The Flutter CLI will generate a Swift Package called FlutterGeneratedPluginSwiftPackage, which will have local dependencies on all Swift Package compatible Flutter plugins.
The FlutterGeneratedPluginSwiftPackage package will be added to the Xcode project via altering of the project.pbxproj.
In addition, a "Pre-action" script will be added via altering of the Runner.xcscheme. This script will invoke the flutter tool to copy the Flutter/FlutterMacOS framework to the BUILT_PRODUCTS_DIR directory before the build starts. This is needed because plugins need to be linked to the Flutter framework and fortunately Swift Package Manager automatically uses BUILT_PRODUCTS_DIR as a framework search path.
CocoaPods will continue to run and be used to support non-Swift Package compatible Flutter plugins.
Not Included Features
It does not include the following (will be added in future PRs):
- Create plugin template
- Create app template
- Add-to-App integration
Pre-launch Checklist
- [x] I read the Contributor Guide and followed the process outlined there for submitting PRs.
- [x] I read the Tree Hygiene wiki page, which explains my responsibilities.
- [x] I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
- [x] I signed the CLA.
- [x] I listed at least one issue that this PR fixes in the description above.
- [x] I updated/added relevant documentation (doc comments with
///). - [x] I added new tests to check the change I am making, or this PR is test-exempt.
- [x] I followed the breaking change policy and added Data Driven Fixes where supported.
- [x] All existing and new tests are passing.
If you need help, consider asking for advice on the #hackers-new channel on Discord.
Instructions on how to use Swift Package Manager - WIP (Not final)
Enable Swift Package Manager
Enable Swift Package Manager
Run the following command:
flutter config --enable-swift-package-manager
Disable Swift Package Manager
Disable Swift Package Manager
Disabling Swift Package Manager will cause Flutter to use CocoaPods for all dependencies. However, Swift Package Manager will remain intregrated with your project. To remove integration, follow "How to remove Swift Package Manager integration" instructions below.
Disable for a single project
In the project's pubspec.yaml, under the flutter section, add disable-swift-package-manager: true.
# The following section is specific to Flutter packages.
flutter:
disable-swift-package-manager: true
Disable globally for all projects
Run the following command:
flutter config --no-enable-swift-package-manager
Converting an existing Objective-C Flutter Plugin to a Swift Package
Converting an existing Objective-C Flutter Plugin to a Swift Package
Replace plugin_name throughout this guide with the name of your plugin.
- Start by creating a directory under the
ios,macos, and/ordarwindirectories. Name this new directory the name of the platform package. The below example usesios, replaceioswithmacos/darwinif applicable.
/plugin_name/plugin_name_ios/ios/plugin_name_ios
- Within this new directory, create the following files/directories:
- Package.swift (file)
- Sources (directory)
- Sources/plugin_name_ios (directory)
- Sources/plugin_name_ios/include (directory)
- Sources/plugin_name_ios/include/plugin_name_ios (directory)
- Sources/plugin_name_ios/include/plugin_name_ios/.gitkeep (file)
- Needed to ensure the directory is committed, even if empty. Can be removed if files are added to the directory.
/plugin_name/plugin_name_ios/ios/plugin_name_ios/Package.swift /plugin_name/plugin_name_ios/ios/plugin_name_ios/Sources /plugin_name/plugin_name_ios/ios/plugin_name_ios/Sources/plugin_name_ios /plugin_name/plugin_name_ios/ios/plugin_name_ios/Sources/plugin_name_ios/include /plugin_name/plugin_name_ios/ios/plugin_name_ios/Sources/plugin_name_ios/include/plugin_name_ios /plugin_name/plugin_name_ios/ios/plugin_name_ios/Sources/plugin_name_ios/include/plugin_name_ios/.gitkeep
- Use the following template in the
Package.swift
// 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(
name: "plugin_name_ios",
platforms: [
.iOS("12.0"),
.macOS("10.14")
],
products: [
// If the plugin name contains "_", replace with "-" for the library name
.library(name: "plugin-name-ios", targets: ["plugin_name_ios"])
],
dependencies: [],
targets: [
.target(
name: "plugin_name_ios",
dependencies: [],
resources: [
// 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 these lines. For more information, see
// https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
// .process("PrivacyInfo.xcprivacy"),
// 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: [
.headerSearchPath("include/plugin_name_ios")
]
)
]
)
- If the plugin name contains
_, the library name must be a-separated version of the plugin name.
- If your plugin has a
PrivacyInfo.xcprivacy, move it toSources/plugin_name_ios/PrivacyInfo.xcprivacyand uncomment the resource in the Package.swift.
resources: [
// 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 these lines. For more information, see
// https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
- // .process("PrivacyInfo.xcprivacy"),
+ .process("PrivacyInfo.xcprivacy"),
// 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
],
-
Move any resource files from
ios/AssetstoSources/plugin_name_ios(or a subdirectory). Then add them to your Package.swift if applicable. See https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package for more instructions. -
Move any public headers from
ios/ClassestoSources/plugin_name_ios/include/plugin_name_ios- If you're unsure what headers are public, check your
podspecforpublic_header_files. If not found, that means all of your headers were public. You should consider whether or not you want all of your headers to be public. - The
pluginClassdefined in your pubspec.yaml must be public and within this directory.
- If you're unsure what headers are public, check your
-
Handling modulemap (skip this step if not using a custom modulemap)
If you're using a modulemap for CocoaPods to create a Test submodule, consider removing it for Swift Package Manager. Note that this will make all public headers available via the module.
To remove it for Swift Package Manager but keep it for CocoaPods, exclude the modulemap and umbrella header in the plugin's Package.swift. The example below assumes they are located within the
Sources/plugin_name_iosdirectory..target( name: "plugin_name_ios", dependencies: [], + exclude: ["cocoapods_plugin_name_ios.modulemap", "plugin_name_ios-umbrella.h"],If you want to keep your unit tests compatible with both CocoaPods and Swift Package Manager, you can try the following:
@import plugin_name_ios; - @import plugin_name_ios.Test; + #if __has_include(<plugin_name_ios/plugin_name_ios-umbrella.h>) + @import plugin_name_ios.Test; + #endifIf you would like to continue using a custom modulemap, please refer to Swift Package Manager's documentation.
-
Move all remaining files from
ios/ClassestoSources/plugin_name_ios -
ios/Assets,ios/Resources,ios/Classesshould now be empty and can be deleted -
If your header files were previously within the same directory as your implementation files, you may need to change your import statements.
For example, if the following changes were made:
* ios/Classes/PublicHeaderFile.h --> Sources/plugin_name_ios/include/plugin_name_ios/PublicHeaderFile.h
* ios/Classes/ImplementationFile.m --> Sources/plugin_name_ios/ImplementationFile.m
Within ImplementationFile.m, the import would change:
- #import "PublicHeaderFile.h"
+ #import "./include/plugin_name_ios/PublicHeaderFile.h"
- If using pigeon, you'll want to update your pigeon input file
- objcHeaderOut: 'ios/Classes/messages.g.h',
+ objcHeaderOut: 'ios/plugin_name_ios/Sources/plugin_name_ios/messages.g.h',
- objcSourceOut: 'ios/Classes/messages.g.m',
+ objcSourceOut: 'ios/plugin_name_ios/Sources/plugin_name_ios/messages.g.m',
If your objcHeaderOut file is no longer within the same directory as the objcSourceOut, you can change the #import using ObjcOptions.headerIncludePath:
objcHeaderOut: 'ios/plugin_name_ios/Sources/plugin_name_ios/include/plugin_name_ios/messages.g.h',
objcSourceOut: 'ios/plugin_name_ios/Sources/plugin_name_ios/messages.g.m',
+ objcOptions: ObjcOptions(
+ headerIncludePath: './include/plugin_name_ios/messages.g.h',
+ ),
-
Update your Package.swift with any customizations you may need
- Open
/plugin_name/plugin_name_ios/ios/plugin_name_ios/in Xcode- If package does not show any files in Xcode, quit Xcode (Xcode > Quit Xcode) and reopen
- You don't need to edit your Package.swift through Xcode, but Xcode will provide helpful feedback
- If Xcode isn't updating after you make a change, try clicking File > Packages > Reset Package Caches
- Add dependencies
- If your package must be linked explicitly
staticordynamic(not recommended), update the Product to define the type
products: [ .library(name: "plugin-name-ios", type: .static, targets: ["plugin_name_ios"]) ],- Make any other customizations - see https://developer.apple.com/documentation/packagedescription for more info on how to write a Package.swift.
- If you add additional targets to your Package.swift, try to name them uniquely. If your target name conflicts with another target from another package, this can cause issues that may require manual intervention to be able to use your plugin.
- Open
-
Update your
plugin_name_ios.podspecto point to new paths.
- s.source_files = 'Classes/**/*.{h,m}'
+ s.source_files = 'plugin_name_ios/Sources/plugin_name_ios/**/*.{h,m}'
- s.public_header_files = 'Classes/**/*.h'
+ s.public_header_files = 'plugin_name_ios/Sources/plugin_name_ios/include/**/*.h'
- s.module_map = 'Classes/cocoapods_plugin_name_ios.modulemap'
+ s.module_map = 'plugin_name_ios/Sources/plugin_name_ios/include/cocoapods_plugin_name_ios.modulemap'
- s.resource_bundles = {'plugin_name_ios_privacy' => ['Resources/PrivacyInfo.xcprivacy']}
+ s.resource_bundles = {'plugin_name_ios_privacy' => ['plugin_name_ios/Sources/plugin_name_ios/PrivacyInfo.xcprivacy']}
- Update getting of resources from bundle to use
SWIFTPM_MODULE_BUNDLE
#if SWIFT_PACKAGE
NSBundle *bundle = SWIFTPM_MODULE_BUNDLE;
#else
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
#endif
NSURL *imageURL = [bundle URLForResource:@"image" withExtension:@"jpg"];
- Note:
SWIFTPM_MODULE_BUNDLEwill only work if there are actual resources (either defined in the Package.swift or automatically included by Xcode). Otherwise, it will fail.
- If your
plugin_name_ios/Sources/plugin_name_ios/includedirectory only contains a.gitkeep, you'll want update your.gitignoreto include the following:
!.gitkeep
Then run flutter pub publish --dry-run to ensure the include directory will be published.
-
Verify plugin still works with CocoaPods
- Disable Swift Package Manager
flutter config --no-enable-swift-package-manager- Run
flutter runwith the example app and ensure it builds and runs
-
Verify plugin works with Swift Package Manager
- Enable Swift Package Manager
flutter config --enable-swift-package-manager- Run
flutter runwith the example app and ensure it builds and runs - Open the example app in Xcode and ensure Package Dependencies show in the left Project Navigator
-
Verify tests pass
- If your plugin has Native unit tests (XCTest), make sure you also complete "Updating unit tests in plugin example app" below.
- Follow instructions for testing plugins
Converting an existing Swift Flutter Plugin to a Swift Package
Converting an existing Swift Flutter Plugin to a Swift Package
Replace plugin_name throughout this guide with the name of your plugin.
- Start by creating a directory under the
ios,macos, and/ordarwindirectories. Name this new directory the name of the platform package. The below example usesios, replaceioswithmacos/darwinif applicable.
/plugin_name/plugin_name_ios/ios/plugin_name_ios
- Within this new directory, create the following files/directories:
- Package.swift (file)
- Sources (directory)
- Sources/plugin_name_ios (directory)
/plugin_name/plugin_name_ios/ios/plugin_name_ios/Package.swift /plugin_name/plugin_name_ios/ios/plugin_name_ios/Sources /plugin_name/plugin_name_ios/ios/plugin_name_ios/Sources/plugin_name_ios
- Use the following template in the
Package.swift
// 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(
name: "plugin_name_ios",
platforms: [
.iOS("12.0"),
.macOS("10.14")
],
products: [
// If the plugin name contains "_", replace with "-" for the library name
.library(name: "plugin-name-ios", targets: ["plugin_name_ios"])
],
dependencies: [],
targets: [
.target(
name: "plugin_name_ios",
dependencies: [],
resources: [
// 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 these lines. For more information, see
// https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
// .process("PrivacyInfo.xcprivacy"),
// 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
]
)
]
)
- If the plugin name contains
_, the library name must be a-separated version of the plugin name.
- If your plugin has a
PrivacyInfo.xcprivacy, move it toSources/plugin_name_ios/PrivacyInfo.xcprivacyand uncomment the resource in the Package.swift.
resources: [
// 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 these lines. For more information, see
// https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
- // .process("PrivacyInfo.xcprivacy"),
+ .process("PrivacyInfo.xcprivacy"),
// 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
],
- Move any resource files from
ios/AssetstoSources/plugin_name_ios(or a subdirectory). Then add them to your Package.swift if applicable. See https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package for more instructions. - Move all files from
ios/ClassestoSources/plugin_name_ios ios/Assets,ios/Resources,ios/Classesshould now be empty and can be deleted- If using pigeon, you'll want to update your pigeon input file
- swiftOut: 'ios/Classes/messages.g.swift',
+ swiftOut: 'ios/plugin_name_ios/Sources/plugin_name_ios/messages.g.swift',
- Update your Package.swift with any customizations you may need
- Open
/plugin_name/plugin_name_ios/ios/plugin_name_ios/in Xcode- If package does not show any files in Xcode, quit Xcode (Xcode > Quit Xcode) and reopen
- You don't need to edit your Package.swift through Xcode, but Xcode will provide helpful feedback
- If Xcode isn't updating after you make a change, try clicking File > Packages > Reset Package Caches
- Add dependencies
- If your package must be linked explicitly
staticordynamic, update the Product to define the type
products: [ .library(name: "plugin-name-ios", type: .static, targets: ["plugin_name_ios"]) ],- Make any other customizations - see https://developer.apple.com/documentation/packagedescription for more info on how to write a Package.swift.
- If you add additional targets to your Package.swift, try to name them uniquely. If your target name conflicts with another target from another package, this can cause issues that may require manual intervention to be able to use your plugin.
- Open
- Update your
plugin_name_ios.podspecto point to new paths.
- s.source_files = 'Classes/**/*.swift'
+ s.source_files = 'plugin_name_ios/Sources/plugin_name_ios/**/*.swift'
- s.resource_bundles = {'plugin_name_ios_privacy' => ['Resources/PrivacyInfo.xcprivacy']}
+ s.resource_bundles = {'plugin_name_ios_privacy' => ['plugin_name_ios/Sources/plugin_name_ios/PrivacyInfo.xcprivacy']}
- Update getting of resources from bundle to use
Bundle.module
#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
- Note:
Bundle.modulewill only work if there are actual resources (either defined in the Package.swift or automatically included by Xcode). Otherwise, it will fail.
- Verify plugin still works with CocoaPods
- Disable Swift Package Manager
flutter config --no-enable-swift-package-manager- Run
flutter runwith the example app and ensure it builds and runs
- Verify plugin works with Swift Package Manager
- Enable Swift Package Manager
flutter config --enable-swift-package-manager- Run
flutter runwith the example app and ensure it builds and runs - Open the example app in Xcode and ensure Package Dependencies show in the left Project Navigator
- Verify tests pass
- If your plugin has Native unit tests (XCTest), make sure you also complete "Updating unit tests in plugin example app" below.
- Follow instructions for testing plugins
Updating unit tests in plugin example app
Updating unit tests in plugin example app
If your plugin has native XCTests, you may need to update them to work with Swift Package Manger if one of the following is true:
- You're using a CocoaPod dependency for the test
- Your plugin is explicitly set to
type: .dynamicin its Package.swift
- Open your
example/ios/Runner.xcworkspacein Xcode - If you were using a CocoaPod dependency for tests, such as
OCMock, you'll want to remove it from your Podfile
target 'RunnerTests' do
inherit! :search_paths
- pod 'OCMock', '3.5'
end`
Then in the terminal, run pod install in the plugin_name_ios/example/ios directory
- Navigate to Package Dependencies for the project
- Click the
+button and add any test-only dependencies by searching for them in the top right search bar.
Note: OCMock uses unsafe build flags and cant only be used if targeted by commit. fe1661a3efed11831a6452f4b1a0c5e6ddc08c3d is the commit for the 3.9.3 version.
- Ensure it is added to the
RunnerTestsTarget and clickAdd Packagebutton
-
If you've explicitly set your plugin's library type to
.dynamicin its Package.swift (not recommended), you'll also need to add it as a dependency to theRunnerTeststarget.-
First, ensure
RunnerTestshas aLink Binary With LibrariesBuild Phase -
If it does not already exist, create one by selecting the
+button and selectingNew Link Binary With Libraries Phase -
Navigate to Package Dependencies for the project
-
Click the
+button -
Click the
Add Local...button on the bottom of the dialog that opens -
Navigate to
plugin_name/plugin_name_ios/ios/plugin_name_iosand clickAdd Packagebutton -
Ensure it is added to the
RunnerTestsTarget and clickAdd Packagebutton
-
-
Ensure tests pass Product > Test
How to manually add Swift Package Manager integration to iOS project if Flutter CLI fails to automatically migrate
How to manually add Swift Package Manager integration to iOS project if Flutter CLI fails to automatically migrate
Please file a bug before manually migrating to help the Flutter team improve the automatic migration. Please include the error message you received and consider including a copy of the of the following files in your bug report:
- ios/Runner.xcodeproj/project.pbxproj
- ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (or the xcsheme for the flavor used)
Part 1: Add FlutterGeneratedPluginSwiftPackage Package Dependency
- Open your app (your_app/ios/Runner.xcworkspace) in Xcode
- Navigate to Package Dependencies for the project
- Click the
+button - Click the
Add Local...button on the bottom of the dialog that opens - Navigate to
your_app/ios/Flutter/Packages/FlutterGeneratedPluginSwiftPackageand clickAdd Packagebutton - Ensure it is added to the Runner Target and click
Add Packagebutton
- Ensure
FlutterGeneratedPluginSwiftPackagewas added to Frameworks, Libraries, and Embedded Content
Part 2: Add Run Prepare Flutter Framework Script Pre-Action
The following must be completed for each flavor.
- Next, select Product > Scheme > Edit Scheme
- Click the
>next to "Build" in the left side bar - Select Pre-actions
- Click the
+button and selectNew Run Script Actionfrom the menu - Click the "Run Script" title and change to
Run Prepare Flutter Framework Script. - Change the "Provide build settings from" to the app.
- Input the following in the text box:
/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare
Part 3: Run app
- Run the app in Xcode and ensure
FlutterGeneratedPluginSwiftPackageis a target dependency andRun Prepare Flutter Framework Scriptis being run as a pre-action.
- Also, ensure the app runs on the command line with
flutter run.
How to manually add Swift Package Manager integration to macOS project if Flutter CLI fails to automatically migrate
How to manually add Swift Package Manager integration to macOS project if Flutter CLI fails to automatically migrate
Please file a bug before manually migrating to help the Flutter team improve the automatic migration. Please include the error message you received and consider including a copy of the of the following files in your bug report:
- macos/Runner.xcodeproj/project.pbxproj
- macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (or the xcscheme for the flavor used)
Part 1: Add FlutterGeneratedPluginSwiftPackage Package Dependency
- Open your app (your_app/macos/Runner.xcworkspace) in Xcode
- Navigate to Package Dependencies for the project
- Click the
+button - Click the
Add Local...button on the bottom of the dialog that opens - Navigate to
your_app/macos/Flutter/Packages/FlutterGeneratedPluginSwiftPackageand clickAdd Packagebutton - Ensure it is added to the Runner Target and click
Add Packagebutton
- Ensure
FlutterGeneratedPluginSwiftPackagewas added to Frameworks, Libraries, and Embedded Content
Part 2: Add Run Prepare Flutter Framework Script Pre-Action
The following must be completed for each flavor.
- Next, select Product > Scheme > Edit Scheme
- Click the
>next to "Build" in the left side bar - Select Pre-actions
- Click the
+button and selectNew Run Script Actionfrom the menu - Click the "Run Script" title and change to
Run Prepare Flutter Framework Script. - Change the "Provide build settings from" to the Runner target.
- Input the following in the text box:
"$FLUTTER_ROOT"/packages/flutter_tools/bin/macos_assemble.sh prepare
Part 3: Run app
- Run the app in Xcode and ensure
FlutterGeneratedPluginSwiftPackageis a target dependency andRun Prepare Flutter Framework Scriptis being run as a pre-action.
- Also, ensure the app runs on the command line with
flutter run.
How to use a Swift Package Flutter plugin that requires a higher OS version
If a Swift Package Flutter plugin requires a higher OS version than the project, you may get an error like this:
Target Integrity (Xcode): The package product 'plugin_name_ios' requires minimum platform version 14.0 for the iOS platform, but this target supports 12.0
To still be able to use the plugin, you'll need to increase the Minimum Deployment of your project to match. Keep in mind, this will increase the minimum OS version that your app can run on.
How to add Swift Package Manager integration to a custom target
How to add Swift Package Manager integration to a custom target
Follow the steps in How to manually add Swift Package Manager integration to iOS/macOS project if Flutter CLI fails to automatically migrate.
In Part 1, Step 6 use your custom target instead of the Flutter target.
In Part 2, Step 6 use your custom target instead of the Flutter target.
How to remove Swift Package Manager integration
How to remove Swift Package Manager integration
- Disable Swift Package Manager (see "Disable Swift Package Manager" instructions above).
- Open your app (your_app/ios/Runner.xcworkspace) in Xcode
- Navigate to Package Dependencies for the project
- Click on the
FlutterGeneratedPluginSwiftPackagepackage and then click the-button
- Navigate to Frameworks, Libraries, and Embedded Content for the Runner target
- Click on
FlutterGeneratedPluginSwiftPackageand then click the-button
FAQS
Coming soon...
- Disabling SPM via pubspec.yaml (will disable for the specific project)
What's the use case for project-level control? It would be nice if we could minimize the number of knobs and switches that we have to support.
- Disabling SPM via pubspec.yaml (will disable for the specific project)
What's the use case for project-level control? It would be nice if we could minimize the number of knobs and switches that we have to support.
By default, it'll use CocoaPods and Swift Package Manager together, but they don't actually work together. For example, if a SPM-supported plugin and CocoaPod-only plugin is being used and both plugins have a same transitive dependency, it can cause errors about duplicate symbols or redefined modules. The only workaround then is to remove one of the plugins or fallback to just using CocoaPods only (aka via disabling SPM in the pubspec.yaml). Hypothetically could disable it globally, but that would be annoying to have to run the enable/disable command per project.
Ah right, I forgot about that limitation. Makes sense!
I converted url_launcher_ios following your excellent instructions (one minor issue, but that was entirely due to me missing part of a step), and it worked in a test project flawlessly. Manually verified end-to-end plugin functionality, and no Pods created!
@loic-sharma fyi if you're interested in the first draft of this.
@stuartmorgan I had to make a couple updates to the instructions.
-
Library names in the Package.swift manifest can't contain underscores, so I changed them to be hyphen separated.
Reason: If the package is linked dynamically, SPM automatically uses the library name as the
CFBundleIdentifier.The bundle ID string must contain only alphanumeric characters (A–Z, a–z, and 0–9), hyphens (-), and periods (.)
https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleidentifier
Apparently, it will cause error when you try to upload to app store: https://stackoverflow.com/questions/62477747/swift-package-manager-problem-with-bundle-ids
-
I decided to change the way we do units tests back to using modulemaps. Using a package's testTarget directly doesn't launch a simulator and install the app, and some of our tests assume that it does.
Also it seems some tests may require permissions from Info.plist, entitlements, etc which is again part of the app.
Also, this keeps any test-specific dependencies (like OCMock) out of the plugin itself - which will improve speeds to users consuming the plugin since it won’t download extra unneeded dependencies.
~~I updated the instructions to reflect these new changes, could you try it again when you have time? https://github.com/flutter/flutter/pull/146256#issuecomment-2038565077~~ JK, not ready for re-review yet
@vashworth I migrated file_selector_ios and file_selector_macos with the new instructions, and everything worked!
(I kept the weird structure where the headers that are supposed to be test-only are actually public; I don't remember why exactly we have to do that for our modulemap-based plugins, but it's a pre-existing problem, and longer term Swift migrations will eliminate the issue so I didn't worry about it.)
+1 to that! There's good test coverage so we're well positioned to iterate, all of my manual tests went smoothly, and we probably won't find weird edge-case bugs until we actually have some real-worth early adopters trying it.
Once it lands we can migrate our own plugins ASAP (I have a couple done locally, and should have time this week to do more), to accelerate real-world trials.
Once it lands we can migrate our own plugins ASAP (I have a couple done locally, and should have time this week to do more), to accelerate real-world trials.
@stuartmorgan I created https://github.com/flutter/flutter/issues/146922 to track migrating our plugins. I assigned you the ones you mentioned you've already worked on. I assigned myself ones I've already worked on. Once this lands, feel free to migrate any of them - just assign yourself so we don't duplicate work
Thanks, I was going to make that bugtree myself but you beat me to it :)