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, дайте знать разработчикам.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 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'
        

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

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