Dependency Management with Rubygems & Cocoapods

A lot of people use dependency management tools such as Rubygems or Cocoapods in their applications. These tools make it very convenient to pull in open source libraries and use them in your project without all the hassle of manually downloading projects, copying files, and keeping track of versions. While using these tools to pull in widely used open source libraries is very nice, don’t forget that you can also use them to manage pieces of shared code between your own private applications.

The last few projects that I have worked on included multiple applications that pass data between them in some non-standard way. In order for each application to correctly interpret data from the other, they each had to have a common definition of what structure of that data was — a protocol.

There are many ways of defining a protocol, and I won’t go into those details here, but once you’ve defined a protocol, how do you share it amongst the various parties that use it?

Including C code with a Rubygem

Most version control systems include features for pulling in bundles of shared code. Git has submodules, mercurial has subrepositories, etc, but these features are often difficult to use, clunky, and easy to mess up. If your shared code is written in C, you might not think to package it into a Rubygem, but it is very simple to do. Create a separate repo for the shared code, add a gemspec file and that’s it, you have a gem! A simple gemspec looks like this:

Gem::Specification.new do |spec|
  spec.name = "my_proj"
  spec.version = "1.0.0"
  spec.authors = ["George Washington"]
  spec.email = ["[email protected]"]
  spec.summary = "My shared library for x"
  spec.description = "My shared library for x"
  spec.homepage = "washington.com"
  spec.license = "Proprietary"
  spec.files = `git ls-files -z`.split("\x0")
  spec.executables = []
  spec.test_files = []
  spec.require_paths = []
  spec.add_development_dependency "bundler", "~> 1.6"
  spec.add_development_dependency "rake"
end

Once your code is packaged in a gem, it’s very simple to use Bundler to ensure that a specific version of your gem is always used. Bundler also makes it simple to pull in the latest version of a gem at any time by simply running the comand bundle update <gemname>.

Bringing C into iOS with Cocoapods

What if you’re writing an iOS app but you want to pull in that same C code?  Cocoapods is usually just used for bundling Objective-C, but a pod doesn’t actually have to contain any Objective-C. Just as we bundled C code as a Rubygem, we can also bundle it as a Cocoapod by adding a minimal podspec file to the repository. An example of such a podspec is here:

Pod::Spec.new do |s|
  s.name = "my_proj"
  s.version = "1.0.0"
  s.summary = "A shared library"
  s.description = "My shared library for x"
  s.homepage = "foo.com"
  s.license = 'Proprietary'
  s.author = { "George Washington" => "[email protected]" }
  s.source = { :git => "path/to/a/git/repo", :tag => s.version.to_s }
  s.platform = :ios, '7.0'
  s.requires_arc = false
  s.source_files = "some/directory/**/*.{c,h}"
  s.resource_bundles = {}
end

To pull this pod into your Xcode project just, add the following line to your podfile and run the command pod install

pod 'my_proj', git: "path/to/a/git/repo"

Of course there are many ways of including shared code into your project. There are certainly pros and cons of each technique. What’s important is to explore several options and find one that works better for your needs. If updating your dependancies is difficult, annoying, or error prone, find a different approach. Think outside the box and remember that just because your dependency doesn’t contain Ruby or Objective-C doesn’t mean you can’t use Rubygems or Cocoapods.