Saltar al contenido principal

Swift Package Manager para autores de plugins

Cómo agregar compatibilidad con Swift Package Manager a los plugins de iOS y macOS

Para obtener información sobre cómo desactivar y activar SwiftPM, visita Swift Package Manager para desarrolladores de aplicaciones.

Cómo agregar soporte de Swift Package Manager a un plugin de Flutter existente

#

Esta guía muestra cómo agregar soporte de Swift Package Manager a un plugin que ya soporta CocoaPods. Esto asegura que el plugin sea utilizable por todos los proyectos de Flutter.

Los plugins de Flutter deben soportar tanto Swift Package Manager como CocoaPods hasta nuevo aviso.

La adopción de Swift Package Manager será gradual. A partir de Flutter 3.44, los plugins que no soportan CocoaPods no son utilizables por proyectos que aún no se han migrado a Swift Package Manager. Los plugins que no soportan Swift Package Manager pueden causar problemas a los proyectos que se han migrado. Por favor, migra tus plugins lo antes posible.

Reemplaza plugin_name a lo largo de esta guía con el nombre de tu plugin. El ejemplo a continuación utiliza ios, reemplaza ios con macos o darwin, según corresponda.

  1. Asegúrate de que estás utilizando Flutter 3.44 o posterior. Esto activa SwiftPM por defecto.

  2. Comienza creando un directorio bajo los directorios ios, macos y/o darwin. Nombra este nuevo directorio con el nombre del paquete de la plataforma.

    • plugin_name/
      • ios/
        • plugin_name/
  3. Dentro de este nuevo directorio, crea los siguientes archivos/directorios:

    • Package.swift (archivo)
    • Sources (directorio)
    • Sources/plugin_name (directorio)

    Tu plugin debería verse así:

    • plugin_name/
      • ios/
        • plugin_name/
          • Package.swift
          • Sources/
            • plugin_name/
  4. Utiliza la siguiente plantilla en el archivo Package.swift:

    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: [
            .package(name: "FlutterFramework", path: "../FlutterFramework")
        ],
        targets: [
            .target(
                // TODO: Update your target name.
                name: "plugin_name",
                dependencies: [
                    .product(name: "FlutterFramework", package: "FlutterFramework")
                ],
                resources: [
                    // TODO: If your plugin requires a privacy manifest
                    // (e.g. if it uses any required reason APIs), update the PrivacyInfo.xcprivacy file
                    // to describe your plugin's privacy impact, and then uncomment this line.
                    // For more information, visit:
                    // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
                    // .process("PrivacyInfo.xcprivacy"),
    
                    // TODO: If you have other resources that need to be bundled with your plugin, refer to
                    // the following instructions to add them:
                    // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package
                ]
            )
        ]
    )
    
  5. Actualiza las plataformas soportadas en tu archivo Package.swift.

    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. Actualiza los nombres del paquete, biblioteca y target en tu archivo Package.swift.

    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: [
            .package(name: "FlutterFramework", path: "../FlutterFramework")
        ],
        targets: [
            .target(
                // TODO: Update your target name.
                name: "plugin_name",
                dependencies: [
                    .product(name: "FlutterFramework", package: "FlutterFramework")
                ],
                resources: [
                    // TODO: If your plugin requires a privacy manifest
                    // (e.g. if it uses any required reason APIs), update the PrivacyInfo.xcprivacy file
                    // to describe your plugin's privacy impact, and then uncomment this line.
                    // For more information, visit:
                    // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
                    // .process("PrivacyInfo.xcprivacy"),
    
                    // TODO: If you have other resources that need to be bundled with your plugin, refer to
                    // the following instructions to add them:
                    // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package
                ]
            )
        ]
    )
    
  7. Si tu plugin tiene un archivo PrivacyInfo.xcprivacy, muévelo a ios/plugin_name/Sources/plugin_name/PrivacyInfo.xcprivacy y desmarca el comentario del recurso en el archivo Package.swift.

    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, visit:
                    // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
                    .process("PrivacyInfo.xcprivacy"),
    
                    // TODO: If you have other resources that need to be bundled with your plugin, refer to
                    // the following instructions to add them:
                    // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package
                ],
    
  8. Mueve cualquier archivo de recursos de ios/Assets a ios/plugin_name/Sources/plugin_name (o un subdirectorio). Agrega los archivos de recursos a tu archivo Package.swift, si corresponde. Para obtener más instrucciones, visita Bundling resources with a Swift package.

  9. Mueve todos los archivos de ios/Classes a ios/plugin_name/Sources/plugin_name.

  10. Agrega FlutterFramework como una dependencia.

    Actualiza Package.swift para incluir FlutterFramework:

    Package.swift
    swift
    dependencies: [
        .package(name: "FlutterFramework", path: "../FlutterFramework")
    ],
    targets: [
        .target(
            // TODO: Update your target name.
            name: "plugin_name",
            dependencies: [
                .product(name: "FlutterFramework", package: "FlutterFramework")
            ],
       )
    ]
    
  11. Los directorios ios/Assets, ios/Resources y ios/Classes ahora deberían estar vacíos y se pueden eliminar.

  12. Si tu plugin utiliza Pigeon, actualiza tu archivo de entrada de Pigeon.

    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(),
    
  13. Actualiza tu archivo Package.swift con las personalizaciones que puedas necesitar.

    1. En Xcode, abre el directorio ios/plugin_name/.

    2. En Xcode, abre tu archivo Package.swift. Verify Xcode doesn't produce any warnings or errors for this file.

    3. Si tu archivo ios/plugin_name.podspec tiene dependencias de CocoaPods, agrega las correspondientes dependencias de Swift Package Manager a tu Package.swift.

    4. Si tu paquete debe vincularse explícitamente como static o dynamic (no recomendado por Apple), actualiza el Product para definir el tipo:

      Package.swift
      swift
      products: [
          .library(name: "plugin-name", type: .static, targets: ["plugin_name"])
      ],
      
    5. Realiza cualquier otra personalización. Para obtener más información sobre cómo escribir un archivo Package.swift, visita PackageDescription.

  14. Actualiza tu archivo ios/plugin_name.podspec para que apunte a las nuevas rutas.

    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']}
    
  15. Actualiza la carga de recursos desde el bundle para usar 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
    
  16. Si tu archivo .gitignore no incluye los directorios .build/ y .swiftpm/, querrás actualizar tu .gitignore para incluir:

    .gitignore
    .build/
    .swiftpm/
    

    Confirma los cambios de tu plugin en tu sistema de control de versiones.

  17. Verifica que el plugin aún funcione con CocoaPods.

    1. Desactivar Swift Package Manager.

      sh
      flutter config --no-enable-swift-package-manager
      
    2. Navega a la aplicación de ejemplo del plugin.

      sh
      cd path/to/plugin/example/
      
    3. Asegúrate de que la aplicación de ejemplo del plugin se compile y se ejecute.

      sh
      flutter run
      
    4. Navega al directorio de nivel superior del plugin.

      sh
      cd path/to/plugin/
      
    5. Ejecuta los lints de validación de CocoaPods.

      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
      
  18. Verifica que el plugin funcione con Swift Package Manager.

    1. Activar Swift Package Manager.

      sh
      flutter config --enable-swift-package-manager
      
    2. Navega a la aplicación de ejemplo del plugin.

      sh
      cd path/to/plugin/example/
      
    3. Asegúrate de que la aplicación de ejemplo del plugin se compile y se ejecute.

      sh
      flutter run
      
    4. En Xcode, abre la aplicación de ejemplo del plugin. Asegúrate de que Package Dependencies se muestre en el Project Navigator de la izquierda.

  19. Verifica que las pruebas pasen.

