Git

Rebecca NessonBuilding an iOS Application Platform

Rebecca Nesson posted on Monday, March 21st, 2011 | Git, iPhone, Mobile | No Comments

In the last several months PRX introduced a new product to stations: an iPhone public radio station app platform. In the upcoming months the first several apps built on this platform will begin to appear in the iTunes app store. The goal with these apps is to provide a highly customizable application that allows stations to showcase their content and their brand. The challenge for the stations is that they are on constrained budgets and cannot pay the large app development costs usually associated with a custom built iPhone app. The challenge for us to is provide them with a satisfying alternative that has a full feature set, a custom feel, and a price tag that they can afford. To meet this challenge we’ve developed a platform that abstracts as much data as possible out of the application and streamlines the maintenance and updates of shared code between our applications. Here’s a laundry list of the techniques we’re using to accomplish this.

The Server Backend

We use a rails application backend server to maintain the data about the stations. This includes the information about their programs and their schedules as well as other app-related data such as the content of various HTML-based views that appear in the app. We also use this backend to integrate with third-party providers of data for the app, such as Publink.com which provides access to some stations’ member or listener discount systems. Using a backend server application makes it easy to update data in the app without a new application release to the iTunes store, offers the potential of letting stations maintain their own data, and helps us to standardize the models and formats of the data on which the iPhone code relies.

App Customization using Inheritance

After removing as much of the data (and the variations in the data formats) from the phone as possible, there still remains the problem of how to provide a core set of features and native views for the iPhone app but also to give stations a lot of leeway to customize the way their app looks and functions and to maintain the ability to provide improvements to features without breaking or overwriting customizations. We’re using inheritance to solve this problem. Each model, view, and controller in our app has both a “Base” version and a “Custom” version where the Base version is the parent class of the Custom version. We develop all features in the Base versions and expect clients not to make customizations or other changes within these files. That way when we update a Base version of a class we can push the change into all of the apps without fear of overwriting a customization. Throughout the code outside of the class we refer only to the Custom version of a class so that any customizations made in the custom version will be used instead of the code in the Base. This allows a station to make small changes to a particular area of their app or even to completely redo the way a particular area of the application works.

Managing Improvements Using git branches

One of the trickiest parts of keeping the platform development manageable was figuring out how to maintain the code bases for each app in a way that allows easy integration of improvements over time. We’re using git (and GitHub) for this purpose. We maintain a master version of the code as a main branch. It includes all of the features and the Custom classes but no customizations. Each station app has its own branch based on the master branch but including all of the customizations. When I’m developing, I always work in a specific station’s branch. When I make changes to Custom classes or non-shared resources I commit them in the station branch. When I make a change to Base classes or other shared code or resources, I commit in the master branch and merge back into the branch I’m working on. When it’s time to go to work on another station’s app, I check out the branch and the first thing I do is merge with the master branch to pull in the latest Base changes.

A cool side effect of this process is that GitHub maintains a helpful view of how up-to-date a given app is with the changes to Base as well as how customized an a given app is. Here’s a snapshot of that view right now:

Customized Graphics with Photoshop Contact Sheets

It was important to us and to our clients to work with their own designers to create a their look for their apps. In order to do this we abstracted out as many common and reusable user interface elements as we could and created a Photoshop “contact sheet” for the app. This contact sheet provides templates of the graphics that are used throughout the app separated into a single layer per item. We provide this contact sheet to our clients’ designers and they replace the default graphics with their own designs. This allows stations to use the defaults where they like but also to come up with their own design and look for the app. We then created an AppleScript that exports all of the images and saves them with the file names the app expects. This keeps the designer’s role strictly to design and limits the amount of time we as developers have to spend to incorporate the graphics into the app.

Using .strings Files for Text Customization

One other facet of customization worth mentioning, although we still haven’t quite worked out the kinks in it, is the repurposing of iOS internationalization features to do text customization within the app. Rather than using literal strings for text throughout the app, we pull the strings out of .strings files. This allows stations to provide their own “translations” for each bit of text in the app without having to make a customization within the code. I call this customization method half-baked because when we add new strings to the app it is best if we can regenerate the strings files which will cause them to be regenerated using the defaults specified in the application’s code. To avoid this we could add new strings in new .strings files, but this would result in a proliferation of these files over time.

Chris RhodenGit Hooks and Ruby

Chris Rhoden posted on Wednesday, March 16th, 2011 | Git, iPhone, Ruby | 1 Comment

Happy Wednesday, everyone! I don’t have very much time this week, so I’ll keep this short and sweet: While we’ve historically used Subversion at PRX, we have recently been migrating to Git to take advantage of some awesome tools and to better interact with the community.

We’re also most comfortable with Ruby, so when I was asked to look into setting up a build server for our iOS apps, there wasn’t much question as to how I would do it.

I set up a bare git repository running on a spare MacBook Pro with XCode and found the commands that were necessary to run when a new build was ready to be deployed. The next step was to set up the appropriate hook for that Git repository so that the builds could be triggered by a push.

In the SVN world, this would be a post-commit hook, but because Git works differently (one push can contain many commits), the hook we are interested in is the post-receive hook. You can take a look in your .git/hooks directory for some samples, most of which are written in sh. We wanted something in ruby, and here’s what we came up with:

#!/usr/bin/env ruby

require 'rubygems'
require 'grit'

repo = Grit::Repo.new(File.join(File.dirname(__FILE__), '..','..'))
while msg = gets
  old_sha, new_sha, ref = msg.split(' ', 3)
  
  commit = repo.commit(new_sha)
  
  case ref
  when %r{^refs/heads/(.*)$}
    branch = $~[1]
    if old_sha.to_i(16) == 0
      
      # A new branch was pushed
    
    else
      
      # A branch was updated
      
    end
    
  when %r{^refs/tags/}
    tag_object = Grit::GitRuby::Repository.new(repo.path).get_object_by_sha1(new_sha)
    tag = tag_object.tag
    tag_message = tag_object.message
    if old_sha.to_i(16) == 0
      
      # A tag was created
      
    else
      
      # A tag was moved
      
    end
  end
end

Simply save this in your .git/hooks/post-receive file and make it executable. Then, every time you push to this remote, the script will execute. You can make whatever modifications are necessary for your specific application.

I hope this helps everyone working with Git hooks and Ruby!

Support Us!

PRX

Categories & projects

Archives