web-dev-qa-db-fra.com

Xcode 6 iOS Création d'un framework Cocoa Touch - Problèmes d'architecture

J'essaie de créer un cadre dynamique pour une application iOS. Grâce à la nouvelle version de Xcode (6), nous pouvons sélectionner un framework Cocoa Touch lorsque nous créons un nouveau projet et il n’est plus nécessaire d’ajouter une cible agrégée, d’exécuter un script et donc d’en créer un. Je n'ai aucun problème quand je construis le cadre. Mais lorsque j'essaie de l'utiliser dans une application iOS, des problèmes d'architecture se posent.

ld: warning: ignoring file /Library/Frameworks/MyFramework.framework/MyFramework, file was built for x86_64 which is not the architecture being linked (arm64): /Library/Frameworks/MyFramework.framework/MyFramework
Undefined symbols for architecture arm64:
  "_OBJC_CLASS_$_MyFrameworkWebService", referenced from:
      objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

ld: warning: ignoring file /Library/Frameworks/MyFramework.framework/MyFramework, file was built for x86_64 which is not the architecture being linked (armv7): /Library/Frameworks/MyFramework.framework/MyFramework
Undefined symbols for architecture armv7:
  "_OBJC_CLASS_$_MyFrameworkWebService", referenced from:
      objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Eh bien, j'ai essayé de modifier les paramètres du projet de cadre et de la cible (Architectures et construire une architecture valide uniquement et des architectures valides). J'ai fait la même chose pour le projet d'application iOS mais rien ne fonctionne. Je pense qu'il y a quelque chose que je n'ai pas compris.

Par exemple, lorsque je construis un cadre uniquement pour la vérification i386 (simulateur iOS) à l'aide de la ligne de commande "xcrun lipo -info MyFramework", un problème survient.

ld: avertissement: en ignorant le fichier /Library/Frameworks/MyFramework.framework/MyFramework, le fichier a été construit pour x86_64, ce qui n’est pas l’architecture liée (i386) ...

Si quelqu'un peut m'aider à obtenir un framework qui fonctionne pour toutes les architectures iOS, y compris les simulateurs.

49

Désolé de laisser ce sujet ouvert depuis si longtemps ... Certains d'entre vous ont bien répondu à ma question. Lorsque j'ai écrit cette question, je n'ai pas compris qu'il y avait deux tranches dans un cadre complet (également appelé gras) (prêt à l'emploi). Un pour les appareils iOS et un pour le simulateur. Quand j'ai découvert que j'avais créé un script en utilisant la commande lipo pour fusionner les deux tranches, j'ai obtenu automatiquement une structure complète.

0

Sur la base de toutes les réponses, le post sur raywenderlich.com et le Gist créé par Chris Conway je suis venu avec cela .

En exécutant les étapes suivantes, j'ai pu construire un framework Cocoa Touch (y compris les Swift et Objective-C)) qui contient toutes les architectures pour le simulateur et le périphérique:

  1. Créez une nouvelle cible (globale) dans le projet de votre framework
  2. Sous "Build Phases", sélectionnez "Add Run Script" et copiez le contenu de ce fichier
  3. Sélectionnez la cible agrégée dans le champ . ) Schéma menu déroulant de sélection
  4. Construit la cible pour la schéma d'agrégat

J'espère que ça aide :)

UPDATE: Correction d'une erreur dans le Gist où les chemins d'accès à l'étape 3 étaient incorrects. Merci à Tokurik !!

