Hi all, here's a talk I recently gave at the local Cocoaheads meetup (Cocoaheads is a meetup for iOS/Mac developers). My topic was 'Secret Keys: Hiding secrets in your iOS app'. It was based on a blog post I wrote earlier here: Storing Secret Keys.
Hope it's helpful to someone!
So I'm finishing up my latest contract at Dimmi, a place which does online restaurant reservations, working solo for 3 months on their brand-new iPhone app. It's turned out great, and we've released it to the app store today!
You can check it out here: Dimmi App
The app has over 30 separate view controllers! It is much more complex than you'd believe when taking a cursory overview.
Here is a contact sheet with (almost) all the screens that are accessible within the app, to give an idea of the depth of the app's functionality. Click to enlarge below:
The app started out as a loose collection of designs from an agency. These designs were quickly re-thought and improved, using the great collection of inspirational apps from pttrns.com. It took us over 4 iterations (full implementations) of the home screen to get it 'just right' in our eyes, with all the capabilities required but not looking too busy. In the end, we're all really stoked with how it turned out.
I'm really proud to feature this app on my portfolio. I really hope it helps drive some new users to their business!
In this post, I want to show you how I recommend configuring your Xcode project to satisfy the following common requirements:
Everyone has their own opinion on how these things should be set up, and there's a continuum from simple to complex that may be applicable for different situations, but at the least you may learn something here. And at best, you may copy my style verbatim. If I were to describe my setup, it is: As simple as possible, while still meeting the above requirements.
Oh, and if the images don't work in your RSS reader, be sure to open this post on my blog. And if they're too zoomed in, right click them and choose Open Image in New Tab (or whatever your browser's equivalent is).
It helps to think of the various dimensions that are involved here:
All the dimensions are unrelated to each other (orthogonal). Eg you will want a Test API build that runs in debug or release mode, and you'll want a Production API build that runs in debug or release mode. The exception is the app store build - there's no need a debug build for this one.
Xcode 6 adds its own wrinkle to the picture. Previously, you could sign your Release build configuration archives with your App Store profile, and re-sign them with your Ad hoc profile for TestFlight/Hockey distribution (eg TestFlight's desktop app re-signs them for you).
But that no longer works, because Apple's added a new entitlement called 'beta-reports-active' to all the App Store profiles, for the purposes of the new 1000 Apple id's beta testing distribution. And when you re-sign a build that has that entitlement with an Ad hoc profile that is missing the entitlement, it won't install on your users devices. That's a bit of a recent gotcha to beware. So this is why I'm creating a separate profile and scheme specifically for the App Store build.
So now we can whittle it down to specifics. I recommend configuring your app to have the following build configurations:
If you forget how to add build configurations, open the project in Xcode's project navigator (the left pane), go to the project level, choose the Info tab, and click the '+' button underneath Configurations.
If you want to simplify further, you don't really need the App Store one - you can simply change Release's provisioning profile prior to each time you make an app store release, and then revert the change immediately using Git. But I personally keep the App Store config.
If you're using Cocoapods, keep in mind that whenever you add a build configuration you'll need to run
pod install again so it'll automatically add the configurations to the pods project too.
It should look like this:
I use user-defined build settings to drive the different bundle id and bundle display name for each version of the app. This gives us the ability to keep the test and production API versions of the app separate in Testflight, and makes it easier for testers to know which version they're using.
At the project level, select the 'Build Settings' tab, and select 'All' (not Basic), and 'Levels' (not Combined). We'll next need to add a couple user-defined settings that we can vary per build configuration. From the menu, choose Editor > Add Build Setting > Add User-Defined Setting to add each setting. Once you've added the two settings, click the triangle next to them to expand them, so you can add different values for each build configuration.
Here's the settings I recommend:
It should look like this:
Of course, these user-defined settings on their own won't make a lick of difference to your bundle id/name, I'll get into applying them to your Info.plist in a few paragraphs.
You'll also need to set some further build settings for code signing and preprocessor macros.
Firstly, the simple ones: Navigate to the project-level settings, select the Build Settings tab, and search for 'Preprocessor Macros'. You simply need to change the two Test API ones to include TEST_API=1. Note that the 'Debug Test API' configuration should already have DEBUG=1 set, which you do not want to overwrite, you want to add TEST_API to that. Note that this macro has 'API' in the name, and is not simply 'TEST' - this is to emphasise the fact that this is intended to influence the API dimension only. These macros are used to make the app talk to your different backends, which I'll describe in a later paragraph. It should look like the following:
Next, you need to configure your code signing. Firstly, I recommend visiting
~/Library/MobileDevice/Provisioning Profiles, inspecting all your profiles using Provisioning Quick Look Generator, and deleting all the ones that you don't need. I even go so far as to put it in my Finder's sidebar and tidying this folder often. It really pays dividends when it comes to making sure Xcode signs your code predictably - I'm never left scratching my head wondering 'what did Xcode just do?' when it comes to code signing, which is where you should be as a professional. But I digress.
After removing the junk, you'll want to create two distribution provisioning profiles (unless you already have them) in the Apple Member Center - one for your Ad Hoc distribution (or an enterprise one if you're lucky), and one for App Store distribution. I recommend calling them 'MyCompany TestFlight' and 'MyCompany App Store'. Download them, then double-click to install them. You'll see them appear in the aforementioned library folder.
Once you've culled your installed provisioning profiles down to just what you need, then go into your target level build settings, and set the following:
It should look like this:
You'll recall earlier that we created user defined build settings to vary the bundle id/name between build configurations. We need to change the Info.plist as follows so that these settings will be used:
It should look like this:
In your code, wherever you make your API calls, you'll need some kind of constant to use as the base URL for API calls. You'll want to use
#if to drive different base URLs for the test API, using the preprocessor macros you set earlier on the test api build configurations:
#if TEST_API static NSString *kBaseUrl = @"http://api-test.mycompany.com/v1/"; #else static NSString *kBaseUrl = @"https://api.mycompany.com/v1/"; #endif
Notice that the DEBUG macro has no bearing on whether you're pointing at the test API or not. We want to keep these dimensions orthogonal as mentioned earlier.
You'll want to make a few schemes to make it easy to choose the build configurations. I make the following three:
When creating these three schemes, I make sure to check 'Shared' against them in the 'Manage Schemes' screen, so that they can be checked into Git.
In the project settings, at target level, the General tab, under the Identity heading, there is a setting for Team. I set this to 'none' because I use my personal account for the tethered debugging provisioning profile. But you may want to use all your profiles from the same team, in which case you'll want to set this, as it drives the automatic code signing to be more specific.
Use a wildcard profile for the Ad hoc Testflight provisioning profile if you can get away with it, so you don't have separate ones for your .myapp and your .myapp.test bundle ids.
That's all for tonight, I hope this has been helpful!
You can see older posts in the right panel, under 'archive'.
Secret Keys talk 16 Nov 2014
Dimmi 11 Nov 2014
Project setup in Xcode6 22 Oct 2014
Uploading to an S3 bucket from iOS 15 Oct 2014
iOS8 App Testing Roundup 28 Sep 2014
Storing obfuscated secret keys in your iOS app 16 Sep 2014
New season 1 Aug 2014
House finished 17 Jun 2014
WebP decoding on iOS 9 Feb 2014
Moving on again 22 Jan 2014
Lossy images for retina iPads - JPEG vs WebP 30 Nov 2013
Positivity and your friends 7 Oct 2013
Tactility 26 Jul 2013
WWDC-induced narcolepsy 15 Jul 2013
Back on rails 31 May 2013
Full circle 6 May 2013
Programmatic UI on iOS 3 May 2013
Screencasts and positivity 8 Apr 2013
Year of positivity 14 Mar 2013
iOS Dev State of the Union 6 Feb 2013
Adventures with IAPs 3 Feb 2013
No longer a Googler 23 Dec 2012
Fight back (app biz update 13) 12 Nov 2012
Sent to the backburner (app biz update 12) 25 Oct 2012
Lisi Schappi 7 Oct 2012
Today's happy plateau (app biz update 11) 26 Aug 2012
First week's sales of Today (app biz update 10) 19 Aug 2012
Approved! (app biz update 8) 5 Aug 2012
Hurry up and wait (app biz update 7) 30 Jul 2012
Today app marketing site 27 Jul 2012
Today app submitted 25 Jul 2012
UIAlertView input wrapper 24 Jul 2012
Mentoring 23 Jul 2012
This is too hard! (app biz update 6) 20 Jul 2012
Perspectives (app biz update 5) 9 Jul 2012
4th starting-my-own-biz update 1 Jul 2012
ScrumFox landing page 28 Jun 2012
Server Scope landing page 27 Jun 2012
Telstra Calls and Data Usage 26 Jun 2012
Service History + Dropbox 26 Jun 2012
Impromptu Presenter 26 Jun 2012
Fertility Tracker 26 Jun 2012
Baby Allergy Tracker 26 Jun 2012
Starting my own business, update 3 22 Jun 2012
Starting my own business, update 2 17 Jun 2012
Starting my own business - First update 10 Jun 2012
I must be crazy 6 Jun 2012
Finding your location on an iPhone 7 May 2012
A generous career 4 May 2012
Skeleton Key Cocoaheads presentation 3 May 2012
That book about that Steve Jobs guy 30 Apr 2012
Another app marketing idea 23 Apr 2012
Sweet grouped tables on the iPhone 17 Apr 2012
Skeleton Key App 11 Apr 2012
Another app marketing idea... 5 Apr 2012
Skeleton Key Password Manager with Dropbox 2 Apr 2012
RC Boat motor finally mounted 2 Apr 2012
Promoting apps presentation slides 1 Apr 2012
The Finishing Line 20 Mar 2012
Indie iPhone app marketing, part 2 19 Feb 2012
Indie iPhone App Marketing part 1 7 Feb 2012
Perspectives 2 Feb 2012
Accountability and Free Will 1 Feb 2012
Badassery 31 Jan 2012
Sacrifice 30 Jan 2012
Lead Yourself First 29 Jan 2012
How to ping a server in Objective-C / iPhone 26 Jan 2012
iOS Automated Builds with Xcode4 16 Jan 2012
Xcode 4 - Command line builds of iPhone apps 15 Jan 2012
Guest post by Jason McDougall 13 Jan 2012
Scouts, Games and Motivation 10 Jan 2012
2011 Re-cap 8 Jan 2012
Ruby script to increment a build number 4 Jan 2012
Turning 30? All ideas, no execution? 18 Dec 2011
Memory management in Objective-C 4 Dec 2011
Starting small 29 Nov 2011
Dictionary Types Helper 29 Nov 2011
Observer Pattern in Objective-C 16 Nov 2011
Why you should give presentations 13 Nov 2011
Custom nav bar / toolbar backgrounds in iOS5 8 Nov 2011
Stuck 27 Oct 2011
Dead easy singletons in Obj-C 19 Oct 2011
JSON vs OCON (Objective-C Object Notation) 18 Oct 2011
In defence of Objective-C 16 Oct 2011
Icons 11 Oct 2011
Markdown Presentations 1 Oct 2011
More MegaComet testing: Ruling out keepalives 15 Sep 2011
MegaComet test #4 - This time with more kernel 14 Sep 2011
Building People 10 Sep 2011
Hacker News Reader 20 Aug 2011
MegaComet testing part 2 3 Aug 2011
Australian Baby Colours 28 Jul 2011
Boat prop shaft 25 Jul 2011
Megacomet with 1 million queued messages 24 Jul 2011
Installed the strut and rudder 18 Jul 2011
Painted the inside of the boat 17 Jul 2011
My 3 Data and Calls Usage 11 Jul 2011
Reading a line from the console in node.js 10 Jul 2011
Final finish 9 Jul 2011
MessagePack parser for Objective-C / iPhone 30 Jun 2011
Lacquering the starboard side 25 Jun 2011
Lacquering the hull 23 Jun 2011
Staining the boat 22 Jun 2011
NSMutableSet with weak references in objective-c 20 Jun 2011
Baby Allergy Tracker 12 Jun 2011
Power sanding the deck 10 Jun 2011
Planing the edge of the deck 2 Jun 2011
Figured out the deck 2 Jun 2011
Boat bulkheads 2 Jun 2011
Simulating iOS memory warnings 31 May 2011
Putting a UIButton in a UIToolbar 29 May 2011
Random Chef 17 May 2011
Little Orchard 13 May 2011
Boat update 13 May 2011
Design portfolio 10 May 2011
Centered section headers on a UITableView 5 May 2011
Christmas in may 4 May 2011
Git on the Mac 19 Apr 2011
Skinning the port side of the boat 8 Apr 2011
Skinning the side of the boat 5 Apr 2011
Skinning the bottom of the boat 31 Mar 2011
Extremely simple python threading 29 Mar 2011
New rescue boat 26 Mar 2011
Simple C# Wiki engine 30 Sep 2010
How to use sessions with Struts 2 30 Jul 2010
Using Oracle XE with Hibernate 20 Jul 2010
Ruby hex dumper 4 May 2010
Using Java libraries in a C# app with IKVM 16 Dec 2009
Learning Java tutorial 27 Nov 2009
Using generic database providers with C# 17 Nov 2009
Scheduled task executable batch babysitter 29 Oct 2009
Still fighting with String.Format? 9 Sep 2009
How I'd build the next Google 24 Aug 2009
C# Cryptography - Encrypting a bunch of bytes 14 Jul 2009
Reconciling/comparing huge data sets with C# 9 Jul 2009
Some keyboard-friendly DHTML tricks 10 Jun 2009
Adding a column to a massive Sql server table 16 Mar 2009
Multithreading using Delegates in C# 10 Mar 2009
Using threads and lock in C# 3 Feb 2009
Bare minimum HTML that validates 22 Dec 2008
MS Sql Server 2005 locking 17 Dec 2008
Simple Comet demo for Ruby on Rails 19 Nov 2008
Converting HSV to RGB colour using C# 14 Aug 2008
A simple server: DigitalMars' D + Libev 6 Jun 2008
Travelling from Rails 1 to Rails 2 9 Apr 2008
Rocketsale 9 Apr 2008
Online Rostering System 9 Apr 2008
DanceInforma 9 Apr 2008
Client Analyser 9 Apr 2008
How good is this??? 16 Dec 2007
First post 17 Oct 2007