├── .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 | 
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 |
--------------------------------------------------------------------------------