├── .gitignore └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/* 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Best Practices for iOS Software Design 2 | ====================================== 3 | 4 | This article's goal is to help you write stable code for your iOS applications. I highly encourage you to 5 | contribute your own best practices via Github's 6 | [pull requests](https://github.com/jverkoey/iOS-Best-Practices/pull/new/master). 7 | 8 | Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License. 9 | 10 | Originally written by: Jeff Verkoeyen (@featherless) 11 | 12 | Table of Contents 13 | ================= 14 | 15 | - [Be Mindful of the Lifetime of Views](#be-mindful-of-the-lifetime-of-views) 16 | * [Do not access self.view in init- methods](#do-not-access-selfview-in-init--methods) 17 | * [Use data source protocols to strongly separate data from views](#use-data-source-protocols-to-strongly-separate-data-from-views) 18 | - [UIViewController](#uiviewcontroller) 19 | * [Use the existing navigation item object](#use-the-existing-navigation-item-object) 20 | - [NSObject](#nsobject) 21 | * [Only expose public properties and methods in headers](#only-expose-public-properties-and-methods-in-headers) 22 | - [Debugging](#debugging) 23 | * [Use lldb for debugging](#use-lldb-for-debugging) 24 | * [Use NSZombieEnabled to find object leaks](#use-nszombieenabled-to-find-object-leaks) 25 | 26 | Be Mindful of the Lifetime of Views 27 | ----------------------------------- 28 | 29 | > Remind yourself constantly that, at any time, your views may be destroyed. 30 | 31 | ### Do not access self.view in init- methods 32 | 33 | You should **never** access `self.view` in your controller's initialization methods. Doing so almost always leads to 34 | hard to debug bugs because that initialization logic will not be executed again after a memory warning. 35 | 36 | Consider a simple example: 37 | 38 | ```obj-c 39 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 40 | if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) { 41 | self.view.backgroundColor = [UIColor underPageBackgroundColor]; 42 | } 43 | return self; 44 | } 45 | ``` 46 | 47 | Imagine this controller is the root of a navigation stack and a memory warning occurs. When we pop back to this 48 | controller, the view will no longer have `underPageBackgroundColor` as its background color. This leads to 49 | debugging frustration, even for experienced iOS engineers. 50 | 51 | ### Use data source protocols to strongly separate data from views 52 | 53 | When designing views that interact with data sets, always fetch the data via a data source protocol rather than 54 | exposing setters. Views are not data containers and should not be designed to enable any such abuse. Rather, 55 | views should be treated as expendable components that may leave existence at any point in time. 56 | 57 | As a general rule of thumb, anything beyond static presentation information in a view should be requested via a 58 | data source or delegate. 59 | 60 | UILabel is a good example of a view that does not need a data source. All of its properties are set once and are 61 | generally not expected to change throughout the lifetime of the view. 62 | 63 | UITableView on the other hand requires a data source to fetch its data. Let's imagine what using UITableView would 64 | be like if it didn't have a data source and instead only provided setters. 65 | 66 | This design will lead to inevitable abuse when developers attempt to use the table view object as a place to store 67 | their data. When the table view is inevitably released due to a memory warning the data will also be lost! This 68 | means that we need to store the data elsewhere anyway in order to guarantee its lifetime across multiple instances 69 | of the table view. 70 | 71 | UIViewController 72 | ---------------- 73 | 74 | > View controllers are the binding between your models and your views. 75 | 76 | ### Use the existing navigation item object 77 | 78 | Every instance of a UIViewController has a `navigationItem` property which should be used to specify the left and 79 | right navigation buttons and the title view. You should *not* create a `UINavigationItem` object because the 80 | base UIViewController implementation will automatically create one when you access `self.navigationItem`. Simply 81 | access `self.navigationItem` and set its properties accordingly. 82 | 83 | ```obj-c 84 | // UIViewController will automatically create the navigationItem object. 85 | self.navigationItem.rightBarButtonItem = doneButton; 86 | ``` 87 | 88 | NSObject 89 | -------- 90 | 91 | ### Only expose public properties and methods in headers 92 | 93 | Objective-c allows you to define private properties in a category interface within your `.m` files; take 94 | advantage of this fact to provide better headers. 95 | 96 | This is equivalent to defining ivars as `@private` with the added benefit of changes not causing build propagation 97 | when modifications are made to the internal structure of an object. This can be particularly helpful if an object 98 | is being refactored in a fairly large project. 99 | 100 | #### Example 101 | 102 | **ViewController.h** 103 | 104 | ```obj-c 105 | @interface ViewController : UIViewController 106 | @property (nonatomic, readonly, assign) NSInteger objectId; 107 | @end 108 | ``` 109 | 110 | **ViewController.m** 111 | 112 | ```obj-c 113 | #import "ViewController.h" 114 | 115 | @interface ViewController() 116 | @property (nonatomic, readwrite, assign) NSInteger objectId; 117 | // Notice that this property doesn't need to be in the .h. Objective-C will create this 118 | // property on the fly! 119 | @property (nonatomic, readwrite, retain) UILabel* objectLabel; 120 | @end 121 | 122 | @implementation ViewController 123 | @synthesize objectId; 124 | @synthesize objectLabel; 125 | 126 | ... 127 | 128 | @end 129 | ``` 130 | 131 | Debugging 132 | --------- 133 | 134 | ### Use lldb for debugging 135 | 136 | lldb allows you to inspect properties on classes that don't have explicit ivars declared in the object's interface. 137 | 138 | To use lldb, select "Edit Scheme..." from the "Product" menu (or press Cmd+Shift+<). Select the "Run" tab 139 | on the left-hand side of the scheme editor. Change the debugger drop down to "LLDB". 140 | 141 | ### Use NSZombieEnabled to find object leaks 142 | 143 | When [NSZombieEnabled](http://cocoadev.com/wiki/NSZombieEnabled) is enabled, objects that are released from memory 144 | will be kept around as "zombies". If you attempt to access the released object again in the future, rather than 145 | crashing with very little context, you will be shown the name of the object that was being accessed. This can be 146 | incredibly helpful in determining where memory leaks are occurring. 147 | 148 | To turn on NSZombieEnabled, select "Edit Scheme..." from the "Product" menu (or press Cmd+Shift+<). Select the "Run" tab 149 | on the left-hand side of the scheme editor. Select the "Arguments" tab in that page. Add a new Environment Variable 150 | and call it `NSZombieEnabled`. Set its value to `YES`. 151 | 152 | Documentation 153 | ------------- 154 | 155 | ### Data Source Protocols 156 | 157 | For required methods, start the preamble with "Tells the data source" and end it with "(required)". For example: 158 | > Tells the data source to return the number of rows in a given section of a table view. (required) 159 | 160 | For optional methods, start the documentation with "Asks the data source". For example: 161 | > Asks the data source to return the number of pages in the launcher view. 162 | 163 | ### Delegate Protocols 164 | 165 | For methods that simply notify the delegate that an action has occurred, start the preamble with "Informs the delegate". 166 | For example: 167 | > Informs the delegate that the specified item on the specified page has been selected. 168 | 169 | 170 | --------------------------------------------------------------------------------