web-dev-qa-db-fra.com

Importation de CommonCrypto dans un cadre Swift

Comment importer CommonCrypto dans un framework Swift pour iOS?

Je comprends comment utiliser CommonCrypto dans une application Swift: Vous ajoutez #import <CommonCrypto/CommonCrypto.h> à l'en-tête de pontage . Cependant, les cadres Swift ne prennent pas en charge les en-têtes de pontage. La documentation dit:

Vous pouvez importer des infrastructures externes comportant une base de code pure Objective-C, une base de code Swift pure ou une base de code multilingue. Le Le processus d'importation d'un cadre externe est le même, que le fichier framework est écrit dans une seule langue ou contient des fichiers des deux langues. Lorsque vous importez un cadre externe, assurez-vous que le fichier Définit le paramètre de construction du module pour le cadre que vous importez est défini à oui.

Vous pouvez importer une structure dans n’importe quel fichier Swift dans un fichier .__ différent. cible en utilisant la syntaxe suivante:

import FrameworkName

Malheureusement, importer CommonCrypto ne fonctionne pas. De plus, l'ajout de #import <CommonCrypto/CommonCrypto.h> à l'en-tête de parapluie.

170
hpique

J'ai trouvé un projet GitHub qui utilise avec succès CommonCrypto dans un cadre Swift: SHA256-Swift . En outre, cet article sur le même problème avec sqlite3 était utile.

Sur la base de ce qui précède, les étapes sont les suivantes:

1) Créez un répertoire CommonCrypto dans le répertoire du projet. Dans, créez un fichier module.map. La carte du module nous permettra d’utiliser la bibliothèque CommonCrypto en tant que module au sein de Swift. Son contenu est:

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) Dans les paramètres de construction, dans Swift Compiler - Search Paths, ajoutez le répertoire CommonCrypto à Import Paths (Swift_INCLUDE_PATHS).

Build Settings

3) Enfin, importez CommonCrypto dans vos fichiers Swift en tant que tout autre module. Par exemple:

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

Limites

L'utilisation de la structure personnalisée dans un autre projet échoue lors de la compilation avec l'erreur missing required module 'CommonCrypto'. En effet, le module CommonCrypto ne semble pas être inclus dans la structure personnalisée. Une solution de contournement consiste à répéter l'étape 2 (définition de Import Paths) dans le projet utilisant le cadre.

La carte du module n'est pas indépendante de la plate-forme (elle pointe actuellement vers une plate-forme spécifique, le simulateur iOS 8). Je ne sais pas comment rendre le chemin d'en-tête relatif à la plate-forme actuelle.

Mises à jour pour iOS 8 <= Nous devrions supprimer la ligne link "CommonCrypto", pour obtenir la compilation réussie.

UPDATE/EDIT

J'ai continué à avoir l'erreur de construction suivante:

