在Swift框架中导入CommonCrypto

时间:2014-08-11 17:03:50

标签: ios swift commoncrypto

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

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

  

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

     

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

import FrameworkName

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

15 个答案:

答案 0 :(得分:131)

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

enter image description here enter image description here

Run Script阶段应包含此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 Dependencies项:

enter image description here

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

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

enter image description here

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

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

答案 1 :(得分:90)

您实际上可以构建一个&#34;正常工作的解决方案&#34; (不需要将 module.modulemap SWIFT_INCLUDE_PATHS设置复制到项目中,这是其他解决方案所要求的),但它确实需要您创建一个虚拟框架/模块。 #39; ll将导入到您的框架中。无论平台如何(iphoneosiphonesimulatormacosx),我们都可以确保其有效。

  1. 向项目添加新的框架目标,并在系统库之后命名,例如,&#34; CommonCrypto&#34;。 (您可以删除伞标题, CommonCrypto.h 。)

  2. 添加新的配置设置文件并将其命名为例如,&#34; CommonCrypto.xcconfig&#34;。 (不要检查任何目标是否包含在内。)使用以下内容填充它:

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

    (替换&#34; Xcode.app&#34;使用&#34; Xcode-beta.app&#34;如果您正在运行测试版。将10.11替换为您当前的操作系统SDK如果没有运行El Capitan。)

  4. 在项目设置的信息标签上的配置下,设置调试发布 CommonCrypto CommonCrypto 的配置(引用 CommonCrypto.xcconfig )。

  5. 在您的框架目标构建阶段标签上,将 CommonCrypto 框架添加到目标依赖关系。另外,将 libcommonCrypto.dylib 添加到 Link Binary With Libraries 构建阶段。

  6. 产品中选择 CommonCrypto.framework ,并确保您的包装器的目标成员设置为可选< /强>

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

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

答案 2 :(得分:82)

我找到了一个在Swift框架中成功使用CommonCrypto的GitHub项目:SHA256-Swift。此外,关于the same problem with 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)。< / p>

Build Settings

3)最后,将Swice文件中的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的更新&lt; =我们应该删除行链接“CommonCrypto”,以便成功编译。

更新/编辑

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

  

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

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

答案 3 :(得分:50)

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

模块图方法

我在CommonCrypto https://github.com/onmyway133/arcane周围的包装中使用modulemaphttps://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 *
      }
    
  • 转到内置设置 - &gt;导入路径

      ${SRCROOT}/Sources/CCommonCrypto
    

采用模块图方法的Cocoapods

公共标题方法

采用公共标题方法的Cocoapods

有趣的相关帖子

答案 4 :(得分:38)

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

只需在swift文件中添加import CommonCrypto即可。

答案 5 :(得分:7)

警告:iTunesConnect可能会reject使用此方法的应用。

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

pod 'CommonCryptoModule', '~> 1.0.2'

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

import CommonCryptoModule

希望别人觉得这很有用。

答案 6 :(得分:4)

模块地图解决方案可以很好,并且可以很好地抵御SDK更改,但我发现它们在实践中使用起来很笨拙,而且在将内容传递给其他人时并不像我想的那样可靠。为了让它变得更加万无一失,我采取了不同的方式:

只需复制标题。

我知道,脆弱。但Apple几乎从不对CommonCrypto做出重大改变,我实现了他们不会以任何重大方式改变它的梦想,而且最终不会使CommonCrypto成为模块化标题。

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

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

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

答案 7 :(得分:4)

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

pod'libCommonCrypto'

其他可用的吊舱对我不起作用。

答案 8 :(得分:4)

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

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

import CommonCrypto

答案 9 :(得分: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
}
}

它按预期工作。

答案 10 :(得分:2)

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

1)将此行添加到您的podspec:

Draw

2)将其保存在您的库文件夹或您喜欢的任何位置(但不要忘记相应地更改script_phase ...)

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

像魅力一样工作:)

答案 11 :(得分:0)

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

Đ

另一个名为module.modulemap:

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

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

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

此后,您应该可以使用

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

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

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

答案 12 :(得分:0)

如果您遇到以下问题:

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

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

添加

  • 导入CommonCrypto

删除

    链接二进制文件中的
  • CommonCrpto lib文件与Build中的库 阶段
  • 从桥接头中导入CommonCrypto

这对我有用!

答案 13 :(得分:0)

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

答案 14 :(得分:-13)

这非常简单。添加

#import <CommonCrypto/CommonCrypto.h>

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

然后转到您的项目Build Settings并查找Swift编译器 - 代码生成。在它下面,将您的桥接标题的名称添加到条目&#34; Objetive-C Bridging Header&#34;。

你已经完成了。您的Swift代码中不需要导入。 Swift可以看到此桥接头文件中列出的任何公共Objective-C标头。