在Swift框架中导入CommonCrypto


184

您如何汇入 CommonCrypto iOS的Swift框架?

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

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

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

import FrameworkName

不幸的是,导入CommonCrypto无效。也不添加#import <CommonCrypto/CommonCrypto.h>到伞头。


CommonCrypto是基于C的框架,而不是Objective-C框架。
rmaddy14年

1
@rmaddy Objective-C是C超集。您是说我们不能使用Swift的CommonCrypto吗?
hpique

4
@rmaddy我刚刚设法通过使用模块映射使CommonCrypto工作。我将完善解决方案并将其发布到今天晚些时候。
hpique

如果发现方便,并且您所寻找的东西已经实现,则可以尝试CryptoSwift
Marcin

1
苹果刚刚开源CommonCrypto。如果有资源,也许我们可以使其运行。
eyeballz 2015年

Answers:


137

更简单,更可靠的方法是,在运行脚本阶段创建一个名为“ 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

使用外壳程序代码,${SDKROOT}意味着您不必对Xcode.app路径进行硬编码,该路径可能会因系统而异,尤其是如果您使用的xcode-select是切换到Beta版本或在安装有多个版本的CI服务器上构建时在非标准位置。您也不需要对SDK进行硬编码,因此它应该适用于iOS,macOS等。您也不需要在项目的源目录中放置任何内容。

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

在此处输入图片说明

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

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

在此处输入图片说明

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

在此处输入图片说明

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

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

Xcode 10更新

Xcode 10现在附带CommonCrypto模块映射,因此不需要这种解决方法。如果您想同时支持Xcode 9和Xcode 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

8
这个答案应该放在首位。简单而优雅
阿卜杜拉·赛义德

1
在这个游戏上晚了-但这应该是选择的答案。这很简单,其他开发人员在同一个项目上进行工作也很容易看到需求。
fatuous.logic

1
好答案。谢谢!
克劳斯·布斯

1
如果在.framework中执行此操作,是否应该在包含此框架的项目中执行相同的操作?
拉维·基兰

2
@IanDundas我用重新编译问题的修复程序以及在macOS上使用此代码的修复程序更新了上面的代码
iwasrobbed

91

实际上,您可以构建一个“可以正常使用”的解决方案(无需按此处其他解决方案的要求将module.modulemapSWIFT_INCLUDE_PATHS设置复制到您的项目中),但是它确实需要您创建一个虚拟框架/模块。将适当地导入到您的框架中。我们也可以保证它工作平台无论是(iphoneosiphonesimulator,或macosx)。

  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. 在项目设置的“ 信息”选项卡上的“ 配置”下,将CommonCrypto的“ 调试”和“ 发布”配置设置CommonCrypto(引用CommonCrypto.xcconfig)。

  5. 在框架目标的Build Phases选项卡上,将CommonCrypto框架添加到Target Dependencies。另外,将libcommonCrypto.dylib添加到“ 使用库链接二进制文件”构建阶段。

  6. 在“ 产品”中选择CommonCrypto.framework,并确保其包装的目标成员身份设置为Optional

您现在应该能够构建,运行并 import CommonCrypto在包装器框架中。

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


4
为我工作,无需步骤(5)。有了它,我得到一个构建错误:ld: cannot link directly with /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.2.sdk/usr/lib/system/libcommonCrypto.dylib. Link against the umbrella framework 'System.framework' instead. for architecture x86_64
斯坦尼

4
优秀的!用这个来制作github.com/soffes/Crypto我我不必链接System.framework。值得注意的是,如果您的框架是跨平台的,则必须为Mac和iOS创建一个单独的包装框架。
山姆·索菲斯

32
人们如何或在哪里找到这样的东西?
hola 2015年

5
只需注意一点,即可清楚表明您需要在步骤1中选择Objective-C作为语言。这很容易被忽略。此外,也许是因为我没有一个名为.dylib,我需要添加.framework在第5步
张栋梁萨托利

7
这太可怕了。我有一个Xcode动物园,每个动物园都以不同的方式破坏了,到头的绝对路径遍布整个地方,这很恶心。库比蒂诺或任何负责此Modulemap混乱的人至少出了点问题
Anton Tropashko 2016年

