2016-07-08 17 views
7

Ich habe ein Swift Framework, das wir kompilieren und an Entwickler von Drittanbietern verteilen. Ich möchte etwas C-Code in das Projekt einfügen, aber die einzige Möglichkeit, es zum Laufen zu bringen, besteht darin, den C-Header in den Header meines Frameworks zu importieren und den C-Header als öffentlich zu definieren. Dies stellt den gesamten C-Code den Drittentwicklern zur Verfügung, was nicht das ist, was wir wollen.Wie kompiliere ich C-Code in Swift Framework

Alles, was ich finden kann online, wie dies zu tun ist eine Variante dieser Methode: https://spin.atomicobject.com/2015/02/23/c-libraries-swift/

Aber der Nachteil dieser Methode wird als „Beachten Sie, dass die Verbraucher über Frameworks Sie diese Technik bauen mit gehen, um auch haben um das Modul ihren Swift-Suchpfaden hinzuzufügen. " Und das haben wir ausprobiert, und unsere Entwickler von Drittanbietern erhalten einen Fehler Missing required module 'CGeodesic', der das Modul ist, das wir auf die C-Kopfzeile definiert haben.

Wie können wir nur den C-Code direkt in unser Framework kompilieren, während der Code privat bleibt und keine Entwickler von Drittanbietern ihre Build-Einstellungen ändern müssen, damit es funktioniert? Es macht mir nichts aus, diese Submodule in unserem Projekt einzurichten, aber am Ende des Tages möchte ich, dass es direkt in ein einziges Flat-Framework-Binary kompiliert wird, ohne dynamische Verknüpfungen oder Suchpfade oder ähnliches.

bearbeiten


Das Projekt ist C-Code Side-by-Side mit Swift-Code. Ich habe ein Unterverzeichnis in meinem Projekt, das wie folgt aussieht:

  • Geodätische/
    • Geodesic.h
    • Geodesic.c
    • Geodesic.swift

Die Die Swift-Datei umschließt den C-Code, damit die Arbeit sauberer wird. Ich hatte auch eine modul.modulemap-Datei in diesem Verzeichnis, aber wie ich schon sagte, das hat nicht funktioniert. Ich würde es vorziehen, den C-Code Seite an Seite mit meinem Swift-Code zu halten, aber ich bin bereit, das aufzugeben, um dieses Problem zu lösen.

+0

Was ist Ihre aktuelle Konfiguration? C-Code Seite an Seite mit Swift-Code? – zneak

+0

https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html. Hilft das? C-Code ist Objective-C, und es sieht so aus, als könnte man ein Framework in einer Mischung aus C und Swift schreiben, und es wird den Benutzern so erscheinen, als ob es in einer einzigen Sprache geschrieben wäre. Ich habe das selbst noch nicht ausprobiert. – OmniProg

+0

@OmniProg also das machen wir gerade. Das Problem mit dieser Methode besteht darin, dass sie alle diese Header notwendigerweise öffentlich verfügbar macht, was wir zu vermeiden versuchen. – user1084447

Antwort

14

Um nur Ihr Problem neu zu formulieren: Sie möchten C-Code in Swift grundsätzlich einwickeln und direkten Zugriff auf den eingekapselten C-Code verhindern. Das bedeutet, den Zugriff sowohl von Swift als auch von Objective-C zu verhindern.

Dies ist möglich, erfordert jedoch einige Anpassungen an Ihrem Build-Prozess und einige Nachbearbeitung.

C und Swift Interop in Frameworks

Jeder C-Code, den Sie in Swift wickeln wollen muss während der Kompilierung entweder über den Schirm Kopf- oder mit Modul Karten öffentlich sein. Der wichtige Teil hier: während der Kompilierzeit. Frameworks haben keine Integritätsprüfungen, was bedeutet, dass Sie eine Nachbearbeitung auf sie anwenden können (z. B. Erstellen eines fetten binären Frameworks mit lipo). Und das müssen wir hier tun.

vorbereiten build

Statt alle C-Header-Abhängigkeiten in den Regenschirm-Header setzen können Sie diese auch in die module.modulemap Datei setzen. Das hat einen Vorteil: Sie können Ihre Header in der Build-Phase Headers privat machen.

daher eine module.modulemap Datei in Ihrem Projekt erstellen und den Weg dorthin in der Module Map File Build-Set im Packaging Abschnitt (MODULEMAP_FILE in xcconfig Dateien) einstellen, zum Beispiel $(SRCROOT)/MyFramework/module.modulemap.

Ihre module.modulemap Datei sollte den folgenden Inhalt haben:

# 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" 
} 

So weit so gut. Sie sollten nun in der Lage sein, Ihren Code zu erstellen und von Swift und Objective-C darauf zuzugreifen. Das einzige Problem: Sie können weiterhin auf Ihre C-Header-Dateien in Swift und Objective-C zugreifen.

nachbearbeiten

Wenn Sie einen Blick auf die endgültige Rahmen bündeln, werden Sie feststellen, dass alle Ihre privaten Header-Dateien in das MyFramework.framework/PrivateHeaders Ordner kopiert wurden. Das bedeutet, dass Sie in Objective-C weiterhin über #import <MyFramework/A.h> darauf zugreifen können.

In Swift können Sie weiterhin auf den C-Code zugreifen, weil wir die Header-Dateien in die module.modulemap-Datei schreiben, die in MyFramework.framework/Modules/module.modulemap kopiert wurde.

Zum Glück können wir nur mit einem wenig Nachbearbeitung dieser beiden Probleme loszuwerden:

  1. die PrivateHeaders Ordner aus dem Rahmenbündel entfernen.
  2. Erstellen Sie eine separate Modulkarte und entfernen Sie alle header "XYZ.h" Anweisungen, z. B. benennen Sie sie public.modulemap.
  3. all das in einer Run Script Build-Phase Put ist

Hier ist die öffentliche modulemap:

# public.modulemap 

framework module MyFramework { 

    umbrella header "MyFramework.h" 

    export * 
    module * {export *} 

    # No more C Headers here 
} 

Und das Run-Skript, das Sie Ihren Rahmen des bis zum Ende der Aufbauphasen hinzufügen sollen:

# 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 

Hoffe, dass hilft!

+0

Funktioniert perfekt! Vielen Dank! – user1084447

+3

Dies ist eine ausgezeichnete Antwort auf ein komplexes Problem, danke. –

+0

diese Antwort ist natürlich unglaublich, @JensMeder, danke.eine Sache - vielleicht könnten Sie eine kurze Ergänzung des * einfacheren Falles * hinzufügen, bei der es * überhaupt keine Notwendigkeit gibt, einen direkten Zugriff zu verhindern. Dies kann die Antwort klären. Ich wette, das wäre für viele hilfreich. (Alle älteren Artikel dazu im www sind mit der Zeit verschwunden! :)) – Fattie

1

Es gibt eine Lösung, obwohl es nicht empfohlen wird. Sie können use @_silgen_name in Ihrer .Swift Datei anstelle von C-Header verwenden.