[iOS] Создание статической библиотеки Static Library с использованием Cocoapods

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Предыстория и сразу к делу

В свое время мне понадобилось обернуть написанный модуль в библиотеку. Порядочно погуглив, я нашел кучу туториалов, суть которых - создается библиотека с одним-двумя .swift - файлами. У меня же был целый проект, да еще с подами (а ля Alamofire, Moya, EasyPeasy и др), и создание библиотеки именно этим и усложнялось, было непонятно как переносить поды, нужно ли их вообще переносить и как в целом правильно сбилдить такую библиотеку.

P.S. Данный туториал не претендует на полноту теории, скорее он из раздела "как сделать правильно и чтоб работало".

P.P.S. Статья будет написана как расширение обычных туториалов для случая использования в библиотеке Static Library - cocoapods, но ее можно использовать и как просто туториал для создания Static Library.

Тестировалось на xcode 12.4, swift 5

1. Билдим библиотеку

Итак, начинаем, cоздаем новый проект в xcode:

Выбираем Static Library, жмем Next,
библиотеку назовем StaticLibraryExample - рекомендую давать название без пробелов!

Получаем пустую библиотеку с одним автоматически созданным файлом StaticLibraryExample.swift:

Теперь мы можем использовать созданный файл(или удалить его), а также создать бесконечно своих файлов и добавить их в библиотеку.

Не забываем указывать модификаторы доступа public и open для классов свойств и функций!

Шрифты, локализацию Localizable.strings, картинки Assets не вносим в библиотеку, их добавим в клиентский проект отдельно!

Если же у нас имеется проект, который нужно сделать подмодулем(как было в моем случае), то берем все необходимые файлы и копируем их из этого проекта в нашу библиотеку:

Получаем что то наподобие(автоматический созданный файл StaticLibraryExample я удалил):

Теперь попробуем сбилдить нашу библиотеку (неважно на симуляторе или устройстве), нажимаем Ctrl+B. Если в вашей библиотеке нет cocoapods зависимостей то все компилируется успешно - билдим проект на устройстве и симуляторе и переходим к пункту 2 туториала.

Если же есть, то вы получите ошибку наподобие этой:

Окей, создаем новый Podfile с необходимыми подами, например мне нужны были следующие(какие поды дело несущественное):

platform :ios, '13.0'

target 'Static Library Example' do
  pod 'Moya' 
  pod 'Alamofire'
  pod 'Kingfisher'
  pod 'EasyPeasy'
  pod 'KeychainAccess'
  pod 'SwiftPhoneNumberFormatter'
end

Устанавливаем поды - pod install. Открываем созданный workspace (на установке подов я не останавливаюсь).
Билдим библиотеку для симулятора и устройства, все должно быть успешно, если есть ошибки рекомендую в Targets -> StaticLibraryExample -> Build phases в разделе Library search paths удалить все, кроме $(inherited).

После того как все билдится успешно, файл библиотеки перестанет подсвечиваться красным, и, нажав на него правой кнопкой и выбрав Show In Finder, мы можем найти его на диске (один файл для iphoneos и второй для iphonesimulator):

В итоге на первом этапе для нас главное получить эти два файла библиотеки - один для айфона, второй - для симулятора.

2. Компилируем Universal Static Library

Под Universal Static Library имеется в виду библиотека (файл), который подходит и под устройство и под симулятор.

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

Добавляем новый таргет - Aggregator:

Жмем Next, назовем агрегатор UniversalLib_Aggregator, жмем Готово:

Далее добавим Run Script Phase - код, который будет выполняться при билде этого агрегатора:

Удаляем все что там написано:

И вставляем код, который я нашел на просторах интернета. Этот код универсален для любой вашей будущей библиотеки, только в переменную LIB_NAME необходимо вписывать имя проекта(библиотеки):

# 1: Declare variables
# Вписываем имя библиотеки:
LIB_NAME="StaticLibraryExample"

RESULT_DIR="libUniversal"
BUILD_DIR_SIMULATOR="Debug-iphonesimulator"
BUILD_DIR_DEVICE="Debug-iphoneos"
LIB_BINARY_NAME="lib$LIB_NAME.a"
LIB_BINARY_NAME_SIMULATOR="lib$LIB_NAME-simulator.a"
LIB_BINARY_NAME_DEVICE="lib$LIB_NAME-device.a"
SWIFTMODULE_DIR=$LIB_NAME".swiftmodule"

# 2: Билд
# Билдим для симулятора
xcodebuild -target $LIB_NAME -configuration ${CONFIGURATION} -sdk iphonesimulator -arch x86_64 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"

# Билдим для устройства
xcodebuild -target $LIB_NAME ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"

# 3: Операции с бинарными файлами
# Переходим в билд директорию
cd $BUILD_DIR

# Удаляем результат предыдущей сборки
rm -rf $BUILD_DIR/$RESULT_DIR 2> /dev/null

# Создаем новую директорию для библиотеки
mkdir $RESULT_DIR

# Копируем двоичный файл симулятора
# в директорию библиотеки и переименовываем его
cp ./$BUILD_DIR_SIMULATOR/$LIB_BINARY_NAME ./$RESULT_DIR/$LIB_BINARY_NAME_SIMULATOR

