
Im Leben vieler Unternehmen, die über einen eigenen Stapel von Bibliotheken und Komponenten verfügen und diese entwickeln, wird es manchmal schwierig, das Volumen dieses Stapels zu verwalten.
Bei der Entwicklung für die iOS-Plattform und allgemein für das Apple-Ökosystem gibt es zwei Möglichkeiten, Bibliotheken als Abhängigkeiten zu verbinden:
- Sammeln Sie sie jedes Mal, wenn Sie die Anwendung erstellen.
- Sammeln Sie sie im Voraus mit den bereits gesammelten Abhängigkeiten.
Bei der Wahl des zweiten Ansatzes wird es logisch, CI / CD-Systeme zu verwenden, um Bibliotheken in gebrauchsfertige Artefakte zu integrieren.
Die Notwendigkeit, Bibliotheken für mehrere Plattformen oder Prozessorarchitekturen im Apple-Ökosystem zu erstellen, erfordert jedoch häufig nicht immer triviale Vorgänge, sowohl beim Erstellen der Bibliothek als auch des Endprodukts, das sie verwendet.
Vor diesem Hintergrund war es schwer, es nicht zu bemerken, und es war äußerst interessant, eine der Innovationen von Apple zu studieren, die auf der WWDC 2019 im Rahmen der Präsentation von Binary Frameworks in Swift vorgestellt wurden - das Verpackungsformat der Frameworks ist XCFramework.
XCFramework bietet mehrere Vorteile gegenüber etablierten Ansätzen:
- Abhängigkeitspaketierung für alle Zielplattformen und -architekturen in einem einzigen Paket.
- Verbindungspaket im XCFramework-Format als einzelne Abhängigkeit für alle Zielplattformen und -architekturen.
- Keine Notwendigkeit, ein fettes / universelles Gerüst aufzubauen.
- X86_64-Slice muss nicht entfernt werden, bevor die endgültigen Anwendungen in den AppStore geladen werden.
In diesem Artikel erklären wir, warum dieses neue Format eingeführt wurde, was es ist und was es dem Entwickler bietet.
Apple hat zuvor den Swift Package Manager- Abhängigkeitsmanager veröffentlicht.
Unter dem Strich können Sie mit Swift PM Bibliotheken in Form von Open Source-Code mit einer Beschreibung der Abhängigkeiten bereitstellen.
Aus der Sicht des Entwicklers, der die Bibliothek bereitstellt, möchte ich zwei Aspekte von Swift PM hervorheben.
- Das offensichtliche Minus ist, dass aus dem einen oder anderen Grund nicht alle Bibliotheksanbieter ihren Quellcode für Verbraucher öffnen möchten.
- Ein offensichtliches Plus: Beim Kompilieren von Abhängigkeiten aus Quellen müssen wir nicht mehr die Binärkompatibilität von Bibliotheken beachten.
XCFramework Apple bietet als neues Binärformat für Paketbibliotheken eine Alternative zu Swift Packages an.
Dieses Format sowie die Möglichkeit, die in XCFramework zusammengestellte Bibliothek zu verbinden, sind ab Xcode 11 und dessen Betaversionen verfügbar.
Was ist XCFramework?
Im Kern ist XCFramework eine neue Möglichkeit, Bibliotheken in ihren verschiedenen Versionen zu verpacken und bereitzustellen.
Das neue Format ermöglicht unter anderem auch das Packen statischer Bibliotheken mit ihren Header-Dateien, einschließlich der in C / Objective-C geschriebenen.
Betrachten Sie das Format genauer.
Abhängigkeitspaketierung für alle Zielplattformen und -architekturen in einem einzigen Paket
Alle Bibliotheksassemblys für jede der Zielplattformen und -architekturen können jetzt in einem einzigen Bundle mit der Erweiterung .xcframework zusammengefasst werden.
Zu diesem Zeitpunkt müssen Sie jedoch Skripts verwenden, um den xcodebuild
mit der neuen -create-xcframework
für Xcode 11 -create-xcframework
.
Der Montage- und Verpackungsprozess wird weiter betrachtet.
Verbindungspaket im XCFramework-Format als einzelne Abhängigkeit für alle Zielplattformen und -architekturen
Da Bundle .xcframework alle erforderlichen Abhängigkeiten-Assembly-Optionen enthält, müssen wir uns keine Gedanken über die Architektur und die Zielplattform machen.
In Xcode 11 ist eine in .xcframework gepackte Bibliothek wie ein reguläres .framework verbunden.
Dies kann in den Zieleinstellungen auf folgende Weise erreicht werden:
- Hinzufügen von .xcframework zum Abschnitt "Frameworks und Bibliotheken" auf der Registerkarte "Allgemein"
- Hinzufügen von .xcframework zu "Binary mit Bibliotheken verknüpfen" auf der Registerkarte "Phasen erstellen"
Keine Notwendigkeit, Fett / universellen Rahmen zu bauen
Bisher mussten zur Unterstützung mehrerer Bibliotheken und mehrerer Architekturen in einer Plug-In-Bibliothek die sogenannten Fat- oder Universal-Frameworks erstellt werden.
Dazu wurde der lipo
, um alle Optionen des zusammengesetzten Frameworks in eine einzige dicke Binärdatei zu nähen.
Weitere Details dazu finden Sie beispielsweise in folgenden Artikeln:
Es ist nicht erforderlich, das x86_64-Segment zu entfernen, bevor Endanwendungen in den AppStore geladen werden
In der Regel wird ein solches Slice verwendet, um Bibliotheken im iOS-Simulator bereitzustellen.
Wenn Sie versuchen, eine Anwendung mit Abhängigkeiten, die x86_64-Slice enthalten, im AppStore herunterzuladen, tritt möglicherweise der bekannte Fehler ITMS-90087 auf .
XCFramework erstellen und verpacken: Theorie
In der zuvor erwähnten Präsentation sind mehrere Schritte erforderlich, um die Bibliothek im XCFramework-Format zusammenzustellen und zu verpacken:
Projektvorbereitung
Zunächst müssen Sie in allen Zielbereichen des Projekts, die für das Erstellen der Bibliothek für die Zielplattformen verantwortlich sind, die neue Einstellung "Bibliotheken für die Verteilung erstellen" für Xcode 11 aktivieren.

