¿Cómo exportar Cocoa Touch Framework “gordo” (para simulador y dispositivo)?

107

Con Xcode 6 tenemos la posibilidad de crear una dinámica propia Cocoa Frameworks.

ingrese la descripción de la imagen aquí

Porque:

  • El simulador todavía usa la 32-bitbiblioteca

  • a partir del 1 de junio de 2015, las actualizaciones de la aplicación enviadas a la App Store deben incluir compatibilidad con 64 bits y estar compiladas con el SDK de iOS 8 ( developer.apple.com )

Tenemos que hacer una gran biblioteca para ejecutar proyectos en dispositivos y simuladores. es decir, soporta 32 y 64 bits en Frameworks.

Pero no encontré ningún manual sobre cómo exportar el marco universal fat para una futura integración con otros proyectos (y compartir esta biblioteca con alguien).

Aquí están mis pasos para reproducir:

  1. Ambientado ONLY_ACTIVE_ARCH=NOen elBuild Settings

    ingrese la descripción de la imagen aquí

  2. Agregue soporte armv7 armv7s arm64 i386 x86_64a Architectures(seguro)

ingrese la descripción de la imagen aquí

  1. Construya Framework y ábralo en Finder:

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

  1. Agregar este marco a otro proyecto

Resultado actual:

Pero al final todavía tengo problemas para ejecutar el proyecto con este marco en los dispositivos y el simulador a la vez.

  • si tomo el marco de la Debug-iphoneoscarpeta, funciona en dispositivos y obtiene un error en los simuladores:ld: symbol(s) not found for architecture i386

      xcrun lipo -info CoreActionSheetPicker

    Las arquitecturas en el archivo fat: CoreActionSheetPicker son: armv7 armv7s arm64

  • si tomo el marco de la Debug-iphonesimulatorcarpeta, funciona en simuladores. y tengo un error en el dispositivo:ld: symbol(s) not found for architecture arm64

      xcrun lipo -info CoreActionSheetPicker

    Las arquitecturas en el archivo fat: CoreActionSheetPicker son: i386 x86_64

Entonces, ¿cómo crear un marco dinámico que funcione en dispositivos y simuladores?

Esta respuesta relacionada con Xcode 6 iOS Creación de un marco Cocoa Touch - Problemas de arquitectura, pero no está duplicada.


Actualizar:

Encontré un "truco sucio" para este caso. Vea mi respuesta a continuación . Si alguien conoce la forma más conveniente, ¡hágamelo saber!

skywinder
fuente
problema duplicado stackoverflow.com/questions/24039470/…
Andrius Steponavičius
@ AndriusSteponavičius esta pregunta se hizo 2 meses antes.
skywinder
Sí, pero hay respuestas mucho más detalladas allí, que creo que los usuarios deberían conocer
Andrius Steponavičius
Establecer ONLY_ACTIVE_ARCH = NO en la Configuración de compilación es un paso importante.
Jedidja
su marco necesita ambos segmentos i386 x86_64 en el binario gordo si desea ejecutarlo en el simulador, INCLUSO SI SU COMPUTADORA TIENE UNA ARQUITECTURA DE 64 BITS !!! Aprendí eso de la manera más difícil.
J.beenie

Respuestas:

82

La realidad de esta respuesta es: julio de 2015. Lo más probable es que las cosas cambien.

TLDR;

Actualmente, Xcode no tiene herramientas para la exportación automática de un marco fat universal, por lo que el desarrollador debe recurrir al uso manual de la lipoherramienta. Además, de acuerdo con este radar, antes de enviarlo al desarrollador de la AppStore, que es el consumidor del marco, también debe usarlo lipopara eliminar las secciones del simulador de un marco.

Sigue una respuesta más larga


Hice una investigación similar sobre el tema (el enlace al final de la respuesta).

