CocoaPods 1.4.0 — Feature packed!

CocoaPods 1.4.0 comes packed with new features such as app host support for tests, script phases, static framework support and a new Swift version DSL!

Just after few months after the release of CocoaPods 1.3.0, we unveil a new version that adds a bunch of new powerful features. Let's get right into it!

App host support for test specs

CocoaPods 1.3.0 introduced support for test specs, but initially, pod authors were unable to use test specs that required an app host. With 1.4.0, you can now specify that an app host is required in order for your tests to execute.

You can accomplish this by using setting the requires_app_host attribute within your test spec:

s.test_spec 'Tests' do |test_spec|
  test_spec.requires_app_host = true
  test_spec.source_files = 'Tests/**/*.{h,m,swift}'
end

Once enabled, CocoaPods will automatically create a stub app host and link it with your test bundle targets. This enables you to describe test sources that use UIKit in order to function. CocoaPods will re-use the same app host across all test specs integrated in your project. For iOS, the app host generated by CocoaPods creates and adds an empty view controller.

Note: App host support is not currently available for the watchOS platform.

We've also made quite a few fixes since the initial launch of test specs in 1.3.0, so we highly recommend upgrading and start using test specs!

Script Phases

Script phase integration has been a long-awaited and a powerful new feature of CocoaPods 1.4.0. Pod authors as well as app developers can now use CocoaPods to integrate script phases within their podfile or podspec.

Podfile

For app developers, each target now allows you to specify a script_phase attribute which will automatically integrate a script phase to your target. Here's an example:

source 'https://github.com/CocoaPods/Specs'

install! 'cocoapods'

platform :ios, '9.0'

target 'SampleApp' do
  pod 'Alamofire'
  script_phase :name => 'Hello World', :script => 'echo "Hello World"'
end

This will yield the following result:

This allows you to represent script phases for your app targets within your podfile without having to maintain them within your .xcodeproj. Each script phase is prefixed with [CP-User] so you can easily distinguish it as your own, and know that it has been automatically added by CocoaPods. You can have multiple script phases per single target and the order in which you specify them is the order that they will be added into your target.

Podspec

It is often the case that libraries may want to perform a few tasks during compilation. Up until now, pod authors were only able to perform a configuration task via the the prepare_command attribute, however, this only executes when your pod is first installed or updated. Additionally, during execution of the prepare_command, none of the Xcode environment variables are set as they are during build execution.

With CocoaPods 1.4.0 you are now able to specify a script_phase attribute within your podspec that executes as part of the build process of your pod, giving you full access to the Xcode build environment. Let's take a look at an example using our favorite sample pod 'CannonPodder':

Pod::Spec.new do |s|
  s.name         = 'CannonPodder'
  s.version      = '1.0.0'
  s.summary      = 'CannonPodder for the win.'
  s.homepage     = 'http://cannonpodder-corp.local/cannonpodder-lib.html'
  s.description  = 'All the Cannons'
  s.source       = { :git => 'http://somewhere.com/cannonpodder.git', :tag => '1.0.0' }
  s.license      = { :type => 'MIT', :text => 'Permission is hereby granted ...' }

  s.ios.deployment_target = '9.0'
  s.osx.deployment_target = '10.10'

  s.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"' }

  s.source_files = 'Sources/**/*.{h,m,swift}'

  s.private_header_files = 'Sources/Internal/**/*.h'
end

The above will automatically add a script phase to the CannonPodder-iOS and CannonPodder-macOS targets generated by CocoaPods. It looks like this:

If your pod supports multiple platforms, you can limit a script phase only to a specific platform, for example:

s.ios.script_phase = { :name => 'Hello iOS World', :script => 'echo "Hello iOS World"' }

The above example will only add a script phase to the CannonPodder-iOS target.

Additionally, there are times where you would like your script phases to be executed before compilation in order to perform a configuration step, such as generate some sources or perhaps perform a lint check. Support for this is included with CocoaPods 1.4.0 via the :execution_position attribute within each script_phase entry.

For example:

s.script_phase = {
  :name => 'Hello World',
  :script => 'echo "Hello World"',
  :execution_position => :before_compile
}

Generates the following:

CocoaPods will automatically add the script phase before the Compile Sources phase. This effectively allows you to control at what stage you prefer a script phase to be executed and allows you to perform different type of tasks depending on the compilation phase.

Here's a slightly more complicated example that demonstrates two script phases running at different stages:

s.script_phases = [
  { :name => 'Precompile',
    :script => '${PODS_TARGET_SRCROOT}/setup.sh',
    :execution_position => :before_compile
  },
  { :name => 'Postcompile',
    :script => 'echo "yay!"',
    :execution_position => :after_compile
  }
]

Currently, the available execution positions are :before_compile, :after_compile and :any which is the default and is reserved internally by CocoaPods. When you use :any, its behavior currently matches that of :after_compile but that might change in the future. This is why it is always recommended to specify the execution_position attribute when defining a script_phase within your podspec.

In the future, we are hoping to expand the :execution_phase attribute and provide more granular options for you specify at which point should your script phase execute.

