
En la vida de muchas compañías que tienen y desarrollan su propia pila de bibliotecas y componentes, llega un momento en que el volumen de esta pila se vuelve difícil de mantener.
En el caso del desarrollo para la plataforma iOS, y en general, el ecosistema de Apple, hay dos opciones para conectar bibliotecas como dependencias:
- Recójalos cada vez que construyas la aplicación.
- Recójalos por adelantado utilizando las dependencias ya recopiladas.
Al elegir el segundo enfoque, resulta lógico usar sistemas CI / CD para ensamblar bibliotecas en artefactos listos para usar.
Sin embargo, la necesidad de construir bibliotecas para varias plataformas o arquitecturas de procesador en el ecosistema de Apple, a menudo requiere operaciones no siempre triviales, tanto al construir la biblioteca como al producto final que la usa.
En este contexto, fue difícil no darse cuenta y fue extremadamente interesante estudiar una de las innovaciones de Apple presentadas en WWDC 2019 como parte de la presentación de Binary Frameworks en Swift : el formato de empaque de los marcos es XCFramework.
XCFramework tiene varias ventajas sobre los enfoques establecidos:
- Empaque de dependencia para todas las plataformas y arquitecturas de destino en un solo paquete listo para usar.
- Paquete de conexión en formato XCFramework, como una dependencia única para todas las plataformas y arquitecturas de destino.
- No es necesario construir un marco gordo / universal.
- No es necesario deshacerse del segmento x86_64 antes de cargar las aplicaciones finales en la AppStore.
En este artículo, explicaremos por qué se introdujo este nuevo formato, qué es y también qué le da al desarrollador.
Apple lanzó anteriormente el administrador de dependencias Swift Package Manager .
La conclusión es que Swift PM le permite entregar bibliotecas en forma de código fuente abierto con una descripción de las dependencias.
Desde la perspectiva del desarrollador que suministra la biblioteca, me gustaría destacar dos aspectos de Swift PM.
- El inconveniente obvio es que, por una razón u otra, no todos los proveedores de bibliotecas desean abrir su código fuente a los consumidores.
- Una ventaja obvia: al compilar dependencias de fuentes, nos deshacemos de la necesidad de observar la compatibilidad binaria de las bibliotecas.
XCFramework Apple ofrece como un nuevo formato binario para bibliotecas de envases, considerándolo como una alternativa a los paquetes Swift.
Este formato, así como la capacidad de conectar la biblioteca ensamblada en XCFramework, está disponible a partir de Xcode 11 y sus versiones beta.
¿Qué es XCFramework?
En esencia, XCFramework es una nueva forma de empaquetar y entregar bibliotecas, en sus diversas versiones.
Entre otras cosas, el nuevo formato también permite el empaquetado de bibliotecas estáticas junto con sus archivos de encabezado, incluidos los escritos en C / Objective-C.
Considere el formato con más detalle.
Empaque de dependencia para todas las plataformas y arquitecturas de destino en un solo paquete listo para usar
Todos los conjuntos de bibliotecas para cada una de las plataformas y arquitecturas de destino ahora se pueden empaquetar en un solo paquete con la extensión .xcframework.
Sin embargo, para esto, en este momento, debe usar scripts para invocar el xcodebuild
con el nuevo -create-xcframework
para Xcode 11.
El proceso de ensamblaje y empaque será considerado más a fondo.
Paquete de conexión en formato XCFramework, como una dependencia única para todas las plataformas y arquitecturas de destino
Dado que el paquete .xcframework contiene todas las opciones de ensamblaje de dependencias necesarias, no debemos preocuparnos por su arquitectura y plataforma de destino.
En Xcode 11, una biblioteca empaquetada en .xcframework está conectada como un .framework normal.
Más específicamente, esto se puede lograr de las siguientes maneras en la configuración de destino:
- agregar .xcframework a la sección "Marcos y bibliotecas" de la pestaña "General"
- agregar .xcframework a "Vincular binario con bibliotecas" en la pestaña "Fases de compilación"
No es necesario construir un marco gordo / universal
Anteriormente, para admitir varias bibliotecas y varias arquitecturas en una biblioteca de complementos, era necesario preparar los llamados marcos universales o gordos.
Esto se hizo usando el lipo
para coser todas las opciones del marco ensamblado en un solo binario grueso.
Se pueden encontrar más detalles sobre esto, por ejemplo, en los siguientes artículos:
No es necesario deshacerse del segmento x86_64 antes de cargar aplicaciones finales en la AppStore
Por lo general, dicho segmento se utiliza para proporcionar bibliotecas en el simulador de iOS.
Cuando intenta descargar una aplicación con dependencias que contienen un segmento x86_64 en la AppStore, puede encontrar el conocido error ITMS-90087 .
Crear y empaquetar XCFramework: teoría
En la presentación mencionada anteriormente, se requieren varios pasos necesarios para ensamblar y empaquetar la biblioteca en formato XCFramework:
Preparación del proyecto
Para comenzar, en todas las áreas de destino del proyecto que son responsables de construir la biblioteca para las plataformas de destino, debe habilitar la nueva configuración Construir bibliotecas para distribución para Xcode 11.

