Using and sharing iOS libraries is tragically difficult given the maturity of the framework, the number of active developers, and the size of the open source community. Compare with Rails which has RubyGems and Bundler, it’s no surprise that Rails/Ruby has a resource like The Ruby Toolbox, while iOS development has…nothing?
Are iOS developers fundamentally less collaborative than Rails developers? When developing on Rails, if I ever find myself developing anything remotely reusable, I can almost always be certain that there is a gem for it (probably with an obnoxiously clever name).
I don’t think the spirit of collaboration is lacking in the iOS developer community; rather, there are a few fundamental challenges with iOS library development:
- Libraries are hard to integrate into a project. Granted, it’s not *that* hard to follow a brief set of instructions, but why can’t this process be more streamlined?
- No standardized versioning standard. Once a library is integrated into a project, there is no standard way of capturing which version of the library was used.
- No dependency specification standard (this is a big problem). Why does facebook-ios-sdk embed it’s own JSON library when there are better ones available? So many libraries come embedded with common libraries that we all use – and worse than that, who knows what version they’re using! Not only can this lead to duplicate symbols, but library developers essentially have to start from scratch instead of leveraging other existing libraries.
Of course, coming up with a naming, versioning, and dependency standard for iOS libraries and convincing everyone to adopt it is a daunting task. One possible approach is follow the example of Homebrew, a popular package manager for OS X. Homebrew turned installing and updating packages on OS X into a simple process. Instead of convincing everyone to comply to some standard, Homebrew maintains a set of formulas that helps describe commonly used packages. These formulas allow Homebrew to automate the installation process as well as enforce dependencies. This works well for Homebrew, although it puts the burden of maintaining package specifications in one place, rather then distributed as it is with Ruby gems.
There seems to be a need for some type of solution here. When we write Rails libraries, we write the readme first to help us understand what problem we’re trying to solve (Readme Driven Development). Below is the readme for an iOS packaging system called Vendor.
Vendor – an iOS library management system
Vendor makes the process of using and managing libraries in iOS easy. Vendor leverages the XCode Workspaces feature introduced with XCode 4 and is modeled after Bundler. Vendor streamlines the installation and update process for dependent libraries. It also tracks versions and manages dependencies between libraries.
Step 1) Specify dependencies
Specify your dependencies in a Vendors file in your project’s root.
source "https://github.com/bazaarlabs/vendor" lib "facebook-ios-sdk" # Formula specified at source above lib "three20" lib "asi-http-request", :git => "https://github.com/pokeb/asi-http-request.git" lib "JSONKit", :git => "https://github.com/johnezang/JSONKit.git"
Step 2) Install dependencies
vendor install git add Vendors.lock
Installing a vendor library gets the latest version of the code, and adds the XCode project to the workspace. As part of the installation process, the library is set up as a dependency of the main project, header search paths are modified, and required frameworks are added. The installed version of the library is captured in the Vendors.lock file.
After a fresh check out of a project from source control, the XCode workspace may contain links to projects that don’t exist in the file system because vendor projects are not checked into source control. Run `vendor install` to restore the vendor projects.
Other commands
# Updating all dependencies will update all libraries to their latest versions. vendor update # Specifying the dependency will cause only the single library to be updated. vendor update facebook-ios-sdk
Adding a library formula
If a library has no framework dependencies, has no required additional compiler/linker flags, and has an XCode project, it doesn’t require a Vendor formula. An example is JSONKit, which may be specified as below. However, if another Vendor library requires JSONKit, JSONKit must have a Vendor formula.
lib "JSONKit", :git => "https://github.com/johnezang/JSONKit.git"
However, if the library requires frameworks or has dependencies on other Vendor libraries, it must have a Vendor formula. As with Brew, a Vendor formula is some declarative Ruby code that is open source and centrally managed.
An example Vendor formula might look like:
require 'formula' class Three20 < Formula url "https://github.com/facebook/three20" libraries libThree20.a frameworks "CoreAnimation" header_path "three20/Build/Products/three20" linker_flags "ObjC", "all_load" vendors "JSONKit" end
Conclusion
Using iOS libraries is way harder than it should be, which has negatively impacted the growth of the open source community for iOS. Even if I was only developing libraries for myself, I would still want some kind of packaging system to help me manage the code. Vendor essentially streamlines the flow described by Jonas Williams here. Unfortunately, programmatically managing XCode projects isn’t supported natively, but people have implemented various solutions, such as Three20, Victor Costan, and XCS.
Open Questions
- Is there an existing solution for this?
- Would this be a useful gem for your iOS development?
- Why hasn’t anyone built something like this already? Impossible to build?