
Em 2014, o Swift foi introduzido, uma nova linguagem para o desenvolvimento de aplicativos de ecossistema da Apple. A novidade trouxe não apenas novos recursos e funções, mas também problemas - para aqueles que queriam usar as boas e antigas bibliotecas C. Neste artigo, discutirei um deles - o agrupamento da biblioteca C na estrutura Swift. Existem várias maneiras de resolvê-lo; neste caso, explicarei como fazer isso com módulos explícitos clang.
Por exemplo, pegamos a
biblioteca C
libgif externa e a incorporamos em nossa estrutura Swift GifSwift. Se você quiser ver o resultado imediatamente, o projeto completo pode ser visto
aqui .
Preparando libgif
Antes de incorporar a biblioteca libgif em nosso projeto, você precisa compilá-la a partir da fonte.
- Faça o download do tarball mais recente aqui .
- Descompacte o arquivo morto, usando o console, vá para a pasta e execute:
./configure && make check
Nota: por simplicidade, estamos construindo uma biblioteca para a plataforma x86-64 e, portanto, ela funcionará apenas no simulador iOS ou no macOS. Construir uma biblioteca estática de várias arquiteturas é um tópico separado, sobre o qual não falo neste artigo. Instruções úteis podem ser encontradas aqui .
- Se tudo ocorrer sem erros, os arquivos da biblioteca podem ser encontrados em
${lib_gif_source}/lib/.libs
. Estamos interessados em dois arquivos:
lib/.libs/libgif.a # lib/gif_lib.h #
Configuração do projeto
Agora vamos personalizar o projeto para as nossas necessidades.
- Crie um novo projeto usando o modelo Cocoa Touch Framework, dê o nome de GifSwift .
- Adicione os arquivos da biblioteca libgif que criamos a um grupo separado dentro do projeto.
- Inclua um novo destino para o aplicativo de teste no projeto para ver o resultado.
A estrutura final do projeto deve se parecer com isso:

Importar para o Swift
Para importar uma biblioteca C para o Swift, devemos descrevê-la como um
módulo . A descrição é um arquivo
.modulemap que contém uma lista de arquivos de cabeçalho para importação e bibliotecas estáticas para vinculação. O módulo resultante pode ser importado para o código Swift ou Objective-C (usando
@import
).
Esse método de importação da biblioteca para a estrutura funcionará na maioria dos casos (leia mais sobre essa abordagem
aqui ). Funciona muito bem se você estiver criando uma estrutura interna ou apenas dividindo seu aplicativo em módulos. Mas esse método também tem desvantagens. Por exemplo, é ineficaz se você deseja transferir sua biblioteca para alguém usando Carthage, Cocoapods ou devido a um artefato binário. O motivo é que a estrutura resultante geralmente não é portátil, porque ao compilá-la é vinculada a um local específico dos arquivos e bibliotecas de cabeçalho do mapa do módulo no seu computador.
Módulo Explícito
Para contornar essas limitações, usaremos outra maneira - o módulo
explícito da biblioteca. Um módulo explícito é um módulo que é declarado como um submódulo usando a palavra-chave
explícita , é colocado no módulo pai e
não é
importado automaticamente. Funciona de maneira semelhante a
*_Private.h
para
*_Private.h
de Objective-C. Se você deseja usar a API declarada nele, importe o módulo
explicitamente (explicitamente).Estamos criando um módulo explícito para a biblioteca C dentro da estrutura. Para fazer isso, precisamos redefinir o módulo Xcode gerado. Além disso, observe que não especificamos a biblioteca libgif.a para o link (link gif), mas o fazemos diretamente no projeto usando a interface do Xcode.
Nota: para saber mais sobre módulos explícitos , clique aqui.- Adicione um arquivo chamado GifSwift.modulemap à pasta raiz do projeto:
framework module GifSwift { umbrella header "GifSwift.h" explicit module CLibgif { private header "gif_lib.h" } export * }
Este arquivo contém a especificação para o módulo CLibgif explícito e consiste em um arquivo de cabeçalho declarado (já que existe apenas um em nossa biblioteca). O arquivo é carregado no módulo resultante para a estrutura.
- O arquivo de descrição do módulo não precisa ser adicionado à estrutura, mas deve ser especificado nas configurações de destino:
Build Settings — Packaging — Module Map (MODULEMAP_FILE) = $SRCROOT/GifSwift/GifSwift.modulemap
- Os arquivos libgif devem ser adicionados ao destino da estrutura como um cabeçalho privado ( gif_lib.h ) e uma biblioteca estática ( libgif.a ). Observe que o arquivo de cabeçalho da biblioteca C foi adicionado ao destino como privado. Isso é necessário para o nosso módulo explícito. Nada impede a adição desse arquivo de cabeçalho como público, mas nossa tarefa é ocultar os detalhes da implementação o mais simples possível.

- Agora você pode importar o módulo explícito dentro da estrutura usando
import GifSwift.CLibgif
Empacotador rápido
Agora você pode fazer a interface do nosso framework. Uma classe é suficiente, que é um gif com algumas propriedades:
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
interfaces de programação de baixo nível para processar arquivos e acessa algumas propriedades, mapeando-as para tipos de fundação mais convenientes.
Verifique
Para testar nossa biblioteca, adicionei o arquivo
cat.gif ao projeto:
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") } } }
Quando executamos esse código no console, veremos o seguinte:
"A imagem possui tamanho: (250.0, 208.0) e contém 44 imagens"
Conclusões
A estrutura resultante contém tudo o que você precisa usar, possui uma interface Swift e, por padrão, oculta o código C dos clientes. No entanto, isso não é totalmente verdade. Como escrevi acima, a importação do
GifSwift.CLibgif fornecerá acesso a todos os módulos privados; no entanto, por padrão, esse método de encapsulamento é suficiente para ocultar os detalhes da implementação da estrutura.