XCFrameworks – Binary Frameworks in Swift

La pasada WWDC nos trajo grandes avances en el desarrollo de apps multi plataforma. Por un lado SwiftUI, con su Learn once writte everywhere, nos da un framework para escribir las vistas de nuestras apps usando una misma sintaxis independientemente de la plataforma para la que estemos desarrollando.

Pero hubo otra novedad que paso un tanto inadvertida pero que también aportan un gran avance en cuanto al desarrollo centrado en la parte de los frameworks. Estamos hablando de los frameworks binarios también llamados XCFrameworks.

¿Pero qué son los XCFrameworks?

Imagina que has desarrollado un framework que vas a usar en tu app para iPhone y iPad. Lo normal es que hayas elegido un tipo de proyecto Framework para esa plataforma.

Al cabo del tiempo añades soporte para macOS, por lo que creas otro nuevo target de tipo Framework para la plataforma Mac, y cuando tienes que distribuirla debes tener en cuanta si el desarrollador lo quiere para una plataforma u otra.

Pues bien, los frameworks binarios te permiten distribuir un único paquete que sirve para todas las plataformas.

XCFrameworks te permiten empaquetar diferentes variantes de tu framework, tantas como sistemas soporte Xcode (iOS, macOS, watchOS, tvOS, Mac Catalyst…).

Adolfo

Y no sólo código, también puedes incluir assets (imágenes, archivos de sonido), documentos JSON, modelos de datos Core Data

Caso práctico. Ambiently

En mi caso tengo una app desarrollada usando SwiftUI llamada Ambiently que sólo está disponible para iPhone y iPad, además, toda la lógica de la aplicación está en un target de tipo Framework para iOS llamada AmbientlyKit.

Ambiently ya está disponible en la App Store

Como desarrollar con SwiftUI es bastante rápido me he decidido a portar la app a la plataforma Mac, y como la lógica de la aplicación es la misma sea cual sea la plataforma, sólo tengo que desarrollar las vistas en SwiftUI para el Mac.

Sin embargo que el código de AmbientlyKit valga tanto para iOS como para Mac no quieres decir que lo pueda usar directamente en el proyecto de app para Mac. Si añades ese framework te dirá que ha sido generado para la plataforma iOS y no puede usarse en el Mac.

Seguro que estás pensado en Mac Catalyst ¿Por qué no marcas el checkbox de soporte para Mac del framework? Pues porque eso genera un framework para una app que use el proyecto Mac Catalyst, no vale para una app 100% Mac.

Manos a la obra. Creando el proyecto para el framework

Lo primero que voy a hacer es crear un único proyecto para el nuevo framework binario. Si abres Xcode y buscar este tipo de framework y no lo encuentras no te preocupes, es que no lo hay.

Lo que he hecho ha sido crear un proyecto vacío (Empty) en el que he creado un grupo llamado Shared Code donde están los archivos Swift, el modelo de Core Data y todos los assets.

Además he creado dos targets, uno de tipo framework para macOS llamada AmbientlyKit y otro de tipo framework para iOS llamado AmbientlyTouch

El código fuente y los targets para iOS y macOS

En los targets sólo he añadido extensiones de las clases o estructuras cuya funcionalidad varía dependiendo de la plataforma, por ejemplo, cuando se usa el framework AVFoundation, que permite o no determinadas opciones dependiendo de la plataforma.

Generando el XCFramework

Por desgracia en Xcode 11 no hay ninguna opción de menú o ajuste de compilación que genere de forma automática un XCFramework. De momento tenemos que hacer uso de Terminal y xcodebuild

Lo primero que tenemos que hacer es establecer una opción Build Libraries for Distribution con el valor Yes en cada uno de los framework que tiene nuestro proyecto.

Opción en la pestaña Build Settings

Una vez hecho esto nos toca abrir Terminal y usar la herramienta de línea de comandos de Xcode llamada xcodebuild. La generación de un xcframework se hace en dos pasos:

  1. xcodebuild archive.
  2. xcodebuild -create-xcframework

La opción archive genera un Archive del proyecto o esquema, esto es, se genera con el modo Release y se empaqueta listo para ser usado en la App Store, bien como app o bien como parte de una, además, se incluye los signos de depuración por lo que los usuarios de nuestros frameworks tendrán información si se produce un error al usar nuestros frameworks.

Por último -create-xcframework hace lo que dice, generar un XCFramework, que no es más que una carpeta con los diferentes frameworks que queremos integrar.

Bajo estás líneas os dejo un script con el que genero el XCFramework con frameworks para iOS, macOS, Simulador de iOS y Mac Catalist.

Los parámetros que uso en este caso para generar el archivo son…

  • -scheme El esquema del proyecto que queremos compilar
  • -destination La plataforma para la que queremos generar el framework.
  • -archivePath Ruta donde se generar el resultado de la operación de Archive
  • SKIP_INSTALL Con el valor a NO le decimos a xcodebuild que instale el framework en el archive resultante

Las plataformas disponibles que acepta el parámetro -destination en este momento son las siguientes

  • iOS
  • iOS Simulator
  • watchOS
  • watchOS Simulator
  • tvOS
  • tvOS Simulator
  • macOS
  • macOS/variant=Mac Catalyst

Y por último tenemos que generar el XCFramework con xcodebuild -create-xcframework que acepta los parámetros…

  • -framework Ubicación del archivo *.framework que queremos incorporar en nuestro XCFramework
  • -output Carpeta en la que queremos que se genere el XCFramework

El shell script hay que ejecutarlo en la carpeta donde se encuentra el archivo de proyecto de Xcode

Añadiendo el XCFramework al proyecto

Si todo ha ido bien en la carpeta de destino que hemos especificado en nuestro shell script tendremos la carpeta XCFramework., ya sólo nos queda usarlo en el proyecto de nuestra app.

Y esto podemos hacer arrastrando la carpeta XCFramework a la lista de Frameworks, Libraries and Embebed Content de la pestaña General de la vista de detalles del target de la app

No importa si es la app para iOS o si es la app para macOS, el XCFramework es el mismo.

Enlaces de interés