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




xcode cocoa-touch (4)

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

TLDR;

Attualmente Xcode non dispone di strumenti per l'esportazione automatica del framework universale dei grassi, pertanto lo sviluppatore deve ricorrere all'uso manuale dello strumento lipo . Inoltre, secondo questo radar, prima dell'invio allo sviluppatore di AppStore, che è il consumatore del framework, è necessario utilizzare anche lipo per 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 sulla distribuzione, quindi la mia ricerca si basava sull'esplorazione dei forum degli sviluppatori Apple, sui progetti di Cartagine e Realm e sui miei esperimenti con gli strumenti xcodebuild , lipo , codesign .

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

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

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

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

  • Inoltre, ti consigliamo di assicurarti che lo schema sia impostato su una build di rilascio e crea la destinazione del framework rispetto alla versione. Se stai ancora riscontrando un errore libreria non caricata, controlla le sezioni di codice nel framework.

  • Usa lipo -info MyFramworkBinary ed esamina il risultato.

lipo -info MyFrameworkBinary

Il risultato è i386 x86_64 armv7 arm64

  • I quadri universali moderni includeranno 4 sezioni, ma potrebbero includerne altre: i386 x86_64 armv7 arm64 Se non vedi almeno questo 4 potrebbe essere dovuto all'impostazione Build Active Architecture.

Questo descrive praticamente lo stesso processo di @skywinder nella sua risposta.

Ecco come Cartagine usa lipo e Realm usa lipo .

DETTAGLIO IMPORTANTE

Esiste 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 al riguardo su Realm#1163 e Carthage#188 che si è conclusa con una soluzione alternativa:

prima dell'invio ai binari del framework iOS di AppStore devono essere rimossi dai segmenti del simulatore

Cartagine ha un codice speciale: CopyFrameworks e relativa documentazione:

Questo script funziona attorno a un bug di invio dell'App Store attivato da file binari universali.

Realm ha uno script speciale: strip-frameworks.sh e la relativa documentazione:

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

C'è anche un buon articolo: Stripping di architetture indesiderate da librerie dinamiche in Xcode .

Io stesso ho usato strip-frameworks.sh di Realm che ha funzionato perfettamente per me senza alcuna modifica, anche se ovviamente chiunque è libero di scriverne uno da zero.

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

Con Xcode 6 abbiamo la possibilità di creare i propri Cocoa Frameworks dinamici di Cocoa Frameworks .

Per colpa di:

  • Il simulatore utilizza ancora la libreria a 32-bit

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

Dobbiamo creare una libreria di grasso per eseguire progetti 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 l'integrazione futura con altri progetti (e condividere questa libreria con qualcuno).

Ecco i miei passaggi per riprodurre:

  1. Impostare ONLY_ACTIVE_ARCH=NO nelle Build Settings

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

  1. Crea Framework e aprilo nel Finder:

  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 cartella Debug-iphoneos - funziona sui dispositivi e viene visualizzato l'errore 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 framework dalla cartella Debug-iphonesimulator - funziona su 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 funziona su dispositivi e simulatori?

Questa risposta è correlata a Xcode 6 iOS Creazione di Cocoa Touch Framework - Problemi di architettura ma non è duplicato.

Aggiornare:

Ho trovato un "trucco sporco" per questo caso. Vedi la mia risposta qui sotto . Se qualcuno conosce il modo più conveniente - per favore, fammi sapere!


La mia risposta copre i punti seguenti:

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

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

  • Simboli indefiniti per l'architettura x86_64

  • ld: simbolo (i) non trovato per l'architettura x86_64

Passaggi 1: prima costruisci i tuoi framework con il target del simulatore

Passaggi 2: Dopo aver completato con successo il processo di creazione del simulatore, ora crea il tuo framework con la selezione del target del dispositivo o la selezione del dispositivo iOS generico

Step 3: Ora seleziona il tuo target di framework e per quello Sotto "Build Phases" seleziona "Aggiungi Run Script" e copia il codice di script qui sotto)

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 seguire correttamente i passaggi 1 e 2 sopra)

Vedi l'immagine di riferimento:

Inserisci il codice seguente 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"


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

  1. Impostare ONLY_ACTIVE_ARCH=NO nelle Build Settings

    • Crea libreria per simulatore
    • Crea libreria per dispositivo
  2. Apri nella cartella Products console per il tuo framework (puoi aprirlo aprendo la cartella framework e cd .. da lì)

  1. Esegui this script dalla cartella Products . Crea grasso framework in questa cartella. (o farlo manualmente come spiegato di seguito in 3. 4. )

O:

  1. Combina questi 2 YourFrameworkName utilizzando lipo con questo script (sostituisci YourFrameworkName con il nome del tuo framework)

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

    cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
    mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
  1. Profitto: ./YourFrameworkName.framework - è un binario grasso pronto all'uso! Puoi importarlo nel tuo progetto!

Per il progetto, quello non in Aree di lavoro:

Puoi anche provare a usare questa sintesi come descritto here . Ma sembra che non funzioni per i progetti nelle aree di lavoro.


Voglio solo aggiornare questa grande risposta di @odm. Da Xcode 10, la variabile CURRENT_ARCH non riflette più l'architettura di compilazione. Quindi ho modificato 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 della copia, perché ho notato che i file aggiuntivi nelle sottodirectory non verrebbero sovrascritti altrimenti.







ios-universal-framework