So binden Sie eine C-Bibliothek in ein Swift-Framework ein



2014 wurde Swift eingeführt, eine neue Sprache für die Entwicklung der Ökosystemanwendungen von Apple. Die Neuheit brachte nicht nur neue Features und Funktionen mit sich, sondern auch Probleme - für diejenigen, die die guten alten C-Bibliotheken nutzen wollten. In diesem Artikel werde ich eine davon diskutieren - die Bündelung von C-Bibliotheken im Swift-Framework. Es gibt verschiedene Möglichkeiten, dies zu lösen. In diesem Fall werde ich erklären, wie dies mit clang expliziten Modulen gemacht wird.

Zum Beispiel nehmen wir die externe libgif C-Bibliothek und binden sie in unser Swift GifSwift Framework ein. Wenn Sie das Ergebnis sofort sehen möchten, können Sie das vollständige Projekt hier anzeigen.

Libgif vorbereiten


Bevor Sie die libgif-Bibliothek in unser Projekt einbetten, müssen Sie sie aus der Quelle kompilieren.

  1. Laden Sie hier den neuesten Tarball herunter.
  2. Entpacken Sie das Archiv über die Konsole, wechseln Sie in den Ordner und führen Sie Folgendes aus:

    ./configure && make check 

    Hinweis: Der Einfachheit halber kompilieren wir eine Bibliothek für die x86-64-Plattform und daher funktioniert sie nur im iOS-Simulator oder unter macOS. Das Erstellen einer statischen Bibliothek mit mehreren Architekturen ist ein separates Thema, das ich in diesem Artikel nicht anspreche. Nützliche Anweisungen finden Sie hier .
  3. Wenn alles fehlerfrei verläuft, finden Sie die Bibliotheksdateien in ${lib_gif_source}/lib/.libs . Wir interessieren uns für zwei Dateien:

     lib/.libs/libgif.a #   lib/gif_lib.h #  

Projekteinrichtung


Jetzt werden wir das Projekt an unsere Bedürfnisse anpassen.

  1. Erstellen Sie ein neues Projekt mit der Cocoa Touch Framework-Vorlage und geben Sie ihr den Namen GifSwift .
  2. Fügen Sie die von uns erstellten libgif- Bibliotheksdateien einer separaten Gruppe innerhalb des Projekts hinzu.
  3. Fügen Sie dem Projekt ein neues Ziel für die Testanwendung hinzu, um das Ergebnis anzuzeigen.

Die endgültige Struktur des Projekts sollte ungefähr so ​​aussehen:



In Swift importieren


Um eine C-Bibliothek in Swift zu importieren, müssen wir sie als Modul beschreiben . Die Beschreibung ist eine .modulemap- Datei, die eine Liste von Header-Dateien für den Import und statische Bibliotheken zum Verknüpfen enthält. Das resultierende Modul kann (mit @import ) in @import oder Objective-C-Code @import .

Diese Methode zum Importieren der Bibliothek in das Framework funktioniert in den meisten Fällen (lesen Sie hier mehr über diesen Ansatz). Es funktioniert hervorragend, wenn Sie ein internes Framework erstellen oder Ihre Anwendung nur in Module aufteilen. Diese Methode hat aber auch Nachteile. Zum Beispiel ist es unwirksam, wenn Sie Ihre Bibliothek an jemanden übertragen möchten, der Karthago, Cocoapods oder ein binäres Artefakt verwendet. Der Grund dafür ist, dass das resultierende Framework im Allgemeinen nicht portierbar ist, da es beim Kompilieren an einen bestimmten Speicherort der Header-Dateien und -Bibliotheken aus der Modulzuordnung auf Ihrem Computer gebunden ist.

Explizites Modul


Um diese Einschränkungen zu umgehen, verwenden wir einen anderen Weg - das explizite Modul für die Bibliothek. Ein explizites Modul ist ein Modul, das mit dem expliziten Schlüsselwort als Submodul deklariert wird, im übergeordneten Modul platziert und nicht automatisch importiert wird . Es funktioniert ähnlich wie *_Private.h für Objective-C-Frameworks. Wenn Sie die darin deklarierte API verwenden möchten, müssen Sie das Modul explizit (explizit) importieren .

