¿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:
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:
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.
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
):
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
).
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 ).
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
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).
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 ).
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 .
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
).
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 "" } }
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
Aquí está el podspec https://github.com/onmyway133/Arcane/blob/master/Arcane.podspec
s.source_files = 'Sources/**/*.swift' s.xcconfig = { 'SWIFT_INCLUDE_PATHS' => '$(PODS_ROOT)/CommonCryptoSwift/Sources/CCommonCrypto' } s.preserve_paths = 'Sources/CCommonCrypto/module.modulemap'
Usar module_map
no funciona, ver https://github.com/CocoaPods/CocoaPods/issues/5271
El uso de Local Development Pod with path
no funciona, consulte https://github.com/CocoaPods/CocoaPods/issues/809
Es por eso que ves que mi archivo de ejemplo Podfile https://github.com/onmyway133/CommonCrypto.swift/blob/master/Example/CommonCryptoSwiftDemo/Podfile apunta al repository de git
target 'CommonCryptoSwiftDemo' do pod 'CommonCryptoSwift', :git => 'https://github.com/onmyway133/CommonCrypto.swift' end
🐘 enfoque de encabezado público
Ji es un contenedor de libxml2 y utiliza un enfoque de encabezado público
Tiene un archivo de encabezado https://github.com/honghaoz/Ji/blob/master/Source/Ji.h con Target Membership
establecida en Public
Tiene una lista de archivos de encabezado para libxml2 https://github.com/honghaoz/Ji/tree/master/Source/Ji-libxml
Tiene Configuraciones de comstackción -> Rutas de búsqueda de encabezado
$(SDKROOT)/usr/include/libxml2
Tiene Configuraciones de comstackción -> Otras banderas enlazadoras
-lxml2
🐏 Cocoapods con enfoque de encabezado público
Eche un vistazo al podspec https://github.com/honghaoz/Ji/blob/master/Ji.podspec
s.libraries = "xml2" s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2', 'OTHER_LDFLAGS' => '-lxml2' }
🐝 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.