Импорт CommonCrypto в среде Swift

Как вы импортируете CommonCrypto в фреймворке Swift для iOS?

Я понимаю как пользоваться CommonCrypto в приложении Swift: вы добавляете #import <CommonCrypto/CommonCrypto.h> к соединительному заголовку. Однако платформы Swift не поддерживают мостовые заголовки. В документации сказано:

Вы можете импортировать внешние фреймворки, которые имеют чистую кодовую базу Objective-C, чистую кодовую базу Swift или кодовую базу смешанного языка. Процесс импорта внешней платформы одинаков, независимо от того, написана ли структура на одном языке или содержит файлы с обоих языков. При импорте внешней инфраструктуры убедитесь, что для параметра сборки модуля "Определения" для импортируемой платформы установлено значение "Да".

Вы можете импортировать фреймворк в любой файл Swift в другой цели, используя следующий синтаксис:

import FrameworkName

К сожалению, импорт CommonCrypto не работает Также не добавление #import <CommonCrypto/CommonCrypto.h> к заголовку зонтика.

15 ответов

Решение

Я нашел проект GitHub, который успешно использует CommonCrypto в среде Swift: SHA256-Swift. Также эта статья о той же проблеме с 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 Compiler - пути поиска добавьте CommonCrypto каталог к путям импорта (SWIFT_INCLUDE_PATHS).

Настройки сборки

3) Наконец, импортируйте CommonCrypto внутри ваших файлов Swift, как и любые другие модули. Например:

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 <= Мы должны удалить ссылку на строку "CommonCrypto", чтобы получить успешную компиляцию.

ОБНОВЛЕНИЕ / РЕДАКТИРОВАТЬ

Я продолжал получать следующую ошибку сборки:

ld: библиотека не найдена для -lCommonCrypto для архитектуры x86_64 clang: error: сбой команды компоновщика с кодом выхода 1 (используйте -v для просмотра вызова)

Если я не удалил линию link "CommonCrypto" от module.map файл, который я создал. Как только я удалил эту строку, она построена нормально.

Что-то более простое и надежное - создать Aggregate target под названием "CommonCryptoModuleMap" с фазой Run Script для автоматической генерации карты модулей с правильным путем Xcode/SDK:

Фаза 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

Использование шелл-кода и ${SDKROOT} означает, что вам не нужно жестко кодировать путь Xcode.app, который может варьироваться от системы к системе, особенно если вы используете xcode-select переключиться на бета-версию или на CI-сервере, где несколько версий установлены в нестандартных местах. Вам также не нужно жестко кодировать SDK, так что это должно работать для iOS, macOS и т. Д. Вам также не нужно ничего размещать в исходном каталоге вашего проекта.

После создания этой цели сделайте свою библиотеку / фреймворк зависимой от нее с помощью элемента Target Dependencies:

Это будет гарантировать, что карта модуля будет сгенерирована до того, как будет построена ваша структура

примечание macOS: если вы поддерживаете macOS а вам нужно будет добавить macosx к Supported Platforms создать настройку для новой только что созданной совокупной цели, иначе она не установит правильную карту модуля Debug папка производных данных с остальными продуктами платформы.

Затем добавьте родительский каталог карты модуля, ${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap, к настройке сборки "Пути импорта" в разделе Swift (SWIFT_INCLUDE_PATHS):

Не забудьте добавить $(inherited) линии, если у вас есть пути поиска, определенные на уровне проекта или xcconfig.

Вот и все, теперь вы должны быть в состоянии 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

На самом деле вы можете создать решение, которое "просто работает" (не нужно копировать module.modulemap и SWIFT_INCLUDE_PATHS настройки для вашего проекта, как того требуют другие решения здесь), но для этого требуется создать фиктивную структуру / модуль, которую вы импортируете в свою структуру. Мы также можем гарантировать, что он работает независимо от платформы (iphoneos, iphonesimulator, или же macosx).

  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 с вашим текущим OS SDK, если не работает El Capitan.)

  4. На вкладке " Информация " параметров вашего проекта в разделе " Конфигурации" задайте для параметров " Отладка" и " Выпуск" CommonCrypto значение CommonCrypto (ссылается на CommonCrypto.xcconfig).

  5. На вкладке Фазы сборки целевого фреймворка добавьте каркас CommonCrypto в зависимости от цели. Дополнительно добавьте libcommonCrypto.dylib на этапе сборки Link Binary With Libraries.

  6. Выберите CommonCrypto.framework в Продуктах и убедитесь, что для его Целевого членства для вашей оболочки установлено значение Необязательно.

