Swift Package Manager - 针对插件作者

Swift Package Manager for plugin authors

Packages & pluginschevron_rightSwift Package Manager for Flutterchevron_rightSwift Package Manager for plugin authors

warning请注意

Flutter is migrating to Swift Package Manager to manage iOS and macOS native dependencies. Flutter's support of Swift Package Manager is under development. The implementation might change in the future. Swift Package Manager support is only available on the main channel. Flutter continues to support CocoaPods.

Flutter's Swift Package Manager integration has several benefits:

  1. Access to the Swift package ecosystem. Flutter plugins can use the growing ecosystem of Swift packages!
  2. Simplifies Flutter installation. Swift Package Manager is bundled with Xcode. In the future, you won’t need to install Ruby and CocoaPods to target iOS or macOS.

If you find a bug in Flutter's Swift Package Manager support, open an issue.

How to turn on Swift Package Manager

#

Flutter's Swift Package Manager support is turned off by default. To turn it on:

  1. Switch to Flutter's main channel:

    sh

    flutter channel main --no-cache-artifacts
    

    content_copy

  2. Upgrade to the latest Flutter SDK and download artifacts:

    sh

    flutter upgrade
    

    content_copy

  3. Turn on the Swift Package Manager feature:

    sh

    flutter config --enable-swift-package-manager
    

    content_copy

Using the Flutter CLI to run an app migrates the project to add Swift Package Manager integration. This makes your project download the Swift packages that your Flutter plugins depend on. An app with Swift Package Manager integration requires Flutter version 3.24 or higher. To use an older Flutter version, you will need to remove Swift Package Manager integration from the app.

Flutter falls back to CocoaPods for dependencies that do not support Swift Package Manager yet.

How to turn off Swift Package Manager

#

Plugin authors

Plugin authors need to turn on and off Flutter's Swift Package Manager support for testing. App developers do not need to disable Swift Package Manager support, unless they are running into issues.

If you find a bug in Flutter's Swift Package Manager support, open an issue.

Disabling Swift Package Manager causes Flutter to use CocoaPods for all dependencies. However, Swift Package Manager remains integrated with your project. To remove Swift Package Manager integration completely from your project, follow the How to remove Swift Package Manager integration instructions.

Turn off for a single project

#

In the project's pubspec.yaml file, under the flutter section, add disable-swift-package-manager: true.

pubspec.yaml

yaml

# The following section is specific to Flutter packages.
flutter:
  disable-swift-package-manager: true

content_copy

This turns off Swift Package Manager for all contributors to this project.

Turn off globally for all projects

#

Run the following command:

sh

flutter config --no-enable-swift-package-manager

content_copy

This turns off Swift Package Manager for the current user.

If a project is incompatible with Swift Package Manager, all contributors need to run this command.

How to add Swift Package Manager support to an existing Flutter plugin

#

This guide shows how to add Swift Package Manager support to a plugin that already supports CocoaPods. This ensures the plugin is usable by all Flutter projects.

Flutter plugins should support both Swift Package Manager and CocoaPods until further notice.

Swift Package Manager adoption will be gradual. Plugins that don't support CocoaPods won't be usable by projects that haven't migrated to Swift Package Manager yet. Plugins that don't support Swift Package Manager can cause problems for projects that have migrated.