No había encontrado ninguna documentación oficial sobre la distribución de lo que mi investigación se basó en la exploración de Apple Developer foros, proyectos de Cartago y el reino y mis propios experimentos con xcodebuild, lipo, codesignherramientas.

Aquí hay una cita larga (con un poco de marcado de mi parte) de la aplicación de exportación de hilos de los foros de desarrolladores de Apple con marco integrado :

¿Cuál es la forma correcta de exportar un marco desde un proyecto marco?

Actualmente, la única forma es exactamente lo que ha hecho:

  • Cree el objetivo tanto para el simulador como para el dispositivo iOS.
  • Navegue a la carpeta DerivedData de Xcode para ese proyecto y lipo los dos binarios juntos en un solo marco. Sin embargo, cuando construya el objetivo del marco en Xcode, asegúrese de ajustar la configuración del objetivo 'Construir solo arquitectura activa' a 'NO'. Esto permitirá que Xcode construya el objetivo para múltiples tipos de binarty (arm64, armv7, etc.). Esta sería la razón por la que funciona desde Xcode pero no como un binario independiente.

  • También querrá asegurarse de que el esquema esté configurado para una versión de versión y compile el objetivo del marco contra la versión. Si aún recibe un error de biblioteca no cargada, verifique los segmentos de código en el marco.

  • Utilice lipo -info MyFramworkBinaryy examine el resultado.

lipo -info MyFrameworkBinary

El resultado es i386 x86_64 armv7 arm64

  • Los marcos universales modernos incluirán 4 porciones, pero podrían incluir más: i386 x86_64 armv7 arm64 si no ve al menos estos 4, podría deberse a la configuración Construir arquitectura activa.

Esto describe el proceso más o menos igual que @skywinder lo hizo en su respuesta.

Así es como Carthage usa lipo y Realm usa lipo .


DETALLE IMPORTANTE

Hay radar: Xcode 6.1.1 y 6.2: los marcos de iOS que contienen segmentos de simulador no se pueden enviar a la App Store y una larga discusión al respecto en Realm # 1163 y Carthage # 188 que terminó con una solución especial:

antes de enviarlo a la AppStore, los binarios del marco de trabajo de iOS deben eliminarse de las secciones del simulador

Carthage tiene un código especial: CopyFrameworks y la documentación correspondiente:

Este script soluciona un error de envío de la App Store provocado por binarios universales.

Realm tiene un script especial: strip-frameworks.sh y la documentación correspondiente:

Este paso es necesario para solucionar un error de envío de la App Store al archivar binarios universales.

También hay un buen artículo: Eliminación de arquitecturas no deseadas de bibliotecas dinámicas en Xcode .

Yo mismo utilicé Realm's, strip-frameworks.shque me funcionó perfectamente sin ninguna modificación, aunque, por supuesto, cualquiera es libre de escribir uno desde cero.


El enlace a mi tema que recomiendo leer porque contiene otro aspecto de esta pregunta: firma de código - Creación de marcos de trabajo iOS / OSX: ¿es necesario codificarlos antes de distribuirlos a otros desarrolladores?

Stanislav Pankevich
fuente
1
Usé lipo, pero cuando el marco se está construyendo en el simulador, muestra un identificador sin resolver con el nombre de la clase, pero en el dispositivo funciona. Si usa la versión del simulador de binario, entonces está funcionando ... ¿alguna idea?
Susim Samanta
2
No he encontrado ninguna evidencia de que Xcode 8.2 haya cambiado en diciembre de 2016.: /
Geoffrey Wiseman
1
@Geoffrey, ¿ha cambiado esto en Xcode 9.2 o hay algo diferente? Esta es la primera vez que creo un marco binario para la distribución, y ya tengo miedo ...
ScottyB
No he hecho esto en un momento, lamentablemente, no puedo decirlo. Buena suerte.
Geoffrey Wiseman
57

