CocoaPods, or how I learned to stop worrying and love Objective-C dependency management

· 5 minute read

Not invented here syndrome” is a well known software development mentality, where programmers favor building from scratch over using code made available by a third-party. Sometimes this pays off, as you’re able ensure that what you’re working on is of the upmost quality and continues to evolve in a way that doesn’t diverge from your needs. This mindset also has its drawbacks. In some cases, your need is complex and an attempt to build it yourself might not produce as good of a result as a battle-tested, third-party component already being run in production by some of the most popular apps on the App Store. In other cases, it may simply not be worth it to “reinvent the wheel,” or spend time solving a solved problem.

Most seasoned developers will agree that while building everything yourself isn’t the best use of one’s time, relying too heavily on third-party code isn’t wise either. A line in the sand must be drawn, where development is optimized by pulling in dependencies where appropriate to spend the majority of your time working on what makes your application unique.

Once you’ve decided that you’d like to include third-party code, you’re now faced with two separate challenges: finding this code, and integrating it with your project. The rise of GitHub, founded in 2008, brought open-source to the mainstream in a way that it arguably never had been before. While GitHub made it far easier to find high-quality dependencies, actually adding one to your iOS or OS X app still required a manual, error-prone set of steps. A dependency manager could help alleviate exactly this.

Most languages/platforms have at least one popular dependency manager: Java has Ivy and Maven, Node.js has NPM, and Ruby has RubyGems and Bundler. Objective-C hasn’t historically had a successful dependency manager of its own, but a tool called CocoaPods has made huge strides in recent years towards filling this void.

Of course, “dependency” doesn’t specifically imply something built by a third-party. CocoaPods is just as beneficial if you want to reuse your own code across all of the different applications that you’re working on.

Generally speaking, a dependency manager is responsible for:

  • Downloading the versions of the libraries that your project depends on. This includes fetching the dependencies of your dependencies, and so on and so forth
  • Resolving conflicts that arise if the dependency tree includes multiple different versions of the same library
  • Integrating the dependencies with your project

This third point above is the primary reason why an Objective-C dependency manager has not existed up until now.

The process of turning Objective-C code into runnable iOS and OS X applications involves some complication. Developers leverage a number of frameworks provided by Apple while building their applications, such as UIKit for iOS user interface elements and Core Location for accessing the user’s whereabouts. For an Objective-C application to be successfully built, it must be linked against any frameworks that its code references. This means that if an application includes dependencies, and the dependencies need to be linked against certain frameworks, the application would need to know that and be configured as such.

Similarly, different pieces of code may need to be compiled with different options. iOS devices contain a limited amount of memory and apps need to relinquish memory that is no longer in use, less they run the risk of being terminated by the operating system. Prior to iOS 5, developers needed to manually manage this memory, a time-consuming and error-prone process. iOS 5 introduced Automated Reference Counting (ARC), a compiler enhancement that allowed developers to no longer worry about the majority of cases in which memory would have previously had to have been manually managed. If a new application it built using ARC, yet includes a dependency that was not written for ARC, the application would need to be configured to compile the dependency differently than the rest of the code.

Xcode is the program that Apple provides to developers to create their own iOS and OS X applications with. All of the information about how a specific application is built, from which frameworks need to be linked against to which compiler options need to be used for which files, is stored inside an Xcode project file. Any worthwhile Objective-C dependency manager would need to know how to modify an Xcode project file in order to properly configure an application to be built along with all of its dependencies.

Here lies the majority of the complexity, and the reason why its taken so long for an Objective-C dependency manager to come into existence: Xcode project files are undocumented. To build a tool that exists alongside Xcode, one would need to reverse engineer how the project file itself is generated. CocoaPods has emerged as the de facto Objective-C dependency manager by doing exactly this.

Written in the Ruby programming language, CocoaPods knows how to read and write Xcode’s esoteric project file format to finally deliver the functionality that other development ecosystems have long benefited from. The project was started by Eloy Durán, a Dutch programmer who was working at a app consultancy at the time. Durán understood how much time that a dependency manager would allow him to save for his clients, and admits that he had hoped someone else would build one for Objective-C before not being able to wait anymore and starting it himself. The first public release of CocoaPods occurred in 2011 yet already includes over 3,000 open-source libraries, known as “pods.” Durán is joined by 10 other core team members while over 140 people have contributed to the project in total.