Reemplaza plugin_name a lo largo de esta guía con el nombre de tu plugin. El ejemplo a continuación utiliza ios, reemplaza ios con macos o darwin, según corresponda.

  1. Asegúrate de que estás ejecutando Flutter 3.44 o posterior. Esto activa SwiftPM por defecto.

  2. Comienza creando un directorio bajo los directorios ios, macos y/o darwin. Nombra este nuevo directorio con el nombre del paquete de la plataforma.

    • plugin_name/
      • ios/
        • plugin_name/
  3. Dentro de este nuevo directorio, crea los siguientes archivos/directorios:

    • Package.swift (archivo)
    • Sources (directorio)
    • Sources/plugin_name (directorio)
    • Sources/plugin_name/include (directorio)
    • Sources/plugin_name/include/plugin_name (directorio)
    • Sources/plugin_name/include/plugin_name/.gitkeep (file)
      • Este archivo asegura que el directorio se confirme. Puedes eliminar el archivo .gitkeep si se agregan otros archivos al directorio.

    Tu plugin debería verse así:

    • plugin_name/
      • ios/
        • plugin_name/
          • Package.swift
          • Sources/plugin_name/include/plugin_name/
            • .gitkeep/
  4. Utiliza la siguiente plantilla en el archivo Package.swift:

    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: [
            .package(name: "FlutterFramework", path: "../FlutterFramework")
        ],
        targets: [
            .target(
                // TODO: Update your target name.
                name: "plugin_name",
                dependencies: [
                    .product(name: "FlutterFramework", package: "FlutterFramework")
                ],
                resources: [
                    // TODO: If your plugin requires a privacy manifest
                    // (in other words, if it uses any required reason APIs),
                    // update the PrivacyInfo.xcprivacy file
                    // to describe your plugin's privacy impact, and then uncomment this line.
                    // For more information, visit:
                    // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
                    // .process("PrivacyInfo.xcprivacy"),
    
                    // TODO: If you have other resources that need to be bundled with your plugin, refer to
                    // the following instructions to add them:
                    // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package
                ],
                cSettings: [
                    // TODO: Update your plugin name.
                    .headerSearchPath("include/plugin_name")
                ]
            )
        ]
    )
    
  5. Actualiza las plataformas soportadas en tu archivo Package.swift.

    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. Actualiza los nombres del paquete, biblioteca y target en tu archivo Package.swift.

    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: [
            .package(name: "FlutterFramework", path: "../FlutterFramework")
        ],
        targets: [
            .target(
                // TODO: Update your target name.
                name: "plugin_name",
                dependencies: [
                    .product(name: "FlutterFramework", package: "FlutterFramework")
                ],
                resources: [
                    // TODO: If your plugin requires a privacy manifest
                    // (for example, if it uses any required reason APIs),
                    // update the PrivacyInfo.xcprivacy file
                    // to describe your plugin's privacy impact, and then uncomment this line.
                    // For more information, visit:
                    // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
                    // .process("PrivacyInfo.xcprivacy"),
    
                    // TODO: If you have other resources that need to be bundled with your plugin, refer to
                    // the following instructions to add them:
                    // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package
                ],
                cSettings: [
                    // TODO: Update your plugin name.
                    .headerSearchPath("include/plugin_name")
                ]
            )
        ]
    )
    
  7. Si tu plugin tiene un archivo PrivacyInfo.xcprivacy, muévelo a ios/plugin_name/Sources/plugin_name/PrivacyInfo.xcprivacy y desmarca el comentario del recurso en el archivo Package.swift.

    Package.swift
    swift
                resources: [
                    // TODO: If your plugin requires a privacy manifest
                    // (for example, if it uses any required reason APIs),
                    // update the PrivacyInfo.xcprivacy file
                    // to describe your plugin's privacy impact, and then uncomment this line.
                    // For more information, visit:
                    // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
                    .process("PrivacyInfo.xcprivacy"),
    
                    // TODO: If you have other resources that need to be bundled with your plugin, refer to
                    // the following instructions to add them:
                    // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package
                ],
    
  8. Mueve cualquier archivo de recursos de ios/Assets a ios/plugin_name/Sources/plugin_name (or a subdirectory). Agrega los archivos de recursos a tu archivo Package.swift, si corresponde. Para obtener más instrucciones, visita https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package.

  9. Mueve cualquier cabecera pública de ios/Classes a ios/plugin_name/Sources/plugin_name/include/plugin_name.

    • Si no estás seguro de qué cabeceras son públicas, comprueba el atributo public_header_files de tu archivo podspec. Si este atributo no está especificado, todas tus cabeceras eran públicas. Deberías considerar si deseas que todas tus cabeceras sean públicas.

    • La clase pluginClass definida en tu archivo pubspec.yaml debe ser pública y estar dentro de este directorio.

  10. Manejo de modulemap.

    Omite este paso si tu plugin no tiene un modulemap.

    Si estás usando un modulemap para CocoaPods para crear un submódulo de prueba, considera eliminarlo para Swift Package Manager. Ten en cuenta que esto hace que todas las cabeceras públicas estén disponibles a través del módulo.

    Para eliminar el modulemap para Swift Package Manager pero mantenerlo para CocoaPods, excluye el modulemap y la cabecera umbrella en el archivo Package.swift del plugin.

    El ejemplo a continuación asume que el modulemap y la cabecera umbrella se encuentran en el directorio ios/plugin_name/Sources/plugin_name/include.

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

    Si deseas mantener tus pruebas unitarias compatibles tanto con CocoaPods como con Swift Package Manager, puedes intentar lo siguiente:

    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
    

    Si deseas utilizar un modulemap personalizado con tu paquete Swift, consulta la documentación de Swift Package Manager.

  11. Mueve todos los archivos restantes de ios/Classes a ios/plugin_name/Sources/plugin_name.

  12. Los directorios ios/Assets, ios/Resources y ios/Classes ahora deberían estar vacíos y se pueden eliminar.

  13. Agrega FlutterFramework como una dependencia.

    Actualiza Package.swift para incluir FlutterFramework:

    Package.swift
    swift
    dependencies: [
        .package(name: "FlutterFramework", path: "../FlutterFramework")
    ],
    targets: [
        .target(
            // TODO: Update your target name.
            name: "plugin_name",
            dependencies: [
                .product(name: "FlutterFramework", package: "FlutterFramework")
            ]
        )
    ]
    
  14. Si tus archivos de cabecera ya no están en el mismo directorio que tus archivos de implementación, debes actualizar tus declaraciones de importación.

    Por ejemplo, imagina la siguiente migración:

    • Antes:

      ios/Classes/
      ├── PublicHeaderFile.h
      └── ImplementationFile.m
      
    • Después:

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

    En este ejemplo, se deben actualizar las declaraciones de importación en ImplementationFile.m:

    Sources/plugin_name/ImplementationFile.m
    objc
    #import "PublicHeaderFile.h"
    #import "./include/plugin_name/PublicHeaderFile.h"
    
  15. Si tu plugin utiliza Pigeon, actualiza tu archivo de entrada de Pigeon.

    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',
    

    Si tu archivo objcHeaderOut ya no está dentro del mismo directorio que objcSourceOut, puedes cambiar el #import usando 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',
    

    Ejecuta Pigeon para volver a generar su código con la última configuración.

  16. Actualiza tu archivo Package.swift con las personalizaciones que puedas necesitar.

    1. En Xcode, abre el directorio ios/plugin_name/.

    2. En Xcode, abre tu archivo Package.swift. Verifica que Xcode no produzca advertencias o errores para este archivo.

    3. Si tu archivo ios/plugin_name.podspec tiene dependencias de CocoaPods, agrega las correspondientes dependencias de Swift Package Manager a tu Package.swift.

    4. Si tu paquete debe vincularse explícitamente como static o dynamic (no recomendado por Apple), actualiza el Product para definir el tipo:

      Package.swift
      swift
      products: [
          .library(name: "plugin-name", type: .static, targets: ["plugin_name"])
      ],
      
    5. Realiza cualquier otra personalización. Para obtener más información sobre cómo escribir un archivo Package.swift, visita https://developer.apple.com/documentation/packagedescription.

  17. Actualiza tu archivo ios/plugin_name.podspec para que apunte a las nuevas rutas.

    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']}
    
  18. Actualiza la carga de recursos desde el bundle para usar 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"];
    
  19. Si tu directorio ios/plugin_name/Sources/plugin_name/include solo contiene un archivo .gitkeep, querrás actualizar tu .gitignore para incluir lo siguiente:

    .gitignore
    !.gitkeep
    

    Ejecuta flutter pub publish --dry-run para asegurar que el directorio include esté publicado.

  20. Confirma los cambios de tu plugin en tu sistema de control de versiones.

  21. Verifica que el plugin aún funcione con CocoaPods.

    1. Desactivar Swift Package Manager:

      sh
      flutter config --no-enable-swift-package-manager
      
    2. Navega a la aplicación de ejemplo del plugin.

      sh
      cd path/to/plugin/example/
      
    3. Asegúrate de que la aplicación de ejemplo del plugin se compile y se ejecute.

      sh
      flutter run
      
    4. Navega al directorio de nivel superior del plugin.

      sh
      cd path/to/plugin/
      
    5. Ejecuta los lints de validación de CocoaPods:

      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
      
  22. Verifica que el plugin funcione con Swift Package Manager.

    1. Activar Swift Package Manager:

      sh
      flutter config --enable-swift-package-manager
      
    2. Navega a la aplicación de ejemplo del plugin.

      sh
      cd path/to/plugin/example/
      
    3. Asegúrate de que la aplicación de ejemplo del plugin se compile y se ejecute.

      sh
      flutter run
      
    4. En Xcode, abre la aplicación de ejemplo del plugin. Asegúrate de que Package Dependencies se muestre en el Project Navigator de la izquierda.

  23. Verifica que las pruebas pasen.