[~ # ~] mettre à jour [~ # ~] Sous Xcode 7 et 8, cliquez sur Fichier> Nouveau> Cible ... et sélectionnez "Autre". groupe pour sélectionner cible globale

67
cromandini

Essayez les étapes suivantes pour créer un espace de travail contenant un projet-cadre et un projet d'application.

Espace de travail:

  • Créer un espace de travail.

Projet cadre:

  • Créez un projet iOS Cocoa touch Framework dans Workspace.
  • Ajoutez une simple classe Objective C MyClass (en-tête .h et fichier d'implémentation .m) et créez une méthode (void) greetings.
  • Lancer le projet Construire les phases > En-têtes > Déplacer MyClass. h de Projet à Public .
  • Changez de schéma en projet-cadre et choisissez simulateur iOS, puis générez. (Choisissez Périphérique iOS si l'application qui intègre ce cadre s'exécute sur un périphérique. Je continuerai à utiliser simulateur dans cet exemple)
  • Vous ne devriez pas avoir de problème de construction, la construction du framework se trouve dans votre répertoire Données dérivées que vous pouvez trouver dans Organiseur. .

Projet d'application:

  • Créez une application Swift Single View dans Workspace.
  • Faites glisser au-dessus de la structure du simulateur iOS (trouvé dans Debug-iphonesimulator ou Release-iphonesimulator) dans votre projet.
  • Créez un en-tête de pontage pour exposer les méthodes de classe Objective C à Swift.
  • Importez MyClass.h dans le fichier d’en-tête de pontage.
  • Notez que si la définition MyClass n’est pas trouvée, ajoutez ensuite le cadre En-têtes du chemin pour construire les paramètres Chemin de recherche des en-têtes .
  • Créez une instance de MyClass dans viewDidLoad of ViewController.Swift, puis appelez salutations .
  • Ajouter une structure à la cible> Binaires incorporés
  • Modifiez le schéma en projet d'application et choisissez le simulateur iOS , puis générez.
  • Vous devriez pouvoir voir les messages de bienvenue.

Notez que l'exemple ci-dessus montre comment créer une application qui s'exécute dans le simulateur . Si vous devez créer une bibliothèque statique universelle fonctionnant à la fois sur simulateur et devices, les étapes générales sont les suivantes:

  • Construire une bibliothèque pour simulator
  • Construire une bibliothèque pour device
  • Combinez-les en utilisant lipo

Il existe de bonnes références sur le Web à ce sujet, ici par exemple.

Créer un binaire universel pour le framework: accédez au répertoire de données dérivées du framework puis /Build/Products , la commande suivante devrait vous aider à créer un binaire universel dans Répertoire des produits :

lipo -create -output "framework-test-01-universal" "Debug-iphonesimulator/framework-test-01.framework/framework-test-01" "Debug-iphoneos/framework-test-01.framework/framework-test-01" 

Notez que framework-test-01 est le nom de mon projet-cadre.

47
vladof81

Voici les étapes à suivre pour créer un cadre gras (universel):

  1. Ensemble ONLY_ACTIVE_Arch=NO dans le Build Settings dans votre projet.

    • Construire une bibliothèque pour simulateur
    • Construire une bibliothèque pour le périphérique
  2. Ouvrir le dossier console Products de votre framework (vous pouvez l’ouvrir en ouvrant le dossier framework et cd .. à partir de là)

enter image description hereenter image description here

  1. Exécuter ce script à partir du dossier Products. Cela crée un gros framework dans ce dossier. (ou faites-le manuellement comme expliqué ci-dessous dans 4. 5. )

    framework_name="${$(basename $(find ./Debug-iphoneos -type d -name '*.framework' -maxdepth 1))%.*}" && \
    cp -R Debug-iphoneos/$framework_name.framework ./$framework_name.framework && \
    lipo -create -output "$framework_name.framework/$framework_name" "Debug-iphonesimulator/$framework_name.framework/$framework_name" "Debug-iphoneos/$framework_name.framework/$framework_name"
    

Ou:

  1. Combinez ces 2 cadres en utilisant lipo par ce script (remplacez YourFrameworkName par le nom de votre cadre)

    lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
    
  2. Remplacez par un nouveau binaire parmi les cadres existants:

    cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
    mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
    

  1. Bénéfice: ./YourFrameworkName.framework - est un gros binaire prêt à l'emploi! Vous pouvez l'importer dans votre projet!

Pour le projet, cela ne se trouve pas dans xcworkspace - es:

Vous pouvez également essayer d'utiliser ce Gist Mais il semble que cela ne fonctionne pas pour les projets dans les espaces de travail.

13
skywinder

La façon dont je l'ai fait est similaire à vladof, mais un peu plus simple, espérons-le. J'ai fait du framework un sous-projet du projet de l'application.

Projet cadre

  • Créez un nouveau framework iOS Cocoa Touch. Appelez ça MyLib. Cela créera un seul MyLib.h
  • Ajoutez une simple classe Cocoa Touch Obj-C, MyClass (.h & .m) et, dans l’implémentation du fichier .m, créez une méthode qui renvoie une chaîne de caractères - (NSString *) greetings;
  • Dans MyLib.h, ajoutez ceci près du bas, #import "MyClass.h"
  • Dans la section Build Phases/Headers de la cible, déplacez MyClass.h de la section Project vers la section Public.
  • Faire une construction (cmd-B)
  • Fermer le projet

Projet d'application

  • Créez un nouveau projet d’application Single View, Swift ou Obj-C. Appelez-le MyApp.
  • Depuis le Finder, faites glisser votre fichier de projet MyLib vers la section organisateur de gauche de votre fenêtre MyApp et assurez-vous que la ligne d'insertion est juste en dessous de MyApp. Cela fait de MyLib un sous-projet de MyApp. (Il peut toujours être utilisé de la même manière dans d'autres projets)
  • Cliquez sur MyApp dans l'organiseur, puis sélectionnez la cible MyApp et sélectionnez Build Phases.
  • Dans Dependances cibles, cliquez sur le signe + et ajoutez MyLib.
  • Dans Link with Libraries, cliquez sur le signe + et ajoutez MyLib.framework.

Pour une application Obj-C

  • Dans ViewController.m, ajoutez #import
  • Dans viewDidLoad, ajoutez les lignes suivantes:
  • MyLib * x = [[MyLib alloc] init];
  • NSLog (@ "% @", x.greetings);
  • Exécutez le projet et vous devriez voir le message dans la fenêtre de débogage. -

Pour un Swift app

  • Dans ViewController.Swift, ajoutez import MyLib
  • dans viewDidLoad, ajoutez les lignes suivantes:
  • var x: MyLib = MyLib ()
  • println ("(x.greetings ())") -

En procédant de cette façon, l'application dépend du cadre. Ainsi, si vous modifiez les classes du cadre, vous n'avez pas besoin de changer de cible pour construire le cadre séparément, il ne fera que compiler le cadre en premier, puis l'application.

10
pizzafilms

J'ai un peu modifié le script de quelqu'un pour prendre en charge toutes les architectures de Simulator:

UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_Arch=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_Arch=NO -configuration ${CONFIGURATION} -sdk iphonesimulator VALID_ARCHS="x86_64 i386" BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

# Step 2. Copy the framework structure to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"

# Step 3. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"

# Step 4. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"

# Step 5. Delete temporary build directory in the project's directory
rm -rf "${PROJECT_DIR}/build"

# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"
7
Nadzeya

Une ancienne vidéo que j'ai faite depuis le lien ci-dessus n'est plus disponible.

Comment créer un framework Cocoa Touch en utilisant Xcode 6

2
naz

Enfin, je l’ai fait fonctionner pour moi! Et désolé pour le grand cadre jaune, je ne sais pas comment le formater mieux.

La solution est venue de Claudio Romandi mais le script lié a un problème mineur. Je ne peux pas commenter son message car j'ai besoin de 50 points de réputation, je n'ai donc pas d'autre choix que de le copier pour obtenir une solution complète ..

  1. Créer une nouvelle cible (globale) dans votre projet de cadre
  2. Sélectionnez l'agrégat dans l'espace de travail et ajoutez une "nouvelle phase de script d'exécution".
  3. Mettez le code suivant dedans:

    #!/bin/sh
    
    UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
    
    # make sure the output directory exists mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
    
    # Step 1. Build Device and Simulator versions xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_Arch=NO -configuration ${CONFIGURATION}
    -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_Arch=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
    
    # Step 2. Copy the framework structure (from iphoneos build) to the universal folder cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
    
    # Step 3. Copy Swift modules (from iphonesimulator build) to the copied framework directory cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/." "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
    
    # Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory lipo -create
    -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
    
    # Step 5. Convenience step to copy the framework to the project's directory cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
    
    # Step 6. Convenience step to open the project's directory in Finder open "${PROJECT_DIR}"
    
  4. Sélectionnez le total dans la liste déroulante Scheme Selection

  5. Construisez, vous avez terminé!

Le problème était que le répertoire du simulateur pointait vers un répertoire non existant, changer "Framework" en "$ {PROJECT_NAME}" à 2 endroits a été une solution :)

