Как вы импортируете CommonCrypto
в платформу Swift для iOS?
Я понимаю, как использовать CommonCrypto
в приложении Swift: вы добавляете #import <CommonCrypto/CommonCrypto.h>
в заголовок моста. Однако платформы Swift не поддерживают мостовые заголовки. В документации сказано:
Вы можете импортировать внешние фреймворки, которые имеют чистую кодовую базу Objective-C, чистую кодовую базу Swift или кодовую базу смешанного языка. Процесс импорта внешней платформы одинаков, независимо от того, написана ли структура на одном языке или содержит файлы с обоих языков. При импорте внешней инфраструктуры убедитесь, что для параметра сборки модуля "Определения" для импортируемой платформы задано значение "Да".
Вы можете импортировать фреймворк в любой файл Swift с другой целью, используя следующий синтаксис:
import FrameworkName
К сожалению, импорт CommonCrypto
не работает. Также не добавляется #import <CommonCrypto/CommonCrypto.h>
в заголовок зонтика.
Я нашел проект 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
).
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
который я создал. Как только я удалил эту строку, она построена нормально.
$SDKROOT
должна позволять вам использовать независимые от платформы пути, но я не знаю, как это сделать в Swift.
Что-то более простое и надежное заключается в создании целевой совокупности, называемой "CommonCryptoModuleMap", с этапом сценария запуска для автоматического создания карты модуля и с правильным ходом Xcode/SDK:
Этап запуска сценария должен содержать этот 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
на ваш текущий SDK ОС, если не используете El Capitan.)
На вкладке Инфо параметров вашего проекта в разделе Конфигурации установите Отладка и Отпустить. > конфигурации CommonCrypto на CommonCrypto (ссылка CommonCrypto.xcconfig).
На вкладке Настроить факс на платформе добавьте структуру CommonCrypto в Зависимости целей. Кроме того, добавьте libcommonCrypto.dylib в фазу сборки Link Binary With Libraries.
Выберите CommonCrypto.framework в Продукты и убедитесь, что для целевого членства для вашей оболочки установлено значение Необязательно.
Теперь вы можете создавать, запускать и import CommonCrypto
в своей оболочке.
В качестве примера см., как SQLite.swift использует фиктивный sqlite3.framework.
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
В этом ответе обсуждается, как заставить его работать внутри рамки, а также с 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 с модульным подходом
Здесь находится podspec 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
Использование Local Development Pod с path
не работает, см. Https://github.com/CocoaPods/CocoaPods/issues/809
Вот почему вы видите, что мой пример Podfile https://github.com/onmyway133/CommonCrypto.swift/blob/master/Example/CommonCryptoSwiftDemo/Podfile указывает на git repo
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 с подходом общего заголовка
Взгляните на podspec https://github.com/honghaoz/Ji/blob/master/Ji.podspec
s.libraries = "xml2"
s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2', 'OTHER_LDFLAGS' => '-lxml2' }
Интересные связанные должности
/CommonCrypto/CommonCrypto.h
в Applications/XCode.app/Contents/Developer/Platforms/iPhoneOS....
, который требовал ручного изменения для люди, которые переименовали XCode. Переключение этой строки для просмотра "/usr/include/CommonCrypto/CommonCrypto.h"
похоже, отлично работает для команды с несколькими версиями XCode. Спасибо вам большое!
pod lib lint
, но сборка не удалась с ошибкой: такого модуля CommonCrypto не было. Как я могу справиться с этим.
Хорошие новости! Swift 4.2 (Xcode 10) наконец-то предоставляет CommonCrypto!
Просто добавьте import CommonCrypto
в ваш файл swift.
CommonCrypto
, таким образом, подозревая, что Apple теперь при условии, что я удалил мой обходной путь и вот! Это было правдой Я написал об этом в Твиттере, и инженер Apple подтвердил, что это было задумано.
ПРЕДУПРЕЖДЕНИЕ: iTunesConnect может отклонять приложения, которые используют этот метод.
Новый член моей команды случайно сломал решение, данное одним из лучших ответов, поэтому я решил объединить его в небольшой проект-оболочку под названием CommonCryptoModule. Вы можете установить его вручную или через Cocoapods:
pod 'CommonCryptoModule', '~> 1.0.2'
Затем все, что вам нужно сделать, это импортировать модуль, в котором вам нужен CommonCrypto
, например:
import CommonCryptoModule
Надеюсь, что кто-то найдет это полезным.
Я думаю, что у меня есть отличная работа Майка Уэллера.
Добавьте этап сценария запуска до этапа " 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.
@mogstad был достаточно любезен, чтобы обернуть решение @stephencelis в Cocoapod:
pod 'libCommonCrypto'
Другие доступные модули не работали для меня.
Модульные решения могут быть хорошими и надежными в отношении изменений SDK, но я счел их неудобными использовать на практике, а не так надежно, как хотелось бы, когда передавал вещи другим. Чтобы попытаться сделать его более надежным, я пошел по-другому:
Просто скопируйте заголовки.
Я знаю, хрупкий. Но Apple почти никогда не вносит существенных изменений в CommonCrypto, и я живу мечтой, что они не изменят ее каким-либо значительным образом, не превратив CommonCrypto в модульный заголовок.
"Скопируйте заголовки", я имею в виду "вырезать и вставить все заголовки, которые вам нужны, в один массивный заголовок в вашем проекте, как это сделал бы препроцессор". В качестве примера этого можно скопировать или адаптироваться, см. RNCryptor.h.
Обратите внимание, что все эти файлы лицензированы в APSL 2.0, и этот подход намеренно поддерживает уведомления об авторских правах и лицензиях. Мой этап конкатенации лицензируется в рамках MIT и распространяется только на следующее уведомление о лицензии).
Я не говорю, что это прекрасное решение, но до сих пор это было невероятно простое решение как для реализации, так и для поддержки.
Security.framework
автоматически связывается (пройдет немного времени с момента запуска нового проекта). Если вы получаете ошибки, это рамки для ссылок.
Я добавил магию 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"
Работает как шарм :)
Я знаю, что это старый вопрос. Но я рассматриваю альтернативный способ использования библиотеки в проекте 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
}
}
Работает так, как ожидалось.
NSData+NSDataEncryptionExtension.h
не должен быть публичным).
Для тех, кто использует Swift 4.2 с Xcode 10:
Модуль CommonCrypto теперь предоставляется системой, поэтому вы можете напрямую импортировать его, как и любой другой системный каркас.
import CommonCrypto
Если у вас есть следующая проблема:
ld: библиотека не найдена для -lapple_crypto clang: error: сбой команды компоновщика с кодом выхода 1 (используйте -v для просмотра вызова)
В Xcode 10, Swift 4.0. CommonCrypto является частью структуры.
добавлять
Удалить
Это сработало для меня!
Я не уверен, что с 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), исключительно просто для не слишком сложного хакера подключить отладчик к вашему приложению и выяснить, какие ключи передаются этим функциям.
Если здесь для алгоритмов хеширования, например SHA, MD5 и т.д., Не используйте громоздкую библиотеку CommonCrypto. Найдите конкретную библиотеку хеширования, которую вы ищете.
Например, для MD5 вы можете пойти с SwiftHash
Это очень просто. Добавить
#import <CommonCrypto/CommonCrypto.h>
в .h файл (заголовок заголовочного файла вашего проекта). В качестве соглашения вы можете назвать его "YourProjectName-Bridging-Header.h".
Затем перейдите в свой проект Build Settings и найдите Swift Compiler - Code Generation. Под ним добавьте имя своего заголовка моста к записи "Objetive-C Bridging Header".
Вы закончили. Импорт не требуется в вашем коде Swift. Любые общедоступные заголовки Objective-C, перечисленные в этом файле заголовка моста, будут видны для Swift.