Пользователь
0,0
рейтинг
11 октября 2013 в 16:17

Разработка → Xcode: управляем зависимостями собственных библиотек в проектах. Cocoapods advanced

Cocoapods – это менеджер зависимостей библиотек в Xcode проектах. Я не буду рассказывать, как с его помощью подключить в проект уже существующую библиотеку, информации достаточно, в том числе и эта статья на Хабре. Я расскажу что делать, если вы не нашли нужную вам библиотеку в списке, или, что ещё хуже, вы хотите создать свою библиотеку и, как вариант, не делать её доступной.

Часть I: подключаем библиотеки через podfile

Для начала стоит посмотреть, какие возможности даёт нам Cocoapods, для подключения библиотеки в проект (через podfile):

  1. Подключить библиотеку из списка поддерживаемых:
    pod 'Reachability'
    pod 'AFNetworking/Reachability'
    pod 'JSONKit',       '~> 1.4'
    
    Самый простой способ (он же основной), при этом можно указать привязку к конкретной версии и подключить не всю библиотеку, а только её часть (через subspec)

  2. Подключить библиотеку, но при этом указать путь к спецификации
    pod 'ZipKit', :podspec => 'ZipKit.podspec'
    
    Можно использовать тогда, когда существующая Cocoapods спецификация вас каким-то образом не устраивает (например, в спецификации к библиотеке стоит iOS 6.1, а у вас в проекте Deployment target выставлен в 6.0). Сохраняем себе спецификацию, редактируем её под свои нужды, сохраняем в корень проекта – в результате у вас всё работает, и при этом нет необходимости добавлять потенциально вредные изменения в публичную спецификацию.

  3. Подключить библиотеку по локальному пути (вместе со спецификацией):
    pod 'SuperLibrary', :path => 'Submodules/SuperLibrary'
    
    Этот вариант уже интереснее, так как можно указать путь к совместному коду (subversion external, git submodule...). При таком способе файлы библиотеки включаются в проект со ссылками на этот путь, что позволяет нам редактировать библиотеку и сохранять измения в системе контроля версий. Более подробно вернёмся к этому позже

  4. Подключить библиотеку (вместе со спецификацией), расположенную в системе контроля версий, или просто по ссылке на архив:
    pod 'SuperLibrary', :git => 'git@bitbucket.org:bestcompany/SuperLibrary.git', :branch => 'development'
    
    Основное отличие от предыдущего пункта в том, что редактировать исходный код библиотеки уже нельзя (технически можно, однако при установке зависимостей в проект будут просто добавлены копии файлов, которые никак не будут ссылаться на оригинал и будут переписаны на оригинальные файлы при последующем обновлении зависимостей)


Часть II: пишем спецификацию к собственной библиотеке – «как 2 байта переслать»

Создать спецификацию просто:
pod spec create SuperLibrary
Открываем сгенерированный файл, заполняем сгенерированные разделы, читая комментарии, при затруднениях обращаемся к документации.
И здесь стоит вспомнить про такой механизм, как модули библиотеки (subspec). В кратце, разбиваем нашу библиотеку на некоторые логические модули (в том числе связанные между собой), описываем ресурсы, исходные коды, зависимости по-отдельности, например:
s.subspec 'Data' do |ds|
    ds.source_files = 'Data/*.{h,m}', 'Data/Categories/*.{h,m}', 'Data/Objects/*.{h,m}'
    ds.resources = 'Data/SuperLibrary.xcdatamodeld'
    ds.dependency 'MagicalRecord'
    ds.dependency 'SuperLibrary/Resources'
  end
Доступ к модулю осуществляется через MasterSpec/Subspec, одни модули внутри могут зависеть от других внутри одной спецификации, допускается многоуровневая вложенность. Осталось указать модуль, который будет подключен по умолчанию, например
s.default_subspec = 'Controllers'
И всё, библиотеку можно подключать по частям, например только сетевое ядро, не затрагивая ресурсы и Unit-тесты.
Несколько советов:
Схема базы данных (*.xcdatamodeld и иже с ними) это ресурс а не исходный код, с недавней версии cocoapods подключается нормально, в том числе вместе с версиями схемы.
Зависимости своей библиотеки от других желательно прописывать без привязки к конкректной версии (за исключением, например, Facebook-iOS-SDK, API которой меняется слишком часто).

Часть III: свой репозиторий спецификаций «с шахматами и поэтессами»

Библиотеки подключать знаем как, создавать спецификацию умеем, идея версий библиотек нам нравится, но делиться библиотеками не будем. Весьма частая ситуация в маленьких и больших компаниях, есть много проектов, на них используется совместный код, хорошо бы их оформить как библиотеки и работать с версиями так же просто, как и с обычными cocoapods библиотеками. И тут на помощь приходит приватные репозитории. Что нам для этого потребуется:
  1. Создаём новый репозиторий для спецификаций, который будет доступен вашей команде. Плохая новость, для репозитория спецификаций поддерживается только git. (Хорошая новость, на git должен быть только репозиторий спецификаций, сами библиотеки по-прежнему будут доступны по git/svn или даже по обычной ссылке на архив). Добавляем его в cocoapods простой командой из консоли:
    pod repo add Private-Cocoapods git@bitbucket.org:bestcompany/cocoapods-specs.git
    
    Осталось дело за малым, создаём в корне этого репозитория папку с именем библиотеки, в ней создаём папку с версией библиотеки, куда уже помещаем непосредственно саму спецификацию.

    Всё, осталось отправить эти изменения на репозиторий и последующие команды pod install (или pod update) будут работать с нашей библиотекой так же, как и с официальной, то есть подключать pod можно будет просто по имени библиотеки.


