Importación de CommonCrypto en un marco de Swift

¿Cómo se importa CommonCrypto en Swift framework para iOS?

Entiendo cómo usar CommonCrypto en una aplicación Swift: agrega #import al encabezado de puente.

Sin embargo, los marcos Swift no son compatibles con los encabezados puente. La documentación dice:

Puede importar marcos externos que tengan una base de código Objective-C pura, una base de código Swift pura o una base de código de idioma mixto. El proceso para importar un marco externo es el mismo ya sea que el marco esté escrito en un solo idioma o contenga archivos de ambos idiomas. Cuando importe un marco de trabajo externo, asegúrese de que la configuración de creación de Definir módulo para el marco que está importando esté configurado en Sí.

Puede importar un marco en cualquier archivo Swift dentro de un destino diferente usando la siguiente syntax:

 import FrameworkName 

Desafortunadamente, importar CommonCrypto no funciona. Tampoco agrega #import al encabezado del paraguas.

Algo un poco más simple y más robusto es crear un objective Agregado llamado “CommonCryptoModuleMap” con una fase Run Script para generar el mapa del módulo automáticamente y con la ruta correcta de Xcode / SDK:

enter image description here enter image description here

La fase Run Script debe contener este bash:

 # This if-statement means we'll only run the main script if the CommonCryptoModuleMap directory doesn't exist # Because otherwise the rest of the script causes a full recompile for anything where CommonCrypto is a dependency # Do a "Clean Build Folder" to remove this directory and trigger the rest of the script to run if [ -d "${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap" ]; then echo "${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap directory already exists, so skipping the rest of the script." exit 0 fi mkdir -p "${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap" cat < "${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap/module.modulemap" module CommonCrypto [system] { header "${SDKROOT}/usr/include/CommonCrypto/CommonCrypto.h" export * } EOF 

El uso del código del shell y ${SDKROOT} significa que no es necesario codificar la ruta Xcode.app, que puede variar de sistema a sistema, especialmente si usa xcode-select para cambiar a una versión beta, o si se basa en una Servidor CI donde se instalan múltiples versiones en ubicaciones no estándar. Tampoco necesita codificar el SDK de forma rígida, por lo que debería funcionar para iOS, macOS, etc. Tampoco necesita tener nada en el directorio de origen de su proyecto.

Después de crear este objective, haga que su biblioteca / marco dependa de él con un elemento Dependencias de destino:

enter image description here

Esto asegurará que el mapa del módulo se genere antes de que se cree su marco.

Nota de macOS : si también es compatible con macOS , deberá agregar macosx a la configuración de comstackción Supported Platforms en el nuevo objective agregado que acaba de crear; de lo contrario, no colocará el mapa del módulo en la carpeta de datos derivados de Debug correcta. con el rest de los productos de framework.

enter image description here

