Flutter 整合 Swift Package Manager 有以下幾項優點:

  1. 可存取 Swift 套件生態系統。 Flutter 插件現在可以使用日益壯大的 Swift packages 生態系統!
  2. 簡化 Flutter 安裝流程。 Swift Package Manager 已隨 Xcode 一同安裝。 未來,你不需要再安裝 Ruby 和 CocoaPods 來支援 iOS 或 macOS。

How to turn on Swift Package Manager

#

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

  1. Upgrade to the latest Flutter SDK:

    sh
    flutter upgrade
  2. Turn on the Swift Package Manager feature:

    sh
    flutter config --enable-swift-package-manager

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

#

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

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

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.

如何為現有 Flutter 插件新增 Swift Package Manager 支援

#

本指南說明如何為已支援 CocoaPods 的插件新增 Swift Package Manager 支援。 這可確保該插件可被所有 Flutter 專案使用。

在另行通知前,Flutter 插件應同時支援 Swift Package Manager 和 CocoaPods。

Swift Package Manager 的導入將會是漸進式的。 尚未支援 CocoaPods 的插件,將無法被尚未遷移至 Swift Package Manager 的專案使用。 而不支援 Swift Package Manager 的插件,則可能會對已遷移的專案造成問題。

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("13.0"),
            .macOS("10.15")
        ],
        products: [
            // TODO: Update your library and target names.
            // If the plugin name contains "_", replace with "-" for the library name.
            .library(name: "plugin-name", targets: ["plugin_name"])
        ],
        dependencies: [],
        targets: [
            .target(
                // TODO: Update your target name.
                name: "plugin_name",
                dependencies: [],
                resources: [
                    // TODO: If your plugin requires a privacy manifest
                    // (e.g. if it uses any required reason APIs), update the PrivacyInfo.xcprivacy file
                    // to describe your plugin's privacy impact, and then uncomment this line.
                    // For more information, 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
                ]
            )
        ]
    )
  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("13.0"),
            .macOS("10.15")
        ],
  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("13.0"),
            .macOS("10.15")
        ],
        products: [
            // TODO: Update your library and target names.
            // If the plugin name contains "_", replace with "-" for the library name
            .library(name: "plugin-name", targets: ["plugin_name"])
        ],
        dependencies: [],
        targets: [
            .target(
                // TODO: Update your target name.
                name: "plugin_name",
                dependencies: [],
                resources: [
                    // TODO: If your plugin requires a privacy manifest
                    // (e.g. if it uses any required reason APIs), update the PrivacyInfo.xcprivacy file
                    // to describe your plugin's privacy impact, and then uncomment this line.
                    // For more information, 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
                ]
            )
        ]
    )
  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
                ],
  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(),
  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.

    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"])
      ],
    5. Make any other customizations. For more information on how to write a Package.swift file, see https://developer.apple.com/documentation/packagedescription.

  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']}
  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
  15. If your .gitignore doesn't include .build/ and .swiftpm/ directories, you'll want to update your .gitignore to include:

    .gitignore
    text
    .build/
    .swiftpm/

    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
    2. Navigate to the plugin's example app.

      sh
      cd path/to/plugin/example/
    3. Ensure the plugin's example app builds and runs.

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

      sh
      cd path/to/plugin/
    5. Run CocoaPods validation lints.

      sh
      pod lib lint ios/plugin_name.podspec  --configuration=Debug --skip-tests --use-modular-headers --use-libraries
      sh
      pod lib lint ios/plugin_name.podspec  --configuration=Debug --skip-tests --use-modular-headers
  17. Verify the plugin works with Swift Package Manager.

    1. Turn on Swift Package Manager.

      sh
      flutter config --enable-swift-package-manager
    2. Navigate to the plugin's example app.

      sh
      cd path/to/plugin/example/
    3. Ensure the plugin's example app builds and runs.

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

  18. Verify tests pass.

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)
    • Sources/plugin_name/include (directory)
    • Sources/plugin_name/include/plugin_name (directory)
    • Sources/plugin_name/include/plugin_name/.gitkeep (file)
      • This file ensures the directory is committed. You can remove the .gitkeep file if other files are added to the directory.

    Your plugin should look like:

    plugin_name/ios/
    ├── ...
    └── plugin_name/
       ├── Package.swift
       └── Sources/plugin_name/include/plugin_name/
          └── .gitkeep
  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("13.0"),
            .macOS("10.15")
        ],
        products: [
            // TODO: Update your library and target names.
            // If the plugin name contains "_", replace with "-" for the library name
            .library(name: "plugin-name", targets: ["plugin_name"])
        ],
        dependencies: [],
        targets: [
            .target(
                // TODO: Update your target name.
                name: "plugin_name",
                dependencies: [],
                resources: [
                    // TODO: If your plugin requires a privacy manifest
                    // (e.g. if it uses any required reason APIs), update the PrivacyInfo.xcprivacy file
                    // to describe your plugin's privacy impact, and then uncomment this line.
                    // For more information, 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
                ],
                cSettings: [
                    // TODO: Update your plugin name.
                    .headerSearchPath("include/plugin_name")
                ]
            )
        ]
    )
  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("13.0"),
            .macOS("10.15")
        ],
  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("13.0"),
            .macOS("10.15")
        ],
        products: [
            // TODO: Update your library and target names.
            // If the plugin name contains "_", replace with "-" for the library name
            .library(name: "plugin-name", targets: ["plugin_name"])
        ],
        dependencies: [],
        targets: [
            .target(
                // TODO: Update your target name.
                name: "plugin_name",
                dependencies: [],
                resources: [
                    // TODO: If your plugin requires a privacy manifest
                    // (e.g. if it uses any required reason APIs), update the PrivacyInfo.xcprivacy file
                    // to describe your plugin's privacy impact, and then uncomment this line.
                    // For more information, 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
                ],
                cSettings: [
                    // TODO: Update your plugin name.
                    .headerSearchPath("include/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
                ],
  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 any public headers from ios/Classes to ios/plugin_name/Sources/plugin_name/include/plugin_name.

    • If you're unsure which headers are public, check your podspec file's public_header_files attribute. If this attribute isn't specified, all of your headers were public. You should consider whether you want all of your headers to be public.

    • The pluginClass defined in your pubspec.yaml file must be public and within this directory.

  10. Handling modulemap.

    Skip this step if your plugin does not have a modulemap.

    If you're using a modulemap for CocoaPods to create a Test submodule, consider removing it for Swift Package Manager. Note that this makes all public headers available through the module.

    To remove the modulemap for Swift Package Manager but keep it for CocoaPods, exclude the modulemap and umbrella header in the plugin's Package.swift file.

    The example below assumes the modulemap and umbrella header are located in the ios/plugin_name/Sources/plugin_name/include directory.

    Package.swift
    swift
    .target(
        name: "plugin_name",
        dependencies: [],
        exclude: ["include/cocoapods_plugin_name.modulemap", "include/plugin_name-umbrella.h"],

    If you want to keep your unit tests compatible with both CocoaPods and Swift Package Manager, you can try the following:

    Tests/TestFile.m
    objc
    @import plugin_name;
    @import plugin_name.Test;
    #if __has_include(<plugin_name plugin_name-umbrella.h="">)
      @import plugin_name.Test;
    #endif

    If you would like to use a custom modulemap with your Swift package, refer to Swift Package Manager's documentation.

  11. Move all remaining files from ios/Classes to ios/plugin_name/Sources/plugin_name.

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

  13. If your header files are no longer in the same directory as your implementation files, you should update your import statements.

    For example, imagine the following migration:

    • Before:

      ios/Classes/
      ├── PublicHeaderFile.h
      └── ImplementationFile.m
    • After:

      ios/plugin_name/Sources/plugin_name/
      └── include/plugin_name/
         └── PublicHeaderFile.h
      └── ImplementationFile.m

    In this example, the import statements in ImplementationFile.m should be updated:

    Sources/plugin_name/ImplementationFile.m
    objc
    #import "PublicHeaderFile.h"
    #import "./include/plugin_name/PublicHeaderFile.h"
  14. If your plugin uses Pigeon, update your Pigeon input file.

    pigeons/messages.dart
    dart
    javaOptions: JavaOptions(),
    objcHeaderOut: 'ios/Classes/messages.g.h',
    objcSourceOut: 'ios/Classes/messages.g.m',
    objcHeaderOut: 'ios/plugin_name/Sources/plugin_name/messages.g.h',
    objcSourceOut: 'ios/plugin_name/Sources/plugin_name/messages.g.m',
    copyrightHeader: 'pigeons/copyright.txt',

    If your objcHeaderOut file is no longer within the same directory as the objcSourceOut, you can change the #import using ObjcOptions.headerIncludePath:

    pigeons/messages.dart
    dart
    javaOptions: JavaOptions(),
    objcHeaderOut: 'ios/Classes/messages.g.h',
    objcSourceOut: 'ios/Classes/messages.g.m',
    objcHeaderOut: 'ios/plugin_name/Sources/plugin_name/include/plugin_name/messages.g.h',
    objcSourceOut: 'ios/plugin_name/Sources/plugin_name/messages.g.m',
    objcOptions: ObjcOptions(
      headerIncludePath: './include/plugin_name/messages.g.h',
    ),
    copyrightHeader: 'pigeons/copyright.txt',

    Run Pigeon to re-generate its code with the latest configuration.

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

    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"])
      ],
    5. Make any other customizations. For more information on how to write a Package.swift file, see https://developer.apple.com/documentation/packagedescription.

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

    ios/plugin_name.podspec
    ruby
    s.source_files = 'Classes/**/*.{h,m}'
    s.public_header_files = 'Classes/**/*.h'
    s.module_map = 'Classes/cocoapods_plugin_name.modulemap'
    s.resource_bundles = {'plugin_name_privacy' => ['Resources/PrivacyInfo.xcprivacy']}
    s.source_files = 'plugin_name/Sources/plugin_name/**/*.{h,m}'
    s.public_header_files = 'plugin_name/Sources/plugin_name/include/**/*.h'
    s.module_map = 'plugin_name/Sources/plugin_name/include/cocoapods_plugin_name.modulemap'
    s.resource_bundles = {'plugin_name_privacy' => ['plugin_name/Sources/plugin_name/PrivacyInfo.xcprivacy']}
  17. Update loading of resources from bundle to use SWIFTPM_MODULE_BUNDLE:

    objc
    #if SWIFT_PACKAGE
       NSBundle *bundle = SWIFTPM_MODULE_BUNDLE;
     #else
       NSBundle *bundle = [NSBundle bundleForClass:[self class]];
     #endif
     NSURL *imageURL = [bundle URLForResource:@"image" withExtension:@"jpg"];
  18. If your ios/plugin_name/Sources/plugin_name/include directory only contains a .gitkeep, you'll want update your .gitignore to include the following:

    .gitignore
    text
    !.gitkeep

    Run flutter pub publish --dry-run to ensure the include directory is published.

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

  20. Verify the plugin still works with CocoaPods.

    1. Turn off Swift Package Manager:

      sh
      flutter config --no-enable-swift-package-manager
    2. Navigate to the plugin's example app.

      sh
      cd path/to/plugin/example/
    3. Ensure the plugin's example app builds and runs.

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

      sh
      cd path/to/plugin/
    5. Run CocoaPods validation lints:

      sh
      pod lib lint ios/plugin_name.podspec  --configuration=Debug --skip-tests --use-modular-headers --use-libraries
      sh
      pod lib lint ios/plugin_name.podspec  --configuration=Debug --skip-tests --use-modular-headers
  21. Verify the plugin works with Swift Package Manager.

    1. Turn on Swift Package Manager:

      sh
      flutter config --enable-swift-package-manager
    2. Navigate to the plugin's example app.

      sh
      cd path/to/plugin/example/
    3. Ensure the plugin's example app builds and runs.

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

  22. Verify tests pass.

</plugin_name>

如何更新插件範例 App 的單元測試

#

如果你的插件有原生的 XCTest 單元測試,且符合以下其中一項情況,則你可能需要更新測試以支援 Swift Package Manager:

  • 你的測試使用了 CocoaPod 相依套件。
  • 你的插件在 Package.swift 檔案中明確設定為 type: .dynamic

要更新你的單元測試:

  1. 在 Xcode 中開啟你的 example/ios/Runner.xcworkspace

  2. 如果你原本在測試中使用了 CocoaPod 相依套件(例如 OCMock), 請將其從 Podfile 檔案中移除。

    ios/Podfile
    ruby
    target 'RunnerTests' do
      inherit! :search_paths
    
      pod 'OCMock', '3.5'
    end

    然後在終端機中,在plugin_name_ios/example/ios目錄下執行pod install

  3. 導覽至專案的 Package Dependencies(套件相依性)。

    專案的套件相依性專案的套件相依性

  4. 點擊 + 按鈕,並在右上角的搜尋欄中搜尋,新增任何僅用於測試的相依套件。

    搜尋僅用於測試的相依套件搜尋僅用於測試的相依套件

  5. 確認該相依套件已加入至RunnerTests目標(Target)。

    確保相依套件已加入至`RunnerTests`目標確保相依套件已加入至RunnerTests目標

  6. 點擊 Add Package(新增套件)按鈕。

  7. 如果你已在Package.swift檔案中明確將 plugin 的 library type 設為.dynamicApple 不建議這麼做), 你還需要將其加入RunnerTests目標的相依套件中。

    1. 確認RunnerTestsBuild Phases(建置階段)中有 Link Binary With Libraries 的建置階段:

      `Link Binary With Libraries` 建置階段於`RunnerTests`目標Link Binary With Libraries 建置階段於RunnerTests目標

      如果該建置階段尚未存在,請新增一個。 點擊 add,然後點擊 New Link Binary With Libraries Phase(新增連結二進位檔與函式庫階段)。

      新增`Link Binary With Libraries`建置階段新增Link Binary With Libraries建置階段

    2. 導覽至專案的 Package Dependencies(套件相依性)。

    3. 點擊 add

    4. 在開啟的對話視窗中,點擊 Add Local...(新增本地...)按鈕。

    5. 導覽至plugin_name/plugin_name_ios/ios/plugin_name_ios,然後點擊 Add Package(新增套件)按鈕。

    6. 確認已將其加入RunnerTests目標,並點擊 Add Package(新增套件)按鈕。

  8. 確認測試通過:Product > Test