2016-07-08 65 views
7

Ho uno Swift Framework che compiliamo e distribuiamo a sviluppatori di terze parti. Voglio includere del codice C nel progetto, ma l'unico modo in cui sono riuscito a farlo è importare l'intestazione C nell'intestazione del mio framework e impostare l'intestazione C come pubblica. Questo espone tutto il codice C agli sviluppatori di terze parti che non è ciò che vogliamo.Come compilare il codice C in Swift Framework

tutto ciò che posso trovare on-line per sapere come fare questo è una variante di questo metodo: https://spin.atomicobject.com/2015/02/23/c-libraries-swift/

Ma l'avvertenza di questo metodo è "Si noti che i consumatori di eventuali strutture a costruire con questa tecnica sono anche andando ad avere per aggiungere il modulo ai loro percorsi di ricerca Swift. " E abbiamo provato questo e abbastanza sicuro che i nostri sviluppatori di terze parti hanno ricevuto un errore Missing required module 'CGeodesic' che è il modulo che abbiamo definito che punta all'intestazione C.

Come possiamo semplicemente compilare il codice C nel nostro framework mantenendo il codice privato e non richiedendo agli sviluppatori di terze parti di modificare le impostazioni di generazione per farlo funzionare? Non mi interessa dover configurare questi sottomoduli nel nostro progetto, ma alla fine della giornata voglio che venga compilato direttamente in un unico binario di framework piatto, senza collegamenti dinamici o percorsi di ricerca o qualcosa del genere.

Modifica


Il progetto è il codice C fianco a fianco con il codice Swift. Ho una sottodirectory nel mio progetto che assomiglia a questo:

  • geodetica/
    • Geodesic.h
    • Geodesic.c
    • Geodesic.swift

Il Il file Swift avvolge il codice C per renderlo più pulito con cui lavorare. Avevo anche un file module.modulemap in quella directory, ma come ho detto in precedenza non funzionava. Preferirei mantenere il codice C parallelamente al mio codice Swift, ma sono disposto a rinunciarvi per risolvere questo problema.

+0

Qual è la configurazione attuale? Codice C affiancato al codice Swift? – zneak

+0

https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html. questo aiuta? Il codice C è Objective-C, e sembra che si possa scrivere un framework in un misto di C e Swift, e apparirà agli utenti come se fosse scritto in una sola lingua. Non ho ancora provato questo. – OmniProg

+0

@OmniProg, ecco cosa stiamo facendo attualmente. Il problema con questo metodo è che espone pubblicamente tutte quelle intestazioni, cosa che stiamo cercando di evitare. – user1084447

risposta

14

Giusto per riformulare il problema: in pratica si desidera avvolgere il codice C in Swift e impedire l'accesso diretto al codice C incapsulato. Ciò significa impedire l'accesso sia da Swift che da Objective-C.

Ciò è possibile ma richiede alcune modifiche al processo di compilazione e alcune elaborazioni successive.

C e Swift Interop in quadri

Qualsiasi codice C che si desidera avvolgere in Swift deve essere pubblico durante la fase di compilazione sia tramite l'intestazione ombrello o utilizzando le mappe di moduli. La parte importante qui: durante la compilazione. I framework non hanno controlli di integrità, il che significa che è possibile applicare loro la post-elaborazione (ad esempio, creare un framework binario grasso con lipo). Ed è quello che dobbiamo fare qui.

Preparare l'accumulo

Invece di mettere tutte le dipendenze di intestazione C nell'intestazione ombrello si può anche metterli nel file module.modulemap. Questo ha un vantaggio: puoi rendere private le tue intestazioni nella Fase di costruzione Headers.

Pertanto, creare un file module.modulemap nel progetto e impostare il percorso nella impostazione nella sezione Packaging (MODULEMAP_FILE in file xcconfig) Module Map File Genera, per esempio, $(SRCROOT)/MyFramework/module.modulemap.

Il file module.modulemap dovrebbe avere il seguente contenuto:

# module.modulemap 

framework module MyFramework { 

    umbrella header "MyFramework.h" 

    export * 
    module * {export *} 

    # All C Files you want to wrap in your Swift code 

    header "A.h" 
    header "B.h" 
} 

Fin qui tutto bene. Ora dovresti essere in grado di creare il tuo codice e accedervi sia da Swift che da Objective-C. L'unico problema: puoi ancora accedere ai tuoi file header C in Swift e Objective-C.

Postprocessing

Se si dispone di uno sguardo al quadro finale fascio si noterà che tutti i file di intestazione private sono stati copiati nella cartella MyFramework.framework/PrivateHeaders. Ciò significa che puoi ancora accedervi tramite #import <MyFramework/A.h> in Objective-C.

In Swift è ancora possibile accedere al codice C perché abbiamo inserito i file di intestazione nel file module.modulemap che è stato copiato in MyFramework.framework/Modules/module.modulemap.

Fortunatamente, possiamo semplicemente sbarazzarsi di quei due problemi con un po 'di post-elaborazione:

  1. rimuovere la cartella PrivateHeaders dal fascio quadro.
  2. Creare una modulemap separata e rimuovere tutte le istruzioni header "XYZ.h", ad esempio denominarla public.modulemap.
  3. Metti tutto questo in una fase Run Script Corporatura

Ecco la modulemap pubblico:

# public.modulemap 

framework module MyFramework { 

    umbrella header "MyFramework.h" 

    export * 
    module * {export *} 

    # No more C Headers here 
} 

e lo script run che si dovrebbe aggiungere alla fine delle fasi di compilazione del quadro:

# Delete PrivateHeaders folder 
rm -rf ${TARGET_BUILD_DIR}/${PRODUCT_NAME}${WRAPPER_SUFFIX}/PrivateHeaders 

# Remove module.modulemap file 
rm ${TARGET_BUILD_DIR}/${PRODUCT_NAME}${WRAPPER_SUFFIX}/Modules/module.modulemap 

# Copy public.modulemap file and rename it to module.modulemap 
cp ${SRCROOT}/test/public.modulemap ${TARGET_BUILD_DIR}/${PRODUCT_NAME}${WRAPPER_SUFFIX}/Modules/module.modulemap 

# Append the Swift module so you can access you Swift code in Objective-C via @import MyFramework.Swift 
echo "module ${PRODUCT_NAME}.Swift { header \"${PRODUCT_NAME}-Swift.h\" }" >> ${TARGET_BUILD_DIR}/${PRODUCT_NAME}${WRAPPER_SUFFIX}/Modules/module.modulemap 

Spero che questo aiuti!

+0

Funziona perfettamente! Grazie! – user1084447

+3

Questa è una risposta eccellente a un problema complesso, grazie. –

+0

ovviamente questa risposta è incredibile, @JensMeder, grazie.una cosa - forse potresti aggiungere una breve appendice del * caso più semplice *, dove non c'è * assolutamente necessario * per impedire l'accesso diretto. Questo potrebbe chiarire la risposta. Scommetto che questo sarebbe utile a molti. (Tutti gli articoli più vecchi su questo sul www sono scomparsi col tempo! :)) – Fattie

1

C'è una soluzione, sebbene non sia raccomandata. È possibile use @_silgen_name all'interno del file .Swift anziché utilizzare l'intestazione C.