Часть 2. Делим наш «pod» на модули. Используем чужой «pod» для разработки своего

    Введение


    Продолжая разрабатывать «pod» в определённый момент приходит понимание, что проект становится большим и похож на лапшу состоит из нескольких логических частей. Чем раньше приходит такое понимание, тем проще выделить различные сущности одного проекта в отдельные блоки. Например, одна часть проекта отвечает за установку соединения с сервером, другая сериализует объекты в JSON и наоборот, третья взаимодействует с UI и т.п. Каждую такую сущность можно выделить отдельным модулем, называемым «subspec» (для простоты изложения далее буду использовать слово «модуль»). Такой подход не только поможет гибче управлять разработкой «pod»'а, но так же даст пользователям вашего «pod»'а возможность использовать только нужные им модули, не захламляя свои проекты ненужным кодом.

    Тестовый проект


    Разрабатывать «pod» сам по себе занятие как минимум странное. Обычно его разработка ведётся в контексте какого-то проекта. А ещё лучше, когда для разработки «pod»'а ведётся специальный проект, покрытый тестами, и с полезными примерами использования в коде. Создадим же наш тестовый проект «Single View Application» в каталоге ~/Documents/PodSample/Project. После чего подключим к нему наш «pod». Для этого создаём в этом же каталоге файл Podfile. Результат должен выглядеть примерно так:

    Заполняем Podfile:
    platform :ios, '7.0'
    pod 'MyLibrary', :path => '~/Documents/PodSample/MyLibrary.podspec'
    

    Являясь разработчиком «pod»'а мы можем упростить себе жизнь и после имени явно указать путь к спецификации — файлу .podspec. В результате установщик не полезет по репозиториям «spec»'ов, а возьмёт явно указанную спецификацию по указанному пути. В принципе, можно не указывать имя файла MyLibrary.podspec — достаточно указать каталог где находится файл спецификации вашего «pod»'а, установщик сам его найдёт. Так же ничто не мешает использовать относительные пути, например:
    platform :ios, '7.0'
    pod 'MyLibrary', :path => '..'
    

    Далее закрываем XCode, или хотя бы тестовый проект MyPodExample.xcodeproj.
    Устанавливаем наш «pod»:
    $ cd ~/Documents/PodSample/Project/
    $ pod install
    Analyzing dependencies
    Fetching podspec for `MyLibrary` from `~/Documents/PodSample/MyLibrary.podspec`
    Downloading dependencies
    Using MyLibrary (0.0.1)
    Generating Pods project
    Integrating client project
    

    Установщик генерирует нам «workspace», куда добавляет тестовый проект и новый проект «Pods». Открываем workspace MyPodExample.xcworkspace и наблюдаем нечто похожее:


    Выделяем модули


    Предположим наш полезный «pod» уже умеет соединяться с сервером, обрабатывать JSON и хранить некие данные в своём внутреннем хранилище. Создадим набор классов и объявим немного методов в их публичных интерфейсах. Результат лежит на GitHub с тэгом mixed.
    Разделение «pod»'а на модули проходит в два этапа. Сначала приводится в порядок код, для чего мы разносим различные сущности по собственным файлам. После чего обновляем «spec» — описываем получившиеся модули в блоках «subspec» и проставляем их зависимости друг от друга параметром «dependency»:
    Pod::Spec.new do |s|
      s.name            = "MyLibrary"
      s.version         = "0.0.2"
      s.summary         = "Example of creating own pod."
      s.homepage        = "https://github.com/username/MyCustomPod"
      s.license         = { :type => 'MIT', :file => 'LICENSE' }
      s.author          = { "Username" => "username@mail.domain" }
      s.platform        = :ios, 7.0
      s.source          = { :git => "https://github.com/username/MyCustomPod.git", :tag => s.version.to_s }
      s.framework       = 'Foundation'
      s.requires_arc    = true
      s.default_subspec = 'Core' # Модуль по умолчанию называется Core
    
      s.subspec 'Core' do |core|
        core.source_files        = 'Classes/AKClass.{h,m}'
        core.public_header_files = 'Classes/*.h'
        core.dependency 'MyLibrary/Connection'
        core.dependency 'MyLibrary/Provider'
      end
    
      s.subspec 'Provider' do |provider|
        provider.source_files = 'Classes/AKProvider.{h,m}'
        provider.frameworks   = 'MapKit', 'CoreData' # Добавлены зависимости от фрэймворков
        provider.platform     = :ios, 5.0 # Этот модуль может запускаться и на iOS 5.0
      end
    
      s.subspec 'AccessToken' do |access_token|
        access_token.source_files = 'Classes/AKAccessToken.{h,m}'
        access_token.libraries    = 'xml2' # Зависимость от библиотеки
        access_token.xcconfig     = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' } # Где искать заголовочные файлы
      end
    
      s.subspec 'Parser' do |parser|
        parser.source_files = 'Classes/AKParser.{h,m}'
      end
    
      s.subspec 'Storage' do |storage|
        storage.source_files = 'Classes/AKStorage.{h,m}'
        storage.dependency 'MyLibrary/AccessToken'
      end
    
      s.subspec 'Connection' do |connection|
        connection.source_files = 'Classes/AKConnection.{h,m}'
        connection.dependency 'MyLibrary/Storage'
        connection.dependency 'MyLibrary/Parser'
      end
    end
    

    Для модуля «Provider» указываем необходимость подключить два фрэймворка — «MapKit» и «CoreData». А для модуля «AccessToken» необходимо подключить библиотеку «xml2», заголовочные файлы искать по указанном пути: $(SDKROOT)/usr/include/libxml2
    Говорят, что с версии «CocoaPods v0.17» модули больше не наследуют неявно значения «source_files» у базового «spec». Чтобы вернуть данную возможность необходимо завести «Core» модуль который включает базовые исходники необходимые создаваемым модулям.
    Так же существует возможность разбивать модули на подмодули:
    …
      s.subspec 'Connection' do |сonnection|
        сonnection.source_files = 'Classes/AKConnection.{h,m}'
        сonnection.dependency 'MyLibrary/Storage'
        сonnection.dependency 'MyLibrary/Parser'
        сonnection.subspec 'Cache' do |cache|
          cache.source_files = 'Classes/AKCache/*.{h,m}'
        end
      end
    …
    

    Так же можно указать платформу и её версию на которых модуль сможет работать. В данном случае мы указали, что модуль Provider запустится даже на «iOS 5.0».

    Проверяем синтаксис:
    $ pod spec lint ~/Documents/PodSample/MyLibrary.podspec --quick
    
     -> MyLibrary (0.0.2)
    
    Analyzed 1 podspec.
    
    MyLibrary.podspec passed validation.
    

    И коммитим изменения в git, ставим тэг и отправляем на GitHub:
    $ git add MyLibrary.podspec && git commit -m "Spec sliced on subspecs"
    $ git add -A && git commit -m "Sliced code"
    $ git tag "0.0.2"
    $ git push origin master --tags
    

    Проверяем весь проект:
    $ pod spec lint ~/Documents/PodSample/MyLibrary.podspec
    
     -> MyLibrary (0.0.2)
    
    Analyzed 1 podspec.
    
    MyLibrary.podspec passed validation.
    


    Использование модуля


    Чтобы использовать только необходимые модули достаточно их перечислить в Podfile:
    platform :ios, '7.0'
    pod 'MyLibrary/Storage'
    pod 'MyLibrary/Parser'
    

    Если не указать ни одного модуля:
    platform :ios, '7.0'
    pod 'MyLibrary'
    

    то pod install установит исходники всех модулей. Если же в данном случае нужно установить только исходники «Core» модуля, то в «spec»'е указывается его имя в параметре «default_subspec» — см. пример выше.

    Укажем в нашем Podfile зависимость тестового проекта от модуля «Provider» и заодно укажем нужную нам сейчас версию — «0.0.2»:
    platform :ios, '7.0'
    pod 'MyLibrary/Provider', :path => '..'
    pod 'MyLibrary/Connection', :path => '..'

    И обновим зависимости проекта:
    $ cd ~/Documents/PodSample/Project/
    $ pod update
    Analyzing dependencies
    Fetching podspec for `MyLibrary` from `..`
    Fetching podspec for `MyLibrary` from `..`
    Downloading dependencies
    Installing MyLibrary (0.0.2)
    Generating Pods project
    Integrating client project
    


    Подключение стороннего «pod»'а


    Иногда бывает так, что разработка «pod»'а доходит до момента, когда хочется написать велосипед поиспользовать существующий «pod». Нетрудно догадаться, что для этого достаточно указать зависимости всего «spec»'а или его модуля(ей) от нужных «pod»'ов. Добавим зависимость от AFNetworking в модуль «Connection»:
    Pod::Spec.new do |s|
      s.name            = "MyLibrary"
      s.version         = "0.0.3"
      s.summary         = "Example of creating own pod."
      s.homepage        = "https://github.com/username/MyCustomPod"
      s.license         = { :type => 'MIT', :file => 'LICENSE' }
      s.author          = { "Username" => "username@mail.domain" }
      s.platform        = :ios, 7.0
      s.source          = { :git => "https://github.com/username/MyCustomPod.git", :tag => s.version.to_s }
      s.framework       = 'Foundation'
      s.requires_arc    = true
      s.default_subspec = 'Core' # Модуль по умолчанию называется Core
    
      s.subspec 'Core' do |core|
        core.source_files        = 'Classes/AKClass.{h,m}'
        core.public_header_files = 'Classes/*.h'
        core.dependency 'MyLibrary/Connection'
        core.dependency 'MyLibrary/Provider'
      end
    
      s.subspec 'Provider' do |provider|
        provider.source_files = 'Classes/AKProvider.{h,m}'
        provider.frameworks   = 'MapKit', 'CoreData' # Добавлены зависимости от фрэймворков
        provider.platform     = :ios, 5.0 # Этот модуль может запускаться и на iOS 5.0
      end
    
      s.subspec 'AccessToken' do |access_token|
        access_token.source_files = 'Classes/AKAccessToken.{h,m}'
        access_token.libraries    = 'xml2' # Зависимость от библиотеки
        access_token.xcconfig     = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' } # Где искать заголовочные файлы
      end
    
      s.subspec 'Parser' do |parser|
        parser.source_files = 'Classes/AKParser.{h,m}'
      end
    
      s.subspec 'Storage' do |storage|
        storage.source_files = 'Classes/AKStorage.{h,m}'
        storage.dependency 'MyLibrary/AccessToken'
      end
    
      s.subspec 'Connection' do |connection|
        connection.source_files = 'Classes/AKConnection.{h,m}'
        connection.dependency 'MyLibrary/Storage'
        connection.dependency 'MyLibrary/Parser'
        connection.dependency 'AFNetworking' # Добавлена зависимость от внешнего "pod"'а
      end
    end
    

    При интеграции могут возникнуть проблемы с версиями платформ, используемых вами и внешними «pod»'ами. Успешно решив их обновляем зависимости (крайне желательно при закрытом XCode или проекте):
    $ cd ~/Documents/PodSample/Project/
    $ pod update
    Analyzing dependencies
    Fetching podspec for `MyLibrary` from `..`
    Fetching podspec for `MyLibrary` from `..`
    Downloading dependencies
    Installing AFNetworking (2.0.1)
    Installing MyLibrary (0.0.3)
    Generating Pods project
    Integrating client project
    

    Скачаются и подключатся необходимые «pod»'ы, настроится workspace. Теперь можно в своём «pod»'е использовать силу всех прочих покемонов «pod»'ов!

    Продолжение следует.

    Запланировано:
    Часть 3. Публикация своего «pod»'а. Общий репозиторий и личный.

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 0

    Only users with full accounts can post comments. Log in, please.