在 Swift 框架中导入 Common Crypto

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

我知道如何在 Swift 应用程序中使用 CommonCrypto: 将 #import <CommonCrypto/CommonCrypto.h>添加到桥接头。 然而,Swift 框架并不支持桥接头,文件表示:

您可以导入具有纯 Objective-C 代码库、纯 Swift 代码库或混合语言代码库的外部框架 导入外部框架的过程是否相同 框架是用单一语言编写的,或者包含来自这两种语言的文件 导入外部框架时,请确保 定义要导入的框架的模块生成设置 是的。

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

import FrameworkName

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

111167 次浏览

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

基于以上所述,步骤如下:

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

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)在构建设置中,在 快速编译器-搜寻路径中,将 CommonCrypto目录添加到 进口路径(SWIFT_INCLUDE_PATHS)。

Build Settings

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)。

模块映射不是平台独立的(它目前指向一个特定的平台,iOS8模拟器)。我不知道如何使头路径相对于当前的平台。

IOS8的更新 < = 我们应该删除 链接“ Common Crypto”行,以获得成功的编译。

更新/编辑

我不断得到下面的构建错误:

Ld: 没有找到用于体系结构 x86 _ 64的-lCommonCrypto 库 错误: 链接器命令失败,退出代码1(使用-v 查看调用)

除非我从我创建的 module.map文件中删除了行 link "CommonCrypto"

很简单,加

#import <CommonCrypto/CommonCrypto.h>

作为一个约定,您可以称之为 YourProjectName-Bridge-Header.h。

然后转到项目“生成设置”,查找快速编译器代码生成。在它下面,将桥接头的名称添加到条目“ Objective-C 桥接头”中。

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

你实际上可以构建一个“正常工作”的解决方案(不需要把 模块,模块地图SWIFT_INCLUDE_PATHS设置复制到你的项目中,就像这里其他解决方案所要求的那样) ,但是它确实需要你创建一个虚拟的框架/模块,你将导入到你的框架中。我们还可以确保它的工作,而不管平台(iphoneosiphonesimulator,或 macosx)。

  1. 向项目中添加一个新的框架目标,并以系统库 例如:。命名它,即“ CommonCrypto”。(可以删除伞头 Common Crypto.h。)

  2. 添加一个新的 配置设置文件并将其命名为 例如:。,“ 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. Create the three referenced module map files, above, and populate them with the following:

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

    (Replace "Xcode.app" with "Xcode-beta.app" if you're running a beta version. Replace 10.11 with your current OS SDK if not running El Capitan.)

  4. On the Info tab of your project settings, under Configurations, set the Debug and Release configurations of CommonCrypto to CommonCrypto (referencing CommonCrypto.xcconfig).

  5. On your framework target's Build Phases tab, add the CommonCrypto framework to Target Dependencies. Additionally add libcommonCrypto.dylib to the Link Binary With Libraries build phase.

  6. Select CommonCrypto.framework in Products and make sure its Target Membership for your wrapper is set to Optional.

You should now be able to build, run and import CommonCrypto in your wrapper framework.

For an example, see how SQLite.swift uses a dummy sqlite3.framework.

这个答案讨论了如何使它在框架内工作,以及如何使用 Cocoapods 和 Carthage

模块映射方法模块映射方法

我在我的包装中使用 modulemap来包装 CommonCrypto https://github.com/onmyway133/arcanehttps://github.com/onmyway133/Reindeer

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

  • 创建一个包含 module.modulemap的文件夹 CCommonCrypto

      module CCommonCrypto {
    header "/usr/include/CommonCrypto/CommonCrypto.h"
    export *
    }
    
  • Go to Built Settings -> Import Paths

      ${SRCROOT}/Sources/CCommonCrypto
    

🌳 Cocoapods with modulemap approach

🐘 public header approach

🐏 Cocoapods with public header approach

🐝 Interesting related posts

模块映射解决方案可能很好,并且对于 SDK 的更改非常健壮,但是我发现它们在实践中使用起来很笨拙,而且在分发东西给其他人时也不像我希望的那样可靠。为了让这一切更加万无一失,我采取了另一种方式:

只要复制标题。

我知道,很脆弱。但是苹果公司几乎从来没有对 CommonCrypto 做过重大的改动,而我的梦想就是他们不会以任何重大的方式改动它,除非最终把 CommonCrypto 变成一个模块化的头文件。

我所说的“复制头文件”是指“将所有需要的头文件剪切并粘贴到项目中的一个大型头文件中,就像预处理器所做的那样。”作为可以复制或改编的示例,请参见 RNCryptor.h

注意,所有这些文件都是在 APSL 2.0下授权的,这种方法有意维护版权和许可通知。我的连接步骤是由 MIT 授权的,并且只适用于下一个许可通知)。

我不是说这是一个漂亮的解决方案,但到目前为止,它似乎是一个难以置信的简单的解决方案,既可以实现也可以提供支持。

