190

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.

Shruti Thombre
  • 989
  • 4
  • 11
  • 27
hpique
  • 119,096
  • 131
  • 338
  • 476
  • CommonCrypto is a C-based framework, not an Objective-C framework. – rmaddy Aug 11 '14 at 17:12
  • 2
    @rmaddy Objective-C is a C superset. Are you saying we can't use CommonCrypto from Swift? – hpique Aug 11 '14 at 17:25
  • 4
    @rmaddy I just managed to get CommonCrypto working by using module maps. I will polish the solution and post it later today. – hpique Aug 11 '14 at 17:38
  • if you find it convenience, and what you looking for is already implemented, you can give a try to [CryptoSwift](https://github.com/krzyzanowskim/CryptoSwift) – Marcin Dec 28 '14 at 02:43
  • 1
    Apple just open sourced CommonCrypto. Maybe we can get it running if we have the sources. – kutschenator Nov 04 '15 at 00:55
  • Is there a timeframe for a real solution, rather than one of these hacks. This and ABI compatibility make me think Apple doesn't get it or just doesn't care. I'm seriously thinking of porting my frameworks back to Objective C. I don't like the language, but at least I don't have to relearn the String class every release. – Roy Falk Jan 23 '18 at 09:19
  • In my framework i have created Objective-C class named MyProjectCrypto, and in that class included CommonCrypto. I use this class in my Umbrella Header, and in swift source files. So this is just Objective-C Facade around C framework. – Martin Berger May 14 '18 at 12:32

16 Answers16

141

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:

enter image description here enter image description here

The Run Script phase should contain this 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-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:

enter image description here

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 add macosx to the Supported Platforms build setting on the new aggregate target you just created, otherwise it won't put the module map in the correct Debug derived data folder with the rest of the framework products.

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):