Montaje de proyectos para plataformas y arquitecturas de destino.
A continuación, tenemos que recopilar todos los objetivos para las plataformas y arquitecturas de destino.
Consideremos ejemplos de comandos de llamada utilizando el ejemplo de una configuración de proyecto específica.
Digamos que en el proyecto tenemos dos esquemas "XCFrameworkExample-iOS" y "XCFramework-macOS".

También en el proyecto hay dos objetivos que recopilan la biblioteca para iOS y macOS.

Para construir todas las configuraciones de biblioteca requeridas, necesitamos recolectar ambos objetivos usando los esquemas correspondientes.
Sin embargo, para iOS, necesitamos dos ensamblajes: uno para dispositivos finales (ARM) y el otro para el simulador (x86_64).
En total, necesitamos recopilar 3 marcos.
Para hacer esto, puede usar el comando xcodebuild
:
Como resultado, obtuvimos 3 marcos ensamblados, que empacaremos en el contenedor .xcframework.
Embalaje ensamblado .framework en .xcframework
Puede hacer esto con el siguiente comando:
xcodebuild -create-xcframework \ -framework "./build/ios.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -framework "./build/ios_sim.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -framework "./build/macos.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -output "./build/XCFrameworkExample.xcframework"
Puede haber muchas -framework
parámetros de -framework
que apuntan a todos los ensamblados .framework que desea adjuntar al contenedor .xcframework.
Preparación de un proyecto de biblioteca para el futuro ensamblaje y empaque de XCFramework
TL; DR: el proyecto terminado se puede descargar desde el repositorio en Github .
Como ejemplo, estamos implementando una biblioteca que estará disponible para dos plataformas: iOS y macOS.
Utilizaremos la configuración del proyecto mencionada en la sección anterior del artículo: dos esquemas y dos objetivos Framework correspondientes para plataformas iOS y macOS.
¿La biblioteca misma nos proporcionará una extensión simple para String?
( Optional where Wrapped == String
), con una sola propiedad.
Llamamos a esta propiedad isNilOrEmpty
y, como su nombre lo indica, ¿nos isNilOrEmpty
cuando esté dentro de String?
valor perdido o la cadena almacenada dentro está vacía.
El código se puede implementar de la siguiente manera:
public extension Optional where Wrapped == String { var isNilOrEmpty: Bool { if case let .some(string) = self { return string.isEmpty } return true } }
Procedemos directamente a la creación y configuración del proyecto.
Para empezar, necesitamos crear un proyecto del tipo "Marco" para una de las dos plataformas de destino que elija: iOS o macOS.
Puede hacer esto en Xcode a través del elemento de menú “Archivo” => “Nuevo” => “Proyecto”, o usando el atajo de teclado ⇧ + + N (por defecto).
A continuación, en la parte superior del cuadro de diálogo, seleccione la plataforma deseada (iOS o macOS), seleccione el tipo de proyecto de Framework y vaya al botón "Siguiente".
En la siguiente pantalla, debemos establecer el nombre del proyecto en el campo "Nombre del producto".
Alternativamente, puede usar el nombre "base" del proyecto, en la configuración mencionada anteriormente es "XCFrameworkExample".
En el futuro, al configurar el proyecto, agregaremos sufijos que denoten plataformas al nombre base utilizado en el nombre del objetivo.
Después de eso, debe crear otro Target de tipo "Framework" en el proyecto para otra de las plataformas enumeradas (excepto para la que se creó originalmente el proyecto).
Para hacer esto, use el elemento de menú "Archivo" => "Nuevo" => "Destino".
A continuación, seleccionamos en el diálogo otra (relativa a la seleccionada en el párrafo 1) de las dos plataformas, después de lo cual seleccionamos nuevamente el tipo de proyecto "Marco".
Para el campo "Nombre del producto", podemos usar inmediatamente el nombre con el sufijo de la plataforma, para lo cual agregamos objetivo en este párrafo. Entonces, si la plataforma es macOS, entonces el nombre podría ser "XCFrameworkExample-macOS" (% base_name% -% platform%).
Configuraremos objetivos y gráficos para que sean más fáciles de distinguir.
Primero, cambie el nombre de nuestros esquemas y los objetivos asociados a ellos para que sus nombres reflejen las plataformas, por ejemplo, así:
- "XCFrameworkExample-iOS"
- "XCFrameworkExample-macOS"
Luego, agregue el archivo con el código de nuestra extensión para String?
al proyecto .swift String?
Agregue un nuevo archivo .swift al proyecto con el nombre "Opcional.swift".
Y en el archivo en sí ponemos la extensión mencionada anteriormente para Optional
.
Es importante no olvidar agregar el archivo de código a ambos objetivos.