A continuación, agregue el directorio principal del mapa del módulo, ${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap , a la configuración de comstackción “Importar rutas” en la sección Swift ( SWIFT_INCLUDE_PATHS ):

enter image description here

Recuerde agregar una línea $(inherited) si tiene rutas de búsqueda definidas en el proyecto o en el nivel xcconfig.

Eso es todo, ahora debería poder import CommonCrypto

Actualización para Xcode 10

Xcode 10 ahora viene con un mapa de módulo CommonCrypto que hace innecesaria esta solución. Si desea admitir tanto Xcode 9 como 10, puede hacer una comprobación en la fase Ejecutar script para ver si el mapa del módulo existe o no, por ejemplo:

 COMMON_CRYPTO_DIR="${SDKROOT}/usr/include/CommonCrypto" if [ -f "${COMMON_CRYPTO_DIR}/module.modulemap" ] then echo "CommonCrypto already exists, skipping" else # generate the module map, using the original code above fi 

De hecho, puede crear una solución que “simplemente funcione” (no es necesario copiar las configuraciones module.modulemap y SWIFT_INCLUDE_PATHS en su proyecto, tal como lo requieren otras soluciones aquí), pero sí requiere que cree un marco / módulo ficticio que usted Importaremos en tu marco de trabajo. También podemos garantizar que funciona independientemente de la plataforma ( iphoneos , iphonesimulator o macosx ).

  1. Agregue un nuevo objective de infraestructura a su proyecto y asígnele un nombre después de la biblioteca del sistema, por ejemplo , “CommonCrypto”. (Puede eliminar el encabezado del paraguas, CommonCrypto.h ).

  2. Agregue un nuevo archivo de configuración de configuración y asígnele un nombre, por ejemplo , “CommonCrypto.xcconfig”. (No marque ninguno de sus objectives para su inclusión.) Rellene con lo siguiente:

     MODULEMAP_FILE[sdk=iphoneos*] = \ $(SRCROOT)/CommonCrypto/iphoneos.modulemap MODULEMAP_FILE[sdk=iphonesimulator*] = \ $(SRCROOT)/CommonCrypto/iphonesimulator.modulemap MODULEMAP_FILE[sdk=macosx*] = \ $(SRCROOT)/CommonCrypto/macosx.modulemap 
  3. Cree los tres archivos de mapa de módulo referenciados, arriba, y rellenelos con lo siguiente:

    • iphoneos.modulemap

       module CommonCrypto [system] { header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/CommonCrypto/CommonCrypto.h" export * } 
    • iphonesimulator.modulemap

       module CommonCrypto [system] { header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/CommonCrypto/CommonCrypto.h" export * } 
    • macosx.modulemap

       module CommonCrypto [system] { header "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/CommonCrypto/CommonCrypto.h" export * } 

    (Reemplace “Xcode.app” por “Xcode-beta.app” si está ejecutando una versión beta. Reemplace 10.11 con su SO SDK actual si no está ejecutando El Capitan).

  4. En la pestaña Información de la configuración de su proyecto, en Configuraciones , establezca las configuraciones de depuración y liberación de CommonCrypto en CommonCrypto ( haciendo referencia a CommonCrypto.xcconfig ).

  5. En la pestaña Crear fases de su objective de marco, agregue el marco CommonCrypto a Dependencias de destino . Además, agregue libcommonCrypto.dylib a la fase de comstackción de Link Binary With Libraries .

  6. Seleccione CommonCrypto.framework en Productos y asegúrese de que su Membresía de destino para su contenedor esté configurada como Opcional .

Ahora debería poder comstackr, ejecutar e import CommonCrypto en su marco de trabajo.

Para ver un ejemplo, vea cómo SQLite.swift usa un ficticio sqlite3.framework .

Encontré un proyecto GitHub que usa CommonCrypto con éxito en un marco Swift: SHA256-Swift . Además, este artículo sobre el mismo problema con sqlite3 fue útil.

En base a lo anterior, los pasos son:

1) Cree un directorio CommonCrypto dentro del directorio del proyecto. En el interior, crea un archivo module.map . El mapa del módulo nos permitirá usar la biblioteca CommonCrypto como un módulo dentro de Swift. Sus contenidos son:

 module CommonCrypto [system] { header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.0.sdk/usr/include/CommonCrypto/CommonCrypto.h" link "CommonCrypto" export * } 

2) En Configuraciones de comstackción, dentro del comstackdor Swift – Rutas de búsqueda , agregue el directorio CommonCrypto a Importar rutas ( SWIFT_INCLUDE_PATHS ).

Configuración de compilación

3) Finalmente, importe CommonCrypto dentro de sus archivos Swift como cualquier otro módulo. Por ejemplo:

 import CommonCrypto extension String { func hnk_MD5String() -> String { if let data = self.dataUsingEncoding(NSUTF8StringEncoding) { let result = NSMutableData(length: Int(CC_MD5_DIGEST_LENGTH)) let resultBytes = UnsafeMutablePointer(result.mutableBytes) CC_MD5(data.bytes, CC_LONG(data.length), resultBytes) let resultEnumerator = UnsafeBufferPointer(start: resultBytes, length: result.length) let MD5 = NSMutableString() for c in resultEnumerator { MD5.appendFormat("%02x", c) } return MD5 } return "" } } 

Limitaciones

El uso del marco personalizado en otro proyecto falla en tiempo de comstackción con el missing required module 'CommonCrypto' error missing required module 'CommonCrypto' . Esto se debe a que el módulo CommonCrypto no parece estar incluido en el marco personalizado. Una solución consiste en repetir el paso 2 (establecer Import Paths ) en el proyecto que utiliza el marco.

El mapa del módulo no es independiente de la plataforma (actualmente apunta a una plataforma específica, el simulador de iOS 8). No sé cómo hacer la ruta del encabezado en relación con la plataforma actual.

Actualizaciones para iOS 8 <= Debemos eliminar el enlace de línea “CommonCrypto” , para obtener la comstackción exitosa.

ACTUALIZAR / EDITAR

Seguí recibiendo el siguiente error de comstackción:

ld: biblioteca no encontrada para -lCommonCrypto para la architecture x86_64 clang: error: comando del enlazador falló con el código de salida 1 (use -v para ver la invocación)

A menos que haya eliminado el link "CommonCrypto" línea link "CommonCrypto" del archivo module.map que creé. Una vez que eliminé esta línea, creo bien.

Esta respuesta discute cómo hacer que funcione dentro de un marco, y con Cocoapods y Cartago

🐟 enfoque de mapa de módulos

Uso modulemap en mi envoltorio alrededor de CommonCrypto https://github.com/onmyway133/arcane , https://github.com/onmyway133/Reindeer

Para aquellos que header not found , por favor, xcode-select --install un vistazo a https://github.com/onmyway133/Arcane/issues/4 o ejecuten xcode-select --install

  • Crear una carpeta CCommonCrypto contenga module.modulemap

      module CCommonCrypto { header "/usr/include/CommonCrypto/CommonCrypto.h" export * } 
  • Ir a Configuraciones Construidas -> Importar Rutas

      ${SRCROOT}/Sources/CCommonCrypto 

🌳 Cocoapods con enfoque modulemap

🐘 enfoque de encabezado público

🐏 Cocoapods con enfoque de encabezado público

🐝 Interesantes publicaciones relacionadas

¡Buenas noticias! ¡Swift 4.2 (Xcode 10) finalmente proporciona CommonCrypto!

ADVERTENCIA: iTunesConnect puede rechazar aplicaciones que utilizan este método.


El nuevo miembro de mi equipo rompió accidentalmente la solución dada por una de las mejores respuestas, así que decidí consolidarlo en un pequeño proyecto de envoltura llamado CommonCryptoModule . Puede instalarlo manualmente o mediante Cocoapods:

 pod 'CommonCryptoModule', '~> 1.0.2' 

Entonces, todo lo que tiene que hacer es importar el módulo donde necesita CommonCrypto , así:

 import CommonCryptoModule 

Espero que alguien más encuentre esto útil.

@mogstad ha tenido la amabilidad de envolver la solución @stephencelis en un Cocoapod:

pod ‘libCommonCrypto’

Los otros pods disponibles no funcionaron para mí.

Las soluciones de módulo de módulo pueden ser buenas y robustas frente a los cambios del SDK, pero me resulta difícil usarlas en la práctica, y no tan confiables como me gustaría cuando se lo entrego a otros. Para intentar que todo sea más infalible, fui de otra manera:

Solo copia los encabezados.

Lo sé, frágil. Pero Apple casi nunca hace cambios significativos en CommonCrypto y estoy viviendo el sueño de que no lo cambiarán de ninguna manera significativa sin convertir finalmente CommonCrypto en un encabezado modular.

Al “copiar los encabezados” me refiero a “cortar y pegar todos los encabezados que necesita en un encabezado masivo en su proyecto, tal como lo haría el preprocesador”. Como ejemplo de esto que puede copiar o adaptar, vea RNCryptor.h .

Tenga en cuenta que todos estos archivos tienen licencia bajo APSL 2.0, y este enfoque mantiene intencionalmente los avisos de derechos de autor y licencia. Mi paso de concatenación está autorizado bajo MIT, y eso solo se aplica hasta el próximo aviso de licencia).