#

Si tu plugin incluye un ejemplo, se recomienda agregar el plugin como un paquete local en la aplicación de ejemplo. Esto no es obligatorio, pero proporciona un mejor soporte de Xcode al editar el código fuente del plugin en la aplicación de ejemplo. Visita el issue #179032.

Agregar el plugin como paquete local

#
  1. En una terminal navega a my_plugin.

  2. En Xcode, ejecuta el siguiente comando para abrir el workspace de la aplicación de ejemplo, (reemplaza ios con macos según corresponda):

bash
open example/ios/Runner.xcworkspace
  1. Haz clic derecho en Flutter > Add Files to “Runner”.

    Add Files to Runner

  2. Selecciona my_plugin/ios/my_plugin (o macos o darwin, según corresponda).

  3. Asegúrate de que “Reference files in place” esté seleccionado (debería ser la opción predeterminada) y haz clic en Finish.

    Selecciona Reference files in place

Esto agrega el plugin como un paquete local, pero está referenciado por una ruta absoluta, lo cual no es deseable para la distribución. Para cambiarlo a una ruta relativa, utiliza las siguientes instrucciones.

Cambiar a ruta relativa

#
  1. Copia la “Ruta completa” (Full Path) del plugin desde el File Inspector.

    Copiar Ruta completa (Full Path)

  2. En la terminal: open -a Xcode example/ios/Runner.xcodeproj/project.pbxproj

  3. Busca lo siguiente:

    path = [COPIED FULL PATH]; sourceTree = "<absolute>"
    

    Por ejemplo:

    path = /Users/username/path/to/my_plugin/ios/my_plugin; sourceTree = "<absolute>"
    
  4. Y reemplaza con la ruta relativa:

    path = ../../ios/my_plugin; sourceTree = "<group>"
    

    (Ajusta ios a macos o darwin según sea necesario).

