Skip to content

Conversation

@vashworth
Copy link
Contributor

@vashworth vashworth commented Apr 4, 2024

This PR adds initial support for Swift Package Manager (SPM). Users must opt in. Only compatible with Xcode 15+.

Fixes #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.
  • 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

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@github-actions github-actions bot added a: tests "flutter test", flutter_test, or one of our tests platform-ios iOS applications specifically tool Affects the "flutter" command-line tool. See also t: labels. framework flutter/packages/flutter repository. See also f: labels. a: desktop Running on desktop f: integration_test The flutter/packages/integration_test plugin labels Apr 4, 2024
@vashworth
Copy link
Contributor Author

vashworth commented Apr 5, 2024

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.

  1. Start by creating a directory under the ios, macos, and/or darwin directories. Name this new directory the name of the platform package. The below example uses ios, replace ios with macos/darwin if applicable.
/plugin_name/plugin_name_ios/ios/plugin_name_ios
  1. 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
  1. 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.
  1. If your plugin has a PrivacyInfo.xcprivacy, move it to Sources/plugin_name_ios/PrivacyInfo.xcprivacy and 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
            ],
  1. Move any resource files from ios/Assets to Sources/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.

  2. Move any public headers from ios/Classes to Sources/plugin_name_ios/include/plugin_name_ios

    • If you're unsure what headers are public, check your podspec for public_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 pluginClass defined in your pubspec.yaml must be public and within this directory.
  3. 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_ios directory.

            .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;
    + #endif

    If you would like to continue using a custom modulemap, please refer to Swift Package Manager's documentation.

  4. Move all remaining files from ios/Classes to Sources/plugin_name_ios

  5. ios/Assets, ios/Resources, ios/Classes should now be empty and can be deleted

  6. 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"
  1. 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',
+ ),
  1. Update your Package.swift with any customizations you may need

    1. 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
    2. Add dependencies
    3. If your package must be linked explicitly static or dynamic (not recommended), update the Product to define the type
    products: [
        .library(name: "plugin-name-ios", type: .static, targets: ["plugin_name_ios"])
    ],
    1. Make any other customizations - see https://developer.apple.com/documentation/packagedescription for more info on how to write a Package.swift.
    2. 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.
  2. Update your plugin_name_ios.podspec to 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']}
  1. 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"];
  1. If your plugin_name_ios/Sources/plugin_name_ios/include directory only contains a .gitkeep, you'll want update your .gitignore to include the following:
!.gitkeep

Then run flutter pub publish --dry-run to ensure the include directory will be published.

  1. Verify plugin still works with CocoaPods

    1. Disable Swift Package Manager
    flutter config --no-enable-swift-package-manager
    
    1. Run flutter run with the example app and ensure it builds and runs
  2. Verify plugin works with Swift Package Manager

    1. Enable Swift Package Manager
    flutter config --enable-swift-package-manager
    
    1. Run flutter run with the example app and ensure it builds and runs
    2. Open the example app in Xcode and ensure Package Dependencies show in the left Project Navigator
  3. Verify tests pass

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.

  1. Start by creating a directory under the ios, macos, and/or darwin directories. Name this new directory the name of the platform package. The below example uses ios, replace ios with macos/darwin if applicable.
/plugin_name/plugin_name_ios/ios/plugin_name_ios
  1. 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
  1. 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.
  1. If your plugin has a PrivacyInfo.xcprivacy, move it to Sources/plugin_name_ios/PrivacyInfo.xcprivacy and 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
            ],
  1. Move any resource files from ios/Assets to Sources/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.
  2. Move all files from ios/Classes to Sources/plugin_name_ios
  3. ios/Assets, ios/Resources, ios/Classes should now be empty and can be deleted
  4. 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',
  1. Update your Package.swift with any customizations you may need
    1. 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
    2. Add dependencies
    3. If your package must be linked explicitly static or dynamic, update the Product to define the type
    products: [
        .library(name: "plugin-name-ios", type: .static, targets: ["plugin_name_ios"])
    ],
    1. Make any other customizations - see https://developer.apple.com/documentation/packagedescription for more info on how to write a Package.swift.
    2. 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.
  2. Update your plugin_name_ios.podspec to 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']}
  1. 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
  1. Verify plugin still works with CocoaPods
    1. Disable Swift Package Manager
    flutter config --no-enable-swift-package-manager
    
    1. Run flutter run with the example app and ensure it builds and runs
  2. Verify plugin works with Swift Package Manager
    1. Enable Swift Package Manager
    flutter config --enable-swift-package-manager
    
    1. Run flutter run with the example app and ensure it builds and runs
    2. Open the example app in Xcode and ensure Package Dependencies show in the left Project Navigator
  3. Verify tests pass
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: .dynamic in its Package.swift
  1. Open your example/ios/Runner.xcworkspace in Xcode
  2. 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

  1. Navigate to Package Dependencies for the project