ld: bibliothèque non trouvée pour -lCommonCrypto pour l'architecture x86_64 clang: erreur: la commande de l'éditeur de liens a échoué avec le code de sortie 1 (utilisez -v pour voir l'invocation) 

Sauf si j'ai supprimé la ligne link "CommonCrypto" du fichier module.map que j'ai créé. Une fois que j'ai enlevé cette ligne, ça a bien fonctionné.

81
hpique

Un élément un peu plus simple et plus robuste consiste à créer une cible Aggregate appelée "CommonCryptoModuleMap" avec une phase d’exécution de script afin de générer la mappe de module automatiquement et avec le chemin correct Xcode/SDK:

 enter image description here  enter image description here

La phase d’exécution du script doit contenir ce 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

Utiliser le code Shell et ${SDKROOT} signifie que vous n’aurez pas à coder en dur le chemin Xcode.app, qui peut varier d’un système à l’autre, en particulier si vous utilisez xcode-select pour passer à une version bêta ou si vous utilisez un serveur CI où plusieurs versions sont utilisées. sont installés dans des emplacements non standard. Vous n'avez pas non plus besoin de coder en dur le SDK, cela devrait donc fonctionner pour iOS, macOS, etc. Il n'est pas nécessaire que rien soit placé dans le répertoire source de votre projet.

Après avoir créé cette cible, faites en sorte que votre bibliothèque/infrastructure en dépende avec un élément Target Dependencies:

 enter image description here

Cela garantira que la carte du module est générée avant la construction de votre infrastructure.

macOS note: si vous prenez également en charge macOS, vous devez ajouter macosx au paramètre de construction Supported Platforms de la nouvelle cible agrégée que vous venez de créer. Sinon, la mappe de module ne sera pas correctement placée dans Debug dossier de données dérivé avec le reste des produits cadre.

 enter image description here

Ensuite, ajoutez le répertoire parent de la mappe de modules, ${BUILT_PRODUCTS_DIR}/CommonCryptoModuleMap, au paramètre de construction "Import Paths" de la section Swift (Swift_INCLUDE_PATHS):

 enter image description here

N'oubliez pas d'ajouter une ligne $(inherited) si des chemins de recherche sont définis au niveau du projet ou de xcconfig.

Ça y est, vous devriez maintenant pouvoir import CommonCrypto

Mise à jour pour Xcode 10

Xcode 10 est maintenant livré avec une carte de module CommonCrypto, ce qui rend cette solution de contournement inutile. Si vous souhaitez prendre en charge Xcode 9 et 10, vous pouvez effectuer une vérification dans la phase d’exécution du script pour voir si la carte du module existe ou non, par exemple.

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
131
Mike Weller

Vous pouvez réellement créer une solution qui "fonctionne" (il n'est pas nécessaire de copier les paramètres module.modulemap et Swift_INCLUDE_PATHS dans votre projet, comme requis par d'autres solutions ici), mais vous devez créer un framework factice./module que vous importerez dans votre propre framework. Nous pouvons également nous assurer que cela fonctionne quelle que soit la plate-forme (iphoneos, iphonesimulator ou macosx).

  1. Ajoutez une nouvelle cible de structure à votre projet et nommez-la après la bibliothèque système, par exemple, "CommonCrypto". (Vous pouvez supprimer l'en-tête d'un parapluie, CommonCrypto.h.)

  2. Ajoutez un nouveau fichier de paramètres de configuration et nommez-le, par exemple, "CommonCrypto.xcconfig". (Ne cochez aucune de vos cibles pour l'inclusion.) Remplissez-le avec les éléments suivants:

    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. Créez les trois fichiers de carte de module référencés, ci-dessus, et remplissez-les avec les éléments suivants:

    • 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 *
      }
      

    (Remplacez "Xcode.app" par "Xcode-beta.app" si vous utilisez une version bêta. Remplacez 10.11 par votre SDK du système d'exploitation actuel si vous n'exécutez pas El Capitan.)

  4. Dans l'onglet Info des paramètres de votre projet, sous Configurations, définissez les configurations Debug et Release de CommonCrypto sur CommonCrypto (référençant CommonCrypto.xcconfig).

  5. Dans l'onglet Build Phases de votre cible d'infrastructure, ajoutez l'infrastructure CommonCrypto à Dependencies cible. Ajoutez en outre libcommonCrypto.dylib à la phase de construction de Link Binary With Libraries.

  6. Sélectionnez CommonCrypto.framework dans Products et assurez-vous que Target Membership pour votre wrapper est défini sur Optional.

Vous devriez maintenant pouvoir construire, exécuter et import CommonCrypto dans votre framework d'encapsulation.

Pour un exemple, voyez comment SQLite.Swift utilise un mannequin sqlite3.framework.

90
stephencelis

Cette réponse explique comment le faire fonctionner dans un cadre, et avec Cocoapods et Carthage

???? approche modulemap

J'utilise modulemap dans mon enveloppe autour de CommonCrypto https://github.com/onmyway133/arcane , https://github.com/onmyway133/Reindeer

Pour ceux qui obtiennent header not found, jetez un œil sur https://github.com/onmyway133/Arcane/issues/4 ou exécutez xcode-select --install

  • Créer un dossier CCommonCrypto contenant module.modulemap

      module CCommonCrypto {
        header "/usr/include/CommonCrypto/CommonCrypto.h"
        export *
      }
    
  • Aller aux paramètres prédéfinis -> Chemins d'importation

      ${SRCROOT}/Sources/CCommonCrypto
    

???? Cocoapods avec approche modulemap

???? approche d'en-tête publique

???? Cocoapods avec approche d'en-tête publique

???? Messages connexes intéressants

50
onmyway133

Bonnes nouvelles! Swift 4.2 (Xcode 10) fournit enfin CommonCrypto!

Ajoutez simplement import CommonCrypto dans votre fichier Swift.

28
mxcl

AVERTISSEMENT: iTunesConnect peut rejeter les applications utilisant cette méthode.


Un nouveau membre de mon équipe a cassé accidentellement la solution donnée par l'une des réponses les plus importantes. J'ai donc décidé de la consolider dans un petit projet d'emballage appelé CommonCryptoModule . Vous pouvez l'installer manuellement ou via Cocoapods: 

pod 'CommonCryptoModule', '~> 1.0.2'

Ensuite, tout ce que vous avez à faire est d’importer le module où vous avez besoin de CommonCrypto, comme ceci:

import CommonCryptoModule

J'espère que quelqu'un trouve cela utile.

7
Nikita Kukushkin

Je pense que l'excellent travail de Mike Weller a été amélioré.

Ajoutez une phase d’exécution de script avant la phase Compile Sources contenant ce 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"

Ce script construit un cadre dépouillé avec le module.map au bon endroit, puis repose sur la recherche automatique par Xcode de BUILT_PRODUCTS_DIR.

J'ai lié le dossier inclus CommonCrypto d'origine en tant que dossier En-têtes du cadre afin que le résultat fonctionne également pour les projets Objective C. 

5
jjrscott

Les solutions modulemap peuvent être bonnes et sont robustes face aux modifications du SDK, mais je les ai trouvées difficiles à utiliser dans la pratique, et pas aussi fiables que je le souhaiterais lorsque je donne des choses à d'autres. Pour essayer de rendre le tout plus infaillible, je suis allé d'une manière différente:

Il suffit de copier les en-têtes.

Je sais, fragile. Mais Apple n’apporte presque jamais de modifications significatives à CommonCrypto et je vis le rêve qu’ils ne le modifieront pas de manière significative sans faire de CommonCrypto un en-tête modulaire.

Par "copier les en-têtes", je veux dire "couper et coller tous les en-têtes dont vous avez besoin dans un en-tête massif dans votre projet, comme le ferait le pré-processeur". Comme exemple de cela, vous pouvez copier ou adapter, voir RNCryptor.h .

Notez que tous ces fichiers sont sous licence APSL 2.0 et que cette approche maintient intentionnellement les notifications de copyright et de licence. Mon étape de concaténation est sous licence MIT, et cela ne s'applique que jusqu'au prochain avis de licence).