82

我找到了一个在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)在“构建设置”的“ Swift编译器-搜索路径”中,将CommonCrypto目录添加到“ 导入路径”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模块似乎未包含在自定义框架中。一种解决方法是Import Paths在使用框架的项目中重复步骤2(设置)。

模块映射不是独立于平台的(当前指向特定的平台,即iOS 8 Simulator)。我不知道如何使标头路径相对于当前平台。

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

更新/编辑

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

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

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


30
e,苹果肯定想让事情变得困难。只需要让我们导入文件/框架而不必经历所有这些BS会杀死Swifties吗?
zaph 2014年

4
这真令人恼火,因为该$SDKROOT变量应该允许您使用平台不可知的路径,但是我不知道如何在Swift中做到这一点。
danimal

2
对我来说,直到我link "CommonCrypto"从module.map文件中删除,它才起作用。
Teo Sartori 2015年

1
谁能确认在Xcode 7.3上可以正常工作?更新后,该解决方案对我停止了工作。
Nikita Kukushkin

1
改正:当我为模拟器构建时,它工作正常,但是当我为iOS 9.3设备构建时,“ ld:找不到用于-lCommonCrypto的库arm64的库”,@ @链接失败
Nikita Kukushkin

50

该答案讨论了如何使其在框架内以及与Cocoapods和Carthage一起工作

🐟modulemap方法

modulemap在我的包装中使用了CommonCrypto https://github.com/onmyway133/arcane https://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

co Cocoapods与Modulemap方法

🐘公共标题方法

🐏带有公共头方法的可可足类

related有趣的相关职位


1
哇!令我惊讶的是,当以前的模块映射文件引起很多问题时,顶部方法(创建module.modulemap文件)的标头路径就很好用了。我一直在挣扎了一会儿使用module.modulemap文件与绝对路径/CommonCrypto/CommonCrypto.hApplications/XCode.app/Contents/Developer/Platforms/iPhoneOS....,这需要手工修改了谁改名XCode的人。切换行以进行查看"/usr/include/CommonCrypto/CommonCrypto.h"对于具有多个XCode版本的团队来说似乎很好。非常感谢!
Natalia

3
在创建容器时,我设置了SWIFT_INCLUDE_PATHS和preserve_paths。当我运行pod lib lint,但构建失败并出现错误:没有此类模块'CommonCrypto'。我该如何处理。
Klein Mioke

1
与问题无关,但我喜欢将表情符号用作项目符号!😊
Fomentia

2
@ onmyway133如果替换为$(PODS_ROOT)/CommonCryptoSwift/Sources/CCommonCrypto,则可以使用本地开发吊舱$(PODS_TARGET_SRCROOT)/Sources/CCommonCryptoPODS_TARGET_SRCROOT为本地广告连播设置正确。
Orkhan Alikhanov

好答案,救了我的命!谢谢你一百万
哈桑·沙巴齐

45

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

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


好消息!您可以将链接添加到文档吗?
Kryštof马捷

我没有指向文档的链接,我在尝试编译一个我在其中使用Xcode 10解决方法的项目时发现了这一点。它抱怨它可以找到两个CommonCrypto模块,因此怀疑Apple现在提供了我删除解决方法并lo!没错 我在推特上发了帖,一位苹果工程师回答说,确认它是有意的。
mxcl

1
App Store仅向我显示9.4.7作为可用更新,您如何获得Xcode 10?
哈马德·塔里克

1
Google会告诉您,它处于beta版本。
mxcl

1
@SomoyDasGupta是的。只需删除先前的导入,然后重新编译即可。换句话说,你不必做从MikeWeller答案的步骤
寒冰

7

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


我团队的新成员不小心打破了最重要答案之一给出的解决方案,因此我决定将其合并到一个名为CommonCryptoModule的小型包装项目中。您可以手动安装它,也可以通过Cocoapods安装它:

pod 'CommonCryptoModule', '~> 1.0.2'

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

import CommonCryptoModule

希望其他人觉得这有用。


