web-dev-qa-db-fra.com

Comment exporter "fat" Cocoa Touch Framework (pour simulateur et périphérique)?

Avec Xcode 6 nous obtenons la possibilité de créer notre propre dynamique Cocoa Frameworks.

enter image description here

En raison de:

  • Le simulateur utilise toujours la bibliothèque 32-bit

  • à compter du 1er juin 2015, les mises à jour d'applications soumises sur l'App Store doivent inclure une prise en charge 64 bits et être générées avec le SDK iOS 8 ( developer.Apple.com )

Nous devons créer une grosse bibliothèque pour exécuter des projets sur des appareils et des simulateurs. c'est-à-dire prendre en charge les formats 32 et 64 bits dans les cadres.

Mais je n'ai trouvé aucun manuel, comment exporter le cadre universel pour une intégration future avec d'autres projets (et partager cette bibliothèque avec quelqu'un).

Voici mes étapes pour reproduire:

  1. Définissez ONLY_ACTIVE_Arch=NO Dans le Build Settings

    enter image description here

  2. Ajouter le support armv7 armv7s arm64 i386 x86_64 À Architectures (bien sûr)

enter image description here

  1. Construire un cadre et l'ouvrir dans le Finder:

enter image description hereenter image description here

  1. Ajouter ce cadre à un autre projet

Résultat actuel:

Mais au final, je rencontre toujours un problème pour exécuter un projet avec ce framework sur les appareils et le simulateur à la fois.

  • si je prends le cadre du dossier Debug-iphoneos - il fonctionne sur les appareils et génère une erreur sur les simulateurs: ld: symbol(s) not found for architecture i386

    xcrun lipo -info CoreActionSheetPicker
    

    Les architectures dans le fichier fat: CoreActionSheetPicker sont: armv7 armv7s arm64

  • si je prends le cadre du dossier Debug-iphonesimulator - cela fonctionne sur les simulateurs. et j'ai une erreur sur le périphérique: ld: symbol(s) not found for architecture arm64

    xcrun lipo -info CoreActionSheetPicker
    

    Les architectures dans le fichier fat: CoreActionSheetPicker sont: i386 x86_64

Alors, comment créer un cadre dynamique qui fonctionne sur les appareils et les simulateurs?

Cette réponse concernait Xcode 6 iOS Création d'un framework Cocoa Touch Framework - Problèmes d'architecture mais ce n'est pas un doublon.


Mise à jour:

J'ai trouvé un "hack sale" pour cette affaire. Voir mon réponse ci-dessous . Si quelqu'un sait plus pratique - s'il vous plaît, faites le moi savoir!

106
skywinder

L’actualité de cette réponse est: juillet 2015. Il est fort probable que les choses vont changer.

TLDR;

Actuellement, Xcode ne dispose pas d'outils pour l'exportation automatique du framework fat universel. Le développeur doit donc recourir à l'utilisation manuelle de l'outil lipo. Toujours selon ce radar avant d'être soumis au développeur de l'AppStore qui est le consommateur du framework, il doit également utiliser lipo pour supprimer les tranches du simulateur d'un framework.

Réponse plus longue suit


J'ai fait des recherches similaires sur le sujet (le lien au bas de la réponse).

Je n'avais trouvé aucune documentation officielle sur la distribution de mes produits, mes recherches étaient donc basées sur l'exploration de Apple Forums de développeurs, projets Carthage et Realm et mes propres expériences avec xcodebuild, lipo, codesign outils.

Voici une longue citation (avec un peu de balisage de ma part) de Apple Exportation de l'application avec un cadre intégré :

Quelle est la bonne manière d'exporter un framework depuis un projet framework?

Actuellement, le seul moyen est exactement ce que vous avez fait:

  • Construisez la cible pour le simulateur et le périphérique iOS.
  • Naviguez jusqu'au dossier DerivedData de Xcode pour ce projet et lissez les deux fichiers binaires dans un seul et même framework. Toutefois, lorsque vous créez la cible de structure dans Xcode, veillez à ajuster le paramètre de cible "Construire une architecture active uniquement" sur "NON". Cela permettra à Xcode de construire la cible pour plusieurs types de binarty (arm64, armv7, etc.). Ce serait pourquoi cela fonctionne à partir de Xcode mais pas comme un binaire autonome.

  • Vous devrez également vous assurer que le schéma est défini sur une version Release et construire la cible de l'infrastructure par rapport à Release. Si vous obtenez toujours une erreur de bibliothèque non chargée, vérifiez les tranches de code dans la structure.

  • Utilisez lipo -info MyFramworkBinary Et examinez le résultat.

lipo -info MyFrameworkBinary

Le résultat est i386 x86_64 armv7 arm64

  • Les frameworks universels modernes comprendront 4 tranches, mais pourraient en inclure davantage: i386 x86_64 armv7 arm64 Si vous ne voyez pas au moins ce 4, cela pourrait être dû au paramètre Build Active Architecture.

Ceci décrit un processus similaire à celui que @skywinder a fait dans sa réponse.

C’est ainsi que Carthage utilise lipo et Le royaume utilise lipo .


DÉTAIL IMPORTANT

Il y a un radar: Xcode 6.1.1 et 6.2: les frameworks iOS contenant des tranches de simulateur ne peuvent pas être soumis à l'App Store et une longue discussion à ce sujet sur Domaine # 116 et Carthage # 188 qui s'est terminé par une solution spéciale:

avant la soumission à l'AppStore iOS, les fichiers binaires du framework doivent être supprimés des tranches du simulateur

Carthage a un code spécial: CopyFrameworks et la documentation correspondante:

Ce script fonctionne autour d'un bug de soumission dans l'App Store déclenché par des fichiers binaires universels.

Le domaine a un script spécial: strip-frameworks.sh et la documentation correspondante:

Cette étape est nécessaire pour contourner un bogue de soumission d'App Store lors de l'archivage de fichiers binaires universels.

Il existe également un bon article: Suppression des architectures indésirables des bibliothèques dynamiques dans Xcode .

J'ai moi-même utilisé le logiciel strip-frameworks.sh De Realm, qui a parfaitement fonctionné sans aucune modification, même si, bien entendu, tout le monde est libre d'en écrire un de toutes pièces.


Le lien vers mon sujet que je recommande de lire car il contient un autre aspect de cette question: signature de code - Création de cadres iOS/OSX: est-il nécessaire de les coder avant de les distribuer à d'autres développeurs?

78

Ce n'est pas une solution si claire, mais il y a un moyen, que je trouve:

  1. Ensemble ONLY_ACTIVE_Arch=NO dans le Build Settings

    • 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 this script du dossier Products. Cela crée un gros framework dans ce dossier. (ou faites-le manuellement comme expliqué ci-dessous dans 3. 4. )

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 les projets, cela ne se fait pas dans les espaces de travail:

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

56
skywinder

La réponse de @Stainlav a été très utile, mais j’ai plutôt choisi de compiler deux versions du cadre (une pour l’appareil et une pour le simulateur), puis d’ajouter le Run Script Phase pour copier automatiquement le framework précompilé requis par l’architecture en cours

echo "Copying frameworks for architecture: $CURRENT_Arch"
if [ "${CURRENT_Arch}" = "x86_64" ] || [ "${CURRENT_Arch}" = "i386" ]; then
  cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
  cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi

De cette façon, je n'ai pas utilisé lipo pour créer un framework fat ni le domaine du royaume strip-frameworks.sh pour supprimer les tranches inutiles lors de la soumission à l'App Store.

10
odm

fondamentalement, j'ai trouvé une très bonne solution. il vous suffit de suivre ces étapes simples.

  1. Créez un cadre tactile cacao.
  2. Définissez le code binaire activé sur Non.
  3. Sélectionnez votre cible et choisissez modifier les schémas. Sélectionnez Exécuter et choisissez Libérer de l'onglet Info.
  4. Aucun autre paramètre requis.
  5. Construisez maintenant la structure de tout simulateur, car celui-ci fonctionne sur une architecture x86.
  6. Cliquez sur le groupe Produits dans Project Navigator et recherchez le fichier .framework.
  7. Faites un clic droit dessus et cliquez sur Afficher dans le Finder. Copiez-le et collez-le dans n'importe quel dossier. Personnellement, je préfère le nom de "simulateur".
  8. Maintenant, construisez le framework pour Generic iOS Device et suivez les étapes 6 à 9. Renommez simplement le dossier 'device' au lieu de 'simulator'.
  9. Copiez le fichier .framework du périphérique et collez-le dans un autre répertoire. Je préfère le super annuaire immédiat des deux. La structure du répertoire devient alors:
    • Bureau
    • dispositif
      • MyFramework.framework
    • simulateur
      • MyFramework.framework
    • MyFramework.framework Maintenant, ouvrez terminal et cd sur le bureau. Maintenant, commencez à taper la commande suivante:

lipo-create 'device/MonFramework.framework/MonFramework' 'simulateur/MyFramework.framework/MyFramework' -output 'MyFramework.framework/MyFramework'

et c'est tout. Ici, nous fusionnons le simulateur et la version de périphérique du fichier binaire MyFramework dans MyFramework.framework. Nous obtenons un cadre universel qui construit pour toutes les architectures, y compris les simulateurs et les appareils.

2
Nrip

Je veux juste mettre à jour cette excellente réponse par @odm. Depuis Xcode 10, la variable CURRENT_Arch Ne reflète plus l'architecture de construction. J'ai donc changé le script pour vérifier la plateforme à la place:

echo "Copying frameworks for platform: $PLATFORM_NAME"
rm -R "${SRCROOT}/Frameworks/Active"
if [ "${PLATFORM_NAME}" = "iphonesimulator" ]; then
    cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
    cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi

J'ai également ajouté une ligne pour effacer le répertoire cible avant la copie, car j'avais remarqué que les fichiers supplémentaires des sous-répertoires ne seraient pas écrasés autrement.

2
Dorian Roy

Ma réponse couvre les points ci-dessous:

  • Créer un framework qui fonctionne à la fois pour le simulateur et pour le périphérique

  • Comment exporter "gras" Cocoa Touch Framework (à la fois pour Simulator et Device)?

  • Symboles non définis pour l'architecture x86_64

  • ld: symbole (s) non trouvé (s) pour l'architecture x86_64

Etapes 1: commencez par construire vos frameworks avec la cible Simulator

Etape 2: Une fois le processus de construction du simulateur réussi, créez maintenant votre infrastructure avec la sélection de cible de périphérique ou la sélection de périphérique iOS générique.

Étape 3: Maintenant, sélectionnez votre cible de structure et pour cela Sous "phases de construction", sélectionnez "Ajouter un script d'exécution" et copiez le code de script ci-dessous)

Étape 4: Enfin, construisez à nouveau et votre infrastructure est prête pour la compatibilité du simulateur et du périphérique. Hourra!!!!

[Remarque: nous devons avoir les deux cadres compatibles prêts avant l'étape finale 4 (simulateur et architecture de périphérique compatibles, sinon, veuillez suivre les étapes 1 et 2 ci-dessus correctement)

Voir l'image de référence:

enter image description here

enter image description here

Mettez le code ci-dessous dans la zone de shell:

#!/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 (if it exists) to the copied framework directory

SIMULATOR_Swift_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."

if [ -d "${SIMULATOR_Swift_MODULES_DIR}" ]; then

cp -R "${SIMULATOR_Swift_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"

fi


# 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 "${BUILD_DIR}/${CONFIGURATION}-universal"
1