1
Tokuriku

Je voulais ajouter quelque chose à la réponse du script lipo fournie par skywinder ici . J'ai suivi les étapes mais je n'ai toujours pas réussi à faire fonctionner mon framework sur simulateur, uniquement sur périphérique. Pour résoudre ce problème:

-Je suis allé dans la version debug-iphonesimulator du framework, et dans Modules/[FrameworkName] .swiftmodule, j'ai copié tous les fichiers i386 et X86.

-Je suis ensuite allé dans la version fat nouvellement créée du framework, et ai navigué dans ce même dossier. J'ai collé dans les fichiers i386 et X86 (pour aller avec les ARM déjà présents)), puis j'ai ajouté mon fat framework à mon projet.

Voilà, elle travaille sur simulateur et appareil réel! Ceci est Xcode 10/Swift 4.2 btw

1
yousefnjr

Quelques points supplémentaires autour de l’approche partagée par vladof qui s’appliquent davantage aux cadres basés sur Swift

  1. Le script dans le lien associé doit être modifié pour copier tous les fichiers d’iphonesimulator et iphoneos puisque Swift contient des fichiers compilés distincts pour arm et i386
  2. Assurez-vous que vous avez ARCHS = "x86_64" ONLY_ACTIVE_Arch = NO dans la construction pour iphonesimulator pour éviter les problèmes d’éditeur de liens pour le simulateur.
  3. Assurez-vous que votre interface/classe étend NSObject, sinon vous rencontrerez des problèmes lorsque vous essayez d'utiliser le code dans Swift (il se plaindra de ne pas pouvoir créer d'objet à l'aide de ()).