Durán says that “besides being a dependency manager, CocoaPods really tries to create an ecosystem for open-source libs to flourish.” One such way that CocoaPods accomplishes this is via CocoaDocs. As pods are created and updated, documentation for them is automatically generated and published, providing a useful reference for developers.

CocoaPods is still technically beta software, and the core team is rightfully conscious about keeping the project focused as they work towards a 1.0 release. One important feature that remains in the works is better access control, to ensure that pods are only updated by their rightful owners. In November, CocoaPods added support for plugins, allowing other developers to extend the project’s functionality as they see fit without resulting in a monolith.

One such example is pod-try, a plugin bundled with CocoaPods by default. pod-try allows a user to open a pod’s demo project with a single command, providing a quick way to both vet a library’s quality as well as learn how to use it. Durán admits that he doesn’t use a lot of third-party libraries himself, which sounds counterintuitive but actually makes a lot of sense. CocoaPods makes it easy to try out different dependencies without overhead, to figure out which, if any, is the right fit for your project.

Between helping to create a thriving open-source Objective-C ecosystem, and providing tools that help ensure quality rather than sprawling dependency graphs, the goals behind CocoaPods are noble ones. Any developer should be able to benefit from and get behind them, regardless of where you fall on the “not invented here” spectrum.


Originally published in The Loop magazine, Issue 20.

Objective-C libraries and Unix philosophy

· 2 minute read

Caleb Davenport recently asked the following question on Twitter:

He knew that this functionality already exists in a number of larger libraries, but couldn’t find exactly what he was looking for; something that did query string serialization correctly and nothing else. So he made it.

I’ve long adored the Node.js community in part for their steadfast belief that modules in NPM should embody the Unix philosophy of “do one thing and do it well.” Node modules are championed if they export a single function and deemed to be inadequate if they expose more surface area than is needed.

Objective-C doesn’t yet have the ecosystem of third-party libraries that languages such as JavaScript or Ruby do, nor even a consensus among developers that using third-party code is a good idea in the first place. This conservative view towards dependency inclusion makes a lot of sense, as fixing bugs in Objective-C software involves getting a new binary onto your customers’ devices, with an opaque and potentially lengthy App Store approval process often standing in the way.

For this very reason, a shift towards building more focused, modular Objective-C libraries should be welcomed. Small modules allow even the most conservative of developers to benefit by including open-source components that are easy to understand, thoroughly tested, and expose only the most pertinent of APIs. The more a library does, the harder it is to understand what it’s doing and how well it’s doing it, making it less likely to be used and far less valuable to the community as a whole.

In my experience, small modules also foster much better collaboration. It’s easy to be tentative about contributing to a large library, as it’s harder to fully understand the architectural underpinnings and how the maintainer plans to structure it as it evolves. Caleb’s library, to the contrary, was so small in scope that I felt comfortable opening a pull request almost immediately. Small modules have explicit goals which makes it easy for others to help.

Has CocoaPods played a role in this shift? I think so. Though CocoaPods allows one to publish a sprawling monolith as easily as it does a single function, lower barriers to create and consume libraries is a win for modularity in general. The more painful it is to integrate a dependency, the fewer times you’ll opt to do so, making larger libraries seem attractive. CocoaPods makes integration so easy that there’s no excuse not to break your code up into small modules.

Creating a Objective-C library will always have more overhead than for example, a Node module, due to header files, project files, etc. But I’m thrilled with the direction that we seem to be moving in as a community.

Java features I miss in Objective-C

· 4 minute read

Packages

One of Objective-C’s biggest annoyances is the lack of proper namespacing. It’s hard to believe, after years of programming in other languages, that I now prefix every class name I ever type with a two if not three-character code.

Java solves this problem by grouping classes together in units known as packages. At the top of a class file, the package that the class belongs to is declared, in reverse DNS format:

package com.tumblr.models;

Once included in a package, the class’s name can be something totally generic, like Post. When one class imports another, it does so using the fully-qualified name:

import com.tumblr.models.Post;

If a class imports more than one Post class, the fully-qualified name is again used to avoid ambiguity when declaring and instantiating:

com.tumblr.models.Post post = new com.tumblr.models.Post();

This is rarely necessary in practice, however.

Generics

Generics are a language feature that allow “type variables” to be used in class definitions. Let’s look at an example:

class ArrayList<E> {
    E get(int index);
    void add(E element);
}