Projektassembly für Zielplattformen und Architekturen
Als Nächstes müssen wir alle Ziele für die Zielplattformen und -architekturen erfassen.
Wir betrachten Beispiele für das Aufrufen von Befehlen am Beispiel einer bestimmten Projektkonfiguration.
Nehmen wir an, wir haben im Projekt zwei Schemata: „XCFrameworkExample-iOS“ und „XCFramework-macOS“.

Ebenfalls im Projekt gibt es zwei Ziele, die die Bibliothek für iOS und macOS sammeln.

Um alle erforderlichen Bibliothekskonfigurationen zu erstellen, müssen beide Ziele mithilfe der entsprechenden Schemata erfasst werden.
Für iOS benötigen wir jedoch zwei Assemblys: eine für Endgeräte (ARM) und eine für den Simulator (x86_64).
Insgesamt müssen wir 3 Frameworks sammeln.
Dazu können Sie den Befehl xcodebuild
:
Als Ergebnis haben wir 3 zusammengestellte Frameworks erhalten, die wir im .xcframework-Container weiter verpacken werden.
Zusammengesetztes .framework in .xcframework packen
Sie können dies mit dem folgenden Befehl tun:
xcodebuild -create-xcframework \ -framework "./build/ios.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -framework "./build/ios_sim.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -framework "./build/macos.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -output "./build/XCFrameworkExample.xcframework"
Es kann viele -framework
Parameter- -framework
, die auf alle .framework-Assemblys verweisen, die Sie an den .xcframework-Container anhängen möchten.
Vorbereiten eines Bibliotheksprojekts für das zukünftige Zusammenstellen und Packen von XCFramework
TL; DR: Das fertige Projekt kann aus dem Repository von Github heruntergeladen werden.
Als Beispiel implementieren wir eine Bibliothek, die für zwei Plattformen verfügbar sein wird: iOS und macOS.
Wir werden die im vorherigen Abschnitt des Artikels erwähnte Projektkonfiguration verwenden: zwei Schemata und zwei entsprechende Framework-Ziele für iOS- und macOS-Plattformen.
Wird die Bibliothek selbst eine einfache Erweiterung für String?
bereitstellen String?
( Optional where Wrapped == String
), mit einer einzelnen Eigenschaft.
Wir nennen diese Eigenschaft isNilOrEmpty
und werden uns, wie der Name schon sagt, wissen lassen, wann sie sich in String?
fehlender Wert oder der darin gespeicherte String ist leer.
Der Code kann wie folgt implementiert werden:
public extension Optional where Wrapped == String { var isNilOrEmpty: Bool { if case let .some(string) = self { return string.isEmpty } return true } }
Wir fahren direkt mit der Erstellung und Konfiguration des Projekts fort.
Zunächst müssen wir ein Projekt vom Typ "Framework" für eine von zwei Zielplattformen Ihrer Wahl erstellen: iOS oder macOS.
Dies können Sie in Xcode über den Menüpunkt „Datei“ => „Neu“ => „Projekt“ oder über das Tastenkürzel ⇧ + + N (standardmäßig) tun .
Wählen Sie im oberen Bereich des Dialogfelds die gewünschte Plattform (iOS oder macOS) aus, wählen Sie den Typ des Framework-Projekts aus und klicken Sie auf die Schaltfläche "Weiter".
Auf dem nächsten Bildschirm müssen wir den Projektnamen im Feld "Produktname" festlegen.
Alternativ können Sie den "Basis" -Namen des Projekts verwenden, in der zuvor erwähnten Konfiguration ist es "XCFrameworkExample".
In Zukunft werden wir bei der Konfiguration des Projekts dem Basisnamen, der im Namen des Ziels verwendet wird, Suffixe für Plattformen hinzufügen.
Danach müssen Sie im Projekt ein anderes Target vom Typ "Framework" für eine andere der aufgelisteten Plattformen erstellen (mit Ausnahme der Plattform, für die das Projekt ursprünglich erstellt wurde).
Verwenden Sie dazu den Menüpunkt "Datei" => "Neu" => "Ziel".
Als nächstes wählen wir im Dialog eine andere (relativ zu der in Absatz 1 ausgewählten) der beiden Plattformen aus, woraufhin wir erneut den Projekttyp "Framework" auswählen.
Für das Feld "Produktname" können wir sofort den Namen mit dem Suffix der Plattform verwenden, für die wir in diesem Absatz das Ziel hinzufügen. Wenn die Plattform also macOS ist, könnte der Name "XCFrameworkExample-macOS" lauten (% base_name% -% platform%).
Wir richten Ziele und Diagramme ein, um die Unterscheidung zu erleichtern.
Benennen Sie zunächst unsere Schemata und die mit ihnen verknüpften Ziele um, sodass ihre Namen die Plattformen widerspiegeln. Beispiel:
- "XCFrameworkExample-iOS"
- "XCFrameworkExample-macOS"
Fügen Sie als Nächstes die Datei mit dem Code unserer Erweiterung für String?
zum Projekt hinzu .swift String?
Fügen Sie dem Projekt eine neue .swift-Datei mit dem Namen "Optional.swift" hinzu.
Und in der Datei selbst haben wir die oben erwähnte Erweiterung für Optional
.
Es ist wichtig, dass Sie nicht vergessen, die Codedatei beiden Zielen hinzuzufügen.

