ios 在 Swift 框架中导入 CommonCrypto

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25248598/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 01:48:19  来源:igfitidea点击:

Importing CommonCrypto in a Swift framework

iosswiftcommoncrypto

提问by hpique

How do you import CommonCryptoin a Swift framework for iOS?

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

I understand how to use CommonCryptoin a Swift app: You add #import <CommonCrypto/CommonCrypto.h>to the bridging header. However, Swift frameworks don't support bridging headers. The documentationsays:

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

You can import external frameworks that have a pure Objective-C codebase, a pure Swift codebase, or a mixed-language codebase. The process for importing an external framework is the same whether the framework is written in a single language or contains files from both languages. When you import an external framework, make sure the Defines Module build setting for the framework you're importing is set to Yes.

You can import a framework into any Swift file within a different target using the following syntax:

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

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

import FrameworkName

Unfortunately, import CommonCryptodoesn't work. Neither does adding #import <CommonCrypto/CommonCrypto.h>to the umbrella header.

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

采纳答案by hpique

I found a GitHub project that successfully uses CommonCrypto in a Swift framework: SHA256-Swift. Also, this article about the same problem with sqlite3was useful.

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

Based on the above, the steps are:

基于以上,步骤如下:

1) Create a CommonCryptodirectory inside the project directory. Within, create a module.mapfile. The module map will allow us to use the CommonCrypto library as a module within Swift. Its contents are:

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) In Build Settings, within Swift Compiler - Search Paths, add the CommonCryptodirectory to Import Paths(SWIFT_INCLUDE_PATHS).

2) 在 Build Settings 中,在Swift Compiler - Search Paths 中,将CommonCrypto目录添加到Import Paths( SWIFT_INCLUDE_PATHS)。

Build Settings

构建设置

3) Finally, import CommonCrypto inside your Swift files as any other modules. For example:

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

Limitations

限制

Using the custom framework in another project fails at compile time with the error missing required module 'CommonCrypto'. This is because the CommonCrypto module does not appear to be included with the custom framework. A workaround is to repeat step 2 (setting Import Paths) in the project that uses the framework.

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

The module map is not platform independent (it currently points to a specific platform, the iOS 8 Simulator). I don't know how to make the header path relative to the current platform.

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

Updates for iOS 8 <= We should remove the line link "CommonCrypto", to get the successful compilation.

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

UPDATE / EDIT

更新/编辑

I kept getting the following build error:

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

ld: library not found for -lCommonCrypto for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)

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

Unless I removed the line link "CommonCrypto"from the module.mapfile I created. Once I removed this line it built ok.

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

回答by Mike Weller

Something a little simpler and more robust is to create an Aggregate target called "CommonCryptoModuleMap" with a Run Script phase to generate the module map automatically and with the correct Xcode/SDK path:

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

enter image description hereenter image description here

在此处输入图片说明在此处输入图片说明

The Run Script phase should contain this bash:

运行脚本阶段应包含以下 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

Using shell code and ${SDKROOT}means you don't have to hard code the Xcode.app path which can vary system-to-system, especially if you use xcode-selectto switch to a beta version, or are building on a CI server where multiple versions are installed in non-standard locations. You also don't need to hard code the SDK so this should work for iOS, macOS, etc. You also don't need to have anything sitting in your project's source directory.

使用 shell 代码${SDKROOT}意味着您不必对 Xcode.app 路径进行硬编码,该路径可能会因系统而异,特别是如果您习惯xcode-select切换到测试版,或者在安装了多个版本的 CI 服务器上构建在非标准位置。您也不需要对 SDK 进行硬编码,因此这应该适用于 iOS、macOS 等。您也不需要在项目的源目录中放置任何东西。

After creating this target, make your library/framework depend on it with a Target Dependencies item:

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

enter image description here

在此处输入图片说明

This will ensure the module map is generated before your framework is built.

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

macOS note: If you're supporting macOSas well, you'll need to add macosxto the Supported Platformsbuild setting on the new aggregate target you just created, otherwise it won't put the module map in the correct Debugderived data folder with the rest of the framework products.

MACOS注:如果你支持macOS的话,你会需要添加macosxSupported Platforms您刚刚创建的新的总目标构建设置,否则将不把模块映射在正确的Debug导出数据文件夹的其余部分框架产品。

enter image description here

在此处输入图片说明

Next, add the module map's parent directory, ${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap, to the "Import Paths" build setting under the Swift section (SWIFT_INCLUDE_PATHS):

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

enter image description here

在此处输入图片说明

Remember to add a $(inherited)line if you have search paths defined at the project or xcconfig level.

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

That's it, you should now be able to import CommonCrypto

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

Update for Xcode 10

Xcode 10 更新

