README
Just like software, this document will rot unless we take care of it. We encourage everyone to help us on that – just open an issue or send a pull request!
Why?
Getting on board with iOS can be intimidating. Neither Swift nor Objective-C are widely used elsewhere, the platform has its own names for almost everything, and it's a bumpy road for your code to actually make it onto a physical device. This living document is here to help you, whether you're taking your first steps in Cocoaland or you're curious about doing things "the right way". Everything below is just suggestions, so if you have a good reason to do something differently, by all means go for it!
Project Setup
A common question when beginning an iOS project is whether to write all views in code or use Interface Builder with Storyboards or XIB files. Both are known to occasionally result in working software. However, there are a few considerations:
Xcode Setup
Please follow the url [macbook-for-developers]
[macbook-for-developers]:http://martiancraft.com/blog/2015/08/macbook-for-developers/
Why code?
- Storyboards are more prone to version conflicts due to their complex XML structure. This makes merging much harder than with code.
- It's easier to structure and reuse views in code, thereby keeping your codebase DRY.
Why Storyboards?
- For the less technically inclined, Storyboards can be a great way to contribute to the project directly, e.g. by tweaking colors or layout constraints. However, this requires a working project setup and some time to learn the basics.
- Iteration is often faster since you can preview certain changes without building the project.
- In Xcode 6, custom fonts and UI elements are finally represented visually in Storyboards, giving you a much better idea of the final appearance while designing.
Project Structure
To keep all those hundreds of source files ending up in the same directory, it's a good idea to set up some folder structure depending on your architecture. For instance, you can use the following:
- ├─ Classes
- ├─── Application Delegate
- ├─── Category
- ├─── Constant
- ├─── Models
- ├─── Network Manager
- ├─── Shared Manager
- ├─── Utility Function
- ├─── Shared Manager
- ├─── View Controller
- ├─ Views
- ├─── StoryBoard.storyboard
- ├─── LaunchScreen.xib
- ├─ Resources
- ├─── Slices
- ├─── Fonts
- ├─── Sounds
First, create them as groups (little yellow "folders") within the group with your project's name in Xcode's Project Navigator. Then, for each of the groups, link them to an actual directory in your project path by opening their File Inspector on the right, hitting the little gray folder icon, and creating a new subfolder with the name of the group in your project directory.
CocoaPods
If you're planning on including external dependencies (e.g. third-party libraries) in your project, CocoaPods offers easy and fast integration. Install it like so:
sudo gem install cocoapods
To get started, move inside your iOS project folder and run
pod init
This creates a Podfile, which will hold all your dependencies in one place. After adding your dependencies to the Podfile, you run
pod install
to install the libraries and include them as part of a workspace which also holds your own project. It is generally recommended to commit the installed dependencies to your own repo, instead of relying on having each developer running pod install after a fresh checkout.
Note that from now on, you'll need to open the .xcworkspace file instead of .xcproject, or your code will not compile. The command
pod update
will update all pods to the newest versions permitted by the Podfile. You can use a wealth of operators to specify your exact version requirements.
Constants
Keep app-wide constants in a Constants.h file that is included in the prefix header.
Instead of preprocessor macro definitions (via #define), use actual constants:
static CGFloat const kBrandingFontSizeSmall = 12.0f;
static NSString * const kAwesomenessDeliveredNotificationName = @"foo";
Actual constants are type-safe, have more explicit scope (they’re not available in all imported/included files until undefined), cannot be redefined or undefined in later parts of the code, and are available in the debugger.
“Event” Patterns
These are the idiomatic ways for components to notify others about things:
- Delegation: (one-to-one) Apple uses this a lot (some would say, too much). Use when you want to communicate stuff back e.g. from a modal view.
- Callback blocks: (one-to-one) Allow for a more loose coupling, while keeping related code sections close to each other. Also scales better than delegation when there are many senders.
- Notification Center: (one-to-many) Possibly the most common way for objects to emit “events” to multiple observers. Very loose coupling — notifications can even be observed globally without reference to the dispatching object.
- Key-Value Observing (KVO): (one-to-many) Does not require the observed object to explicitly “emit events” as long as it is Key-Value Coding (KVC) compliant for the observed keys (properties). Usually not recommended due to its implicit nature and the cumbersome standard library API.
Assets
Asset catalogs are the best way to manage all your project's visual assets. They can hold both universal and device-specific (iPhone 4-inch, iPhone Retina, iPad, etc.) assets and will automatically serve the correct ones for a given name. Teaching your designer(s) how to add and commit things there (Xcode has its own built-in Git client) can save a lot of time that would otherwise be spent copying stuff from emails or other channels to the codebase. It also allows them to instantly try out their changes and iterate if needed.
Using Bitmap Images
Asset catalogs expose only the names of image sets, abstracting away the actual file names within the set. This nicely prevents asset name conflicts, as files such as button_large@2x.png are now namespaced inside their image sets. However, some discipline when naming assets can make life easier:
The modifiers -568h, @2x, ~iphone and ~ipad are not required per se, but having them in the file name when dragging the file to an image set will automatically place them in the right "slot", thereby preventing assignment mistakes that can be hard to hunt down.
Using Vector Images
You can include the original vector graphics (PDFs) produced by designers into the asset catalogs, and have Xcode automatically generate the bitmaps from that. This reduces the complexity of your project (the number of files to manage.)
Coding Style
Naming
Apple pays great attention to keeping naming consistent, if sometimes a bit verbose, throughout their APIs. When developing for Cocoa, you make it much easier for new people to join the project if you follow Apple's naming conventions.
Here are some basic takeaways you can start using right away:
A method beginning with a verb indicates that it performs some side effects, but won't return anything:
- (void)loadView;
- (void)startAnimating;
Any method starting with a noun, however, returns that object and should do so without side effects:
- (UINavigationItem *)navigationItem;
+ (UILabel *)labelWithText:(NSString *)text;
It pays off to keep these two as separated as possible, i.e. not perform side effects when you transform data, and vice versa. That will keep your side effects contained to smaller sections of the code, which makes it more understandable and facilitates debugging.
Building
Build Configurations
Even simple apps can be built in different ways. The most basic separation that Xcode gives you is that between debug and release builds. For the latter, there is a lot more optimization going on at compile time, at the expense of debugging possibilities. Apple suggests that you use the debug build configuration for development, and create your App Store packages using the release build configuration. This is codified in the default scheme (the dropdown next to the Play and Stop buttons in Xcode), which commands that debug be used for Run and release for Archive.
However, this is a bit too simple for real-world applications. You might – no, should! – have different environments for testing, staging and other activities related to your service. Each might have its own base URL, log level, bundle identifier (so you can install them side-by-side), provisioning profile and so on. Therefore a simple debug/release distinction won't cut it. You can add more build configurations on the "Info" tab of your project settings in Xcode.
xcconfig files for build settings
Typically build settings are specified in the Xcode GUI, but you can also use configuration settings files (“.xcconfig files”) for them. The benefits of using these are:
- You can add comments to explain things
- You can
#include other build settings files, which helps you avoid repeating yourself:
- If you have some settings that apply to all build configurations, add a
Common.xcconfig and #include it in all the other files
- If you e.g. want to have a “Debug” build configuration that enables compiler optimizations, you can just
#include "MyApp_Debug.xcconfig" and override one of the settings
- Conflict resolution and merging becomes easier
Find more information about this topic in these presentation slides.
Targets
A target resides conceptually below the project level, i.e. a project can have several targets that may override its project settings. Roughly, each target corresponds to "an app" within the context of your codebase. For instance, you could have country-specific apps (built from the same codebase) for different countries' App Stores. Each of these will need development/staging/release builds, so it's better to handle those through build configurations, not targets. It's not uncommon at all for an app to only have a single target.
Schemes
Schemes tell Xcode what should happen when you hit the Run, Test, Profile, Analyze or Archive action. Basically, they map each of these actions to a target and a build configuration. You can also pass launch arguments, such as the language the app should run in (handy for testing your localizations!) or set some diagnostic flags for debugging.
A suggested naming convention for sche