
En 2014, Swift a été introduit, un nouveau langage pour développer les applications de l'écosystème d'Apple. La nouveauté a apporté non seulement de nouvelles fonctionnalités et fonctions, mais aussi des problèmes - pour ceux qui voulaient utiliser les bonnes vieilles bibliothèques C. Dans cet article, j'examinerai l'un d'entre eux - le regroupement de bibliothèques C dans le cadre Swift. Il existe plusieurs façons de le résoudre; dans ce cas, je vais vous expliquer comment faire cela avec des modules explicites clang.
Par exemple, nous prenons la
bibliothèque C externe de
libgif et l'intégrons dans notre framework Swift GifSwift. Si vous souhaitez voir immédiatement le résultat, le projet complet peut être consulté
ici .
Préparation de libgif
Avant d'incorporer la bibliothèque libgif dans notre projet, vous devez la compiler à partir de la source.
- Téléchargez la dernière tarball ici .
- Décompressez l'archive, à l'aide de la console, accédez au dossier et exécutez:
./configure && make check
Remarque: pour plus de simplicité, nous compilons une bibliothèque pour la plate-forme x86-64, et donc elle ne fonctionnera que dans le simulateur iOS ou sur macOS. La construction d'une bibliothèque statique multi-architecture est un sujet distinct, que je n'aborde pas dans cet article. Des instructions utiles peuvent être trouvées ici .
- Si tout se passe sans erreurs, les fichiers de bibliothèque peuvent être trouvés dans
${lib_gif_source}/lib/.libs
. Nous sommes intéressés par deux fichiers:
lib/.libs/libgif.a # lib/gif_lib.h #
Configuration du projet
Nous allons maintenant personnaliser le projet selon nos besoins.
- Créez un nouveau projet à l'aide du modèle Cocoa Touch Framework, donnez-lui le nom GifSwift .
- Ajoutez les fichiers de bibliothèque libgif que nous avons créés à un groupe distinct dans le projet.
- Ajoutez une nouvelle cible pour l'application de test au projet pour voir le résultat.
La structure finale du projet devrait ressembler à ceci:

Importer dans Swift
Afin d'importer une bibliothèque C dans Swift, nous devons la décrire comme un
module . La description est un fichier
.modulemap contenant une liste de fichiers d'en-tête pour l'importation et des bibliothèques statiques pour la liaison. Le module résultant peut être importé dans du code Swift ou Objective-C (en utilisant
@import
).
Cette méthode d'importation de la bibliothèque dans le framework fonctionnera dans la plupart des cas (en savoir plus sur cette approche
ici ). Cela fonctionne très bien si vous créez un cadre interne ou si vous divisez simplement votre application en modules. Mais cette méthode présente également des inconvénients. Par exemple, il est inefficace si vous souhaitez transférer votre bibliothèque à quelqu'un utilisant Carthage, Cocoapods ou à cause d'un artefact binaire. La raison en est que le framework résultant n'est généralement pas portable, car lors de la compilation, il est lié à un emplacement spécifique des fichiers d'en-tête et des bibliothèques de la carte du module sur votre ordinateur.
Module explicite
Pour contourner ces limitations, nous utiliserons une autre méthode - le module
explicite pour la bibliothèque. Un module explicite est un module qui est déclaré en tant que sous-module à l'aide du mot clé
explicite , est placé dans le module parent et n'est
pas importé automatiquement. Il fonctionne de manière similaire à
*_Private.h
pour les
*_Private.h
Objective-C. Si vous souhaitez utiliser l'API qui y est déclarée, vous devez importer le module
explicitement (explicitement).Nous créons un module explicite pour la bibliothèque C à l'intérieur du framework. Pour ce faire, nous devons redéfinir le module Xcode généré. Notez également que nous ne spécifions pas la bibliothèque libgif.a pour le lien (link gif), mais le faisons directement dans le projet à l'aide de l'interface Xcode.
Remarque: pour en savoir plus sur les modules explicites , cliquez ici.- Ajoutez un fichier appelé GifSwift.modulemap au dossier racine du projet:
framework module GifSwift { umbrella header "GifSwift.h" explicit module CLibgif { private header "gif_lib.h" } export * }
Ce fichier contient les spécifications du module CLibgif explicite et se compose d'un fichier d'en-tête déclaré (car il n'y en a qu'un dans notre bibliothèque). Le fichier est chargé dans le module résultant pour le framework.
- Le fichier de description du module n'a pas besoin d'être ajouté au framework, mais il doit être spécifié dans les paramètres cibles:
Build Settings — Packaging — Module Map (MODULEMAP_FILE) = $SRCROOT/GifSwift/GifSwift.modulemap
- Les fichiers libgif doivent être ajoutés à la cible du framework en tant qu'en-tête privé ( gif_lib.h ) et bibliothèque statique ( libgif.a ). Veuillez noter que le fichier d'en-tête de la bibliothèque C a été ajouté à la cible en tant que privé. Ceci est nécessaire pour notre module explicite. Rien n'empêche d'ajouter ce fichier d'en-tête comme public, mais notre tâche consiste à masquer les détails de l'implémentation aussi simple que possible.

- Vous pouvez maintenant importer le module explicite à l'intérieur du framework en utilisant
import GifSwift.CLibgif
Emballage rapide
Vous pouvez maintenant faire l'interface de notre framework. Une classe suffit, ce qui est un gif avec quelques propriétés:
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
des interfaces de programmation de bas niveau pour le traitement des fichiers et accède à certaines propriétés, en les mappant à des types Foundation plus pratiques.
Vérifier
Afin de tester notre bibliothèque, j'ai ajouté le fichier
cat.gif au projet:
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") } } }
Lorsque nous exécutons ce code dans la console, nous verrons ce qui suit:
"L' image a une taille: (250,0, 208,0) et contient 44 images"
Conclusions
Le framework résultant contient tout ce dont vous avez besoin pour utiliser, a une interface Swift et, par défaut, cache le code C des clients. Cependant, ce n'est pas entièrement vrai. Comme je l'ai écrit ci-dessus, l'importation de
GifSwift.CLibgif vous donnera accès à tous les modules privés, cependant, par défaut, cette méthode d'encapsulation suffit à masquer les détails de la mise en œuvre du framework.