Импорт CommonCrypto в Swift - программирование

Импорт CommonCrypto в Swift

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

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

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

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

import FrameworkName

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

4b9b3361

Ответ 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 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 с модульным подходом

🐘 подход с открытым заголовком

  • 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 с подходом общего заголовка

🐝 Интересные связанные должности

Ответ 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.