No estoy diciendo que esta sea una solución hermosa, pero hasta ahora parece haber sido una solución increíblemente simple tanto para la implementación como para el soporte.

Creo que tengo una mejora en el excelente trabajo de Mike Weller.

Agregue una fase Ejecutar script antes de la fase Compile Sources contiene este bash:

 # This if-statement means we'll only run the main script if the # CommonCrypto.framework directory doesn't exist because otherwise # the rest of the script causes a full recompile for anything # where CommonCrypto is a dependency # Do a "Clean Build Folder" to remove this directory and trigger # the rest of the script to run FRAMEWORK_DIR="${BUILT_PRODUCTS_DIR}/CommonCrypto.framework" if [ -d "${FRAMEWORK_DIR}" ]; then echo "${FRAMEWORK_DIR} already exists, so skipping the rest of the script." exit 0 fi mkdir -p "${FRAMEWORK_DIR}/Modules" cat < "${FRAMEWORK_DIR}/Modules/module.modulemap" module CommonCrypto [system] { header "${SDKROOT}/usr/include/CommonCrypto/CommonCrypto.h" export * } EOF ln -sf "${SDKROOT}/usr/include/CommonCrypto" "${FRAMEWORK_DIR}/Headers" 

Este script construye un framework básico con el module.map en el lugar correcto y luego se basa en la búsqueda automática de Xcode de BUILT_PRODUCTS_DIR para frameworks.

Vinculaba la carpeta de inclusión CommonCrypto original como la carpeta de encabezados del marco, por lo que el resultado también debería funcionar para los proyectos de Objective C.

Sé que esta es una vieja pregunta. Pero descubro una forma alternativa de usar la biblioteca en el proyecto Swift, que podría ser útil para aquellos que no quieren importar el marco presentado en estas respuestas.

En el proyecto Swift, cree un encabezado puente de Objective-C, cree la categoría NSData (o clase personalizada que para usar la biblioteca) en Objective-C. El único inconveniente sería que debe escribir todo el código de implementación en Objective-C. Por ejemplo:

 #import "NSData+NSDataEncryptionExtension.h" #import  @implementation NSData (NSDataEncryptionExtension) - (NSData *)AES256EncryptWithKey:(NSString *)key { //do something } - (NSData *)AES256DecryptWithKey:(NSString *)key { //do something } 

Y luego en su encabezado de puente objective-c, agregue esto

 #import "NSData+NSDataEncryptionExtension.h" 

Y luego en la clase Swift hacemos algo similar:

 public extension String { func encryp(withKey key:String) -> String? { if let data = self.data(using: .utf8), let encrypedData = NSData(data: data).aes256Encrypt(withKey: key) { return encrypedData.base64EncodedString() } return nil } func decryp(withKey key:String) -> String? { if let data = NSData(base64Encoded: self, options: []), let decrypedData = data.aes256Decrypt(withKey: key) { return decrypedData.UTF8String } return nil } } 

Funciona como se esperaba.

He añadido magia de cocoapods a la respuesta de jjrscott en caso de que necesite usar CommonCrypto en su biblioteca de cocoapods.


1) Agregue esta línea a su podspec:

 s.script_phase = { :name => 'CommonCrypto', :script => 'sh $PROJECT_DIR/../../install_common_crypto.sh', :execution_position => :before_compile } 

2) Guárdelo en la carpeta de su biblioteca o donde lo desee (sin embargo, no olvide cambiar el script_phase en consecuencia …)

 # This if-statement means we'll only run the main script if the # CommonCrypto.framework directory doesn't exist because otherwise # the rest of the script causes a full recompile for anything # where CommonCrypto is a dependency # Do a "Clean Build Folder" to remove this directory and trigger # the rest of the script to run FRAMEWORK_DIR="${BUILT_PRODUCTS_DIR}/CommonCrypto.framework" if [ -d "${FRAMEWORK_DIR}" ]; then echo "${FRAMEWORK_DIR} already exists, so skipping the rest of the script." exit 0 fi mkdir -p "${FRAMEWORK_DIR}/Modules" echo "module CommonCrypto [system] { header "${SDKROOT}/usr/include/CommonCrypto/CommonCrypto.h" export * }" >> "${FRAMEWORK_DIR}/Modules/module.modulemap" ln -sf "${SDKROOT}/usr/include/CommonCrypto" "${FRAMEWORK_DIR}/Headers" 

Funciona de maravilla 🙂

Si está aquí para los algoritmos hash, como SHA, MD5, etc., no use la abultada biblioteca CommonCrypto. Encuentre la biblioteca de hashing específica que está buscando en su lugar.

Por ejemplo, para MD5, puede ir con SwiftHash

Es muy sencillo. Añadir

 #import  

a un archivo .h (el archivo de encabezado de puente de su proyecto). Como una convención, puede llamarlo YourProjectName-Bridging-Header.h.

Luego vaya a la configuración de comstackción de su proyecto y busque Swift Compiler – Generación de código. Debajo de él, agregue el nombre de su encabezado de puente a la entrada “Cabecera de puente de Objective-C”.

Ya terminaste No se requieren importaciones en su código Swift. Todos los encabezados públicos de Objective-C listados en este archivo de encabezado de puente serán visibles para Swift.

    Intereting Posts