Note: If you'd like your script phase to access any of the sources of your pod during execution, then it is highly recommended to use the ${PODS_TARGET_SRCROOT} environment variable which is set for each pod and is always relative to the path of your pod sources.

Security Warning

While script phases are a powerful addition to CocoaPods, it is crucial to understand that consuming pods that include script phases may be a high security vulnerability for many. After all, this feature essentially allows pod authors to execute code on your machine if you decide to integrate their pod into your project.

For this reason, CocoaPods will always display a warning when you install a pod or if you update a pod to a new version that adds script phases as part of it's build process.

A warning would be displayed as such (click to enlarge):

Note: This warning cannot be disabled and it will appear the first time you install a pod or when an existing pod is updated and contains script phases. It is imperative that you inspect the contents of the script phase to ensure no harmful code is executed as part of your compilation process.

In a future version, CocoaPods will prompt you before executing a prepare_command for a pod in order for you to inspect its contents.

Static Frameworks

Source static frameworks is another powerful new feature of CocoaPods 1.4.0. For the first time, it is possible to distribute sources and build them into a static framework when use_frameworks! is specified in the Podfile. Because dynamic libraries cannot depend upon static libraries, it now becomes possible to distribute pods that depend upon other static library frameworks, including the many static frameworks that are released as vendored_frameworks today.

Background

A framework is a collection of a library, headers and resources. The library may be either dynamic or static. A static library is linked at build time while a dynamic library is linked and loaded at runtime.

A dynamic library cannot depend on a static library because a static library may not have the necessary relocations to be loaded at runtime and because if multiple dynamic libraries depended upon the same static library, there would be multiple copies of the static library. The multiple copies is only a code size issue for the code itself, but it will lead to functionality issues for any data in the static library.

When Apple and Xcode refer to frameworks, dynamic is typically implied. However, the lower level Apple build tools work correctly with both dynamic and static frameworks.

Historically many binary CocoaPods have been distributed as a vendored_framework that includes a static library.

Before 1.4.0, sources could only be built as dynamic frameworks. Therefore, it was not possible to depend on many vendored_framework CocoaPods. Also, it was not possible for a vendor who delivered their pod in a binary form to convert it to a source CocoaPod and keep it as a static framework.

Usage

To enable static framework support add the following to your podspec:

s.static_framework = true

Voilà!

Swift Version DSL

With CocoaPods 1.4.0, you are now able to specify the Swift version your pod officially supports within your podspec via the swift_version attribute. Since the release of Xcode 9, multiple Swift versions are now allowed per target to be used. CocoaPods always defaulted to the Swift version your app target was using. As Swift evolves the current integration is no longer sustainable. For example, upgrading your project to Swift 4.0 does not necessarily mean that all Swift pods you consume also successfully compile with Swift 4.0 and therefore you had to wait for a new version of a pod to be published before upgrading to the latest version of Swift or resort into using a post_install hook to edit the configuration manually.

As a pod author you can now add the following to your podspec:

s.swift_version = '4.0'

This will automatically configure Swift 4.0 for the pod when it's consumed and regardless of the Swift version the app target uses. As an author you should always test and provide the known version of Swift that successfully works with your pod and update your podspec accordingly.

Note: The usage of the .swift-version file as well as the --swift-version parameter during lint is now deprecated. You may continue to use it but you will receive a warning to use the swift_version attribute instead.

We aim to improve support for this feature in the future by allowing pod authors to specify a range of Swift versions their pod supports instead of a single Swift version, but for the majority this will be sufficient. We would like to remain detached from Apple's Swift roadmap and not to have to perform releases of CocoaPods as new versions of Swift and Xcode are introduced.

Transition to https

With this release, publishing a pod using an unencrypted http source is now deprecated and it is highly recommended to begin the process of hosting your pod using an https source. A warning will appear if you attempt to publish a pod in the master repo that's not using an https source. In a future release this will become more strict and you will be unable to publish a pod without an https source.

Additionally, during installation consumers of pods with unencrypted http source will also receive a warning notifying them that the source in which the pod is hosted may be unsafe.

Development Pod Files

It is often very useful when you are developing locally on a pod (using the :path directive) to have all of its associated files (such as documentation files, its podspec and license files) to be easily available and visible so you can view and edit them. In 1.4.0, all of these files are now automatically added to the generated .xcodeproj! The following files will be automatically added to the workspace:

  • Podspec
  • Docs (in a folder which matches the glob pattern doc{s}{*,.*})
  • Custom module map (using the podspec's module_map attribute)
  • Prefix header (using the Podspec's prefix_header_file attribute)
  • License (using either the Podspec's license attribute, or automatically found with the glob pattern licen{c,s}e{*,.*})
  • README (matching the glob pattern readme{*,.*})

This should enhance your development experience when working with a pod locally.

Summary

CocoaPods 1.4.0 is a huge release for propelling iOS development forward and introduces many powerful features. We are very excited for you to try it and highly recommend you upgrade!

Special thanks to all the contributors for CocoaPods 1.4.0, we couldn't have done it without you.

Checkout the changelog to get the full list of changes.

<3