Разработчики часто сталкиваются с типовыми задачами, которые появляются в новых проектах. Постепенно накапливается база вспомогательного кода, которая собирается в библиотеки и переносится из проекта в проект. И чем больше проектов, тем тяжелее становится поддерживать такие библиотеки.
Скорее всего у вас есть излюбленный набор категорий над NSFoundation/UIKit для удобства или что-то для работы с сетью или с пуш уведомлениями. Логично было бы оформить этот код в библиотеку, хранить в одном месте и подключать с помощью пакетного менеджера, например cocoapods. Cocoapods существенно облегчает интеграцию зависимостей в проект. Он все делает за вас, создает нужные таргеты и интегрирует их проект. Такая легкость и простота приводит к тому, что часто встречаются поды, которые состоят только из исходников и примера интеграции. Такой подход рекомендуется официально. И, фактически, этого достаточно для интеграции через cocoapods, но недостаточно для разработки и поддержки. Для этого лучше всего подойдет xcode проект, с помощью которого можно собирать библиотеку.
Xcode проект
Мы назовем нашу тестовую библиотеку Utilities
и создадим для нее Xcode проект. Преимущества такого подхода очевидны:
- можно настроить необходимые параметры сборки (
-Wall
/-Weverything
, iOS7+/iOS8+ итд) и работать над ошибками во время отладки (а не во время интеграции) - можно добавить юнит тесты
- кроме интеграции через cocoapods, есть возможность интеграции через Carthage, вручную или гит сабмодулями.
Static Library vs Framework
При создании проекта необходимо выбрать что мы будем собирать: библиотеку (static library) или фреймворк (dynamic framework). Основное их отличие в том, что фреймворк не совместим с iOS7, а библиотека не поддерживается свифтом.
В нашем случае поддержка iOS7 не нужна, поэтому подойдет фреймворк:
Структура файлов
После создания проекта необходимо изменить структуру файлов в проекте так, чтобы библиотека была совместима с пакетными менеджерами. Самый часто используемый пакетный менеджер это, конечно же, Cocoapods. Остальные используются меньше, но и поддерживать их проще, так как нет необходимости каждый раз после апдейта загружать в хранилище обновленный podspec
.
Сocoapods
Для поддержки cocoapods необходимо:
- добавить файл
podspec
- добавить файл
LICENSE
В этом вам может помочь команда
pod spec create
Кроме того, если вы не хотите делать библиотеку публичной, нужно создать приватное хранилище пакетов. И добавить в него файл podspec
:
pod repo add myrepostorage https://github.com/user/podspecs
pod repo push myrepostorage Utilities.podspec
Пример файла podspec
:
Pod::Spec.new do |s|
s.name = "Utilities"
s.version = "0.0.1"
s.summary = "My test library"
s.homepage = "https://github.com/user/utilities"
s.authors = "author name"
s.license = "MIT"
s.platform = :ios, "8.0"
s.source = { :git => "https://github.com/user/utilities.git", :tag => "#{s.version}" }
s.source_files = "Sources/**/*"
end
Swift Package Manager
Для поддержки Swift PM необходимо:
- положить исходники в папку
Sources
- добавить файл
Package.swift
Пример Package.swift
:
import PackageDescription
let package = Package(
name: "Utilities"
)
Carthage
Для поддержки carthage необходимо чтобы:
- файл проекта
xcodeproj
находился в корневой папке - схема в проекте была отмечена как shared
Кроме того не помешает:
- добавить описание в
README.md
для гитхаба - положить юнит тесты в папку
Tests
- положить вспомогательные файлы в папку
Supporting Files
В результате должна получиться следующая структура:
В папке Supporting Files
лежит файл Defaults.xcconfig
, который применяет настройки компиляции к проекту. Его удобно положить во все подобные библиотеки, чтобы не настраивать все руками. У нас есть желание сделать строгие настройки, чтобы код был лучше, например такие:
CLANG_WARN_ENUM_CONVERSION = YES
CLANG_WARN_INT_CONVERSION = YES
CLANG_WARN_CONSTANT_CONVERSION = YES
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_ASSIGN_ENUM = YES
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
GCC_WARN_ABOUT_MISSING_NEWLINE = YES
GCC_WARN_SHADOW = YES
GCC_WARN_SIGN_COMPARE = YES
GCC_TREAT_WARNINGS_AS_ERRORS = YES
WARNING_CFLAGS = -Weverything -Wno-objc-missing-property-synthesis -Wno-gnu -Wno-float-equal -Wno-nullable-to-nonnull-conversion -Wno-auto-import -Wno-direct-ivar-access
Зависимости
Если библиотека имеет зависимость, например от AFNetworking
, то интегрировать эту зависимость можно как обычно, привычными методами. Например, с помощью cocoapods или carthage ее можно подключить к нашей библиотеке. Ещё нужно не забыть указать эту зависимость в соответствующих файлах конфигурации для пакетных менеджеров. Для cocoapods это Podfile
, а для carhage это Cartfile
.
Итог
В результате получаем библиотеку, которую легко поддерживать и интегрировать с помощью любого пакетного менеджера.