警告:如果使用此方法,您的应用程序将被拒绝!
Segabond

是的,我们也被拒绝了,很奇怪,因为使用这种方法上传了几个月没有问题。
Nikita Kukushkin,

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构建一个裸露的框架,然后依靠Xcode的自动搜索 BUILT_PRODUCTS_DIR对框架。

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


有关涵盖CocoaPods中用法的改进,请参见dvdblk的答案
jjrscott


4

@mogstad足够友好,可以将@stephencelis解决方案包装在Cocoapod中:

pod'libCommonCrypto'

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


4

modulemap解决方案可能很好,并且可以抵抗SDK更改,但是我发现它们在实践中很难使用,并且不如我想要的可靠。为了使一切变得更加简单,我采取了另一种方式:

只需复制标题即可。

我知道,脆弱。但是,Apple几乎从未对CommonCrypto进行过重大更改,而我梦想着在不最终使CommonCrypto成为模块化头文件的情况下,他们不会以任何重大方式对其进行更改。

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

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

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


我发现它可以可靠地编译,而且很简单。使用该框架的应用程序是否需要链接到任何特殊内容,还是CommonCrypto始终可用?
–codingFriend1

1
我认为它Security.framework是自动链接的(距离开始一个新项目已经有一段时间了)。如果遇到错误,那就是要链接的框架。
罗布·纳皮尔

这似乎是最简单的解决方案,并且在一台机器上运行良好,但是每当我在另一台机器上的另一框架或应用程序中使用该框架时,都会出现“缺少模块”错误。
里弗里弗

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

它按预期工作。


这非常顺利,甚至允许您保持内部内部状态(NSData+NSDataEncryptionExtension.h不必公开)。
拉斐尔

但是,要使用该工具,我应该链接到哪个OS框架?与其他人不同-我必须在Obj-C项目中使用CommonCrypto,并且仅凭MacOS-10.13 SDK在Xcode 9上就
无法解决

@MottiShneor链接任何10.9或更高版本的操作系统。我在相同的环境下工作,效果很好。
特伦斯

2

如果您需要在cocoapods库中使用CommonCrypto,我在jjrscott的答案中添加了一些cocoapods魔术。


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"

奇迹般有效 :)


您可以提供相同的演示或示例框架项目以及pod spec文件吗?
Gowtham

0

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

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

另一个叫做module.modulemap:

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

(我不知道为什么您不能直接在Modulemap文件中引用来自SDKROOT区域的头文件,但我无法使其正常工作)

第三件事是找到“导入路径”设置并将其设置为$(SRCROOT)。实际上,如果您不想在根级别使用它,则可以将其设置为希望CommonCrypto文件夹位于的任何文件夹。

之后,您应该可以使用

import CommonCrypto

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

不过要提个警告-如果您的应用程序使用libCommonCrypto(或libcoreCrypto),那么一个不太老练的黑客将调试器附加到您的应用程序并找出将哪些密钥传递给这些功能非常容易。


0

如果您遇到以下问题:

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

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

 import CommonCrypto

去掉

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

这对我有用!


-1

更新Xcode之后,对我来说也是一样。我尝试了所有可以做的事情,例如重新安装cocoapods和清理项目,但没有成功。现在,重新启动系统后已解决。


-13

非常简单 加

#import <CommonCrypto/CommonCrypto.h>

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

然后转到您的项目“构建设置”,然后寻找“ Swift Compiler-代码生成”。在其下,将桥接头的名称添加到条目“ Objetive-C桥接头”。

你完成了。您的Swift代码中无需导入。Swift可以看到此桥接头文件中列出的所有公共Objective-C头。


您的方法返回错误:不支持将标头与框架目标一起使用
gorgi93,2015年

5
@ gorgi93您不能在框架目标中使用桥接标头,因为错误提示。不幸的是,唯一的选择是将其放在主框架头文件中。
Charles A.

1
如果您实际使用了该线程的标题,那么您会看到该家伙想要将CommonCrypto库导入Swift框架中。您不能在框架中使用桥接头,也不能将CommonCrypto框架导入伞形头。
miken.mkndev '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.