Comment exporter le «gros» Cocoa Touch Framework (pour simulateur et appareil)?

107

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

entrez la description de l'image ici

En raison de:

  • Le simulateur utilise toujours la 32-bitbibliothèque

  • à compter du 1er juin 2015, les mises à jour des applications soumises à l'App Store doivent inclure la prise en charge 64 bits et être créé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 32 et 64 bits dans les cadres.

Mais je n'ai trouvé aucun manuel sur la façon d' exporter Universal Fat Framework pour une intégration future avec d'autres projets (et de partager cette bibliothèque avec quelqu'un).

Voici mes étapes pour reproduire:

  1. Situé ONLY_ACTIVE_ARCH=NOdans leBuild Settings

    entrez la description de l'image ici

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

entrez la description de l'image ici

  1. Construisez Framework et ouvrez-le dans Finder:

entrez la description de l'image ici entrez la description de l'image ici

  1. Ajouter ce cadre à un autre projet

Résultat actuel:

Mais à la fin, j'ai toujours un problème avec l'exécution du projet avec ce cadre sur les appareils et le simulateur à la fois.

  • si je prends le cadre du Debug-iphoneosdossier - cela fonctionne sur les appareils et obtient 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 Debug-iphonesimulatordossier - cela fonctionne sur des simulateurs. et j'ai une erreur sur l'appareil: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 framework dynamique qui fonctionne sur les appareils et les simulateurs?

Cette réponse concernait Xcode 6 iOS Création d'un cadre Cocoa Touch - Problèmes d'architecture, mais ce n'est pas en double.


Mettre à jour:

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

Skywinder
la source
problème en double stackoverflow.com/questions/24039470/…
Andrius Steponavičius
@ AndriusSteponavičius cette question a été posée 2 mois plus tôt.
skywinder
Oui, mais il y a des réponses beaucoup plus détaillées, que je pense que les utilisateurs devraient connaître
Andrius Steponavičius
Définir ONLY_ACTIVE_ARCH = NO dans les paramètres de construction est une étape importante.
Jedidja
votre framework a besoin des deux tranches i386 x86_64 dans le gros binaire si vous voulez l'exécuter sur le simulateur MÊME SI VOTRE ORDINATEUR A UNE ARCHITECTURE 64 BITS !!! J'ai appris cela à la dure.
J.beenie

Réponses:

82

La réalité de cette réponse est: juillet 2015. Il est fort probable que les choses changeront.

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' lipooutil. Toujours selon ce radar, avant la soumission au développeur AppStore, le consommateur du framework doit également utiliser lipopour supprimer les tranches de simulateur d'un framework.

Une réponse plus longue suit


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

Je ne l' avais pas trouvé aucune documentation officielle sur la distribution de ce que ma recherche était basée sur l' exploration d'Apple Forums de développement, les projets de Carthage et Realm et mes propres expériences avec xcodebuild, lipo, des codesignoutils.

Voici une longue citation (avec un peu de balisage de ma part) du fil de discussion Apple Developer Forums Exporting app avec framework intégré :

Quelle est la bonne façon d'exporter un cadre à partir d'un projet de cadre?

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

  • Créez la cible pour le simulateur et l'appareil iOS.
  • Accédez au dossier DerivedData de Xcode pour ce projet et lipo les deux binaires ensemble dans un seul cadre. Cependant, lorsque vous créez la cible du framework dans Xcode, assurez-vous d'ajuster le paramètre cible «Construire l'architecture active uniquement» sur «NON». Cela permettra à Xcode de construire la cible pour plusieurs types de binarty (arm64, armv7, etc.). Ce serait pourquoi il fonctionne à partir de Xcode mais pas en tant que binaire autonome.

  • Vous voudrez également vous assurer que le schéma est défini sur une version Release et créer la cible du framework par rapport à la version. Si vous obtenez toujours une erreur de bibliothèque non chargée, vérifiez les tranches de code dans le framework.

  • Utilisez lipo -info MyFramworkBinaryet examinez le résultat.

lipo -info MyFrameworkBinary