Часть IV: Подключаем всё вместе, или как можо построить процесс разработки

И один из сценариев, как с этим можно успешно работать. Предпосылка: ведётся разработка нескольких продуктов (одновременно или нет, не важно), в приложениях есть совместно используемый код (библиотеки), разработка каждой библиотеки ведётся в собственной ветке репозитория.
Итак, нам надо, чтобы в корне каждой библиотеки лежал актуальный podspec файл, версия в podspec файле идёт с постфиксом dev, параметр source ссылается на текущую ветку, например:
Pod::Spec.new do |s|
  s.name         = "SuperLibrary"
  s.version      = "1.0.5-dev"
  s.source       = { :git => "ssh://git@bitbucket.org:bestcompany/my-super-library.git"}
Таким образом, при подключении этой версии непосредственно из репозитория, мы будем иметь пометку dev, говорящую о том, что версия не готова. После тестирования этот библиотеки создаём tag с именем версии (проверить версию, и, если необходимо, поднять минорную/мажорную версию), копируем podspec файл в наш собственный репозиторий спецификаций, убирая приставку dev из версии и указав конкретный таг в репозитории, который мы только что создали:
Pod::Spec.new do |s|
  s.name         = "SuperLibrary"
  s.version      = "1.1.0"
  s.source       = { :git => "ssh://git@bitbucket.org:bestcompany/my-super-library.git", :tag => "SuperLibrary_v1.1.0" }
Всё, осталось в репозитории библиотеки выставить версию на один больше не убирая постфикс dev (1.1.1-dev в нашем случае) и отправить все изменения в репозиторий.
Разработка, обычно, это процесс бесконечный, и необходимость править библиотеки возникает очень часто. Для таких случаев можно всегда хранить ссылку на текущую версию библиотеки в репозитории через сабмодуль в Git (external в Subversion). При этом в podfile конкретного продукта всегда указана стабильная версия (podspec хранится на нашем репозитории спецификаций), но рядом закоментированная строчка на текущую версию:
	#pod 'SuperLibrary', :path => 'Submodules/SuperLibrary'
	pod 'SuperLibrary'
В случае необходимости внести изменения в библиотеку, убираем комментарий со строчки, обновляем библиотеку до последней версии в репозитории, делаем pod update в консоли и всё, можно смело изменять и тестировать. Перед подготовкой приложения в публикацию стоит зафиксировать все версии библиотек (то есть создать новые версии для всех изменённых библиотек и подключить их из нашего репозитория спецификаций). Всегда проверяйте podfile.lock на то, какие версии библиотек используются, наш постфикс -dev очень помогает определить, что версия библиотеки может быть не протестирована.
И да, делать pod update как часть процесса сборки приложения явно не стоит (по-крайней мере на стадии подготовки версии к релизу, так как из-за обновления библиотеки обязательно что-то перестанет работать в последний момент).

P.S. Cocoapods обновляется постоянно, исправляются ошибки, добавляются новые возможности (и новые ошибки). Если у вас что-то перестало работать (а такое случается), не поленитесь, пожалуйста, найти причину, и, если дело именно в cocoapods, дайте знать разработчикам.
pcholberg @pcholberg
карма
5,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (5)

  • 0
    s.version      = "1.0.5-dev"
    s.source       = { :git => "ssh://git@bitbucket.org:bestcompany/my-super-library.git"}
    

    Так лучше не делать, т.к. не сохранится информация, какой конкретно коммит библиотеки был использован в конкретной ревизии проекта. (Если CocoaPods научился эту информацию сохранять — поправьте меня.)
    Поэтому dev-версии библиотек подключаем всегда через субмодуль по способу 3:
    pod 'SuperLibrary', :path => 'Submodules/SuperLibrary'
    


    А в остальном всё так же: свой репозиторий со спецификациями и т.д.
    • 0
      Это перестраховка как раз для способа 3
      • 0
        А если подключать по способу 3, то ни s.version, ни s.source ни на что не влияют. Ну, строго говоря, s.version пропишется в Podfile.lock и дополнительно напомнит, что используется dev-версия, но это итак вроде очевидно из того факта, что библиотека подключена как субмодуль.

        В общем, лучше не комбинировать такой .podspec со способом 2, и не применять способ 4, если хотим сохранить информацию о соответствии ревизий проекта и библиотеки.
        • 0
          Абсолютно верно: это нужно только для переключения сабмодуль/стабильная версия.
  • +1
    Ещё небольшой tip & trick.
    Когда активно разрабатываешь библиотеку в субмодуле, часто приходится делать pod install. Чтобы этот процесс происходил быстрее, и CocoaPods пропускал шаги обновления репозиториев и интеграции в проект, удобно добавить алиас в ~/.profile:
    alias 'pod-qi'='pod install --no-repo-update --no-integrate'
    

    Он же полезен и при работе оффлайн.

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