E represents a parameterized type that can differ across Array instances. The result is a glorious, best-of-both-worlds situation where arrays remain infinitely flexible yet still provide the compiler with information that it can use to help keep you from making a mistake.

ArrayList<String> stringArray = new ArrayList<String>();
stringArray.add(string);

ArrayList<Integer> integerArray = new ArrayList<Integer>();
integerArray.add(string); // Won't compile

Subclasses of generic classes don’t necessarily have to remain generic. Instead, they can specify a concrete type argument for the parameterized type:

class IntegerArrayList extends ArrayList<Integer> {
}

In this case, there’s no need to re-declare get or add to specify the argument or return types. Trying to pass or return return something other than an Integer will result in a compiler error.

Java even supports type wildcards, which allow a type argument to specify either a lower or upper bound:

List<? extends Number>  // Type must be Number or a subclass
List<? super Number>    // Type must be Number or its superclass

It’s worth noting that generics in Java are simply a compile-time construct, which allowed them to be introduced in Java 5 while maintaining backwards compatibility with older runtimes. I’m unaware of a technical reason why Objective-C couldn’t go the same route.

Annotations

Annotations are a great feature that allows metadata to be added to classes, methods, variables, parameters, and packages. Annotations can be both used by developer tools at compile-time as well as at run-time, by your code itself.

First, let’s use an annotation to tell the compiler that we’re overriding a method that belongs on our superclass (don’t be confused by the @ symbol which obviously has all sorts of meanings in Objective-C but is only used for annotations in Java):

@Override
public String someMethod(Array array, Integer integer) {
}

Why is this useful? Let’s say that a typo was included in the method name, or a wrong type was used for one of the arguments. Normally this wouldn’t generate a warning or error, a new method would simply be declared instead of overriding an existing one. By using the @Override annotation, the compiler can tell us that a mistake has been made rather than failing silently.

The @Override annotation actually ships with the Java SDK, but you can define your own custom annotation types as well. Annotations can include a list of optional key-value pairs, which allow us to build something like the following:

@ResponseField(key = "blog_name", trim = true, required = true)
String blogName;

Say you’re implementing a mobile app that parses data from an API. Annotations allow this to be done declaratively instead of procedurally. Instance variables can be marked up with a custom @ResponseField annotation that indicates that they should be populated with values from the API responses. The response parsing logic can now be completely generic and reused across all of your different model objects:

  • Introspect an instance for variables with the @ResponseField annotation
  • Use the key field to determine where the desired value can be found in the API response dictionary
  • Use other fields to do other things, e.g. trim strings if trim = true, abort if a non-nullable field is missing, run a regular expression to ensure a field contains a valid value, etc.

Enums

You’re thinking “Objective-C already has enums.” Objective-C enums are just integer types while Java enums are instances of a custom class, with their own instance variables and methods.

Say you’re building an application with multiple themes. Each theme has a name, a background color, and a text color. It’s easy to model this using Java enums:

enum Theme {
    DARK("Dark", Colors.darkBackgroundColor, Colors.darkTextColor),
    LIGHT("Light", Colors.lightBackgroundColor, Colors.lightTextColor),

    String name;
    Color backgroundColor;
    Color textColor;

    Theme(String name, Color backgroundColor, Color textColor)  {
        this.name = name;
        this.backgroundColor = backgroundColor;
        this.textColor = textColor;
    }

    String getName() { return name; }
    Color getBackgroundColor() { return backgroundColor; }
    Color getTextColor() { return textColor; }
}

In Objective-C, you’d probably implement this with two singletons, exposed via class methods:

@implementation Theme

@property (nonatomic, readonly) NSString *name;
@property (nonatomic, readonly) UIColor *backgroundColor;
@property (nonatomic, readonly) UIColor *textColor;

+ (instancetype)darkTheme;

+ (instancetype)lightTheme;

@end

The implementation of each class method would create and configure a Theme instance and cache it inside a static instance variable, likely using a Grand Central Dispatch dispatch_once function. This is verbose, and while you could use a macro to avoid copying a bunch of boilerplate code each time you add a new theme, macros involve their own sets of tradeoffs.

The Java implementation is superior not only for its brevity but also because the enumerated values can both be switched on and enumerated:

switch (theme) {
    case DARK:
        // Do something specific to dark themes
        break;
    case LIGHT:
        // Do something specific to light themes
        break;
}

for (Theme theme : Theme.values()) {
    // Create a table cell for each theme
}

Originally published on Tumblr