Screenshot 2024-04-05 at 10 13 56 AM

  1. Click the + button and add any test-only dependencies by searching for them in the top right search bar.

Screenshot 2024-04-09 at 3 11 21 PM

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.

  1. Ensure it is added to the RunnerTests Target and click Add Package button

Screenshot 2024-04-09 at 3 12 12 PM

  1. If you've explicitly set your plugin's library type to .dynamic in its Package.swift (not recommended), you'll also need to add it as a dependency to the RunnerTests target.

    1. First, ensure RunnerTests has a Link Binary With Libraries Build Phase
      Screenshot 2024-04-19 at 3 14 56 PM

    2. If it does not already exist, create one by selecting the + button and selecting New Link Binary With Libraries Phase
      Screenshot 2024-04-19 at 3 13 01 PM

    3. Navigate to Package Dependencies for the project

    4. Click the + button

    5. Click the Add Local... button on the bottom of the dialog that opens

    6. Navigate to plugin_name/plugin_name_ios/ios/plugin_name_ios and click Add Package button

    7. Ensure it is added to the RunnerTests Target and click Add Package button

  2. 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

  1. Open your app (your_app/ios/Runner.xcworkspace) in Xcode
  2. Navigate to Package Dependencies for the project

Screenshot 2024-04-05 at 10 13 56 AM

  1. Click the + button
  2. Click the Add Local... button on the bottom of the dialog that opens
  3. Navigate to your_app/ios/Flutter/Packages/FlutterGeneratedPluginSwiftPackage and click Add Package button
  4. Ensure it is added to the Runner Target and click Add Package button

Screenshot 2024-04-05 at 10 17 21 AM

  1. Ensure FlutterGeneratedPluginSwiftPackage was added to Frameworks, Libraries, and Embedded Content

Screenshot 2024-04-05 at 10 20 12 AM

Part 2: Add Run Prepare Flutter Framework Script Pre-Action

The following must be completed for each flavor.

  1. Next, select Product > Scheme > Edit Scheme
  2. Click the > next to "Build" in the left side bar
  3. Select Pre-actions
  4. Click the + button and select New Run Script Action from the menu
  5. Click the "Run Script" title and change to Run Prepare Flutter Framework Script.
  6. Change the "Provide build settings from" to the app.
  7. Input the following in the text box:
/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare

Screenshot 2024-04-05 at 10 24 44 AM

Part 3: Run app

  1. Run the app in Xcode and ensure FlutterGeneratedPluginSwiftPackage is a target dependency and Run Prepare Flutter Framework Script is being run as a pre-action.

Screenshot 2024-04-05 at 12 31 43 PM

  1. 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

  1. Open your app (your_app/macos/Runner.xcworkspace) in Xcode
  2. Navigate to Package Dependencies for the project

Screenshot 2024-04-05 at 10 13 56 AM

  1. Click the + button
  2. Click the Add Local... button on the bottom of the dialog that opens
  3. Navigate to your_app/macos/Flutter/Packages/FlutterGeneratedPluginSwiftPackage and click Add Package button
  4. Ensure it is added to the Runner Target and click Add Package button

Screenshot 2024-04-05 at 10 17 21 AM

  1. Ensure FlutterGeneratedPluginSwiftPackage was added to Frameworks, Libraries, and Embedded Content

Screenshot 2024-04-05 at 10 20 12 AM

Part 2: Add Run Prepare Flutter Framework Script Pre-Action

The following must be completed for each flavor.

  1. Next, select Product > Scheme > Edit Scheme
  2. Click the > next to "Build" in the left side bar
  3. Select Pre-actions
  4. Click the + button and select New Run Script Action from the menu
  5. Click the "Run Script" title and change to Run Prepare Flutter Framework Script.
  6. Change the "Provide build settings from" to the Runner target.
  7. Input the following in the text box:
"$FLUTTER_ROOT"/packages/flutter_tools/bin/macos_assemble.sh prepare

Screenshot 2024-04-05 at 2 22 56 PM

Part 3: Run app

  1. Run the app in Xcode and ensure FlutterGeneratedPluginSwiftPackage is a target dependency and Run Prepare Flutter Framework Script is being run as a pre-action.

Screenshot 2024-04-05 at 12 31 43 PM

  1. 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.