enter image description here

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.

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
Mike Weller
  • 45,401
  • 15
  • 131
  • 151
  • 1
    Late to the game on this one - but this should be the chosen answer. It's simple and is easier for other dev's working on the same project to see the requirement. – fatuous.logic Jun 07 '17 at 14:30
  • 1
    If I do this in my .framework , should I have to do the same in the projects where I include this framework? – Ravi Kiran Aug 03 '17 at 10:39
  • This solves the problem of using a C library within a framework, but the path generated with the run script is not portable across machines, i.e. developer laptop, CI build machine etc. Including a framework using a module map as mentioned in this solution doesn't seem to work across different machines with different Xcode and SDK paths. – Rich Everhart Aug 04 '17 at 17:58
  • The run script's path is calculated using the correct build variables and I can verify it works in a large team across many different developers, multiple CI servers running versions of Xcode in various different locations. What issue/errors are you seeing? – Mike Weller Aug 10 '17 at 12:32
  • 1
    @MikeWeller Hello, I did follow the steps described above, but getting No such module 'CommonCrypto'. I use xCode 9 now. Is there something more with the new version of xCode to make it work? Or I am missing something. – Vanya Sep 25 '17 at 12:54
  • Found that. Wrong reference for module. – Vanya Sep 25 '17 at 13:19
  • @Vanya did you success with xCode9. – Krishnarjun Banoth Sep 26 '17 at 04:38
  • @KrishnarjunBanoth Actually I needed to move forward really fast, so I just copied the resulted module into the correct directory. Probably gonna look at that later to fix it. There is some wrong with BUILT_PRODUCTS_DIR which point to incorrect directory. Once I solve that I let know. – Vanya Sep 27 '17 at 21:52
  • This worked great, but unfortunately we saw (perhaps obviously, now I think about it) that linking against this Aggregate Build Target in framework targets in our project caused their source to be recompiled from scratch every time when building the main app target. Doh :( – Ian Dundas Oct 03 '17 at 15:57
  • @Vanya : I need to release my (broken) framework urgently and I'm facing the same issue, can you describe the step of "copied the resulted module into the correct directory." ? – HaneTV Oct 13 '17 at 15:30
  • 2
    @IanDundas I updated the code above with a fix for the re-compilation issue as well as a fix for using this on macOS – iwasrobbed Oct 24 '17 at 18:33
  • I am getting error - "No such module 'CommonCrypto'", I have created framework project and it uses Common crypto. I am installing my framework using cocoapod. I think in my case module map is not generating when use framework in other project using cocoa pod, Any solution? – Abhijeet Barge Feb 20 '18 at 13:18
  • Surprised I made this mistake originally, but the run script phase might need to use `${TARGET_BUILD_DIR}` since it is generating files for the current target, and in certain environments e.g. while archiving this can cause issues. Looks like Xcode 9.3 (beta) may have introduced changes which cause this to break. The header search path dependency should still use `${BUILT_PRODUCTS_DIR}` however. – Mike Weller Mar 12 '18 at 11:04
  • Looks like SWIFT_INCLUDE_PATHS also needs to have the module map's directory as of Xcode 9.3 beta to avoid "missing required module" errors from dependent frameworks – Mike Weller Mar 14 '18 at 13:40
  • Hey @MikeWeller. Thanks for the solution. Have you run into any issues with error messages for `missing required architecture` ? Looks like it is not being compiled for all of the architectures for some reason. Thanks! – Max Apr 25 '18 at 18:40
  • One possible improvement to your script build-step. Pay attention that you can define 'input files' and 'output files' to the build step, and then you do not need the conditional in the script, and Xcode will manage the need for rebuilding the module map automatically – Motti Shneor May 07 '18 at 09:50
  • A little more love for @MikeWeller and his timely update on supporting Xcode 10! – SafeFastExpressive Sep 06 '18 at 22:19
  • This works great, but i'm not able to push this framework to pod repo for using through cocoapod. Do you have any solution for that? – Gowtham Sep 17 '18 at 14:45
95

You can actually build a solution that "just works" (no need to copy a module.modulemap and SWIFT_INCLUDE_PATHS settings 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).

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

  4. On the Info tab of your project settings, under Configurations, set the Debug and Release configurations of CommonCrypto to CommonCrypto (referencing CommonCrypto.xcconfig).

  5. On your framework target's Build Phases tab, add the CommonCrypto framework to Target Dependencies. Additionally add libcommonCrypto.dylib to the Link Binary With Libraries build phase.

  6. Select CommonCrypto.framework in Products and make sure its Target Membership for your wrapper is set to Optional.

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

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

stephencelis
  • 4,954
  • 2
  • 29
  • 22
  • 4
    Works for me without step (5). With it I get a build error: `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` – stannie Mar 27 '15 at 15:14
  • 1
    @stannie I've updated the instructions to remove step 5 (it was unnecessary) and also account for a potential app store rejection around embedded sub-frameworks (see https://github.com/stephencelis/SQLite.swift/issues/88). – stephencelis Apr 12 '15 at 18:20
  • 4
    Excellent! Used this to make https://github.com/soffes/Crypto I didn't have to link `System.framework` though. It's worth noting, you have to make a separate wrapper framework for Mac and iOS if your framework is cross platform. – Sam Soffes Apr 21 '15 at 16:42
  • Amazing. Step 5 is not working for me either, but without - like a charm! I owe you a beer or two! – dogsgod Jun 12 '15 at 20:35
  • 33
    How or where do people find out stuff like this? – hola Aug 23 '15 at 07:56
  • 5
    Just a note make it clear that you need to select Objective-C as language in step 1. This is easily overlooked. Also, perhaps because I didn't have a .dylib, I needed to add the .framework in step 5. – Teo Sartori Nov 19 '15 at 15:14
  • 2
    Getting "no such module CommonCrypto" always… Also doesn't have .dylib in step 5. XCode 7.2 – Vasily Dec 24 '15 at 10:37
  • This solution works on our umbrella project but the tests this are failing with a "No such module: 'CommonCrypto'" error. – Danny Bravo Jan 26 '16 at 11:14
  • 7
    this is horrible. I have a zoo of Xcodes each broken in a different ways and having the absolute paths to the headers all over the place is puke invoking. Something is going awfully wrong in cupertino, or at least with whoever is in charge of this modulemap mess – Anton Tropashko Feb 03 '16 at 08:15
  • 3
    7.2.1 rolled out on feb 2nd and release notes says "The Swift compiler is now stricter about including non-modular header files. Debugging a Swift target requires that frameworks be properly modular, meaning all of the publicly-imported headers are either accounted for in the framework's umbrella header, imported from another modular framework, or listed explicitly in a custom module.modulemap file (advanced users only)." this is IMNSHO not for advanced users, this is a feature geared towards true masochists – Anton Tropashko Feb 03 '16 at 08:16
  • Note that if you are using CI this solution will easily break due to the hardcoded header paths. I'm still trying to figure out a workaround. – Reda Lemeden Mar 15 '16 at 10:12
  • Xcode 7.3 - Apple made some changes to CommonCrypto and now this solution doesn't work when running on real device. Anyone knows how to fix this? I get an error related to Bitcode – Oded Regev Apr 27 '16 at 12:19
  • 1
    Trying to build with this method causes build errors using Xcode 7.3 – Adam Carter May 04 '16 at 11:18
  • 1
    Can't make it work at all, it won't build except when running a test on my machine. – Simon Guerout May 10 '16 at 23:01
  • I get the following error when running on real device: "CommonCrypto does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64" – Oded Regev May 15 '16 at 07:07
  • 1
    Just to clarify, as of June 6, 2016, I am able to get this working on both the device and simulator with the latest Xcode release following steps 1–4. Steps 5 & 6 were not needed for me. I am producing a Universal (device and simulator) framework. – picciano Jun 06 '16 at 15:20
  • 2
    Update: I asked some Apple engineers yesterday and they said modulemap is the way to go. – kutschenator Jun 15 '16 at 17:44
  • Note that you must download the command line tools or none of the above will work. xcode-select --install will solve the problem. – Roy Falk Aug 04 '16 at 09:35
  • Not sure if this is a good solution, but I was able to build and run on my device. http://stackoverflow.com/questions/38800435/can-i-use-an-objective-c-class-in-my-swift-framework-library – kailoon Aug 06 '16 at 16:11
  • 1
    Is it possible to replace at least some of `/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/` in the `.modulemap` with something semantic, such as `$SDKROOT`, or something? It seems a bit bonkers that there's not way to provide a relative path as of Xcode 8. Thanks! – Benjohn Oct 24 '16 at 14:41
  • I am using Xcode 8.2.1, and I do not find "libcommonCrypto.dylib" mentioned in step 5. What is the alternative to that? – DShah Dec 30 '16 at 12:15
  • I can't hardcode a specific version of OSX in the path. Other people may checkout the code and have a different version of the OS. Is there an environment variable for that stuff? – i_am_jorf Jan 12 '17 at 23:40
  • 1
    @i_am_jorf You should be able to use "MacOSX.sdk" instead of "MacOSX10.11.sdk" in the path. – Matthew Seaman Feb 04 '17 at 04:46
  • @HuseinBehboodiRad because you can't use a bridging header in a swift framework. – i_am_jorf May 25 '17 at 22:33
  • I'm not sure if/when this changed, but for me, this path works. It should work in future releases, too: `/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/CommonCrypto/CommonCrypto.h` – Ky - Jan 03 '18 at 23:31
84

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 a module.map file. The module map will allow us to use the CommonCrypto library as a module within Swift. Its contents are:

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 CommonCrypto directory to Import Paths (SWIFT_INCLUDE_PATHS).

Build Settings

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

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.

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:

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

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

mxcl
  • 26,392
  • 12
  • 99
  • 98
hpique
  • 119,096
  • 131
  • 338
  • 476
  • 31
    Gee, Apple sure want's to make things difficult. Would it kill the Swifties to just let us import files/frameworks without having to go through all this BS? – zaph Sep 05 '14 at 22:22
  • 4
    This is infuriating since the `$SDKROOT` variable is supposed to allow you platform agnostic paths, but I have no idea how to get to that in Swift. – danimal Oct 02 '14 at 00:29
  • I got a compile error, var result:CCCryptorStatus = CCCrypt( kCCEncrypt, kCCAlgorithm3DES, kCCOptionPKCS7Padding | kCCOptionECBMode, key, kCCKeySize3DES .., – Saorikido Nov 06 '14 at 13:52
  • I got a compile error, it occurred the first line, var result:CCCryptorStatus = CCCrypt(kCCEncrypt... it tells 'Int' is not convertible to CCOperation. do you know how to solve it? – Saorikido Nov 06 '14 at 13:54
  • I've been trying this with `NMSSH`, but I keep getting `ld: library not found for -lNMSSH for architecture arm64` as well as for armv7. I can't figure out why. – Garrett Nov 17 '14 at 14:31
  • @Zaph, your statement could be rephrased as: Gee C makes it really hard to avoid security holes and bugs. – Bodey Baker Dec 05 '14 at 08:13
  • @porcoesphino Ah, Swift would have made Heartbleed impossible? Eliminate poor crypto practices? `UnsafeBufferPointer` is unnecessary? It is not the language, it is the programmers. Even the goto fail bug was mainly due to violating the DRY principle. Heaven help the person saying that "The Emperor has no clothes." – zaph Dec 05 '14 at 11:26
  • Kkk. You know my comment wasn't saying Swift was perfect. I've never coded in Swift but have coded a lot in C. Still it's easy to see that C allows developers to easily make mistakes that can be abused. Swift closes some of those holes. Apple likes closing holes (and pushing their agenda). It's annoying but hardly a surprise. What do they have to gain from doing what you're complaining about? They do have things to lose. – Bodey Baker Dec 08 '14 at 01:46
  • I'm wondering if Swift 1.2 allows an easier mechanism for doing this? – Daniel Feb 11 '15 at 08:10
  • 2
    For me it didn't work until I removed the `link "CommonCrypto"` from the module.map file. – Teo Sartori Jun 09 '15 at 21:18
  • Still necessary in Xcode 7 beta 5. However, note that it appears to be no longer necessary to include the setting in the project using the framework. – Daniel Aug 06 '15 at 19:44
  • @hpique This solution appears to no longer work in Xcode 7.3 beta 1. Struggling to find another solution. See: http://stackoverflow.com/questions/34772239/xcode-7-3-beta-1-vs-commoncrypto-in-swift – Daniel Jan 15 '16 at 19:04
  • 1
    Can anyone confirm this working on Xcode 7.3? This solution stopped working for me after the update. – Nikita Kukushkin Mar 23 '16 at 10:12
  • 1
    Correnction: it's working fine when I build for simulator, but fails @ linking when I build for an iOS 9.3 device with "ld: library not found for -lCommonCrypto for architecture arm64" – Nikita Kukushkin Mar 23 '16 at 11:01
  • @OdedRegev I just followed the instructions in [this answer](http://stackoverflow.com/a/29189873/1607485) and everything works for me now. – Nikita Kukushkin Apr 27 '16 at 13:58
  • It's working for me but out of curiosity... you only have a link to the simulator sdk but it's working on my device?!? how? :) – Georg Apr 25 '17 at 10:32
  • 1
    There is so much BS with Apple XCode and Swift, I dont know where to start. Are you serious ? A absolute path in project setttings file, how in earth am I going to get a team to work on this ? They have to chnage the path everytime before they start work ? Really ? `/Applications/Xcode6-Beta5.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.0.sdk/usr/include/CommonCrypto/CommonCrypto.h"` – Siddharth Feb 07 '18 at 07:35
52

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

modulemap approach

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

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

  • Make a folder CCommonCrypto containing module.modulemap

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

      ${SRCROOT}/Sources/CCommonCrypto
    

Cocoapods with modulemap approach

public header approach

Cocoapods with public header approach

Interesting related posts

Community
  • 1
  • 1
onmyway133
  • 45,645
  • 31
  • 257
  • 263
  • 1
    Wow! To my surprise, the header path from the top approach (creating the module.modulemap file) worked great when previously modulemap files had been causing lots of issues. I had been struggling with this for a while using a module.modulemap file with an absolute path to `/CommonCrypto/CommonCrypto.h` within `Applications/XCode.app/Contents/Developer/Platforms/iPhoneOS....` , which required manual modification for people who renamed XCode. Switching that line to look at `"/usr/include/CommonCrypto/CommonCrypto.h"` seems to work fine for a team with several XCode versions. Thank you so much! – Natalia Jun 12 '17 at 23:55
  • 3
    Im creating a pod, I set the SWIFT_INCLUDE_PATHS and preserve_paths. When I run `pod lib lint`, but build failed with error: no such module 'CommonCrypto'. How can I deal with it. – Klein Mioke Jun 16 '17 at 10:04
  • 2
    Not related to the problem but I love the use of emoji as bullets! – Fomentia Sep 06 '17 at 17:38
  • 2
    @onmyway133 Using local development pod works if you replace `$(PODS_ROOT)/CommonCryptoSwift/Sources/CCommonCrypto` with `$(PODS_TARGET_SRCROOT)/Sources/CCommonCrypto`. `PODS_TARGET_SRCROOT` is set correctly for local pods. – Orkhan Alikhanov Oct 24 '17 at 22:11
  • Great answer, saved my life! Thanks a million – Hassan Shahbazi May 20 '18 at 08:10
46

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

Just add import CommonCrypto in your swift file.

Marián Černý
  • 15,096
  • 4
  • 70
  • 83
mxcl
  • 26,392
  • 12
  • 99
  • 98
  • Great news! Can you add the link to documentation? – Kryštof Matěj Jun 08 '18 at 19:51
  • I don't have a link to documentation, I discovered this while trying to compile a project where I had one of the workarounds here with Xcode 10. It complained that it could find two `CommonCrypto` modules, thus suspecting Apple now provided I removed my workaround and ’lo! It was true. I tweeted about it and an Apple engineer replied confirming that it was intended. – mxcl Jun 11 '18 at 16:58
  • 1
    App store is only showing me 9.4.7 as an available update, how you got Xcode 10? – Hammad Tariq Jun 23 '18 at 10:27
  • 1
    It's in beta as a trivial Google search would have told you. – mxcl Jun 24 '18 at 17:11
  • Awesome! Just do `import CommonCrypto` in your swift file. – Mohammad Zaid Pathan Sep 28 '18 at 12:23
  • Thank you.. That's why I had got a message "redefinition of module 'commoncrypto'" with older projects when updated my xcode to 10.. I tried to figure out what's happening, then I saw your answer – COLD ICE Oct 13 '18 at 13:03
  • @COLDICE How did you solve the redefinition? Did you remove the previous import? – Somoy Das Gupta Oct 30 '18 at 04:21
  • 1
    @SomoyDasGupta yes. Just remove the previous import, and compile it again. In other words, you don't have to do the steps from MikeWeller answer – COLD ICE Oct 30 '18 at 16:40
  • thanks @COLDICE. But would it work with the previous iOS versions? – Somoy Das Gupta Oct 31 '18 at 03:26
  • @SomoyDasGupta yes, I believe so. – COLD ICE Oct 31 '18 at 12:42
7

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:

pod 'CommonCryptoModule', '~> 1.0.2'

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

import CommonCryptoModule

Hope someone else finds this useful.

Nikita Kukushkin
  • 14,648
  • 4
  • 37
  • 45
6

For anyone using swift 4.2 with Xcode 10:

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

import CommonCrypto

kubrick G
  • 856
  • 6
  • 10
5

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

Add a Run Script phase before the Compile Sources phase containing this 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_DIR for frameworks.

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

jjrscott
  • 1,386
  • 12
  • 16
  • See [dvdblk's answer](https://stackoverflow.com/a/50731485/542244) for an improvement that covers usage in CocoaPods. – jjrscott Jul 03 '18 at 20:22
4

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.

iwasrobbed
  • 46,496
  • 21
  • 150
  • 195
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • I found this to compile reliably and it is plain simple. Does the application using the framework need to link to anything special, or is CommonCrypto always available? – codingFriend1 Feb 23 '17 at 14:46
  • 1
    I think `Security.framework` is automatically linked (it's be a little while since a started a new project). If you get errors, that's the framework to link. – Rob Napier Feb 23 '17 at 16:40
  • This does seem like the most simple solution and it works great on one machine, but whenever I use the framework in another framework, or app, on another machine I get the 'missing module' error. – Rich Everhart Aug 04 '17 at 22:51
4

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

pod 'libCommonCrypto'

The other pods available did not work for me.

Jacob Jennings
  • 2,796
  • 1
  • 24
  • 26
2

I know this is an old question. But I figure out an alternative way to use the library in Swift project, which might be helpful for those who don't want to import framework introduced in these answers.

In Swift project, create a Objective-C bridging header, create NSData category (or custom class that to use the library) in Objective-C. The only drawback would be that you have to write all implementation code in Objective-C. For example:

#import "NSData+NSDataEncryptionExtension.h"
#import <CommonCrypto/CommonCryptor.h>

@implementation NSData (NSDataEncryptionExtension)
- (NSData *)AES256EncryptWithKey:(NSString *)key {
    //do something
}

- (NSData *)AES256DecryptWithKey:(NSString *)key {
//do something
}

And then in your objective-c bridging header, add this

#import "NSData+NSDataEncryptionExtension.h"

And then in Swift class do similar thing:

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

It works as expected.

Terence
  • 443
  • 3
  • 13
  • This works very smoothly, and even allows you to keep internals internal (`NSData+NSDataEncryptionExtension.h` doesn't have to be public). – Raphael Jun 01 '17 at 07:31
  • But what OS framework should I link against, to use this thing? Unlike others - I have to work with CommonCrypto in an Obj-C project, and that alone breaks on Xcode 9 with MacOS-10.13 SDK – Motti Shneor Apr 08 '18 at 15:15
  • @MottiShneor Link any OS from 10.9 or above. I am working on same environment and it works fine. – Terence Apr 09 '18 at 15:52
2

I've added some cocoapods magic to jjrscott's answer in case you need to use CommonCrypto in your cocoapods library.


1) Add this line to your podspec:

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

2) Save this in your library folder or wherever you like (however don't forget to change the script_phase accordingly ...)

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

Works like a charm :)

David
  • 2,109
  • 1
  • 22
  • 27
  • Can you provide demo or sample framework project for the same along with the pod spec file? – Gowtham Sep 17 '18 at 06:56
  • @dvdblk In my case I have a my own pod which uses third-party pod which uses `CommonCrypto`. Should I fix my pod or should it be fixed in that third-party pod? – Vyachaslav Gerchicov Jul 31 '20 at 07:29
0

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:

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

And another called module.modulemap:

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

(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

import CommonCrypto

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.

Dylan Nicholson
  • 1,301
  • 9
  • 23
0

In case you have the below issue :

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

In Xcode 10, Swift 4.0. CommonCrypto is a part of the framework.

Add

 import CommonCrypto

Remove

  • CommonCrpto lib file from link binary with libraries from Build phases
  • import CommonCrypto from Bridging header

This worked for me!

Ravi B
  • 1,574
  • 2
  • 14
  • 31
SKSN
  • 1
  • 2
-1

It happened the same to me after updating Xcode. I tried everything I can do such as reinstalling cocoapods and cleaning the project, but it didn't work. Now it's been solved after restart the system.

Joey
  • 2,912
  • 2
  • 27
  • 32
-14

It's very simple. Add

#import <CommonCrypto/CommonCrypto.h>

to a .h file (the bridging header file of your project). As a convention you can call it YourProjectName-Bridging-Header.h.

Then go to your project Build Settings and look for Swift Compiler - Code Generation. Under it, add the name of your bridging header to the entry "Objetive-C Bridging Header".

You're done. No imports required in your Swift code. Any public Objective-C headers listed in this bridging header file will be visible to Swift.

Juanjo Conti
  • 28,823
  • 42
  • 111
  • 133
  • your method returns error: using bridging headers with framework targets is unsupported – gorgi93 Mar 07 '15 at 15:46
  • 5
    @gorgi93 You can't use a bridging header in a framework target, as the error suggests. The only option is to put it in the main framework header file, unfortunately. – Charles A. Mar 10 '15 at 23:12
  • 1
    If you had actually red the title of this thread you would have seen that the guy is wanting to import the CommonCrypto library into a Swift framework. You cannot use bridging headers in frameworks, and you cannot import the CommonCrypto framework into the umbrella header. – miken.mkndev Mar 11 '16 at 15:51