Wir erstellen ein explizites Modul für die C-Bibliothek innerhalb des Frameworks. Dazu müssen wir das generierte Xcode-Modul neu definieren. Beachten Sie außerdem, dass wir die Bibliothek libgif.a nicht für den Link (link gif) angeben, sondern dies direkt im Projekt über die Xcode-Schnittstelle tun.

Hinweis: Um mehr über explizite Module zu erfahren , klicken Sie hier.

  1. Fügen Sie dem Stammordner des Projekts eine Datei mit dem Namen GifSwift.modulemap hinzu:

     framework module GifSwift { umbrella header "GifSwift.h" explicit module CLibgif { private header "gif_lib.h" } export * } 

    Diese Datei enthält die Spezifikation für das explizite CLibgif- Modul und besteht aus einer deklarierten Header-Datei (da es nur eine in unserer Bibliothek gibt). Die Datei wird in das resultierende Modul für das Framework geladen.
  2. Die Modulbeschreibungsdatei muss nicht zum Framework hinzugefügt werden, sondern muss in den Zieleinstellungen angegeben werden:

     Build Settings — Packaging — Module Map (MODULEMAP_FILE) = $SRCROOT/GifSwift/GifSwift.modulemap 
  3. libgif-Dateien sollten dem Framework-Ziel als privater Header ( gif_lib.h ) und statische Bibliothek ( libgif.a ) hinzugefügt werden . Bitte beachten Sie, dass die Header-Datei für die C-Bibliothek dem Ziel als privat hinzugefügt wurde. Dies ist für unser explizites Modul erforderlich. Nichts hindert das Hinzufügen dieser Header-Datei als öffentlich, aber unsere Aufgabe ist es, Implementierungsdetails so einfach wie möglich zu verbergen.


  4. Jetzt können Sie das explizite Modul mit import GifSwift.CLibgif in das Framework import GifSwift.CLibgif

Schnelle Verpackung


Jetzt können Sie die Schnittstelle unseres Frameworks erstellen. Eine Klasse ist genug, was ein GIF mit ein paar Eigenschaften ist:

 import Foundation import GifSwift.CLibgif public class GifFile { private let path: URL private let fileHandlePtr: UnsafeMutablePointer<GifFileType> private var fileHandle: GifFileType { return self.fileHandlePtr.pointee } deinit { DGifCloseFile(self.fileHandlePtr, nil) } // MARK: - API public init?(path: URL) { self.path = path let errorCode = UnsafeMutablePointer<Int32>.allocate(capacity: 1) if let handle = path.path.withCString({ DGifOpenFileName($0, errorCode) }) { self.fileHandlePtr = handle DGifSlurp(handle) } else { debugPrint("Error opening file \(errorCode.pointee)") return nil } } public var size: CGSize { return CGSize(width: Double(fileHandle.SWidth), height: Double(fileHandle.SHeight)) } public var imagesCount: Int { return Int(fileHandle.ImageCount) } } 

GifFile.swift Programmierschnittstellen auf niedriger Ebene für die Verarbeitung von Dateien und greift auf einige Eigenschaften zu, um sie bequemeren Foundation-Typen GifFile.swift .

Überprüfen Sie


Um unsere Bibliothek zu testen, habe ich dem Projekt die Datei cat.gif hinzugefügt:

 import UIKit import GifSwift class ViewController: UIViewController {   override func viewDidLoad() {       super.viewDidLoad()       if let file = GifFile(path: Bundle.main.url(forResource: "cat", withExtension: "gif")!) {           debugPrint("Image has size: \(file.size) and contains \(file.imagesCount) images")       }   } } 

Wenn wir diesen Code in der Konsole ausführen, sehen wir Folgendes:

" Bild hat Größe: (250.0, 208.0) und enthält 44 Bilder"

Schlussfolgerungen


Das resultierende Framework enthält alles, was Sie verwenden müssen, verfügt über eine Swift-Oberfläche und verbirgt standardmäßig den C-Code vor Clients. Dies ist jedoch nicht ganz richtig. Wie ich oben geschrieben habe, erhalten Sie durch den Import von GifSwift.CLibgif Zugriff auf alle privaten Module. Standardmäßig reicht diese Kapselungsmethode jedoch aus, um die Details der Framework-Implementierung zu verbergen.

Source: https://habr.com/ru/post/de435650/


All Articles