# Копируем двоичный файл устройства 
# в директорию библиотеки и переименовываем его
cp ./$BUILD_DIR_DEVICE/$LIB_BINARY_NAME ./$RESULT_DIR/$LIB_BINARY_NAME_DEVICE

# Создаем нашу universal библиотеку(второе название "fat")
lipo -create ./$RESULT_DIR/$LIB_BINARY_NAME_SIMULATOR ./$RESULT_DIR/$LIB_BINARY_NAME_DEVICE -output ./$RESULT_DIR/$LIB_BINARY_NAME

# Удаляем двоичные файлы симулятора и устройства:
rm ./$RESULT_DIR/$LIB_BINARY_NAME_SIMULATOR

rm ./$RESULT_DIR/$LIB_BINARY_NAME_DEVICE

# 4: Создаем .swiftmodule
#
# Создаем директорию
mkdir $RESULT_DIR/$SWIFTMODULE_DIR

# Копируем 'swiftmodule' симулятора в созданную директорию
cp -r $BUILD_DIR_SIMULATOR/$SWIFTMODULE_DIR $RESULT_DIR

# Копируем 'swiftmodule' устройства в созданную директорию
cp -r $BUILD_DIR_DEVICE/$SWIFTMODULE_DIR/* $RESULT_DIR/$SWIFTMODULE_DIR

# Удаляем билд директорию
rm -rf $PROJECT_DIR/build

Билдим наш агрегатор Ctrl+B:

После успешного билда открываем файл библиотеки:

Да, открывается наш старый файл библиотеки для устройства из папки Debug-iphoneos, нам нужно перейти на уровень вверх (например щелкнув два раза на Products):

Перейдя в Products, мы видим нашу Universal Library:

В итоге на этом этапе мы получили нашу Universal Library, все файлы которой находятся в папке libUniversal (Обратите внимание что имя универсальной библиотеки тоже задается в скрипте переменной RESULT_DIR).

3. Интегрируем Static Library в ClientApp

Под "ClientApp" имеется в виду любой проект

Итак, осталось самое простое - внедряем библиотеку в проект.

Создаем новый xcode проект, выбираем App, жмем Next, назовем проект ClientApp

Переносим нашу папку libUniversal в наш проект:

Далее добавляем файл библиотеки libStaticLibraryExample.a в проект:

Должно получиться так:

Далее нам нужно заполнить раздел Import Paths:

Переходим во ViewController.swift и импортируем нашу библиотеку:

Билдим проект Ctrl+B, если вы не используете cocoapods, то все должно сбилдиться и нашу библиотеку можно использовать! (переходите к пункту 4. Тестирование)

Если же используем поды то необходимо в ClientApp установить те же поды, что были у нас при компилировании библиотеки. Также я столкнулся с такой ошибкой:

Решается так: Чистим проект Product -> Clean Build Folder, затем открываем Terminal (необязательно по пути где наш проект), вставляем следующий код(чистим папку DerivedData):

rm -rf ~/Library/Developer/Xcode/DerivedData

Затем билдим проект снова, получаем закономерную ошибку, что наши поды не обнаружены:

Устанавливаем Pods - код для Podfile берем из Static Library (см. выше):

Открываем ClientApp.xcworkspace, и билдим проект - убеждаемся, что все успешно!

Не забудьте перенести шрифты(а также добавить данные о них в Info.plist), файлы локализации Localizable.strings, а также добавить картинки в Assets! Т.е. все те файлы которые вам не нужны в вашей библиотеке, но нужны для запуска ClientApp.

4. Тестируем

В моей библиотеке Static Library Example был такой класс:

public class TestViewController: UIViewController {

    public override func viewDidLoad() {
        super.viewDidLoad()

    }
    
    public func printLog() {
        print("The Universal Library works!")
    }

}

Как видите это простой класс для тестирования работоспособности библиотеки, который содержит в себе публичную функцию printLog().

Давайте вызовем эту функцию из нашего ClientApp:

Отлично! Библиотека работает, вы можете использовать классы, функции и свойства доступных файлов библиотеки!

Источник: https://habr.com/ru/post/586562/


Интересные статьи

Интересные статьи

Меня зовут Андрей, и я занимаюсь разработкой фронтенда на Angular для внутренних продуктов компании. Фреймворк обладает обширными возможностями, одни и те же задачи можно...
Добрый день, уважаемые читатели. Совсем недавно мне пришлось осваивать новую для себя область CI/CD, настраивая с нуля доставку скриптов миграции базы данных в одном из п...
В этой статье я расскажу вам о некоторых самых популярных фреймворках, которые помогут вам при разработке программ для компьютеров под управлением Windows/MacOS/Linux. Ес...
На работе я занимаюсь поддержкой пользователей и обслуживанием коробочной версии CRM Битрикс24, в том числе и написанием бизнес-процессов. Нужно отметить, что на самом деле я не «чист...
Технологии глубокого обучения за короткий срок прошли большой путь развития — от простых нейронных сетей до достаточно сложных архитектур. Для поддержки быстрого распространения этих технологий б...