Je ne dis pas que c'est une belle solution, mais jusqu'à présent, cela semble avoir été une solution incroyablement simple à mettre en œuvre et à prendre en charge.

4
Rob Napier

@mogstad a eu l'amabilité d'envelopper la solution @stephencelis dans un Cocoapod

pod 'libCommonCrypto'

Les autres pods disponibles ne fonctionnaient pas pour moi.

4
Jacob Jennings

Je sais que c'est une vieille question. Mais je trouve une autre façon d'utiliser la bibliothèque dans le projet Swift, ce qui pourrait être utile pour ceux qui ne veulent pas importer la structure introduite dans ces réponses.

Dans le projet Swift, créez un en-tête de pontage Objective-C, créez une catégorie NSData (ou une classe personnalisée utilisant la bibliothèque) dans Objective-C. Le seul inconvénient serait que vous devez écrire tout le code d'implémentation dans Objective-C. Par exemple:

#import "NSData+NSDataEncryptionExtension.h"
#import <CommonCrypto/CommonCryptor.h>

@implementation NSData (NSDataEncryptionExtension)
- (NSData *)AES256EncryptWithKey:(NSString *)key {
    //do something
}

- (NSData *)AES256DecryptWithKey:(NSString *)key {
//do something
}

Et puis dans votre en-tête de pont d'objectif-c, ajoutez ceci