更简单、更健壮的方法是创建一个名为“ CommonCryptoModuleMap”的聚合目标,使用 Run Script 阶段自动生成模块映射,并使用正确的 Xcode/SDK 路径:

enter image description here enter image description here

运行脚本阶段应该包含这个 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.app 路径,这可以改变系统到系统的路径,特别是如果你使用 xcode-select切换到测试版,或正在构建一个 CI 服务器,其中多个版本安装在非标准的位置。您也不需要硬编码 SDK,所以这应该适用于 iOS、 macOS 等。您也不需要在项目的源目录中保存任何内容。

在创建这个目标之后,使您的库/框架依赖于它,并使用一个 Target Dependency 项:

enter image description here

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

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

enter image description here

接下来,将模块映射的父目录 ${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap添加到 Swift 部分(SWIFT_INCLUDE_PATHS)下的“ Import Path”构建设置中:

enter image description here

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

就是这样,你现在应该能够 import CommonCrypto

Xcode 10的更新

Xcode 10现在附带了一个 CommonCrypto 模块映射,这使得这个解决方案变得不必要了。如果您希望同时支持 Xcode 9和10,那么可以在 Run Script 阶段执行一个检查,以查看模块映射是否存在,例如。

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

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

在 Swift 项目中,创建 Objective-C 桥接头,在 Objective-C 中创建 NSData 类别(或使用该库的自定义类)。唯一的缺点是必须用 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
}

然后在您的目标-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
}
}

一切正常。

@ mogstad 很好心地用 Cocoapod 包裹了 stephencelis 溶液:

Pod‘ libCommonCrypto’

其他可用的豆荚对我不起作用。

警告: iTunesConnect 可能会使用此方法的 拒绝应用程序。


我团队中的新成员不小心打破了一个顶级答案给出的解决方案,所以我决定将它合并到一个名为 共用加密模块的小包装项目中。你可以手动安装,也可以通过 Cocoapods 安装:

pod 'CommonCryptoModule', '~> 1.0.2'

然后,你所要做的就是在需要 CommonCrypto的地方导入模块,像这样:

import CommonCryptoModule

希望有人觉得这个有用。

我觉得我比 Mike Weller 的优秀作品有进步。

在包含 bash 的 Compile Sources阶段之前添加一个 Run Script 阶段:

# 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 <<EOF > "${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"

这个脚本使用 module.map 在正确的位置构造了一个基本框架,然后依赖于 Xcode 对框架的 BUILT_PRODUCTS_DIR自动搜索。

我将原来的 CommonCrypto include 文件夹链接为框架的 Header 文件夹,因此结果应该也适用于 Objective C 项目。

好消息! Swift 4.2(Xcode 10)终于提供了 CommonCrypto!

只要在快速文件中添加 import CommonCrypto即可。

我已经在 jjrscott 的答案中添加了一些 cocoapods 魔法,以防您需要在 cocoapods 库中使用 CommonCrypto。


1) 添加这一行到你的 podspec:

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

2) 将其保存在库文件夹或任何您喜欢的地方(但是不要忘记相应地更改 script _ stage...)

# 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"

非常有效:)

我不确定 Xcode 9.2是否有所改变,但现在实现这一点要简单得多。我只需要在我的框架项目目录中创建一个名为“ CommonCrypto”的文件夹,并在其中创建两个文件,其中一个名为“ cc.h”,如下所示:

#include <CommonCrypto/CommonCrypto.h>
#include <CommonCrypto/CommonRandom.h>

还有一个叫做 module.modulemap:

module CommonCrypto {
export *
header "cc.h"
}

(我不知道为什么不能直接在模块映射文件中引用 SDKROOT 区域的头文件,但我无法让它工作)

第三件事是找到“ Import Path”设置并将其设置为 $(SRCROOT)。 实际上,如果您不希望它位于根级别,那么您可以将它设置为任何您希望 CommonCrypto 文件夹位于其下的文件夹。

在这之后,你应该能够使用

import CommonCrypto

在任何快速文件中,所有类型/函数/等都是可用的。

警告一句——如果你的应用程序使用 libCommonCrypto (或者 libcoreCrypto) ,一个不太老练的黑客可以非常容易地将调试器附加到你的应用程序上,并找出传递给这些函数的密钥。

如你有以下问题:

未找到-lapple _ crypto 库 错误: 链接器命令失败,退出代码1(使用-v 查看调用)

在 Xcode 10中,Swift 4.0。 CommonCrypto 是框架的一部分。

 import CommonCrypto

拿开

  • 来自 link 二进制文件的 CommonCrpto lib 文件和来自 Build 的库 阶段
  • 桥接头的 import CommonCrypto

这招对我管用!

对于任何使用 Swift 4.2Xcode 10的人:

CommonCrypto 模块现在由系统提供,因此您可以像任何其他系统框架一样直接导入它。

import CommonCrypto


在更新 Xcode 之后,我也遇到了同样的情况。 我尝试了所有我能做的事情,比如重新安装 cocoapods 和清理项目,但是没有用。 现在 重新开始系统解决了这个问题。