Screenshot 2024-04-05 at 3 04 09 PM

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

  1. Disable Swift Package Manager (see "Disable Swift Package Manager" instructions above).
  2. Open your app (your_app/ios/Runner.xcworkspace) in Xcode
  3. Navigate to Package Dependencies for the project
  4. Click on the FlutterGeneratedPluginSwiftPackage package and then click the - button

Screenshot 2024-04-05 at 2 24 48 PM

  1. Navigate to Frameworks, Libraries, and Embedded Content for the Runner target
  2. Click on FlutterGeneratedPluginSwiftPackage and then click the - button

Screenshot 2024-04-05 at 2 25 25 PM

FAQS

Coming soon...

@stuartmorgan-g
Copy link
Contributor

  • 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.

@vashworth
Copy link
Contributor Author

  • 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.

@stuartmorgan-g
Copy link
Contributor

Ah right, I forgot about that limitation. Makes sense!

@vashworth vashworth changed the title [WIP] Add Swift Package Manager as new opt-in feature for iOS and macOS Add Swift Package Manager as new opt-in feature for iOS and macOS Apr 5, 2024
@vashworth vashworth marked this pull request as ready for review April 5, 2024 20:24
@vashworth vashworth requested a review from jmagman April 5, 2024 20:24
@stuartmorgan-g
Copy link
Contributor

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!

Copy link
Contributor

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't reviewed the Xcode project editor or the tests yet, but posting comments for the rest now. Overall this looks really good!

