This development guide outlines the standards and the best practices that are adopted by the iOS team at 2359 Media.
This is the first draft of the guide. We are eager for the feedback from our developers. Please feel free to create issues for any suggestions.
- Why This Is Required
- Objective-C Coding Conventions
- Swift Coding Conventions
- Project Technical Conventions
- Recommended Third-party Libraries for Objective-C
- Recommended Third-Party Libraries for Swift
- Git
- References
This guide should be considered as rules for first timers, but guidelines for experienced developers.
Follow these two style guides:
Follow swift style guide from Raywinderlinch
Always use the most recent release version of Xcode. At the time of writing, the lastest version is Xcode 5. You can download it from Mac App Store or at Apple developer center.
However, if a project still requires a base SDK of an old version of iOS, it'd handy to keep the lastest version of Xcode that supports the old SDK. For example, during the transition from iOS 6 to iOS 7, some legacy projects might not be ready to upgrade to iOS 7 SDK, because it would require a lot of effort to do so. In that case, we still need to keep Xcode 4.6.3 (and iOS 6 SDK) for these projects.
-
Add old SDKs in new Xcode
Each version of Xcode comes with only one SDK. For example, Xcode 5 comes with iOS 7.0 SDK while Xcode 4.6 comes with iOS 6.1 SDK. It's however possible to add iOS 6.1 SDK from Xcode 4.6 to Xcode 5. To do that, you simply copy the iOS 6.1 SDK folder inside Xcode 4.6 app bundle to the same position in Xcode 5 app bundle. Assuming you rename Xcode 4.6 app bundle to
Xcode4.app
, this is the command to do that:cp /Applications/Xcode4.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs
If you prefer symbolic link instead of copying the entire folder, use this command:
ln -s /Applications/Xcode4.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs
Use CocoaPods to manage libraries. Don't copy library source files into the project.
There is a repository of CocoaPods specifications (podspec
):
CocoaPods/Specs, which contains
most of the common libraries. To add a library dependency, specify the
name and the version.
-
Library with
podspec
in CocoaPods/Specspod 'AFNetworking', '2.0.0-RC3'
You can also specify a library that is outside of CocoaPods/Specs.
-
Library with
podspec
in the root of the repository. Either local repositories or remote repositories.pod 'AFNetworking', :path => '~/Documents/AFNetworking' pod 'AFNetworking', :git => 'https://github.com/AFNetworking/AFNetworking.git'
If a library doesn't have a podspec
, you can create a podspec
on
your own and host it in somewhere like gist.github.com, or even in a
local folder.
-
Library without
podspec
. Createpodspec
on you own.pod 'JSONKit', :podspec => 'https://raw.github.com/gist/1346394/1d26570f68ca27377a27430c65841a0880395d72/JSONKit.podspec' pod 'JSONKit', :path => '~/Documents/JSONKit.podspec'
- Use Asset Catalogs in Xcode.
- Must include images for all sized.
- Use JPEG format for large images, e.g. background images.
- Talk to designer to optimize pngs before adding it to the Asset Catalog.
We recommend the project group structure as shown in the following screenshots.
Assuming MyApp is the project name, we follow these conventions:
- Only 4 groups at the top level: MyApp, MyAppTests, Frameworks and Products.
AppDelegate
should be in the root level of MyApp, not inside any of its subgroups.- MyApp has 8 subgroups:
- Storyboards: obviously storyboard files
- Models: all model classes, including Core Data classes and
xcdatamodeld
file - Views: all custom views, e.g. custom table view cells
- controllers: all view controllers go here
- Managers: other controller-like classes that are not view controllers, for example, a http client class that handles all the API calls
- Categories: all categories
- Resources: resource files, like images, custom fonts or preloaded data
- Supporting Files: a default group with Xcode template
- No group for external libraries. Use CocoaPods to manage them.
- Use intent based grouping. For each intent, create a new group with all the corresponding files (viewControllers, views, storyboards, xib) inside it. For e.g if MyApp has Account and Products create two groups
Account
andProduct
under MyApp - Each intent would've it's own storyboard. If Intent has multiple viewControllers, then each viewController would have it's own storyboard. Creating separate storyboards would prevent merge conflicts when developers are working on same stream.
No warning is allowed in release builds. Treat warnings as errors.
Use Xcode Scheme, Build Configuration and Build Settings to manage different builds, like staging build, production build or App Store build.
-
Create Build Configurations.
Both staging and production builds require Debug and Release configuration, while App Store only need Release configuration. As a result, we usually create 5 build configurations: Debug, Debug Staging, Release, Release Staging and App Store. Note that App Store is created by duplicating Release configuration.
-
Add User-Defined Build Settings.
Typically, we will create User-Defined Settings for Bundle ID, app icon names, Facebook App ID, etc. So we will be able to set different Bundle ID, icon names or Facebook App ID for different Build Configurations.
These settings will be used in Info.plist. If the User-Defined Setting is
FACEBOOK_APP_ID
, you use it in Info.plist with${FACEBOOK_APP_ID}
. -
Create Schemes with Build Configurations.
Each build needs one Scheme, so we will create 3 Schemes: MyAppStaging, MyAppProduction and MyAppAppStore, for staging build, production build and App Store build respectively. Note that these schemes should be marked "Shared", so that they will be added in the Git repository.
MyAppStaging Scheme uses Debug Staging and Release Staging.
Similarly, MyAppProduction Scheme uses Debug and Release, and MyAppAppStore Scheme uses only App Store. It's summarized in the followed table.
After we have Schemes, we can, for example, switch to staging build by easily selecting MyAppStaging Scheme. Bundle ID, Facebook App ID and app icon will be changed automatically.
In order to change the Base URL of API server for staging or production,
we can implement a function that returns the correct URL based on Bundle
ID. For example, the following function returns the staging Base URL if
the Bundle ID has a prefix of com.2359media
.
NSString * MDABaseURL()
{
if ([[[NSBundle mainBundle] bundleIdentifier] hasPrefix:@"com.2359media"]) {
return @"http://myapp-staging.2359media.net";
}
else {
return @"http://myapp.2359media.net";
}
}
Use Instruments to profile your app. For example, locating memory issues, analyzing CPU usage, mesauring I/O acitivity or graphics peformance.
Resources for learning Instruments:
- Instruments User Guide
- WWDC 2010 Advanced Memory Analysis with Instruments
- WWDC 2010 Advanced Performance Analysis with Instruments
- WWDC 2011 iOS Performance and Power Optimization with Instruments
- WWDC 2011 iOS Performance in Depth
- WWDC 2012 Learning Instruments
- WWDC 2012 iOS App Performance: Responsiveness
- WWDC 2012 iOS App Performance: Graphics and Animations
- WWDC 2012 iOS App Performance: Memory
- WWDC 2013 Fixing Memory Issues
- WWDC 2013 Core Data Performance Optimization and Debugging
Create unit tests for critical code. Especially, test the following cases:
- Complex code that have many edge cases to consider. Write unit tests to cover these edge cases.
- Whenever a bug is found, write a unit test to cover it before fixing the bug.
Resources for getting started with unit testing in Xcode :
Use the following 3rd party libraries if you want to do BDD style testing.
It's quite often that we need to support older iOS versions. There are few ways to do this:
-
From Apple Documentation's SDK Compatibility Guide.
-
From a more simplified article in Ray Wenderlich's Supporting Multiple iOS Versions and Devices.
But, the most obvious way is by checking the Foundation framework version number:
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
// Handle the case for iOS 6.1 or earlier
} else {
// Handle the case for iOS 7 or later
}
Also, make sure to read Supporting iOS 6 in iOS 7 UI Transition Guide.
Here are a list of libraries that are widely used in our projects. Developers are recommended to choose the listed library for that particular feature. If, however, developers want to use a different library or experiment a new library. Please create a new issue to argue that why an alternate library is better.
AFNetworking by Mattt Thompson
A delightful iOS and OS X networking framework. http://afnetworking.com
AFNetworking 2.0 officially supports iOS 6+, Mac OS X 10.8+, and Xcode 5. If you'd like to use AFNetworking in a project targeting a base SDK of iOS 5, or Mac OS X 10.7, use the latest tagged 1.x release. For iOS 4.3 and Mac OS X 10.6 support, use the latest tagged 0.10.x release.
-
NSJSONSerialization (iOS 5+)
-
JSONKit by John Engelhart
A Very High Performance Objective-C JSON Library
AFUrbanAirshipClient by Mattt Thompson
An API Client for Registering and Unregistering Devices with Urban Airship.
SVProgressHUD by Sam Vermette
A clean and lightweight progress HUD for your iOS app.
http://samvermette.com/199
Reachability by Tony Million
ARC and GCD Compatible Reachability Class for iOS and MacOS. Drop in replacement for Apple Reachability.
-
UIRefreshControl (iOS 6+)
-
SVPullToRefresh by Sam Vermette
Give pull-to-refresh & infinite scrolling to any UIScrollView with 1 line of code.
http://samvermette.com/314
-
Use system sharing sheet, so no custom UI is allowed. Use it whenever possible.
-
Social.framework (iOS 6+)
-
Twitter.framework (Twitter only and iOS 5 only, deprecated in iOS 6)
-
Facebook SDK for iOS by Facebook
Use it for everything related to Facebook, including Facebook login, Facebook sharing, user profile, friend list, etc.
PSTCollectionView by Peter Steinberger
Open Source, 100% API compatible replacement of UICollectionView for iOS4.3+
TTTAttributedLabel by Mattt Thompson
A drop-in replacement for UILabel that supports attributes, data detectors, links, and more
MMDrawerController by Mutual Mobile
A lightweight, easy to use, Side Drawer Navigation Controller
ViewUtils by Nick Lockwood
ViewUtils is a collection of category methods designed that extend UIView with all the handy little properties and functionality that you always wished were built-in to begin with.
DDPageControl by Damien DeVille
An easily customizable alternative to UIKit's UIPageControl
PSAlertView by Peter Steinberger
Modern block-based wrappers for UIAlertView and UIActionSheet.
HTTP networking library written in Swift.
JSON parser in Swift.
AutoLayout utility library.
We use Git as our source code management (SCM) system. We host repositories in GitHub. Here are the best practices of using Git and GitHub.
-
Commit early and commit often.
-
Don't change published history. Particularly, don't rebase remote branch.
Once you git push your changes to the authoritative upstream repository or otherwise make the commits or tags publicly visible, you should ideally consider those commits etched in diamond for all eternity.
-
Use git-flow.
Gitflow is a Git workflow that Vincent Driessen introduced in his post A successful Git branching model. In addition to that, he released git-flow, which is a collection of Git extensions to provide high-level repository operations for his branching model.
-
Write good commit messages.
Here's a model Git commit message:
Capitalized, short (50 chars or less) summary More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together. Write your commit message in the imperative: "Fix bug" and not "Fixed bug" or "Fixes bug." This convention matches up with commit messages generated by commands like git merge and git revert. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here - Use a hanging indent
Read Tim Pope's post A Note About Git Commit Messages for more discussions.
-
Integrate Pivotal Tracker.
If a commit has related Pivotal Tracker stories, use Pivotal Tracker post-commit hooks to link the commits to the particular stories. For all the details, including commit syntax as well as instructions on how to enable SCM integration, please see the API help page.
Here're some typical commit messages that include Pivotal Tracker post-commit hooks:
- [#53928321] Create editorial page
- [Fix #55789490] "$" sign appears in a wrong position
- [Finish #53870315] Update icons for review buttons
Also see Commit Often, Perfect Later, Publish Once: Git Best Practices for more discussions.
We use Pull Requests to initiate code review and genenral discussion about changes before being merged into a main branch.
We follow the maintainer/contributors workflow. The tech lead of each project is the maitainer, who will review the pull requests submitted by the contributors before merging them into the main branch. The reset of developers in the team are the contributors, who will submits their changes through pull requests.
-
iOS Developer Library is where you can find all the programming guides, class references, sample code, videos, etc.
-
objc.io by Chris Eidhof, Florian Kugler and Daniel Eggert.
@phatle collects a huge number of references in his iOS Topics and References, and they are grouped by topics for easily looking up. It's a handy document for developers who just start out iOS development.