#import "NSData+NSDataEncryptionExtension.h"

Et puis en classe Swift faire la même chose:

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
}
}

Cela fonctionne comme prévu.

2
Terence

J'ai ajouté un peu de magie des cocoapodes à la réponse de jjrscott au cas où vous auriez besoin d'utiliser CommonCrypto dans votre bibliothèque de cocoapodes.


1) Ajoutez cette ligne à votre podspec:

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

2) Enregistrez-le dans votre dossier de bibliothèque ou où bon vous semble (n'oubliez cependant pas de modifier le script_phase en conséquence ...) 

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

Fonctionne comme un charme :)

2
dvdblk

Pour ceux qui utilisent Swift 4.2 avec Xcode 10 :

Le module CommonCrypto est maintenant fourni par le système, vous pouvez donc l'importer directement comme n'importe quel autre framework système.

import CommonCrypto

0
kubrick G

Si vous avez le problème ci-dessous:

ld: bibliothèque introuvable pour -lapple_crypto clang: erreur: la commande de l'éditeur de liens a échoué avec le code de sortie 1 (utilisez -v pour voir l'invocation)

Dans Xcode 10, Swift 4.0. CommonCrypto fait partie du framework.

Ajouter 

  • importer CommonCrypto

Retirer 

  • CommonCrpto fichier lib du lien binaire avec les bibliothèques de Build Phases
  • importer l'en-tête CommonCrypto de Bridging

Cela a fonctionné pour moi!

0
SKSN

Je ne sais pas si quelque chose a changé avec Xcode 9.2, mais il est maintenant beaucoup plus simple d'y parvenir. La seule chose que je devais faire était de créer un dossier appelé "CommonCrypto" dans le répertoire de mon projet-cadre et d'y créer deux fichiers, l'un appelé "cc.h", comme suit:

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

Et un autre appelé module.modulemap:

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

(Je ne sais pas pourquoi vous ne pouvez pas référencer les fichiers d'en-tête de la zone SDKROOT directement dans un fichier modulemap mais je ne peux pas le faire fonctionner)

La troisième chose à faire est de trouver le paramètre "Import Paths" (Chemins d'importation) et de le définir sur $ (SRCROOT) . En fait, vous pouvez le définir sur le dossier dans lequel vous souhaitez placer le dossier CommonCrypto, si vous ne le souhaitez pas. niveau racine.

Après cela, vous devriez pouvoir utiliser 

import CommonCrypto

Dans n'importe quel fichier Swift et tous les types/fonctions/etc. sont disponibles.

Un mot d'avertissement cependant: si votre application utilise libCommonCrypto (ou libcoreCrypto), il est extrêmement facile pour un pirate informatique pas trop sophistiqué d'attacher un débogueur à votre application et de savoir quelles clés sont transmises à ces fonctions.

0
Dylan Nicholson

Il m'est arrivé la même chose après la mise à jour de Xcode. J'ai essayé tout ce que je pouvais faire, comme réinstaller des cocoapodes et nettoyer le projet, mais cela n'a pas fonctionné. Maintenant, il a été résolu après redémarrage le système.

0
Joey