Ahora tenemos un proyecto que podemos armar en XCFramework usando los comandos de la etapa anterior.
En esta etapa, puede usar el script bash en un archivo separado para construir la biblioteca y empaquetarla en el formato .xcframework. Además, esto permitirá en el futuro utilizar estos desarrollos para integrar la solución en el sistema CI / CD.
El script parece feo y simple y, de hecho, reúne los comandos de ensamblaje mencionados anteriormente:
Contenido de Xcframework
Como resultado de la secuencia de comandos de ensamblaje del párrafo anterior del artículo, obtenemos el codiciado paquete .xcframework, que se puede agregar al proyecto.
Si miramos dentro de este paquete, que, como .framework, es esencialmente una carpeta simple, veremos la siguiente estructura:

Aquí vemos que dentro de .xcframework hay ensamblados en formato .framework, desglosados por plataforma y arquitectura. También para describir el contenido del paquete .xcframework, dentro hay un archivo Info.plist.
El archivo Info.plist tiene los siguientes contenidos <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>AvailableLibraries</key> <array> <dict> <key>LibraryIdentifier</key> <string>ios-arm64</string> <key>LibraryPath</key> <string>XCFrameworkExample.framework</string> <key>SupportedArchitectures</key> <array> <string>arm64</string> </array> <key>SupportedPlatform</key> <string>ios</string> </dict> <dict> <key>LibraryIdentifier</key> <string>ios-x86_64-simulator</string> <key>LibraryPath</key> <string>XCFrameworkExample.framework</string> <key>SupportedArchitectures</key> <array> <string>x86_64</string> </array> <key>SupportedPlatform</key> <string>ios</string> <key>SupportedPlatformVariant</key> <string>simulator</string> </dict> <dict> <key>LibraryIdentifier</key> <string>macos-x86_64</string> <key>LibraryPath</key> <string>XCFrameworkExample.framework</string> <key>SupportedArchitectures</key> <array> <string>x86_64</string> </array> <key>SupportedPlatform</key> <string>macos</string> </dict> </array> <key>CFBundlePackageType</key> <string>XFWK</string> <key>XCFrameworkFormatVersion</key> <string>1.0</string> </dict> </plist>
Puede observar que para la tecla "CFBundlePackageType", en contraste con el formato .framework, se usa el nuevo valor "XFWK", no "FMWK".
Resumen
Por lo tanto, el formato de empaquetado de la biblioteca en XCFramework no es más que un contenedor normal para las bibliotecas compiladas en formato .framework.
Sin embargo, este formato le permite almacenar por separado y usar de forma independiente cada una de las arquitecturas y plataformas presentadas en su interior. Esto elimina una serie de problemas inherentes al enfoque generalizado para construir marcos gordos / universales.
Sea como fuere, en este momento, hay un matiz importante con respecto al tema del uso de XCFramework en proyectos reales: gestión de dependencia, que Apple no ha implementado en formato XCFramework.
Para estos fines, se utilizan habitualmente Swift PM, Carthage, CocoaPods y otros sistemas de gestión de dependencias y sus conjuntos. Por lo tanto, no es sorprendente que el soporte para el nuevo formato ya esté en marcha precisamente en los proyectos CocoaPods y Carthage .