How do you import CommonCrypto
in a Swift framework for iOS?
I understand how to use CommonCrypto
in a Swift app:
You add #import <CommonCrypto/CommonCrypto.h>
to the bridging header.
However, Swift frameworks don't support bridging headers. The documentation says:
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:
import FrameworkName
Unfortunately, import CommonCrypto
doesn't work. Neither does adding #import <CommonCrypto/CommonCrypto.h>
to the umbrella header.
I'm not sure if something's changed with Xcode 9.2 but it's now much simpler to achieve this. The only things I had to do are create a folder called "CommonCrypto" in my framework project directory and create two files inside it, one called "cc.h" as follows:
And another called module.modulemap:
(I don't know why you can't reference header files from the SDKROOT area directly in a modulemap file but I couldn't get it to work)
The third thing is to find the "Import Paths" setting and set to $(SRCROOT). In fact you can set it to whatever folder you want the CommonCrypto folder to be under, if you don't want it at the root level.
After this you should be able to use
In any swift file and all the types/functions/etc. are available.
A word of warning though - if your app uses libCommonCrypto (or libcoreCrypto) it's exceptionally easy for a not-too-sophisticated hacker to attach a debugger to your app and find out what keys are being passed to these functions.
I found a GitHub project that successfully uses CommonCrypto in a Swift framework: SHA256-Swift. Also, this article about the same problem with sqlite3 was useful.
Based on the above, the steps are:
1) Create a
CommonCrypto
directory inside the project directory. Within, create amodule.map
file. The module map will allow us to use the CommonCrypto library as a module within Swift. Its contents are:2) In Build Settings, within Swift Compiler - Search Paths, add the
CommonCrypto
directory to Import Paths (SWIFT_INCLUDE_PATHS
).3) Finally, import CommonCrypto inside your Swift files as any other modules. For example:
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 (settingImport Paths
) in the project that uses the framework.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.
Updates for iOS 8 <= We should remove the line link "CommonCrypto", to get the successful compilation.
UPDATE / EDIT
I kept getting the following build error:
Unless I removed the line
link "CommonCrypto"
from themodule.map
file I created. Once I removed this line it built ok.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:
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.
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.
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).
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.
WARNING: iTunesConnect may reject apps that are using this method.
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:
Then, all you have to do is to import the module where you need
CommonCrypto
, like so:Hope someone else finds this useful.
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:
The Run Script phase should contain this bash:
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 usexcode-select
to 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.After creating this target, make your library/framework depend on it with a Target Dependencies item:
This will ensure the module map is generated before your framework is built.
macOS note: If you're supporting
macOS
as well, you'll need to addmacosx
to theSupported Platforms
build setting on the new aggregate target you just created, otherwise it won't put the module map in the correctDebug
derived data folder with the rest of the framework products.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
):Remember to add a
$(inherited)
line if you have search paths defined at the project or xcconfig level.That's it, you should now be able to
import CommonCrypto
Update for 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.
If are here for the hashing algorithms, like SHA, MD5 etc, don't use the bulky CommonCrypto library. Find the specific hashing library you are looking for instead.
For example, for MD5, you can go with SwiftHash