Replace plugin_name throughout this guide with the name of your plugin. The example below uses ios, replace ios with macos/darwin as applicable.

  1. Turn on the Swift Package Manager feature.

  2. Start by creating a directory under the ios, macos, and/or darwin directories. Name this new directory the name of the platform package.

    plugin_name/ios/
    ├── ...
    └── plugin_name/
    
  3. Within this new directory, create the following files/directories:

    • Package.swift (file)
    • Sources (directory)
    • Sources/plugin_name (directory)

    Your plugin should look like:

    plugin_name/ios/
    ├── ...
    └── plugin_name/
       ├── Package.swift
       └── Sources/plugin_name/
    
  4. Use the following template in the Package.swift file:

    Package.swift

    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(
        // 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("12.0"),
            .macOS("10.14")
        ],
        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, see:
                    // 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
                ]
            )
        ]
    )
    

    content_copy

  5. Update the supported platforms in your Package.swift file.

    Package.swift

    swift

        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("12.0"),
            .macOS("10.14")
        ],
    

    content_copy

  6. Update the package, library, and target names in your Package.swift file.

    Package.swift

    swift

    let package = Package(
        // TODO: Update your plugin name.
        name: "plugin_name",
        platforms: [
            .iOS("12.0"),
            .macOS("10.14")
        ],
        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, see:
                    // 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
                ]
            )
        ]
    )
    

    content_copy

    info提示

    If the plugin name contains _, the library name must be a - separated version of the plugin name.

  7. If your plugin has a PrivacyInfo.xcprivacy file, move it to ios/plugin_name/Sources/plugin_name/PrivacyInfo.xcprivacy and uncomment the resource in the Package.swift file.

    Package.swift

    swift

                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, see:
                    // 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
                ],
    

    content_copy

  8. Move any resource files from ios/Assets to ios/plugin_name/Sources/plugin_name (or a subdirectory). Add the resource files to your Package.swift file, if applicable. For more instructions, see https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package.

  9. Move all files from ios/Classes to ios/plugin_name/Sources/plugin_name.

  10. The ios/Assets, ios/Resources, and ios/Classes directories should now be empty and can be deleted.

  11. If your plugin uses Pigeon, update your Pigeon input file.

    pigeons/messages.dart

    dart

    kotlinOptions: 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(),
    

    content_copy

  12. Update your Package.swift file with any customizations you might need.

    1. Open the ios/plugin_name/ directory in Xcode.

    2. In Xcode, open your Package.swift file. Verify Xcode doesn't produce any warnings or errors for this file.

      lightbulb小提示

      If Xcode doesn't show any files, quit Xcode (Xcode > Quit Xcode) and reopen.

      If Xcode doesn't update after you make a change, try clicking File > Packages > Reset Package Caches.

    3. If your ios/plugin_name.podspec file has CocoaPods dependencys, add the corresponding Swift Package Manager dependencies to your Package.swift file.

    4. If your package must be linked explicitly static or dynamic (not recommended by Apple), update the Product to define the type:

      Package.swift

      swift

      products: [
          .library(name: "plugin-name", type: .static, targets: ["plugin_name"])
      ],
      

      content_copy

    5. Make any other customizations. For more information on how to write a Package.swift file, see https://developer.apple.com/documentation/packagedescription.

      lightbulb小提示

      If you add targets to your Package.swift file, use unique names. This avoids conflicts with targets from other packages.

  13. Update your ios/plugin_name.podspec to point to new paths.

    ios/plugin_name.podspec

    ruby

    s.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']}
    

    content_copy

  14. Update loading of resources from bundle to use 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
    

    content_copy

    info提示

    Bundle.module only works if there are resources defined in the Package.swift file or [automatically included by Xcode](https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package#:~:text=Xcode detects common resource types for Apple platforms and treats them as a resource automatically)). Otherwise, using Bundle.module results in an error.

  15. Commit your plugin's changes to your version control system.

  16. Verify the plugin still works with CocoaPods.

    1. Turn off Swift Package Manager.

      sh

      flutter config --no-enable-swift-package-manager
      

      content_copy

    2. Navigate to the plugin's example app.

      sh

      cd path/to/plugin/example/
      

      content_copy

    3. Ensure the plugin's example app builds and runs.

      sh

      flutter run
      

      content_copy

    4. Navigate to the plugin's top-level directory.

      sh

      cd path/to/plugin/
      

      content_copy

    5. Run CocoaPods validation lints.

      sh

      pod lib lint ios/plugin_name.podspec  --configuration=Debug --skip-tests --use-modular-headers --use-libraries
      

      content_copy

      sh

      pod lib lint ios/plugin_name.podspec  --configuration=Debug --skip-tests --use-modular-headers
      

      content_copy

  17. Verify the plugin works with Swift Package Manager.

    1. Turn on Swift Package Manager.

      sh

      flutter config --enable-swift-package-manager
      

      content_copy

    2. Navigate to the plugin's example app.

      sh

      cd path/to/plugin/example/
      

      content_copy

    3. Ensure the plugin's example app builds and runs.

      sh

      flutter run
      

      content_copy

      info提示

      Using the Flutter CLI to run the plugin's example app with the Swift Package Manager feature turned on migrates the project to add Swift Package Manager integration.

      This raises the example app's Flutter SDK requirement to version 3.24 or higher.

      If you'd like to run the example app using an older Flutter SDK version, do not commit the migration's changes to your version control system. If needed, you can always undo the Swift Package Manager migration.

    4. Open the plugin's example app in Xcode. Ensure that Package Dependencies shows in the left Project Navigator.

  18. Verify tests pass.

How to update unit tests in a plugin's example app

#

If your plugin has native XCTests, you might need to update them to work with Swift Package Manager 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 file.

To update your unit tests:

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

    ios/Podfile

    ruby

    target 'RunnerTests' do
      inherit! :search_paths
    
      pod 'OCMock', '3.5'
    end
    

    content_copy

    Then in the terminal, run pod install in the plugin_name_ios/example/ios directory.

  3. Navigate to Package Dependencies for the project.

    The project's package dependencies
    The project's package dependencies

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

    Search for test-only dependencies
    Search for test-only dependencies

    info提示

    OCMock uses unsafe build flags and can only be used if targeted by commit. fe1661a3efed11831a6452f4b1a0c5e6ddc08c3d is the commit for the 3.9.3 version.

  5. Ensure the dependency is added to the RunnerTests Target.

    Ensure the dependency is added to the  target
    Ensure the dependency is added to the RunnerTests target

  6. Click the Add Package button.

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

    1. Ensure RunnerTests Build Phases has a Link Binary With Libraries build phase:

      The  Build Phase in the  target
      The Link Binary With Libraries Build Phase in the RunnerTests target

      If the build phase doesn't exist already, create one. Click the add and then click New Link Binary With Libraries Phase.

      Add  Build Phase
      Add Link Binary With Libraries Build Phase

    2. Navigate to Package Dependencies for the project.

    3. Click add.

    4. In the dialog that opens, click the Add Local... button.

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

    6. Ensure that it's added to the RunnerTests target and click the Add Package button.

  8. Ensure tests pass Product > Test.

posted on 2024-12-11 09:18  AtlasLapetos  阅读(46)  评论(0)    收藏  举报