Le résultat est i386 x86_64 armv7 arm64

  • Les frameworks universels modernes incluront 4 tranches, mais pourraient en inclure plus: i386 x86_64 armv7 arm64 si vous ne voyez pas au moins ce 4, c'est peut-être à cause du paramètre Build Active Architecture.

Cela décrit le processus à peu près de la même manière que @skywinder l'a fait dans sa réponse.

C'est ainsi que Carthage utilise le lipo et Realm utilise le lipo .


DÉTAIL IMPORTANT

Il y a un radar: Xcode 6.1.1 & 6.2: les frameworks iOS contenant des tranches de simulateur ne peuvent pas être soumis à l'App Store et une longue discussion autour de celui-ci sur Realm # 1163 et Carthage # 188 qui s'est terminée par une solution de contournement spéciale:

avant d'être soumis à AppStore, les binaires du framework iOS doivent être supprimés des tranches de simulateur

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

Ce script fonctionne autour d'un bogue de soumission App Store déclenché par des binaires universels.

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

Cette étape est nécessaire pour contourner un bogue de soumission de l'App Store lors de l'archivage des 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é Realm strip-frameworks.shqui a parfaitement fonctionné pour moi sans aucune modification, bien que tout le monde soit libre d'en écrire un à partir de zéro.


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

Stanislav Pankevich
la source
1
J'ai utilisé lipo mais lorsque le framework est construit dans le simulateur, il affiche un identifiant non résolu avec le nom de la classe, mais dans l'appareil, cela fonctionne. Si vous utilisez la version simulateur de binaire, cela fonctionne ... une idée?
Susim Samanta
2
Je n'ai trouvé aucune preuve que cela a changé par Xcode 8.2 en décembre 2016.: /
Geoffrey Wiseman
1
@ Geoffrey, cela a-t-il changé dans Xcode 9.2 ou y a-t-il quelque chose de différent? C'est la première fois que je crée un cadre binaire pour la distribution, et j'ai déjà peur ...
ScottyB
Je n'ai pas fait cela depuis un moment, malheureusement - je ne peux pas le dire. Bonne chance.
Geoffrey Wiseman
57

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

  1. Situé ONLY_ACTIVE_ARCH=NOdans leBuild Settings

    • Créer une bibliothèque pour le simulateur
    • Créer une bibliothèque pour l'appareil
  2. Ouvrir dans le Productsdossier de la console pour votre framework (vous pouvez l'ouvrir en ouvrant le dossier du framework et à cd ..partir de là)

entrez la description de l'image ici entrez la description de l'image ici

  1. Exécutez ce script à partir du Productsdossier. Il crée de gros Framework dans ce dossier. (ou faites-le manuellement comme expliqué ci-dessous en 3. 4. )

Ou:

  1. Combinez ces 2 Frameworks en utilisant lipo par ce script (remplacez-le YourFrameworkNamepar le nom de votre Framework)

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

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

  1. Profit: ./YourFrameworkName.framework- est un binaire gras prêt à l'emploi! Vous pouvez l'importer dans votre projet!

Pour le projet, que pas dans les espaces de travail:

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

Skywinder
la source
Je pensais qu'Apple n'acceptait plus les gros binaires. kodmunki.wordpress.com/2015/03/04/…
Monstieur
1
@skywinder Avez-vous trouvé un autre moyen simple d'exporter Cocoa Touch Framework vers un binaire prêt à l'emploi? J'utilise la même approche que ci-dessus mais je ne l'aime pas. Xcode devrait en avoir qui automatise le processus.
dev gr
1
@devgr pas encore .. c'est pourquoi je n'ai pas accepté ma propre réponse. Toujours à la recherche d'une meilleure solution.
skywinder
1
Impossible d'exécuter dans le simulateur mais fonctionne dans l'appareil avec les étapes 3 et 4
jose920405
1
@ Quelqu'un pourrait-il expliquer pourquoi seul le Debug-dossier est utilisé avec lipo -create? Ce framework pourrait-il être utilisé pour la Releaseconfiguration et pourquoi? Merci.
Yevhen Dubinin le
10

La réponse @Stainlav a été très utile, mais ce que j'ai fait à la place était de compiler deux versions du framework (une pour l'appareil et une pour le simulateur), puis j'ai ajouté ce qui suit Run Script Phasepour copier automatiquement le framework précompilé requis pour l'architecture en cours d'exécution

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 besoin lipode créer un gros framework ni du Royaume strip-frameworks.shpour supprimer les tranches inutiles lors de la soumission à l'App Store.

