» » » Как встроить С-библиотеку в Swift-фреймворк

 

Как встроить С-библиотеку в Swift-фреймворк

Автор: admin от 10-01-2019, 17:25, посмотрело: 18

Как встроить С-библиотеку в Swift-фреймворк


В 2014 году был представлен Swift, новый язык для разработки приложений экосистемы Apple. Новинка принесла не только новые возможности и функции, но и проблемы — тем, кто хотел пользоваться старыми добрыми C-библиотеками. В этой статье я рассмотрю одну из них — бандлинг C-библиотеки в Swift-фреймворк. Существует несколько способов её решения; в данном случае я объясню, как сделать это при помощи clang explicit-модулей.



Для примера мы возьмём внешнюю C-библиотеку libgif и встроим её в наш Swift-фреймворк GifSwift. Если вы хотите сразу увидеть результат, полностью проект можно посмотреть здесь.

здесь.


  • Распаковываем архив, при помощи консоли заходим в папку и запускаем:



    ./configure && make check


    Примечание: для простоты мы собираем библиотеку для платформы x86-64, а потому она будет работать только в iOS-симуляторе или на macOS. Построение мультиархитектурной статической библиотеки – отдельная тема, которой я не касаюсь в этой статье. Полезные инструкции вы найдёте здесь.


  • Если всё пройдёт без ошибок, файлы библиотеки можно будет найти в ${lib_gif_source}/lib/.libs. Нас интересуют два файла:



    lib/.libs/libgif.a # Статическая библиотека
    lib/gif_lib.h # Интерфейс
    



  • Настройка проекта



    Теперь настроим проект под наши нужды.




    1. Создаём новый проект при помощи шаблона Cocoa Touch Framework, даём ему имя GifSwift.


    2. Добавляем созданные нами файлы библиотеки libgif в отдельную группу внутри проекта.


    3. Добавляем в проект новый таргет для тестового приложения, чтобы посмотреть результат.




    Итоговая структура проекта должна выглядеть примерно так:



    Как встроить С-библиотеку в Swift-фреймворк


    Импортируем в Swift



    Для того чтобы импортировать C-библиотеку в Swift, мы должны описать её как модуль. Описание представляет собой файл .modulemap, содержащий список заголовочных файлов для импорта и статических библиотек для линковки. Полученный модуль может быть импортирован в Swift или Objective-C-код (при помощи @import).



    Этот способ импорта библиотеки во фреймворк будет работать в большинстве случаев (более подробно об этом подходе читайте здесь). Он отлично подходит, если вы создаёте внутренний фреймворк или просто разбиваете своё приложение на модули. Но такой способ также имеет и недостатки. Например, он неэффективен в том случае, если вы хотите передать свою библиотеку кому-то при помощи Carthage, Cocoapods или в виду бинарного артефакта. Причина в том, что получившийся фреймворк в общем случае не портируем, поскольку при компиляции он привязывается к конкретному расположению заголовочных файлов и библиотек из module map на вашем компьютере.



    Явный модуль



    Чтобы обойти эти ограничения, воспользуемся ещё одним способом — explicit-модулем для библиотеки. Еxplicit-модуль — это модуль, который объявляется подмодулем при помощи ключевого слова explicit, помещается в родительский модуль и не импортируется автоматически. Он работает аналогично *_Private.h для фреймворков Objective-C. Если вы хотите использовать объявленные в нём API, необходимо импортировать модуль явно (explicitly).



    Мы создаём явный модуль для C-библиотеки внутри фреймворка. Для этого нам нужно провести переопределение сгенерированного XCode-модуля. Также обратите внимание на то, что мы не указываем библиотеку libgif.a для линковки (link gif), а вместо этого сделаем это прямо в проекте, используя интерфейс XCode.



    Примечание: узнать больше об explicit-модулях можно по ссылке




    1. Добавляем в корневую папку проекта файл с названием GifSwift.modulemap:



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


      Этот файл содержит спецификацию для явного модуля CLibgif и состоит из одного объявленного заголовочного файла (поскольку в нашей библиотеке как раз один такой). Файл загружается в получившийся модуль для фреймворка.


    2. Файл с описанием модуля не нужно добавлять в состав фреймворка, но он должен быть указан в настройках таргета:



      Build Settings — Packaging — Module Map (MODULEMAP_FILE)
      =
      $SRCROOT/GifSwift/GifSwift.modulemap

    3. libgif-файлы должны быть добавлены в таргет фреймворка в виде приватного хедера (gif_lib.h) и статической библиотеки (libgif.a). Обратите внимание на то, что заголовочный файл для C-библиотеки добавлен в таргет как private. Это необходимо для нашего explicit-модуля. Ничто не мешает добавить этот заголовочный файл как public, но наша задача – скрыть детали реализации как можно более простыми средствами.



      Как встроить С-библиотеку в Swift-фреймворк

    4. Теперь можно импортировать явный модуль внутри фреймворка при помощи import GifSwift.CLibgif




    Swift-обёртка



    Теперь можно заняться интерфейсом нашего фреймворка. Достаточно одного класса, представляющего собой гифку с парой свойств:



    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 оборачивает низкоуровневые программные интерфейсы для обработки файлов и получает доступ к некоторым свойствам, отображая их на более удобные типы Foundation.



    Проверка



    Для того чтобы протестировать нашу библиотеку, я добавил в проект файл cat.gif:



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


    При запуске этого кода в консоли мы увидим следующее:



    "Image has size: (250.0, 208.0) and contains 44 images"



    Выводы



    Получившийся фреймворк содержит всё необходимое для использования, имеет Swift-интерфейс и по умолчанию скрывает C-код от клиентов. Впрочем, это не совсем правда. Как я писал выше, импортируя GifSwift.CLibgif, вы получите доступ ко всем закрытым модулям, однако по умолчанию такого метода инкапсуляции достаточно, чтобы скрыть детали реализации фреймворка.

    Источник: Хабр / Интересные публикации

    Категория: Веб-разработка, iOS

    Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
    Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

    Добавление комментария

    Имя:*
    E-Mail:
    Комментарий:
    Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
    Введите два слова, показанных на изображении: *