{{targets}}
]
)
''';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're regenerating this every time plugins change, is there a way for users to make edits (like use_frameworks! and the like in the current pod file)? Or is that not something that will be necessary for SPM?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No this file will not really be editable by the user since it's generated. Unfortunately, swift packages don't have as much control as CocoaPods did.

So use_frameworks! would tell CocoaPods to use dynamic libraries rather than static - that's not possible with SPM. You can define whether the package itself is static or dynamic, but not dependencies.

Also, FYI, SPM recommends not setting what type and letting Xcode decide. It seems to default to static.

I did confirm that privacy manifest are still included in the app even if statically linked. SPM puts them in a .bundle in the Runner.app directory. I also tested that Xcode's Generate Report archival process found them.

@vashworth vashworth added the autosubmit Merge PR when tree becomes green via auto submit App label Apr 18, 2024
@auto-submit auto-submit bot merged commit 6d19fa3 into flutter:master Apr 18, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 19, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 19, 2024
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Apr 19, 2024
flutter/flutter@fb110b9...98685a0

2024-04-19 [email protected] Replace CocoaPods deprecated `exists?` with `exist?` (flutter/flutter#147056)
2024-04-19 [email protected] Roll Flutter Engine from 4ed7a2d6aed9 to 7fafbbf17c02 (2 revisions) (flutter/flutter#147046)
2024-04-19 [email protected] Roll Flutter Engine from 25d773ea90c4 to 4ed7a2d6aed9 (3 revisions) (flutter/flutter#147038)
2024-04-19 [email protected] Update link branches to `main` (continued) (flutter/flutter#146985)
2024-04-19 [email protected] Roll Flutter Engine from b6234dd1984e to 25d773ea90c4 (1 revision) (flutter/flutter#147035)
2024-04-19 [email protected] Roll Flutter Engine from ff471881e90a to b6234dd1984e (2 revisions) (flutter/flutter#147029)
2024-04-19 [email protected] Roll Flutter Engine from 442d14a7e840 to ff471881e90a (1 revision) (flutter/flutter#147025)
2024-04-19 [email protected] Roll Flutter Engine from 6e4a15d31769 to 442d14a7e840 (2 revisions) (flutter/flutter#147023)
2024-04-19 [email protected] Opt out users from GA3 if opted out of GA4 (flutter/flutter#146453)
2024-04-19 [email protected] Roll Flutter Engine from 61bf47d129bb to 6e4a15d31769 (1 revision) (flutter/flutter#147022)
2024-04-18 [email protected] Roll Flutter Engine from 13a6ce419664 to 61bf47d129bb (4 revisions) (flutter/flutter#147017)
2024-04-18 [email protected] Add a breadcrumb for the pub autoroller (flutter/flutter#146786)
2024-04-18 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Add generic type for result in PopScope (#139164)" (flutter/flutter#147015)
2024-04-18 [email protected] Redundant message fix (flutter/flutter#143978)
2024-04-18 [email protected] Clean up flutterRoot (flutter/flutter#147010)
2024-04-18 49699333+dependabot[bot]@users.noreply.github.com Bump actions/upload-artifact from 4.3.1 to 4.3.2 (flutter/flutter#147011)
2024-04-18 [email protected] Add Swift Package Manager as new opt-in feature for iOS and macOS (flutter/flutter#146256)
2024-04-18 [email protected] Refactor framework coverage tests (flutter/flutter#146210)
2024-04-18 [email protected] Roll Flutter Engine from 46ff024bff10 to 13a6ce419664 (3 revisions) (flutter/flutter#147006)
2024-04-18 [email protected] Changing the renderer on the web target should change its build key. (flutter/flutter#147003)
2024-04-18 [email protected] Roll Flutter Engine from 6abfa565a9f9 to 46ff024bff10 (1 revision) (flutter/flutter#147005)
2024-04-18 [email protected] Add generic type for result in PopScope (flutter/flutter#139164)
2024-04-18 [email protected] Roll Flutter Engine from aa6f7411c219 to 6abfa565a9f9 (1 revision) (flutter/flutter#147002)
2024-04-18 [email protected] [tools] Make SnapshotType.platform non-nullable (flutter/flutter#146958)
2024-04-18 [email protected] Roll Flutter Engine from b8e802515b5a to aa6f7411c219 (1 revision) (flutter/flutter#146996)
2024-04-18 [email protected] Roll Flutter Engine from 2c3e9c8bfce6 to b8e802515b5a (2 revisions) (flutter/flutter#146993)
2024-04-18 [email protected] Roll Packages from d39830e to 0e3809d (9 revisions) (flutter/flutter#146992)
2024-04-18 [email protected] Fix memory leaks in navigation rail (flutter/flutter#146988)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
gilnobrega pushed a commit to gilnobrega/flutter that referenced this pull request Apr 22, 2024
Xcode 15 will be required for iOS App Store submission 
> Please note that as of April 2024 all iOS and iPadOS apps submitted to the App Store must be built with a minimum of Xcode 15 and the iOS 17 SDK.

https://developer.apple.com/ios/submit/

And will also be required for Swift Package Manager support flutter#146256.

We could swap to "required" but macOS developers don't technically need to upgrade.  We can let the Store itself enforce its policies.  And we can swap to Xcode 15 "required" when SPM adoption is further along.

Part of flutter#144582
gilnobrega pushed a commit to gilnobrega/flutter that referenced this pull request Apr 22, 2024
)

Convert `ProjectMigration.run()` and `ProjectMigrator.migrate()` to be async.

Needed for Swift Package Manager migration, which requires some async processes: flutter#146256
gilnobrega pushed a commit to gilnobrega/flutter that referenced this pull request Apr 22, 2024
…utter#146256)

This PR adds initial support for Swift Package Manager (SPM). Users must opt in. Only compatible with Xcode 15+.

Fixes flutter#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.
* 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
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Apr 24, 2024
…ift Package Manager (#6557)

Tests new Swift Package Manager feature added in flutter/flutter#146256.

Fixes flutter/flutter#146901.
TecHaxter pushed a commit to TecHaxter/flutter_packages that referenced this pull request May 22, 2024
flutter/flutter@fb110b9...98685a0

2024-04-19 [email protected] Replace CocoaPods deprecated `exists?` with `exist?` (flutter/flutter#147056)
2024-04-19 [email protected] Roll Flutter Engine from 4ed7a2d6aed9 to 7fafbbf17c02 (2 revisions) (flutter/flutter#147046)
2024-04-19 [email protected] Roll Flutter Engine from 25d773ea90c4 to 4ed7a2d6aed9 (3 revisions) (flutter/flutter#147038)
2024-04-19 [email protected] Update link branches to `main` (continued) (flutter/flutter#146985)
2024-04-19 [email protected] Roll Flutter Engine from b6234dd1984e to 25d773ea90c4 (1 revision) (flutter/flutter#147035)
2024-04-19 [email protected] Roll Flutter Engine from ff471881e90a to b6234dd1984e (2 revisions) (flutter/flutter#147029)
2024-04-19 [email protected] Roll Flutter Engine from 442d14a7e840 to ff471881e90a (1 revision) (flutter/flutter#147025)
2024-04-19 [email protected] Roll Flutter Engine from 6e4a15d31769 to 442d14a7e840 (2 revisions) (flutter/flutter#147023)
2024-04-19 [email protected] Opt out users from GA3 if opted out of GA4 (flutter/flutter#146453)
2024-04-19 [email protected] Roll Flutter Engine from 61bf47d129bb to 6e4a15d31769 (1 revision) (flutter/flutter#147022)
2024-04-18 [email protected] Roll Flutter Engine from 13a6ce419664 to 61bf47d129bb (4 revisions) (flutter/flutter#147017)
2024-04-18 [email protected] Add a breadcrumb for the pub autoroller (flutter/flutter#146786)
2024-04-18 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Add generic type for result in PopScope (#139164)" (flutter/flutter#147015)
2024-04-18 [email protected] Redundant message fix (flutter/flutter#143978)
2024-04-18 [email protected] Clean up flutterRoot (flutter/flutter#147010)
2024-04-18 49699333+dependabot[bot]@users.noreply.github.com Bump actions/upload-artifact from 4.3.1 to 4.3.2 (flutter/flutter#147011)
2024-04-18 [email protected] Add Swift Package Manager as new opt-in feature for iOS and macOS (flutter/flutter#146256)
2024-04-18 [email protected] Refactor framework coverage tests (flutter/flutter#146210)
2024-04-18 [email protected] Roll Flutter Engine from 46ff024bff10 to 13a6ce419664 (3 revisions) (flutter/flutter#147006)
2024-04-18 [email protected] Changing the renderer on the web target should change its build key. (flutter/flutter#147003)
2024-04-18 [email protected] Roll Flutter Engine from 6abfa565a9f9 to 46ff024bff10 (1 revision) (flutter/flutter#147005)
2024-04-18 [email protected] Add generic type for result in PopScope (flutter/flutter#139164)
2024-04-18 [email protected] Roll Flutter Engine from aa6f7411c219 to 6abfa565a9f9 (1 revision) (flutter/flutter#147002)
2024-04-18 [email protected] [tools] Make SnapshotType.platform non-nullable (flutter/flutter#146958)
2024-04-18 [email protected] Roll Flutter Engine from b8e802515b5a to aa6f7411c219 (1 revision) (flutter/flutter#146996)
2024-04-18 [email protected] Roll Flutter Engine from 2c3e9c8bfce6 to b8e802515b5a (2 revisions) (flutter/flutter#146993)
2024-04-18 [email protected] Roll Packages from d39830e to 0e3809d (9 revisions) (flutter/flutter#146992)
2024-04-18 [email protected] Fix memory leaks in navigation rail (flutter/flutter#146988)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
TecHaxter pushed a commit to TecHaxter/flutter_packages that referenced this pull request May 22, 2024
sfshaza2 pushed a commit to flutter/website that referenced this pull request Jul 16, 2024
Flutter iOS/macOS is migrating to Swift Package Manager for native
dependencies. This is an experimental feature this isn't ready yet for
app developers. However, plugin authors should add Swift Package Manager
support to their packages.

The faster plugin authors add SwiftPM support, the sooner we can drop
CocoaPods (and make it easier to install Flutter iOS!). There's [~10K
Flutter iOS
plugins](https://gist.github.com/loic-sharma/891a358f8abab36133e8a9395f719296)
that'll need to migrate, so we really need the plugin migration docs to
be as easy to follow as possible. Any and all feedback is appreciated!
:)
 
This is based off @vashworth's
[guide](flutter/flutter#146256 (comment)).

Question for reviewers: What do you think of the doc structure? Should
we rearrange things to make it clearer that some sections are to be
followed when things go wrong? Or is the current structure clear enough?

Preview URLs:

1. [Swift Package Manager for app
developers](https://flutter-docs-prod--pr10827-spm-compatibility-ilxd3b00.web.app/packages-and-plugins/swift-package-manager/for-app-developers)
2. [Swift Package Manager for plugin authors

](https://flutter-docs-prod--pr10827-spm-compatibility-ilxd3b00.web.app/packages-and-plugins/swift-package-manager/for-plugin-authors)

Part of flutter/flutter#126005

## Presubmit checklist

- [ ] This PR is marked as draft with an explanation if not meant to
land until a future stable release.
- [x] This PR doesn’t contain automatically generated corrections
(Grammarly or similar).
- [x] This PR follows the [Google Developer Documentation Style
Guidelines](https://developers.google.com/style) — for example, it
doesn’t use _i.e._ or _e.g._, and it avoids _I_ and _we_ (first person).
- [x] This PR uses [semantic line
breaks](https://github.com/dart-lang/site-shared/blob/main/doc/writing-for-dart-and-flutter-websites.md#semantic-line-breaks)
of 80 characters or fewer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop a: tests "flutter test", flutter_test, or one of our tests autosubmit Merge PR when tree becomes green via auto submit App f: integration_test The flutter/packages/integration_test plugin framework flutter/packages/flutter repository. See also f: labels. platform-ios iOS applications specifically tool Affects the "flutter" command-line tool. See also t: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Swift Package Manager as opt-in feature

4 participants