Part 2. We divide our "pod" into modules. We use someone else's “pod” to develop our own

    Introduction


    Continuing to develop the “pod” at some point, it comes to the understanding that the project is getting large and looks like noodles and consists of several logical parts. The sooner this understanding comes, the easier it is to separate the various entities of one project into separate blocks. For example, one part of the project is responsible for establishing a connection to the server, the other serializes objects in JSON and vice versa, the third interacts with the UI, etc. Each such entity can be distinguished by a separate module called “subspec” (for simplicity of presentation, I will use the word “module” below). This approach will not only help to more flexible manage the development of “pod”, but it will also give users of your “pod” an opportunity to use only the modules they need, without cluttering up their projects with unnecessary code.

    Test project


    Developing a “pod” in itself is at least strange. Usually its development is carried out in the context of a project. And even better, when a special project is being developed for the development of “pod”, it is covered with tests and with useful examples of use in the code. Let's create our test project “Single View Application” in the ~ / Documents / PodSample / Project directory . Then connect to it our "pod". To do this, create a Podfile file in the same directory . The result should look something like this:

    Fill the Podfile :
    platform :ios, '7.0'
    pod 'MyLibrary', :path => '~/Documents/PodSample/MyLibrary.podspec'
    

    Being the developer of “pod”, we can simplify our life and, after the name, explicitly indicate the path to the specification - the .podspec file . As a result, the installer will not get into the spec repositories, but will take the explicitly specified specification on the specified path. In principle, you can omit the file name MyLibrary.podspec - just specify the directory where the specification file for your “pod” is located, and the installer will find it. Also, nothing prevents the use of relative paths, for example:
    platform :ios, '7.0'
    pod 'MyLibrary', :path => '..'
    

    Next, close Xcode, or at least the test project MyPodExample.xcodeproj .
    Install our "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
    

    The installer generates a “workspace” for us, where he adds a test project and a new “Pods” project. Open the workspace MyPodExample.xcworkspace and observe something similar:


    Select modules


    Suppose our useful “pod” is already able to connect to the server, process JSON and store some data in its internal storage. Create a set of classes and declare a few methods in their public interfaces. The result is on GitHub with the tag mixed .
    The separation of “pod” into modules takes place in two stages. First, the code is put in order, for which we distribute various entities in our own files. Then we update “spec” - we describe the resulting modules in “subspec” blocks and assign their dependencies to each other with the “dependency” parameter:
    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
    

    For the “Provider” module, we indicate the need to connect two frameworks - “MapKit” and “CoreData”. And for the AccessToken module, you need to connect the xml2 library, look for the header files at the specified path: $ (SDKROOT) / usr / include / libxml2
    They say that from version CocoaPods v0.17 the modules no longer implicitly inherit the value source_files at the base "spec". To return this opportunity it is necessary to get a “Core” module which includes the basic source code needed for the modules being created.
    It is also possible to break modules into submodules:
    …
      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
    …
    

    You can also specify the platform and its version on which the module can work. In this case, we indicated that the Provider module will start even on "iOS 5.0".

    Check the syntax:
    $ pod spec lint ~/Documents/PodSample/MyLibrary.podspec --quick
     -> MyLibrary (0.0.2)
    Analyzed 1 podspec.
    MyLibrary.podspec passed validation.
    

    And commit the changes to git, put the tag and send it to 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
    

    Check the whole project:
    $ pod spec lint ~/Documents/PodSample/MyLibrary.podspec
     -> MyLibrary (0.0.2)
    Analyzed 1 podspec.
    MyLibrary.podspec passed validation.
    


    Module usage


    To use only the necessary modules, it is enough to list them in the Podfile :
    platform :ios, '7.0'
    pod 'MyLibrary/Storage'
    pod 'MyLibrary/Parser'
    

    If you do not specify a single module:
    platform :ios, '7.0'
    pod 'MyLibrary'
    

    then pod install will install the sources of all modules. If in this case it is only necessary to install the source code for the “Core” module, then in “spec” it is indicated its name in the parameter “default_subspec” - see the example above.

    We indicate in our Podfile the dependence of the test project on the Provider module and at the same time indicate the version we need now - 0.0.2:
    platform :ios, '7.0'
    pod 'MyLibrary/Provider', :path => '..'
    pod 'MyLibrary/Connection', :path => '..'

    And update the project dependencies:
    $ 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
    


    Connecting a third-party pod


    Sometimes it happens that the development of “pod” 'comes to the point where you want to write a bike to use the existing “pod”. It is easy to guess that for this it is enough to indicate the dependences of the entire “spec” and its module (s) on the necessary “pods”. Add a dependency on AFNetworking to the Connection module:
    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
    

    Integration may cause problems with versions of platforms used by you and external pods. Having successfully solved them, we update the dependencies (it is highly desirable when the Xcode or project is closed):
    $ 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
    

    The necessary “pods” are downloaded and connected, the workspace is configured. Now you can use the power of all other Pokemon pods in your “pod”!

    To be continued.

    Planned:
    Part 3. Publishing your “pod” a. Shared repository and personal.

    Also popular now: