在Swift框架中导入CommonCrypto

如何在iOS的Swift框架中导入CommonCrypto?

我明白如何在Swift应用程序中使用CommonCrypto:将#import <CommonCrypto/CommonCrypto.h>到桥接标头。

但是,Swift框架不支持桥头。 该文件说:

您可以导入具有纯粹的Objective-C代码库,纯Swift代码库或混合语言代码库的外部框架。 无论框架是用单一语言编写还是包含来自两种语言的文件,导入外部框架的过程都是相同的。 当您导入外部框架时,请确保您要导入的框架的“定义模块”构build设置设置为“是”。

您可以使用以下语法将框架导入到不同目标中的任何Swift文件:

 import FrameworkName 

不幸的是,导入CommonCrypto不起作用。 将#import <CommonCrypto/CommonCrypto.h>到伞头中也没有。

实际上,您可以构build一个“正常工作”的解决scheme(无需将module.modulemapSWIFT_INCLUDE_PATHS设置复制到您的项目中,正如其他解决scheme所要求的那样),但它确实需要您创build一个虚拟框架/模块将导入到你的框架本身。 我们也可以确保它可以工作,不pipe平台( iphoneosiphonesimulatormacosx )。

  1. 将新的框架目标添加到您的项目中,并在系统库之后命名, 例如 “CommonCrypto”。 (您可以删除伞头CommonCrypto.h 。)

  2. 添加一个新的configuration设置文件并命名, 例如 “CommonCrypto.xcconfig”。 (不要检查你的目标是否包括在内)。用下面的代码填充它:

     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. 在上面创build三个引用的模块映射文件,并使用以下代码填充它们:

    • 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 * } 

    (如果你正在运行testing版本,请将“Xcode.app”replace为“Xcode-beta.app”,如果没有运行El Capitan,请将10.11replace为当前的操作系统SDK。

  4. 在项目设置的“ 信息”选项卡上的“ configuration”下,将CommonCryptodebugging发布configuration设置CommonCrypto (引用CommonCrypto.xcconfig )。

  5. 在您的框架目标的构build阶段选项卡上,将CommonCrypto框架添加到目标依赖项 。 另外将libcommonCrypto.dylib添加到链接二进制库生成阶段。

  6. select产品中的 CommonCrypto.framework ,并确保其包装的目标成员资格设置为可选

您现在应该能够在您的包装框架中构build,运行和import CommonCrypto

有关示例,请参阅SQLite.swift如何使用虚拟sqlite3.framework

我发现一个GitHub项目成功地在Swift框架中使用CommonCrypto: SHA256-Swift 。 另外,这篇关于sqlite3的相同问题的文章很有用。

基于以上,步骤是:

1)在项目目录中创build一个CommonCrypto目录。 在其中,创build一个module.map文件。 模块映射将允许我们使用CommonCrypto库作为Swift中的一个模块。 其内容是:

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

2)在Build Settings中,在Swift Compiler – Search Paths中 ,添加CommonCrypto目录到导入pathSWIFT_INCLUDE_PATHS )。

构建设置

3)最后,将您的Swift文件中的CommonCrypto作为任何其他模块导入。 例如:

 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<CUnsignedChar>(result.mutableBytes) CC_MD5(data.bytes, CC_LONG(data.length), resultBytes) let resultEnumerator = UnsafeBufferPointer<CUnsignedChar>(start: resultBytes, length: result.length) let MD5 = NSMutableString() for c in resultEnumerator { MD5.appendFormat("%02x", c) } return MD5 } return "" } } 

限制

在另一个项目中使用自定义框架在编译时失败,错误missing required module 'CommonCrypto' 。 这是因为CommonCrypto模块似乎不包含在自定义框架中。 解决方法是在使用框架的项目中重复步骤2(设置Import Paths )。

模块映射不是平台独立的(它目前指向一个特定的平台,iOS 8模拟器)。 我不知道如何制作相对于当前平台的标题path。

iOS 8更新<=我们应该删除线路链接“CommonCrypto” ,以获得成功的编译。

编辑

我一直在得到

ld:找不到-lCommonCrypto用于体系结构x86_64的库clang:error:linker命令失败,退出代码1(使用-v查看调用)

除非我删除link "CommonCrypto" 。 一旦我删除这条线,它build立好了。

更简单,更健壮的一点是,创build一个名为“CommonCryptoModuleMap”的Aggregate目标,并使用Run Script阶段自动生成模块映射,并使用正确的Xcode / SDKpath:

在这里输入图像说明 在这里输入图像说明

运行脚本阶段应该包含这个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 <<EOF > "${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap/module.modulemap" module CommonCrypto [system] { header "${SDKROOT}/usr/include/CommonCrypto/CommonCrypto.h" export * } EOF 

使用shell代码和${SDKROOT}意味着您不必硬编码Xcode.apppath,这可能会改变系统到系统,特别是如果您使用xcode-select切换到beta版本,或者正在构buildCI服务器,其中多个版本安装在非标准位置。 您也不需要对SDK进行硬编码,因此这可以在iOS,MacOS等中使用。您也不需要在项目的源目录中放置任何东西。

在创build这个目标之后,让你的库/框架依靠Target Dependencies项目:

在这里输入图像说明

这将确保在构build框架之前生成模块映射。

macOS注意 :如果您也支持macOS ,则需要将macosx添加到刚刚创build的新聚合目标上的“ Supported Platforms构build设置,否则它将不会将模块映射放在正确的“ Debug派生”数据文件夹中与其余的框架产品。

在这里输入图像说明

接下来,将模块映射的父目录${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap到“标题searchpath”构build设置:

在这里输入图像说明

如果您在项目或xcconfig级别定义了searchpath,请记住添加$(inherited)行。

就是这样,你现在应该可以import CommonCrypto

这个答案讨论了如何使它在框架内工作,并与Cocoapods和Carthage一起工作

🐟模块映射方法

我在modulemap的封装器中使用modulemap映射https://github.com/onmyway133/arcane,https://github.com/onmyway133/Reindeer

对于那些header not found ,请看看https://github.com/onmyway133/Arcane/issues/4或者运行;xcode-select --install

  • 创build一个包含module.modulemap文件夹

      module CCommonCrypto { header "/usr/include/CommonCrypto/CommonCrypto.h" export * } 
  • 转到内置设置 – >导入path

      ${SRCROOT}/Sources/CCommonCrypto 

module与模块地图方法的Cocoapods

🐘公共标题的方法

public Cocoapods公共标题的方法

🐝有趣的相关post

模块映射解决scheme可以很好,对SDK的变化也很强大,但是我发现它们在实践中使用起来很尴尬,而且在把事情交给其他人的时候并不像我想的那样可靠。 为了使这一切更加万无一失,我采取了不同的方式:

只需复制标题。

我知道,脆弱。 但是,苹果公司几乎从来没有对CommonCrypto进行过重大的改变,而且我的梦想是,如果没有最终使CommonCrypto成为模块化的头部,他们也不会以任何重大的方式改变它。

通过“复制标题”,我的意思是“将所需的所有标题剪切并粘贴到项目中的一个大型标题中,就像预处理程序所做的那样”。 作为您可以复制或修改的示例,请参阅RNCryptor.h 。

请注意,所有这些文件都是根据APSL 2.0许可的,这种方法有意维护版权和许可证声明。 我的级联步骤在麻省理工学院授权,只适用于下一个许可证通知)。

我并不是说这是一个很好的解决scheme,但到目前为止,这似乎是一个非常简单的解决scheme,实施和支持。

@mogstad已经足够把@stephencelis解决scheme包装在Cocoapod中:

pod'libCommonCrypto'

其他可用的吊舱没有为我工作。

我的团队中的新成员不小心打破了其中一个最佳答案给出的解决scheme,因此我决定将其整合到一个名为CommonCryptoModule的小包装项目中。 您可以手动或通过Cocoapods进行安装:

 pod 'CommonCryptoModule', '~> 1.0.1' 

那么,你所要做的就是将模块导入你需要的CommonCrypto ,如下所示:

 import CommonCryptoModule 

希望别人认为这个有用。

我知道这是一个古老的问题。 但是我想出了一个在Swift项目中使用库的另一种方法,这对于那些不想导入这些答案中引入的框架的人来说可能是有帮助的。

在Swift项目中,创build一个Objective-C桥接头,在Objective-C中创buildNSData类(或使用该库的自定义类)。 唯一的缺点是你必须在Objective-C中编写所有的实现代码。 例如:

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

然后在你的objective-c桥接头文件中添加这个

 #import "NSData+NSDataEncryptionExtension.h" 

然后在Swift类中做类似的事情:

 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 } } 

它按预期工作。

这很简单。 加

 #import <CommonCrypto/CommonCrypto.h> 

到一个.h文件(您的项目的桥接头文件)。 作为惯例,您可以将其称为YourProjectName-Bridging-Header.h。

然后转到您的项目构build设置并寻找Swift编译器 – 代码生成。 在它下面,将桥接头的名称添加到条目“Objetive-C Bridging Header”中。

你完成了。 在你的Swift代码中不需要导入。 在这个桥接头文件中列出的任何公共Objective-C头文件对于Swift都是可见的。