189

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

我了解如何CommonCrypto在 Swift 应用程序中使用:您添加#import <CommonCrypto/CommonCrypto.h>到桥接头。但是,Swift 框架不支持桥接头。文档说:

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

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

import FrameworkName

不幸的是,导入CommonCrypto不起作用。也不会添加#import <CommonCrypto/CommonCrypto.h>到伞形标题中。

4

16 回答 16

141

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

在此处输入图像描述 在此处输入图像描述

运行脚本阶段应该包含这个 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切换到 beta 版本,或者在安装了多个版本的 CI 服务器上构建在非标准位置。您也不需要对 SDK 进行硬编码,因此这应该适用于 iOS、macOS 等。您也不需要在项目的源目录中放置任何内容。

创建此目标后,使用 Target Dependencies 项使您的库/框架依赖它:

在此处输入图像描述

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

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

在此处输入图像描述

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

在此处输入图像描述

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

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

Xcode 10 更新

Xcode 10 现在附带了一个 CommonCrypto 模块映射,因此不需要这种解决方法。如果您想同时支持 Xcode 9 和 10,您可以在运行脚本阶段检查模块映射是否存在,例如

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
于 2017-03-17T08:38:32.943 回答
94

您实际上可以构建一个“正常工作”的解决方案(无需将module.modulemapSWIFT_INCLUDE_PATHS设置复制到您的项目,正如此处其他解决方案所要求的那样),但它确实需要您创建一个虚拟框架/模块将正确导入您的框架。我们还可以确保它不受平台(iphoneos、、iphonesimulatormacosx)的影响。

  1. 将一个新的框架目标添加到您的项目中,并在系统库之后为其命名,例如“CommonCrypto”。(您可以删除伞头文件CommonCrypto.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. 创建上面的三个引用的模块映射文件,并使用以下内容填充它们:

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

    (如果您运行的是 beta 版本,请将“Xcode.app”替换为“Xcode-beta.app”。10.11如果未运行 El Capitan,请替换为您当前的 OS SDK。)

  4. 在项目设置的Info选项卡上的Configurations下,将 CommonCrypto 的DebugRelease配置设置为CommonCrypto(引用CommonCrypto.xcconfig)。

  5. 在框架目标的Build Phases选项卡上,将CommonCrypto框架添加到Target Dependencies。此外,将libcommonCrypto.dylib添加到Link Binary With Libraries构建阶段。

  6. Products中选择CommonCrypto.framework并确保其包装器的Target Membership设置为Optional

您现在应该能够import CommonCrypto在您的包装框架中构建、运行和运行。

例如,查看SQLite.swift如何使用虚拟sqlite3.framework

于 2015-03-22T01:08:53.820 回答
84

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

基于以上,步骤如下:

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

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) 在 Build Settings 中,在Swift Compiler - Search Paths中,将CommonCrypto目录添加到Import Paths ( SWIFT_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 模拟器)。我不知道如何使标题路径相对于当前平台。

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

更新/编辑

我不断收到以下构建错误:

ld:未找到用于架构 x86_64 的 -lCommonCrypto 的库 clang:错误:链接器命令失败,退出代码为 1(使用 -v 查看调用)

除非我link "CommonCrypto"module.map我创建的文件中删除了该行。一旦我删除了这条线,它就构建好了。

于 2014-08-11T19:02:04.097 回答
52

这个答案讨论了如何使它在框架内工作,并与 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

  • 制作一个CCommonCrypto包含module.modulemap

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

      ${SRCROOT}/Sources/CCommonCrypto
    

带有模块映射方法的 Cocoapods

公共标头方法

具有公共标头方法的 Cocoapods

有趣的相关帖子

于 2016-05-09T21:28:18.967 回答
46

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

只需添加import CommonCrypto您的 swift 文件。

于 2018-06-05T00:09:06.633 回答
7

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


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

pod 'CommonCryptoModule', '~> 1.0.2'

然后,您所要做的就是在您需要的地方导入模块CommonCrypto,如下所示:

import CommonCryptoModule

希望其他人觉得这很有用。

于 2017-11-03T13:56:48.800 回答
5

我认为我对 Mike Weller 的出色工作有所改进。

Compile Sources在包含此 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 <<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 构建了一个基本框架,然后依赖于 XcodeBUILT_PRODUCTS_DIR对框架的自动搜索。

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

于 2018-05-09T15:25:01.573 回答
5

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

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

import CommonCrypto

于 2019-02-03T07:18:14.700 回答
4

@mogstad 非常友好地将 @stephencelis 解决方案包装在 Cocoapod 中:

吊舱'libCommonCrypto'

其他可用的 pod 对我不起作用。

于 2017-05-03T22:13:08.460 回答
4

modulemap 解决方案可能很好,并且对 SDK 更改很健壮,但我发现它们在实践中使用起来很尴尬,并且在将东西分发给其他人时并不像我想要的那样可靠。为了让它更加万无一失,我采取了不同的方式:

只需复制标题。

我知道,脆弱。但是 Apple 几乎从未对 CommonCrypto 做出重大改变,我梦想着他们不会在没有最终使 CommonCrypto 成为模块化标头的情况下以任何重大方式改变它。

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

请注意,所有这些文件都在 APSL 2.0 下获得许可,并且这种方法有意维护版权和许可声明。我的连接步骤已获得 MIT 许可,并且仅适用于下一个许可通知)。

我并不是说这是一个漂亮的解决方案,但到目前为止,它似乎是一个非常简单的实现和支持解决方案。

于 2016-11-01T15:10:36.327 回答
2

我在 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_phase ...)

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

奇迹般有效 :)

于 2018-06-07T00:43:15.737 回答
2

我知道这是一个老问题。但是我想出了一种在 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
}

然后在你的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
}
}

它按预期工作。

于 2017-04-26T21:50:09.703 回答
0

如果您有以下问题:

ld:找不到-lapple_crypto clang的库:错误:链接器命令失败,退出代码为1(使用-v查看调用)

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

添加

 import CommonCrypto

消除

  • 来自链接二进制文件的 CommonCrpto lib 文件与来自构建阶段的库
  • import CommonCrypto从桥接头

这对我有用!

于 2018-11-01T11:34:09.157 回答
0

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

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

另一个叫做module.modulemap:

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

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

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

在此之后,您应该可以使用

import CommonCrypto

在任何 swift 文件和所有类型/功能/等中。可用。

不过要提醒一句 - 如果您的应用程序使用 libCommonCrypto(或 libcoreCrypto),那么对于不太老练的黑客来说,将调试器附加到您的应用程序并找出传递给这些函数的密钥非常容易。

于 2018-10-03T03:17:56.953 回答
-1

更新 Xcode 后,我也遇到了同样的情况。我尝试了我能做的所有事情,例如重新安装 cocoapods 和清理项目,但没有奏效。现在重启系统后解决了。

于 2019-08-25T23:25:39.493 回答
-14

这很简单。添加

#import <CommonCrypto/CommonCrypto.h>

到 .h 文件(项目的桥接头文件)。作为约定,您可以将其命名为 YourProjectName-Bridging-Header.h。

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

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

于 2015-03-03T15:19:41.803 回答