Импорт 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
).
Добавьте новый целевой объект фреймворка в ваш проект и назовите его после системной библиотеки, например, "CommonCrypto". (Вы можете удалить заголовок зонтика, CommonCrypto.h.)
Добавьте новый файл настроек конфигурации и назовите его, например, "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
Создайте три указанных выше файла карты модулей и заполните их следующими данными:
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.)На вкладке " Информация " параметров вашего проекта в разделе " Конфигурации" задайте для параметров " Отладка" и " Выпуск" CommonCrypto значение CommonCrypto (ссылается на CommonCrypto.xcconfig).
На вкладке Фазы сборки целевого фреймворка добавьте каркас CommonCrypto в зависимости от цели. Дополнительно добавьте libcommonCrypto.dylib на этапе сборки Link Binary With Libraries.
Выберите 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 с модульным подходом
Вот подспец https://github.com/onmyway133/Arcane/blob/master/Arcane.podspec
s.source_files = 'Sources/**/*.swift' s.xcconfig = { 'SWIFT_INCLUDE_PATHS' => '$(PODS_ROOT)/CommonCryptoSwift/Sources/CCommonCrypto' } s.preserve_paths = 'Sources/CCommonCrypto/module.modulemap'
С помощью
module_map
не работает, см. https://github.com/CocoaPods/CocoaPods/issues/5271Использование локального модуля разработки с
path
не работает, см. https://github.com/CocoaPods/CocoaPods/issues/809Вот почему вы видите, что мой пример подфайла https://github.com/onmyway133/CommonCrypto.swift/blob/master/Example/CommonCryptoSwiftDemo/Podfile указывает на репозиторий git
target 'CommonCryptoSwiftDemo' do pod 'CommonCryptoSwift', :git => 'https://github.com/onmyway133/CommonCrypto.swift' end
подход публичного заголовка
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 с подходом публичного заголовка
Посмотрите на подспец https://github.com/honghaoz/Ji/blob/master/Ji.podspec
s.libraries = "xml2" s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2', 'OTHER_LDFLAGS' => '-lxml2' }
Интересные похожие посты
Хорошие новости! 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.