Теперь вы должны быть в состоянии построить, запустить и import CommonCrypto в вашей оболочке.

Для примера посмотрите, как SQLite.swift использует фиктивную sqlite3.framework.

В этом ответе обсуждается, как заставить его работать внутри фреймворка, а также с Cocoapods и Carthage.

модульный подход

я использую modulemap в моей обертке вокруг CommonCrypto https://github.com/onmyway133/arcane, https://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 *
      }
    
  • Зайдите в Встроенные настройки -> Пути импорта

      ${SRCROOT}/Sources/CCommonCrypto
    

Cocoapods с модульным подходом

подход публичного заголовка

  • Ji является оберткой вокруг libxml2 и использует подход с открытым заголовком

  • Он имеет заголовочный файл https://github.com/honghaoz/Ji/blob/master/Source/Ji.h с Target Membership установлен в Public

  • У него есть список файлов заголовков для libxml2 https://github.com/honghaoz/Ji/tree/master/Source/Ji-libxml

  • У этого есть Настройки сборки -> Пути поиска Заголовка

      $(SDKROOT)/usr/include/libxml2
    
  • В нем есть Настройки сборки -> Другие флаги компоновщика.

      -lxml2
    

Cocoapods с подходом публичного заголовка

Интересные похожие посты

Хорошие новости! Swift 4.2 (Xcode 10) наконец-то предоставляет CommonCrypto!

ВНИМАНИЕ: iTunesConnect может отклонить приложения, использующие этот метод.


Новый член моей команды случайно сломал решение, предоставленное одним из лучших ответов, поэтому я решил объединить его в небольшой проект-обертку под названием CommonCryptoModule. Вы можете установить его вручную или через Cocoapods:

pod 'CommonCryptoModule', '~> 1.0.2'

Затем все, что вам нужно сделать, это импортировать модуль туда, где вам нужно CommonCrypto, вот так:

import CommonCryptoModule

Надеюсь, кто-то еще найдет это полезным.

Для тех, кто использует Swift 4.2 с Xcode 10:

Модуль CommonCrypto теперь предоставляется системой, поэтому вы можете напрямую импортировать его, как и любой другой системный каркас.

import CommonCrypto

Я думаю, что у меня есть улучшение к отличной работе Майка Уэллера.

Добавьте фазу Run Script перед 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"

Этот сценарий создает базовую платформу с правильным модулем module.map, а затем использует автоматический поиск Xcode BUILT_PRODUCTS_DIR для рамок.

Я связал исходную папку CommonCrypto include в качестве папки Headers платформы, поэтому результат также должен работать для проектов Objective C.

Решения с модульной картой могут быть хорошими и устойчивыми к изменениям в SDK, но я нашел их неудобными для использования на практике и не такими надежными, как хотелось бы при передаче вещей другим. Чтобы попытаться сделать все это более надежным, я пошел другим путем:

Просто скопируйте заголовки.

Я знаю, хрупкий. Но Apple почти никогда не вносит существенных изменений в CommonCrypto, и я живу мечтой, что они не изменят его каким-либо существенным образом, не сделав, наконец, CommonCrypto модульным заголовком.

Под "копировать заголовки" я подразумеваю "вырезать и вставлять все нужные заголовки в один массивный заголовок в вашем проекте, как это делал бы препроцессор". В качестве примера этого, который вы можете скопировать или адаптировать, смотрите RNCryptor.h.