Esta no es una solución tan clara, pero hay una única manera que encuentro:

  1. Ambientado ONLY_ACTIVE_ARCH=NOen elBuild Settings

    • Construir biblioteca para simulador
    • Construir biblioteca para dispositivo
  2. Abrir en la Productscarpeta de la consola para su marco (puede abrirlo abriendo la carpeta del marco y cd ..desde allí)

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

  1. Ejecute este script desde la Productscarpeta. Crea Fat Framework en esta carpeta. (o hágalo manualmente como se explica a continuación en 3. 4. )

O:

  1. Combine estos 2 Frameworks usando lipo por este script (reemplace YourFrameworkNamea su nombre de Framework)

    lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
  2. Reemplazar con un nuevo binario de los marcos existentes:

    cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
    mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
    

  1. Beneficio: ./YourFrameworkName.framework- ¡ es un binario gordo listo para usar! ¡Puedes importarlo a tu proyecto!

Para el proyecto, que no está en los espacios de trabajo:

También puede intentar utilizar esta esencia como se describe aquí . Pero parece que no funciona para proyectos en espacios de trabajo.

skywinder
fuente
Pensé que Apple ya no acepta binarios gordos. kodmunki.wordpress.com/2015/03/04/…
Monstieur
1
@skywinder ¿Encontraste alguna otra forma sencilla de exportar Cocoa Touch Framework a un binario gordo listo para usar? Estoy usando el mismo enfoque que el anterior, pero no me gusta. Xcode debería tener algo que automatice el proceso.
dev gr
1
@devgr aún no ... es por eso que no acepté mi propia respuesta. Sigo buscando una mejor solución.
skywinder
1
No se puede ejecutar en el simulador pero funciona en el dispositivo con los pasos de 3 y 4
jose920405
1
@ ¿Alguien podría explicar por qué solo Debug-se usa la carpeta lipo -create? ¿Podría usarse este marco para la Releaseconfiguración y por qué? Gracias.
Yevhen Dubinin
10

La respuesta de @Stainlav fue muy útil, pero lo que hice fue compilar dos versiones del marco (una para el dispositivo y otra para el simulador) y luego agregué lo siguiente Run Script Phasepara copiar automáticamente el marco precompilado requerido para la arquitectura en ejecución

echo "Copying frameworks for architecture: $CURRENT_ARCH"
if [ "${CURRENT_ARCH}" = "x86_64" ] || [ "${CURRENT_ARCH}" = "i386" ]; then
  cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
  cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi

De esta manera, no tengo uso lipopara crear un marco gordo ni el Realm strip-frameworks.shpara eliminar las porciones innecesarias al enviarlo a la App Store.

odm
fuente
¿Con cuál te vinculas?
Jaka Jančar
@ JakaJančar Enlace contra los que están en la ${SRCROOT}/Frameworks/Activecarpeta. Se reemplazan por los marcos precompilados adecuados para la arquitectura activa en tiempo de compilación.
odm
2
¡Quiéralo! Esto es mucho más simple que el lipoenfoque de combinar y luego desgarrar .
clozach
2

básicamente para esto encontré una muy buena solución. solo tienes que seguir estos sencillos pasos.

  1. Crea un marco de toque de cacao.
  2. Establezca el código de bits habilitado en No.
  3. Seleccione su objetivo y elija editar esquemas. Seleccione Ejecutar y elija Liberar desde la pestaña Información.
  4. No se requiere ningún otro ajuste.
  5. Ahora cree el marco para cualquier simulador, ya que el simulador se ejecuta en arquitectura x86.
  6. Haga clic en el grupo Productos en el Navegador de proyectos y busque el archivo .framework.
  7. Haga clic derecho sobre él y haga clic en Mostrar en el buscador. Cópialo y pégalo en cualquier carpeta, personalmente prefiero el nombre 'simulador'.
  8. Ahora cree el marco para el dispositivo iOS genérico y siga los pasos del 6 al 9. Simplemente cambie el nombre de la carpeta a "dispositivo" en lugar de "simulador".
  9. Copie el archivo .framework del dispositivo y péguelo en cualquier otro directorio. Prefiero el super directorio inmediato de ambos. Entonces la estructura del directorio ahora se convierte en:
    • Escritorio
    • dispositivo
      • MyFramework.framework
    • simulador
      • MyFramework.framework
    • MyFramework.framework Ahora abra el terminal y vaya al escritorio. Ahora comience a escribir el siguiente comando:

lipo -create 'dispositivo / MyFramework.framework / MyFramework' 'simulador / MyFramework.framework / MyFramework' -output 'MyFramework.framework / MyFramework'

y eso es. Aquí fusionamos el simulador y la versión del dispositivo del binario MyFramework presente dentro de MyFramework.framework. Obtenemos un marco universal que se construye para todas las arquitecturas, incluidos el simulador y el dispositivo.

Nrip
fuente
Quiero crear un archivo FAT con bitcode habilitado. Por favor guíame.
user3898700
2

Solo quiero actualizar esta gran respuesta de @odm. Desde Xcode 10, la CURRENT_ARCHvariable ya no refleja la arquitectura de compilación. Así que cambié el script para verificar la plataforma en su lugar:

echo "Copying frameworks for platform: $PLATFORM_NAME"
rm -R "${SRCROOT}/Frameworks/Active"
if [ "${PLATFORM_NAME}" = "iphonesimulator" ]; then
    cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
    cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi

También agregué una línea para borrar el directorio de destino antes de copiar, porque noté que los archivos adicionales en los subdirectorios no se sobrescribirán de otra manera.

Dorian Roy
fuente
1

Mi respuesta cubre los siguientes puntos:

  • Hacer un marco que funcione tanto para el simulador como para el dispositivo

  • ¿Cómo exportar Cocoa Touch Framework “gordo” (tanto para Simulador como para Dispositivo)?

  • Símbolos no definidos para la arquitectura x86_64

  • ld: símbolo (s) no encontrado para la arquitectura x86_64

Pasos 1: primero cree sus marcos con Simulator target

Pasos 2: después del éxito del proceso de construcción del simulador, ahora cree para su marco con la selección de destino del dispositivo o la selección de dispositivo iOS genérico

Paso 3: Ahora seleccione el objetivo de su marco y para eso, en "Fases de compilación", seleccione "Agregar secuencia de comandos de ejecución" y copie el siguiente código de secuencia de comandos)

Paso 4: Ahora, finalmente, compile de nuevo y su marco estará listo para la compatibilidad tanto con el simulador como con el dispositivo. ¡¡¡¡Viva!!!!

[Nota: Debemos tener ambos marcos compatibles listos antes del paso 4 final (el simulador y la arquitectura del dispositivo son compatibles, de lo contrario, siga los pasos 1 y 2 anteriores correctamente)

Ver la imagen de referencia:

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Ponga el código debajo en el área de shell:

#!/bin/sh


UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal


# make sure the output directory exists

mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"


# Step 1. Build Device and Simulator versions

xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build


# Step 2. Copy the framework structure (from iphoneos build) to the universal folder

cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"


# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory

SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."

if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then

cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"

fi


# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory

lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"


# Step 5. Convenience step to copy the framework to the project's directory

cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"


# Step 6. Convenience step to open the project's directory in Finder

open "${BUILD_DIR}/${CONFIGURATION}-universal"

Sandip Patel - SM
fuente
Este script parece llamarse a sí mismo y, por lo tanto, provoca un bucle infinito. ¡Tuve que reiniciar mi computadora después de ejecutarla! Estaba generando continuamente nuevos procesos de xcodebuild ... y abriendo nuevas ventanas de búsqueda
Votaría
Consulte la respuesta de @ l0gg3r en este SO Q / A para ver un script similar sin el problema de recursividad.
J.beenie