Xcode 10 now ships with a CommonCrypto module map making this workaround unnecessary. If you would like to support both Xcode 9 and 10 you can do a check in the Run Script phase to see if the module map exists or not, e.g.

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

回答by stephencelis

You can actually build a solution that "just works" (no need to copy a module.modulemapand SWIFT_INCLUDE_PATHSsettings over to your project, as required by other solutions here), but it does require you to create a dummy framework/module that you'll import into your framework proper. We can also ensure it works regardless of platform (iphoneos, iphonesimulator, or macosx).

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

  1. Add a new framework target to your project and name it after the system library, e.g., "CommonCrypto". (You can delete the umbrella header, CommonCrypto.h.)

  2. Add a new Configuration Settings Fileand name it, e.g., "CommonCrypto.xcconfig". (Don't check any of your targets for inclusion.) Populate it with the following:

    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.11with your current OS SDK if not running El Capitan.)

  4. On the Infotab of your project settings, under Configurations, set the Debugand Releaseconfigurations of CommonCryptoto CommonCrypto(referencing CommonCrypto.xcconfig).

  5. On your framework target's Build Phasestab, add the CommonCryptoframework to Target Dependencies. Additionally add libcommonCrypto.dylibto the Link Binary With Librariesbuild phase.

  6. Select CommonCrypto.frameworkin Productsand make sure its Target Membershipfor your wrapper is set to Optional.

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

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

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

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

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

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

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

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

举个例子,看看SQLite.swift如何使用一个虚拟的sqlite3.framework

回答by onmyway133

This answer discusses how to make it work inside a framework, and with Cocoapods and Carthage

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

modulemap approach

模块映射方法

I use modulemapin my wrapper around CommonCrypto https://github.com/onmyway133/arcane, https://github.com/onmyway133/Reindeer

我用modulemap我的包装器CommonCrypto https://github.com/onmyway133/arcanehttps://github.com/onmyway133/Reindeer

For those getting header not found, please take a look https://github.com/onmyway133/Arcane/issues/4or run xcode-select --install

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

  • Make a folder CCommonCryptocontaining module.modulemap

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

      ${SRCROOT}/Sources/CCommonCrypto
    
  • 制作一个CCommonCrypto包含module.modulemap

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

      ${SRCROOT}/Sources/CCommonCrypto
    

Cocoapods with modulemap approach

使用 modulemap 方法的 Cocoapods

public header approach

公共标题方法

Cocoapods with public header approach

带有公共头方法的 Cocoapods

Interesting related posts

有趣的相关帖子

回答by mxcl

Good news! Swift 4.2 (Xcode 10) finally provides CommonCrypto!

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

Just add import CommonCryptoin your swift file.

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

回答by Nikita Kukushkin

WARNING: iTunesConnect may rejectapps that are using this method.

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



New member on my team accidentally broke the solution given by one of the top answers, so I decided to consolidate it in a small wrapper project called CommonCryptoModule. You can install it manually or via Cocoapods:

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

pod 'CommonCryptoModule', '~> 1.0.2'

Then, all you have to do is to import the module where you need CommonCrypto, like so:

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

import CommonCryptoModule

Hope someone else finds this useful.

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

回答by jjrscott

I think I have an improvement to Mike Weller's excellent work.

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

Add a Run Script phase before the Compile Sourcesphase containing this bash:

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"

This script constructs a bare bones framework with the module.map in the correct place and then relies on Xcode's automatic search of BUILT_PRODUCTS_DIRfor frameworks.

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

I linked the original CommonCrypto include folder as the framework's Headers folder so the result should also function for Objective C projects.

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

回答by kubrick G

For anyone using swift 4.2with Xcode 10:

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

CommonCrypto module is now provided by the system, so you can directly import it like any other system framework.

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

import CommonCrypto

回答by Jacob Jennings

@mogstad has been kind enough to wrap @stephencelis solution in a Cocoapod:

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

pod 'libCommonCrypto'

吊舱'libCommonCrypto'

The other pods available did not work for me.

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

回答by Rob Napier

The modulemap solutions can be good, and are robust against SDK changes, but I've found them awkward to use in practice, and not as reliable as I'd like when handing things out to others. To try to make it all more foolproof, I went a different way:

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

Just copy the headers.

只需复制标题。

I know, fragile. But Apple almost never makes significant changes to CommonCrypto and I'm living the dream that they will not change it in any significant way without also finally making CommonCrypto a modular header.

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

By "copy the headers" I mean "cut and paste all of the headers you need into one massive header in your project just like the preprocessor would do." As an example of this that you can copy or adapt, see RNCryptor.h.

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

Note that all of these files are licensed under APSL 2.0, and this approach intentionally maintains the copyright and license notices. My concatenation step is licensed under MIT, and that only applies up to the next license notice).

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

I am not saying this is a beautiful solution, but so far it seems to have been an incredibly simple solution to both implement and support.

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