odm
la source
Lequel établissez-vous un lien?
Jaka Jančar
@ JakaJančar Je crée un lien avec ceux du ${SRCROOT}/Frameworks/Activedossier. Ils sont remplacés par les bons frameworks précompilés pour l'architecture active au moment de la compilation.
au
2
Aimer! C'est tellement plus simple que l' lipoapproche combiner puis déchirer .
clozach
2

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

  1. Créez un cadre tactile cacao.
  2. Réglez le bitcode activé sur Non.
  3. Sélectionnez votre cible et choisissez les schémas d'édition. Sélectionnez Exécuter et choisissez Libérer dans l'onglet Informations.
  4. Aucun autre paramètre requis.
  5. Construisez maintenant le framework pour n'importe quel simulateur car le simulateur 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, je préfère personnellement le nom de «simulateur».
  8. Maintenant, construisez le framework pour Generic iOS Device et suivez les étapes 6 à 9. Renommez simplement le dossier en «appareil» au lieu de «simulateur».
  9. Copiez le fichier .framework du périphérique et collez-le dans n'importe quel autre répertoire. Je préfère le super répertoire immédiat des deux. La structure des répertoires devient donc:
    • Bureau
    • dispositif
      • MyFramework.framework
    • simulateur
      • MyFramework.framework
    • MyFramework.framework Ouvrez maintenant le terminal et le cd sur le bureau. Maintenant, commencez à taper la commande suivante:

lipo -create 'appareil / MyFramework.framework / MyFramework' 'simulateur / MyFramework.framework / MyFramework' -output 'MyFramework.framework / MyFramework'

et c'est tout. Ici, nous fusionnons le simulateur et la version de l'appareil du binaire MyFramework présent dans MyFramework.framework. Nous obtenons un cadre universel qui se construit pour toutes les architectures, y compris le simulateur et l'appareil.

Nrip
la source
Je veux créer un fichier FAT avec le bitcode activé. Guidez-moi s'il-vous-plaît.
user3898700
2

Je veux juste mettre à jour cette excellente réponse par @odm. Depuis Xcode 10, la CURRENT_ARCHvariable ne reflète plus l'architecture de construction. J'ai donc changé le script pour vérifier la plate-forme à 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 de copier, car j'ai remarqué que les fichiers supplémentaires dans les sous-répertoires ne seraient pas écrasés autrement.

Dorian Roy
la source
1

Ma réponse couvre les points ci-dessous:

  • Créer un cadre qui fonctionne à la fois pour le simulateur et l'appareil

  • Comment exporter le «gros» Cocoa Touch Framework (pour Simulator et Device à la fois)?

  • Symboles non définis pour l'architecture x86_64

  • ld: symbole (s) introuvable (s) pour l'architecture x86_64

Étapes 1: commencez par créer vos frameworks avec la cible Simulator

Étapes 2: Une fois le processus de création du simulateur réussi, créez maintenant pour votre infrastructure avec la sélection de la cible de l'appareil ou la sélection de l'appareil iOS générique

Étape 3: Sélectionnez maintenant votre cadre cible et pour cela, sous "Build Phases", sélectionnez "Add Run Script" et copiez le code de script ci-dessous)

Étape 4: Enfin, reconstruisez à nouveau et votre framework est prêt pour la compatibilité du simulateur et des appareils. Hourra!!!!

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

Voir l'image de référence:

entrez la description de l'image ici

entrez la description de l'image ici

Mettez le code ci-dessous dans la zone 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"

Sandip Patel - SM
la source
Ce script semble s'appeler et provoquer ainsi une boucle infinie !! J'ai dû redémarrer mon ordinateur après l'avoir exécuté! Créait continuellement de nouveaux processus xcodebuild ... et ouvrait de nouvelles fenêtres de recherche
Voterait
Consultez la réponse de @ l0gg3r dans ce SO Q / A pour un script similaire sans le problème de récursivité.
J.beenie