Isaac Overacker

maker of things

Introducing Alcove

I have been spending quite a bit of time working with Xcode Server for continuous integration of a fairly large iOS project. As part of the build process, I wanted to generate a test coverage report and place it in on the web server running on our build server. I investigated several existing tools that are capable of generating reports, but found things about each that I disliked. Frankencover is a good solution, but it requires installing the JDK, which is not a normal part of the iOS development stack. XcodeCoverage was another option that I investigated, but it required some executable files that I didn’t want to store in our repository. Dissatisfied with the existing options, I decided that a simple Ruby script could do the job and set out to write my first gem.

Alcove

Alcove is a simple gem that can quickly and reliably generate a code coverage report for an iOS project. Installation is simple, since it’s a gem.

Terminal
1
gem install alcove

That’s it. (You’ll also need to install lcov, either with Homebrew or MacPorts.)

To generate a report, you just need to give alcove your ${PRODUCT_NAME} and it will do the rest.

Terminal
1
alcove --product-name YourProductName

After crunching some files, the report will be ready to view in ./alcove-report/.

Check out the README for more information (be sure to check out the options), or the demo project to see it in action.

Creating an Xcode Plugin: A Quick-Start Guide

Xcode has a rich ecosystem of third-party plugins, exposed and cataloged by the wonderful Xcode plugin Alcatraz. Some of my favorites: Auto-Importer, BBUFullIssueNavigator, and FuzzyAutocomplete.

However extensive the Xcode plugin catalog may be, there are surely other useful plugins to be written. The process of writing a plugin is not very well documented, and frustratingly, much of the code required to interact with Xcode is still private. However, thanks to the efforts of a few clever developers, getting started is much easier than it used to be. Read on for a quick-start guide to creating an Xcode (5+) plugin.

Image Caching With AFNetworking

AFNetworking is one of the most popular open-source networking frameworks for iOS. You’re probably already using it, but if you’re not, you should consider it. One of the lesser-known features of AFNetworking is the ability to easily load an image from a URL to be used in a UIImageView.

Integrating Test Dependencies With CocoaPods

Most Xcode projects should have some level of test coverage, and any time unit tests are involved it’s nice to have a framework for generating mock objects. However, we don’t want to bloat our production app package with these test frameworks, so it’s nice to link these libraries only with the test target. Configuring this properly can be tricky, and the CocoaPods documentation isn’t very clear on exactly how to accomplish this in the podfile.

The cleanest solution I’ve found to this problem involves specifying the pods for each target individually and using a Ruby function to import the common pods. Here is a sample podfile that accomplishes this:

Podfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
platform :ios, '7.0'

def import_common_pods
   pod 'AFNetworking'
end

target 'MyAppTarget' do
   import_common_pods
end

target 'MyAppTestTarget' do
   import_common_pods
   pod 'OCMock'
end

Note that you may run into a few issues with this if you’re adding this to an existing project and the previous podfile did not explicitly specify the non-test target. In that case, you may need to delete the old Pods.xcconfig and libPods.a files then regenerate your workspace with pod install.

Update

As of CocoaPods 0.34, there’s a cleaner solution built-in:

Podfile
1
2
3
4
5
6
7
8
9
source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '7.0'

pod 'AFNetworking'

target "MyAppTestTarget", :exclusive => true do
   pod 'OCMock'
end

Architecture Headaches With CocoaPods

I recently inherited an iOS project from another developer. This project was not using CocoaPods for dependency management, so naturally, I converted it to using CocoaPods as soon as I could. When I first installed the pods, I received this warning in the console:

Terminal
1
2
3
[!] Found multiple values (`armv7`, `armv7s`) for the
architectures (`ARCHS`) build setting for the `Pods` 
target definition. Using the first.

It seemed harmless enough, and the project built without problems for the iPhone simulator. I carried on for a few weeks working on new features, and then I tried to build a version of the app targeting the iOS devices. Suddenly, I had linker errors.

1
2
3
4
"_OBJC_CLASS_$_FBSession", referenced from:
  objc-class-ref in MySourceFile.m
  ...
ld: symbol(s) not found for architecture armv7s

I looked around for solutions, but of course with linker errors it’s can be very difficult to find answers for a particular issue. Finally, after many hours of searching, reading, and highly scientific trial and error, I decided to look into the multiple architecture warning from CocoaPods, and that’s where I found my issue. In this particular case, the Pods-* targets in my Pods project were targeting armv7 (which is pretty obvious from the CocoaPods warning, but a few weeks had passed), whereas my main app target architectures included both armv7 and armv7s, hence the linker error above.

The solution was to update the target architectures for the app to $(ARCHS_STANDARD_32_BIT) instead of manually specifying both armv7 and armv7s. Then, when running pod install, the $(ARCHS_STANDARD_32_BIT) is set for the Pods-* projects as well and everything is happy.

Note: the reason armv7 and armv7s architectures had been specified for the app is that there is a dependency on a library that does not yet support 64-bit architectures. It looks like $(ARCHS_STANDARD_32_BIT) is a much better way to handle that situation.