Импорт CommonCrypto в Swift
Как вы импортируете CommonCrypto
в платформу Swift для iOS?
Я понимаю, как использовать CommonCrypto
в приложении Swift: вы добавляете #import <CommonCrypto/CommonCrypto.h>
в заголовок моста. Однако платформы Swift не поддерживают мостовые заголовки. В документации сказано:
Вы можете импортировать внешние фреймворки, которые имеют чистую кодовую базу Objective-C, чистую кодовую базу Swift или кодовую базу смешанного языка. Процесс импорта внешней платформы одинаков, независимо от того, написана ли структура на одном языке или содержит файлы с обоих языков. При импорте внешней инфраструктуры убедитесь, что для параметра сборки модуля "Определения" для импортируемой платформы задано значение "Да".
Вы можете импортировать фреймворк в любой файл Swift с другой целью, используя следующий синтаксис:
import FrameworkName
К сожалению, импорт CommonCrypto
не работает. Также не добавляется #import <CommonCrypto/CommonCrypto.h>
в заголовок зонтика.
Ответы
Ответ 1
Я нашел проект 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 - Search Paths добавьте каталог CommonCrypto
в Import Paths (SWIFT_INCLUDE_PATHS
).
![Build Settings]()
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 Simulator). Я не знаю, как сделать путь заголовка относительно текущей платформы.
Обновления для iOS 8 <= Мы должны удалить строку "CommonCrypto", чтобы получить успешную компиляцию.
ОБНОВЛЕНИЕ/РЕДАКТИРОВАНИЕ
Я продолжал получать следующую ошибку сборки:
ld: библиотека не найдена для -lCommonCrypto для архитектуры x86_64 clang: ошибка: команда компоновщика не удалась с кодом выхода 1 (используйте -v, чтобы увидеть вызов)
Если я не удалял link "CommonCrypto"
линии link "CommonCrypto"
из файла module.map
который я создал. Как только я удалил эту строку, она построена нормально.
Ответ 2
Что-то более простое и надежное заключается в создании целевой совокупности, называемой "CommonCryptoModuleMap", с этапом сценария запуска для автоматического создания карты модуля и с правильным ходом Xcode/SDK:
![enter image description here]()
Этап запуска сценария должен содержать этот 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:
![enter image description here]()
Это обеспечит создание карты модуля до создания вашей структуры.
Заметка macOS: если вы поддерживаете macOS
, вам нужно добавить macosx
к настройке сборки Supported Platforms
в новом созданном целевом агрегате, иначе он не поместит карту модуля в правильную папку данных Debug
с остальными продуктами рамок.
![enter image description here]()
Затем добавьте родительский каталог карты модулей, ${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap
, в настройку сборки "Импорт путей" в разделе Swift (SWIFT_INCLUDE_PATHS
):
![enter image description here]()
Не забудьте добавить $(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
Ответ 3
Фактически вы можете создать решение, которое "просто работает" (нет необходимости копировать параметры 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
на ваш текущий SDK ОС, если не используете El Capitan.)
-
На вкладке Инфо параметров вашего проекта в разделе Конфигурации установите Отладка и Отпустить. > конфигурации CommonCrypto на CommonCrypto (ссылка CommonCrypto.xcconfig).
-
На вкладке Настроить факс на платформе добавьте структуру CommonCrypto в Зависимости целей. Кроме того, добавьте libcommonCrypto.dylib в фазу сборки Link Binary With Libraries.
-
Выберите CommonCrypto.framework в Продукты и убедитесь, что для целевого членства для вашей оболочки установлено значение Необязательно.
Теперь вы можете создавать, запускать и import CommonCrypto
в своей оболочке.
В качестве примера см., как SQLite.swift использует фиктивный sqlite3.framework.
Ответ 4
В этом ответе обсуждается, как заставить его работать внутри рамки, а также с 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 с модульным подходом
🐘 подход с открытым заголовком
🐏 Cocoapods с подходом общего заголовка
🐝 Интересные связанные должности
Ответ 5
Хорошие новости! Swift 4.2 (Xcode 10) наконец-то предоставляет CommonCrypto!
Просто добавьте import CommonCrypto
в ваш файл swift.
Ответ 6
ПРЕДУПРЕЖДЕНИЕ: iTunesConnect может отклонять приложения, которые используют этот метод.
Новый член моей команды случайно сломал решение, данное одним из лучших ответов, поэтому я решил объединить его в небольшой проект-оболочку под названием CommonCryptoModule. Вы можете установить его вручную или через Cocoapods:
pod 'CommonCryptoModule', '~> 1.0.2'
Затем все, что вам нужно сделать, это импортировать модуль, в котором вам нужен CommonCrypto
, например:
import CommonCryptoModule
Надеюсь, что кто-то найдет это полезным.
Ответ 7
Я думаю, что у меня есть отличная работа Майка Уэллера.
Добавьте этап сценария запуска до этапа " 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"
Этот скрипт создает прозрачную структуру костей с модулем.map в правильном месте, а затем использует автоматический поиск Xcode BUILT_PRODUCTS_DIR
для фреймворков.
Я связал исходную папку CommonCrypto include как папку с заголовками Framework, поэтому результат должен также работать для проектов Objective C.
Ответ 8
Модульные решения могут быть хорошими и надежными в отношении изменений SDK, но я счел их неудобными использовать на практике, а не так надежно, как хотелось бы, когда передавал вещи другим. Чтобы попытаться сделать его более надежным, я пошел по-другому:
Просто скопируйте заголовки.
Я знаю, хрупкий. Но Apple почти никогда не вносит существенных изменений в CommonCrypto, и я живу мечтой, что они не изменят ее каким-либо значительным образом, не превратив CommonCrypto в модульный заголовок.
"Скопируйте заголовки", я имею в виду "вырезать и вставить все заголовки, которые вам нужны, в один массивный заголовок в вашем проекте, как это сделал бы препроцессор". В качестве примера этого можно скопировать или адаптироваться, см. RNCryptor.h.
Обратите внимание, что все эти файлы лицензированы в APSL 2.0, и этот подход намеренно поддерживает уведомления об авторских правах и лицензиях. Мой этап конкатенации лицензируется в рамках MIT и распространяется только на следующее уведомление о лицензии).
Я не говорю, что это прекрасное решение, но до сих пор это было невероятно простое решение как для реализации, так и для поддержки.
Ответ 9
@mogstad был достаточно любезен, чтобы обернуть решение @stephencelis в Cocoapod:
pod 'libCommonCrypto'
Другие доступные модули не работали для меня.
Ответ 10
Для тех, кто использует Swift 4.2 с Xcode 10:
Модуль CommonCrypto теперь предоставляется системой, поэтому вы можете напрямую импортировать его, как и любой другой системный каркас.
import CommonCrypto
Ответ 11
Я знаю, что это старый вопрос. Но я рассматриваю альтернативный способ использования библиотеки в проекте 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
}
И затем в заголовке моста Objective-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
}
}
Работает так, как ожидалось.
Ответ 12
Я добавил магию cocoapods к jjrscott ответу, если вам нужно использовать CommonCrypto в своей библиотеке cocoapods.
1) Добавьте эту строку в свой podspec:
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"
Работает как шарм :)
Ответ 13
Я не уверен, что с Xcode 9.2 что-то изменилось, но теперь гораздо проще добиться этого. Единственное, что мне нужно было сделать, это создать папку под названием "CommonCrypto" в моей директории проекта framework и создать в ней два файла, один из которых называется "cc.h" следующим образом:
#include <CommonCrypto/CommonCrypto.h>
#include <CommonCrypto/CommonRandom.h>
И еще один модуль module.modulemap:
module CommonCrypto {
export *
header "cc.h"
}
(Я не знаю, почему вы не можете ссылаться на заголовочные файлы из области SDKROOT непосредственно в файле modulemap, но я не мог заставить его работать)
В-третьих, нужно найти параметр "Импорт путей" и установить значение $ (SRCROOT). Фактически вы можете установить его в любую папку, в которую хотите попасть папка CommonCrypto, если вы не хотите ее на корневом уровне.
После этого вы сможете использовать
import CommonCrypto
В любом быстром файле и всех типах/функциях/и т.д. доступны.
Слово предупреждения, хотя - если ваше приложение использует libCommonCrypto (или libcoreCrypto), исключительно просто для не слишком сложного хакера подключить отладчик к вашему приложению и выяснить, какие ключи передаются этим функциям.
Ответ 14
Если у вас есть проблема ниже:
ld: библиотека не найдена для -lapple_crypto clang: error: сбой команды компоновщика с кодом выхода 1 (используйте -v для просмотра вызова)
В Xcode 10, Swift 4.0. CommonCrypto является частью структуры.
добавлять
import CommonCrypto
Удалить
- Файл lib CommonCrpto из бинарного файла с библиотеками на этапах сборки
-
import CommonCrypto
из заголовка Bridging
Это сработало для меня!
Ответ 15
Это случилось со мной после обновления Xcode.
Я перепробовал все, что мог, например переустановить cocoapods и очистить проект, но это не сработало.
Теперь это было решено после перезапуска системы.
Ответ 16
Если здесь для алгоритмов хеширования, например SHA, MD5 и т.д., Не используйте громоздкую библиотеку CommonCrypto. Найдите конкретную библиотеку хеширования, которую вы ищете.
Например, для MD5 вы можете пойти с SwiftHash
Ответ 17
Это очень просто. Добавить
#import <CommonCrypto/CommonCrypto.h>
в .h файл (заголовок заголовочного файла вашего проекта). В качестве соглашения вы можете назвать его "YourProjectName-Bridging-Header.h".
Затем перейдите в свой проект Build Settings и найдите Swift Compiler - Code Generation. Под ним добавьте имя своего заголовка моста к записи "Objetive-C Bridging Header".
Вы закончили. Импорт не требуется в вашем коде Swift. Любые общедоступные заголовки Objective-C, перечисленные в этом файле заголовка моста, будут видны для Swift.