1
jhash

Cette question a été posée il y a quelque temps (Xcode 6) mais j'ai rencontré le même problème récemment avec Xcode 10.

Le problème est donc le suivant: le framework construit ne supporte pas suffisamment d'architectures.

Au cas où vous ne sauriez pas quelles architectures représentent, différents appareils iPhone ont des architectures différentes, voici une liste complète:

  • arm6: iPhone 1, 2, 3G
  • arm7: utilisé dans les appareils les plus anciens supportant iOS 7 [32 bits]. iPhone 4, 4s
  • arm7s: Tel qu'utilisé dans les iPhone 5 et 5C [32 bits]. iPhone 5, 5c
  • arm64: Pour le processeur 64 bits ARM de l’iPhone 5S [64 bits]. iPhone 5s et versions ultérieures.
  • arm64e: utilisé sur le chipset A12. iPhone XS/XS Max/XR
  • i386: pour le simulateur 32 bits
  • x86_64: utilisé dans un simulateur 64 bits

Ainsi, si vous utilisez le framework sur simulateur, celui-ci doit prendre en charge i386 ou x86_64. Si vous exécutez votre application sur iPhone 6, le framework doit prendre en charge l’architecture arm64.

Par conséquent, dans la plupart des cas, un cadre doit prendre en charge toutes les architectures susmentionnées.

Revenons maintenant à la façon de résoudre le problème. Nous devons construire le cadre pour les appareils et les simulateurs.

Comment construire pour les périphériques:

  1. Dans "Général", spécifiez "Cible de déploiement".
  2. Dans "Paramètres de construction", réglez "Construire les architectures actives uniquement" sur "Non"
  3. Dans "Paramètres de construction", assurez-vous que "Architectures valides" répertorie toutes les architectures dont vous avez besoin. Habituellement, nous utilisons simplement les options par défaut (arm64, arm64e, armv7, armv7s).
  4. Allez dans "Edit Scheme" -> "Run" -> "Build Configuration", changez "Build Configuration" en "Release"
  5. Sélectionnez le schéma actif "Périphérique iOS générique". Appuyez sur Cmd + B pour construire le projet.
  6. Cliquez avec le bouton droit de la souris sur [MyFrameworkProject] .framework dans le dossier "Produits" de votre projet Xcode, puis cliquez sur "Afficher dans le Finder". [MyFrameworkProject] .framework prend en charge toutes les architectures spécifiées à l'étape 2.
  7. Faites glisser [MyFrameworkProject] .framework vers le projet devant utiliser ce cadre. De plus, faites glisser [MyFrameworkProject] .framework vers "Binaires incorporés".

Comment construire pour les simulateurs:

  1. Étape 1 à 4 comme ci-dessus.
  2. Sélectionnez n'importe quel simulateur en tant que schéma actif. Appuyez sur Cmd + B pour construire le projet.
  3. Étape 6 à 7 comme ci-dessus.

Comment construire des appareils et des simulateurs:

Une fois que vous avez le cadre pour les périphériques, vous devez combiner le cadre pour les simulateurs et le cadre pour les périphériques avec la commande "lipo". Renommez le cadre pour les simulateurs en [MyFrameworkProject] _sim.framework et copiez les deux cadres dans le même dossier. Exécutez la commande ci-dessous dans Terminal (assurez-vous d'être dans le dossier):

lipo -create -output [MyFrameworkProject].framework/[MyFrameworkProject] [MyFrameworkProject].framework/MyFrameworkProject [MyFrameworkProject]_sim.framework/MyFrameworkProject

Maintenant, [MyFrameworkProject] .framework est le produit final qui prend en charge les simulateurs et les appareils.

1
nyus2006