Come esportare Cocoa Touch Framework "grasso" (per simulatore e dispositivo)?


107

Con Xcode 6 abbiamo la possibilità di creare la propria dinamica Cocoa Frameworks.

inserisci qui la descrizione dell'immagine

Per colpa di:

  • Il simulatore usa ancora la 32-bitlibreria

  • a partire dal 1 ° giugno 2015 gli aggiornamenti delle app inviati all'App Store devono includere il supporto a 64 bit ed essere realizzati con iOS 8 SDK ( developer.apple.com )

Dobbiamo creare una libreria fat per eseguire il progetto su dispositivi e simulatori. cioè supporta sia 32 che 64 bit in Frameworks.

Ma non ho trovato alcun manuale su come esportare Universal Fat Framework per una futura integrazione con altri progetti (e condividere questa libreria con qualcuno).

Ecco i miei passaggi per riprodurre:

  1. Situato ONLY_ACTIVE_ARCH=NOnelBuild Settings

    inserisci qui la descrizione dell'immagine

  2. Aggiungi supporto armv7 armv7s arm64 i386 x86_64a Architectures(di sicuro)

inserisci qui la descrizione dell'immagine

  1. Crea Framework e aprilo nel Finder:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

  1. Aggiungi questo framework a un altro progetto

Risultato attuale:

Ma alla fine ho ancora problemi con l'esecuzione del progetto con questo framework su dispositivi e simulatore contemporaneamente.

  • se prendo il framework dalla Debug-iphoneoscartella, funziona sui dispositivi e ottiene errori sui simulatori:ld: symbol(s) not found for architecture i386

      xcrun lipo -info CoreActionSheetPicker

    Le architetture nel file fat: CoreActionSheetPicker sono: armv7 armv7s arm64

  • se prendo il framework dalla Debug-iphonesimulatorcartella, funziona sui simulatori. e ho un errore sul dispositivo:ld: symbol(s) not found for architecture arm64

      xcrun lipo -info CoreActionSheetPicker

    Le architetture nel file fat: CoreActionSheetPicker sono: i386 x86_64

Quindi, come creare un framework dinamico che funzioni su dispositivi e simulatori?

Questa risposta riguardava Xcode 6 iOS Creazione di un framework Cocoa Touch - Problemi di architettura ma non è duplicato.


Aggiornare:

Ho trovato un "trucco sporco" per questo caso. Vedi la mia risposta di seguito . Se qualcuno conosce un modo più conveniente, per favore, fammelo sapere!



@ AndriusSteponavičius questa domanda è stata posta 2 mesi prima.
skywinder

Sì, ma ci sono risposte molto più dettagliate che penso che gli utenti dovrebbero conoscere
Andrius Steponavičius

Impostare ONLY_ACTIVE_ARCH = NO nelle impostazioni di build è un passaggio importante.
Jedidja

il tuo framework ha bisogno di entrambe le slice i386 x86_64 nel fat binary se vuoi eseguirlo sul simulatore ANCHE SE IL TUO COMPUTER HA UN'ARCHITETTURA A 64 BIT !!! L'ho imparato a mie spese.
J.beenie

Risposte:


82

L'attualità di questa risposta è: luglio 2015. È molto probabile che le cose cambieranno.

TLDR;

Attualmente Xcode non dispone di strumenti per l'esportazione automatica del framework fat universale, quindi lo sviluppatore deve ricorrere all'uso manuale dello lipostrumento. Anche secondo questo radar prima della presentazione allo sviluppatore dell'AppStore che è il consumatore del framework, deve anche utilizzare lipoper rimuovere le sezioni del simulatore da un framework.

Segue una risposta più lunga


Ho fatto ricerche simili sull'argomento (il link in fondo alla risposta).

Non avevo trovato alcuna documentazione ufficiale circa la distribuzione di così la mia ricerca si è basata sull'esplorazione di di Apple Developer Forum, progetti Cartagine e Realm e miei esperimenti con xcodebuild, lipo, codesignattrezzi.