Cómo actualizar las pruebas unitarias en la aplicación de ejemplo de un plugin

#

Si tu plugin tiene XCTests nativos, es posible que debas actualizarlos para que funcionen con Swift Package Manager si se cumple una de las siguientes condiciones:

  • Estás usando una dependencia de CocoaPods para la prueba.
  • Tu plugin está configurado explícitamente como type: .dynamic en su archivo Package.swift.

Para actualizar tus pruebas unitarias:

  1. En Xcode, abre tu example/ios/Runner.xcworkspace.

  2. Si estabas usando una dependencia de CocoaPods para las pruebas, como OCMock, querrás eliminarla de tu archivo Podfile.

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

    Luego en la terminal, ejecuta pod install en el directorio plugin_name_ios/example/ios.

  3. Navega a Package Dependencies para el proyecto.

    Las dependencias de paquetes del proyecto

    Las dependencias de paquetes del proyecto

  4. Haz clic en el botón + y agrega cualquier dependencia exclusiva de pruebas buscándola en la barra de búsqueda superior derecha.

    Buscar dependencias exclusivas de pruebas

    Buscar dependencias exclusivas de pruebas

  5. Asegúrate de que la dependencia esté agregada al Target RunnerTests.

    Asegúrate de que la dependencia esté agregada al target `RunnerTests`

    Asegúrate de que la dependencia esté agregada al target RunnerTests

  6. Haz clic en el botón Add Package.

  7. Si has configurado explícitamente el tipo de biblioteca de tu plugin como .dynamic en su archivo Package.swift (no recomendado por Apple), también deberás agregarlo como una dependencia al target RunnerTests.

    1. Asegúrate de que RunnerTests Build Phases tenga una fase de compilación Link Binary With Libraries:

      La fase de compilación `Link Binary With Libraries` en el target `RunnerTests`

      La fase de compilación Link Binary With Libraries en el target RunnerTests

      Si la fase de compilación no existe ya, crea una. Haz clic en el botón add y luego haz clic en New Link Binary With Libraries Phase.

      Agregar la fase de compilación `Link Binary With Libraries`

      Agregar la fase de compilación Link Binary With Libraries

    2. Navega a Package Dependencies para el proyecto.

    3. Haz clic en el botón add.

    4. En el diálogo que se abre, haz clic en el botón Add Local....

    5. Navega a plugin_name/plugin_name_ios/ios/plugin_name_ios y haz clic en el botón Add Package.

    6. Asegúrate de que esté agregado al target RunnerTests y haz clic en el botón Add Package.

  8. Asegúrate de que las pruebas pasen Product > Test.