Обратите внимание, что все эти файлы лицензированы в соответствии с APSL 2.0, и этот подход намеренно поддерживает уведомления об авторских правах и лицензии. Мой этап объединения лицензирован в рамках MIT, и это относится только к следующему уведомлению о лицензии).

Я не говорю, что это прекрасное решение, но до сих пор это было невероятно простое решение как для реализации, так и для поддержки.

@mogstad был достаточно любезен, чтобы обернуть раствор @stephencelis в Cocoapod:

под 'libCommonCrypto'

Другие доступные стручки не работали для меня.

Я знаю, что это старый вопрос. Но я придумываю альтернативный способ использования библиотеки в проекте Swift, который может быть полезен для тех, кто не хочет импортировать фреймворк, представленный в этих ответах.

В проекте Swift создайте заголовок моста Objective C, создайте категорию NSData (или пользовательский класс, который будет использовать библиотеку) в Objective C. Единственным недостатком будет то, что вам придется писать весь код реализации в 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
}

А затем в заголовке моста target-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
}
}

Работает как положено.

Я добавил магию cocoapods к ответу jjrscott на случай, если вам понадобится CommonCrypto в вашей библиотеке cocoapods.


1) Добавьте эту строку в ваш подспец:

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

2) Сохраните это в своей папке библиотеки или где угодно (но не забудьте соответственно изменить script_phase...)

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

Работает как шарм:)

Если у вас есть следующая проблема:

ld: библиотека не найдена для -lapple_crypto clang: error: сбой команды компоновщика с кодом выхода 1 (используйте -v для просмотра вызова)

В Xcode 10, Swift 4.0. CommonCrypto является частью структуры.

добавлять

  • импорт CommonCrypto

Удалить

  • Файл lib CommonCrpto из бинарного файла с библиотеками на этапах сборки
  • импортировать CommonCrypto из заголовка Bridging

Это сработало для меня!

Я не уверен, что что-то изменилось с Xcode 9.2, но теперь это сделать намного проще. Единственное, что мне нужно было сделать, - это создать папку с именем "CommonCrypto" в моей директории проекта фреймворка и создать в ней два файла, один из которых называется "cc.h" следующим образом:

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

И еще один называется module.modulemap:

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

(Я не знаю, почему вы не можете ссылаться на файлы заголовков из области SDKROOT непосредственно в файле modulemap, но я не смог заставить его работать)

В-третьих, нужно найти параметр "Пути импорта" и установить значение $(SRCROOT). Фактически вы можете установить его в любую папку, в которой вы хотите разместить папку CommonCrypto, если вы не хотите, чтобы она находилась на корневом уровне.

После этого вы сможете использовать

import CommonCrypto

В любом файле swift и во всех типах / функциях / и т.д. доступны.

Однако, предупреждающее слово - если ваше приложение использует libCommonCrypto (или libcoreCrypto), для не слишком искушенного хакера исключительно легко подключить отладчик к вашему приложению и выяснить, какие ключи передаются этим функциям.

То же произошло и со мной после обновления Xcode. Я пробовал все, что мог, например, переустановку cocoapods и очистку проекта, но это не сработало. Теперь это решено после перезагрузки системы.

Если вы здесь для алгоритмов хеширования, таких как SHA, MD5 и т. Д., Не используйте громоздкую библиотеку CommonCrypto. Вместо этого найдите конкретную библиотеку хеширования, которую вы ищете.

Например, для MD5 вы можете пойти с SwiftHash

Это очень просто. добавлять

#import <CommonCrypto/CommonCrypto.h>

в файл.h (файл заголовка моста вашего проекта). В качестве соглашения вы можете назвать его YourProjectName-Bridging-Header.h.

Затем перейдите в свой проект Build Settings и найдите Swift Compiler - Code Generation. Под ним добавьте имя заголовка моста к записи "Заголовок моста Objetive-C".

Вы сделали Нет импорта в вашем коде Swift. Любые общедоступные заголовки Objective C, перечисленные в этом файле заголовка моста, будут видны Swift.

Другие вопросы по тегам