Ecco una lunga citazione (con un po 'di markup da parte mia) dal thread dei forum degli sviluppatori Apple App di esportazione con framework incorporato :

Qual è il modo corretto per esportare un framework da un progetto framework?

Attualmente l'unico modo è esattamente quello che hai fatto:

  • Crea il target sia per il simulatore che per il dispositivo iOS.
  • Passa alla cartella DerivedData di Xcode per quel progetto e lipo i due binari insieme in un unico framework. Tuttavia, quando si crea la destinazione del framework in Xcode, assicurarsi di regolare l'impostazione di destinazione "Crea solo architettura attiva" su "NO". Ciò consentirà a Xcode di creare il target per più tipi di binarty (arm64, armv7, ecc.). Questo sarebbe il motivo per cui funziona da Xcode ma non come binario autonomo.

  • Inoltre, ti consigliamo di assicurarti che lo schema sia impostato su una build di rilascio e di creare l'obiettivo del framework rispetto al rilascio. Se ricevi ancora un errore di libreria non caricata, controlla le sezioni di codice nel framework.

  • Usa lipo -info MyFramworkBinaryed esamina il risultato.

lipo -info MyFrameworkBinary

Il risultato è i386 x86_64 armv7 arm64

  • I framework universali moderni includeranno 4 sezioni, ma potrebbero includerne di più: i386 x86_64 armv7 arm64 se non vedi almeno questo 4, potrebbe essere a causa dell'impostazione Build Active Architecture.

Questo descrive il processo più o meno come @skywinder ha fatto nella sua risposta.

Ecco come Carthage usa lipo e Realm usa lipo .


DETTAGLIO IMPORTANTE

C'è un radar: Xcode 6.1.1 e 6.2: i framework iOS contenenti sezioni del simulatore non possono essere inviati all'App Store e una lunga discussione su di esso su Realm # 1163 e Carthage # 188 che si è conclusa con una soluzione speciale:

prima dell'invio ad AppStore, i file binari del framework iOS devono essere rimossi dalle sezioni del simulatore

Cartagine ha un codice speciale: CopyFrameworks e la documentazione corrispondente:

Questo script aggira un bug di invio di App Store attivato da binari universali.

Realm ha uno script speciale: strip-frameworks.sh e la parte di documentazione corrispondente:

Questo passaggio è necessario per aggirare un bug di invio di App Store durante l'archiviazione dei binari universali.

Inoltre c'è un buon articolo: Rimozione di architetture indesiderate da librerie dinamiche in Xcode .

Io stesso ho usato Realm's strip-frameworks.shche ha funzionato perfettamente senza alcuna modifica, anche se ovviamente chiunque è libero di scriverne uno da zero.


Il link al mio topic che consiglio di leggere perché contiene un altro aspetto di questa domanda: firma del codice - Creazione di framework iOS / OSX: è necessario codificarli prima di distribuirli ad altri sviluppatori?


1
Ho usato lipo ma quando il framework viene compilato nel simulatore mostra un identificatore non risolto con il nome della classe ma nel dispositivo funziona. Se usi la versione del simulatore di binario, allora funziona .. qualche idea?
Susim Samanta,

2
Non ho trovato alcuna prova che questo sia cambiato da Xcode 8.2 nel dicembre 2016.: /
Geoffrey Wiseman

1
@ Geoffrey, questo è cambiato in Xcode 9.2 o è diverso? Questa è la prima volta che
creo

Non l'ho fatto per un po ', purtroppo, non posso dirlo. In bocca al lupo.
Geoffrey Wiseman

57

Questa non è una soluzione così chiara, ma c'è solo un modo, che trovo:

  1. Situato ONLY_ACTIVE_ARCH=NOnelBuild Settings

    • Crea una libreria per il simulatore
    • Crea libreria per dispositivo
  2. Apri nella Productscartella della console per il tuo framework (puoi aprirlo dalla cartella del framework aperto e cd ..da lì)

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

  1. Esegui questo script dalla Productscartella. Crea Fat Framework in questa cartella. (o farlo manualmente come spiegato di seguito in 3. 4. )

O:

  1. Combina questi 2 Framework usando lipo con questo script (sostituisci YourFrameworkNamecon il tuo nome Framework)

    lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
  2. Sostituisci con il nuovo binario uno dei framework esistenti:

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

  1. Utile: ./YourFrameworkName.framework- è un file binario grasso pronto per l'uso! Puoi importarlo nel tuo progetto!

Per il progetto, quello non negli spazi di lavoro:

Puoi anche provare a usare questa sintesi come descritto qui . Ma sembra che non funzioni per i progetti negli spazi di lavoro.


Pensavo che Apple non accettasse più i file binari grassi. kodmunki.wordpress.com/2015/03/04/…
Monstieur

1
@skywinder Hai trovato un altro modo semplice per esportare Cocoa Touch Framework in un file binario grasso pronto per l'uso? Sto usando lo stesso approccio di sopra ma non mi piace. Xcode dovrebbe averne alcuni che automatizzano il processo.
dev gr

1
@devgr non ancora .. ecco perché non ho accettato la mia risposta. Ancora alla ricerca di una soluzione migliore.
skywinder

1
Non può essere eseguito nel simulatore ma funziona nel dispositivo con i passaggi 3 e 4
jose920405

1
@ qualcuno potrebbe spiegare perché Debug-viene utilizzata solo la cartella con lipo -create? Questo framework potrebbe essere utilizzato per la Releaseconfigurazione e perché? Grazie.
Yevhen Dubinin

10

La risposta di @Stainlav è stata molto utile, ma quello che ho fatto invece è stato compilare due versioni del framework (una per il dispositivo e una per il simulatore) e poi ho aggiunto quanto segue Run Script Phaseper copiare automaticamente il framework precompilato richiesto per l'architettura in esecuzione

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

In questo modo non devo lipocreare un framework grasso né il Realm's strip-frameworks.shper rimuovere le sezioni non necessarie durante l'invio all'App Store.


Contro quale ti colleghi?
Jaka Jančar

@ JakaJančar mi collego a quelli nella ${SRCROOT}/Frameworks/Activecartella. Vengono sostituiti per i framework precompilati giusti per l'architettura attiva in fase di compilazione.
odm

2
Lo adoro! Questo è molto più semplice dell'approccio combina poi strappa lipo.
clozach

2

fondamentalmente per questo ho trovato un'ottima soluzione. devi solo seguire questi semplici passaggi.

  1. Crea una cornice al tocco di cacao.
  2. Imposta il codice bit abilitato su No.
  3. Seleziona il tuo obiettivo e scegli Modifica schemi. Seleziona Esegui e scegli Versione dalla scheda Informazioni.
  4. Nessun'altra impostazione richiesta.
  5. Ora crea il framework per qualsiasi simulatore poiché il simulatore funziona su architettura x86.
  6. Fare clic sul gruppo Prodotti nel Navigatore progetto e trovare il file .framework.
  7. Fai clic destro su di esso e fai clic su Mostra nel Finder. Copialo e incollalo in una cartella qualsiasi, personalmente preferisco il nome "simulatore".
  8. Ora crea il framework per il dispositivo iOS generico e segui i passaggi da 6 a 9. Rinomina la cartella in "dispositivo" invece che in "simulatore".
  9. Copia il file .framework del dispositivo e incollalo in qualsiasi altra directory. Preferisco la super directory immediata di entrambi. Quindi la struttura della directory ora diventa:
    • Desktop
    • dispositivo
      • MyFramework.framework
    • simulatore
      • MyFramework.framework
    • MyFramework.framework Ora apri il terminale e cd sul desktop. Ora inizia a digitare il seguente comando:

lipo -create 'device / MyFramework.framework / MyFramework' 'simulator / MyFramework.framework / MyFramework' -output 'MyFramework.framework / MyFramework'

e questo è tutto. Qui uniamo la versione del simulatore e del dispositivo del binario MyFramework presente all'interno di MyFramework.framework. Otteniamo un framework universale che si basa su tutte le architetture, inclusi simulatore e dispositivo.


voglio creare file FAT con bitcode abilitato. Per favore guidami.
user3898700

2

Voglio solo aggiornare questa ottima risposta di @odm. Da Xcode 10, la CURRENT_ARCHvariabile non riflette più l'architettura di compilazione. Quindi ho cambiato lo script per controllare invece la piattaforma:

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

Ho anche aggiunto una riga per cancellare la directory di destinazione prima di copiare, perché ho notato che i file aggiuntivi nelle sottodirectory non sarebbero stati sovrascritti altrimenti.


1

La mia risposta copre i seguenti punti:

  • Crea un framework che funzioni sia per il simulatore che per il dispositivo

  • Come esportare Cocoa Touch Framework "grasso" (sia per simulatore che per dispositivo)?

  • Simboli non definiti per l'architettura x86_64

  • ld: simboli non trovati per l'architettura x86_64

Passaggi 1: per prima cosa crea i tuoi framework con il target del simulatore

Passaggi 2: dopo il successo del processo di creazione del simulatore, ora crea per il tuo framework con la selezione della destinazione del dispositivo o la selezione del dispositivo iOS generico

Passaggio 3: ora seleziona il tuo framework di destinazione e per questo in "Build Phases" seleziona "Add Run Script" e copia il codice dello script sottostante)

Step4: Ora finalmente, costruisci di nuovo e il tuo framework è pronto sia per il simulatore che per la compatibilità del dispositivo. Evviva !!!!

[Nota: dobbiamo avere entrambi i framework compatibili pronti prima del passaggio finale 4 (simulatore e architettura del dispositivo compatibili, in caso contrario, segui correttamente i passaggi 1 e 2 precedenti)

Vedi l'immagine di riferimento:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Inserisci il codice sottostante nell'area della 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"


Questo script sembra chiamare se stesso e quindi causare un ciclo infinito !! Ho dovuto riavviare il mio computer dopo averlo eseguito! Generava continuamente nuovi processi xcodebuild ... e apriva nuove finestre del Finder - Vorrei votare
J.beenie

Controlla la risposta di @ l0gg3r in questa domanda / risposta SO per uno script simile senza il problema della ricorsione.
J.beenie
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.