Jetzt haben wir ein Projekt, das wir mit den Befehlen aus der vorherigen Phase in XCFramework zusammenstellen können.
Zu diesem Zeitpunkt können Sie das Bash-Skript in einer separaten Datei verwenden, um die Bibliothek zu erstellen und im .xcframework-Format zu verpacken. Darüber hinaus können diese Entwicklungen künftig genutzt werden, um die Lösung in das CI / CD-System zu integrieren.
Das Skript sieht hässlich einfach aus und fasst die zuvor erwähnten Befehle für die Assemblierung zusammen:
.Xcframework-Inhalt
Als Ergebnis des Assembler-Skripts aus dem vorherigen Absatz des Artikels erhalten wir das begehrte Bundle .xcframework, das dem Projekt hinzugefügt werden kann.
Wenn wir uns dieses Bundle ansehen, das wie .framework im Wesentlichen ein einfacher Ordner ist, sehen wir die folgende Struktur:

Hier sehen wir, dass es sich bei .xcframework um Assemblys im .framework-Format handelt, die nach Plattform und Architektur unterteilt sind. Um auch den Inhalt des Bundles .xcframework zu beschreiben, befindet sich im Inneren eine Info.plist-Datei.
Die Datei Info.plist hat folgenden Inhalt <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>AvailableLibraries</key> <array> <dict> <key>LibraryIdentifier</key> <string>ios-arm64</string> <key>LibraryPath</key> <string>XCFrameworkExample.framework</string> <key>SupportedArchitectures</key> <array> <string>arm64</string> </array> <key>SupportedPlatform</key> <string>ios</string> </dict> <dict> <key>LibraryIdentifier</key> <string>ios-x86_64-simulator</string> <key>LibraryPath</key> <string>XCFrameworkExample.framework</string> <key>SupportedArchitectures</key> <array> <string>x86_64</string> </array> <key>SupportedPlatform</key> <string>ios</string> <key>SupportedPlatformVariant</key> <string>simulator</string> </dict> <dict> <key>LibraryIdentifier</key> <string>macos-x86_64</string> <key>LibraryPath</key> <string>XCFrameworkExample.framework</string> <key>SupportedArchitectures</key> <array> <string>x86_64</string> </array> <key>SupportedPlatform</key> <string>macos</string> </dict> </array> <key>CFBundlePackageType</key> <string>XFWK</string> <key>XCFrameworkFormatVersion</key> <string>1.0</string> </dict> </plist>
Möglicherweise stellen Sie fest, dass für den Schlüssel "CFBundlePackageType" im Gegensatz zum Format ".framework" der neue Wert "XFWK" und nicht "FMWK" verwendet wird.
Zusammenfassung
Das Bibliothekspaketformat in XCFramework ist also nichts anderes als ein regulärer Container für Bibliotheken, die im .framework-Format kompiliert wurden.
Mit diesem Format können Sie jedoch jede der darin enthaltenen Architekturen und Plattformen separat speichern und unabhängig voneinander verwenden. Dies beseitigt eine Reihe von Problemen, die mit dem weit verbreiteten Ansatz zum Aufbau von Fett- / Universalgerüsten verbunden sind.
Wie dem auch sei, im Moment gibt es eine wichtige Nuance in Bezug auf die Verwendung von XCFramework in realen Projekten - das Abhängigkeitsmanagement, das Apple nicht im XCFramework-Format implementiert hat.
Zu diesem Zweck werden üblicherweise Swift PM, Carthage, CocoaPods und andere Abhängigkeitsverwaltungssysteme sowie deren Assemblys verwendet. Daher ist es nicht verwunderlich, dass die Unterstützung für das neue Format gerade in den Projekten CocoaPods und Carthage bereits im Gange ist.