iosswiftcommoncrypto

Importing CommonCrypto in a Swift framework


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.


Solution

  • 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.