Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.

Commit af68683

Browse files
committed
Merge pull request #58 from mobify/swift-code-style
Swift code style
2 parents 9cf9a9f + ccdf200 commit af68683

1 file changed

Lines changed: 301 additions & 0 deletions

File tree

swift/README.md

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
Strings
2+
=======
3+
4+
Prefer interpolation over concatenation:
5+
6+
```swift
7+
let name = "John"
8+
let messsage = "Hello \(name)"
9+
```
10+
11+
Optionals
12+
=========
13+
14+
Use `as` for type coercion if possible (this is enforced statically). Otherwise use `as?`.
15+
16+
DO NOT use `as!` or `value!` because Xcode told you so. Stop. Think. You probably want to use `if let value = value`.
17+
18+
You should use `!` only if you just assigned to an object, you know initialization can not fail, or you are initializing an object during init() but need to pass `self` to another object's init():
19+
20+
```swift
21+
let regex = NSRegularExpression(pattern: "[a-z]", options: NSRegularExpressionOptions.allZeros, error: nil)!
22+
```
23+
24+
init() example:
25+
```swift
26+
class Component {
27+
var controller: CustomViewController! // We want to use it as a non-optional but have to initialize after super.init()
28+
29+
init() {
30+
// controller = CustomViewController(component: self) <-- compiler error
31+
super.init()
32+
controller = CustomViewController(component: self)
33+
}
34+
}
35+
```
36+
37+
How do I handle errors in Swift?
38+
================================
39+
40+
Swift doesn't have exceptions, so errors must somehow be returned to your caller.
41+
42+
You have a couple options:
43+
44+
- Return a tuple (similar to Go)
45+
- Use a result/error enumeration type
46+
47+
We typically use an enumeration type:
48+
49+
```swift
50+
public enum ParseResult {
51+
case Result(AST)
52+
case Error(String)
53+
}
54+
```
55+
56+
```swift
57+
func parse(string: String) -> ParseResult
58+
```
59+
60+
Properties
61+
==========
62+
63+
Use `let` and initialize values when defining properties, if possible:
64+
65+
```swift
66+
class Component {
67+
let viewController = UIViewController() // Assign here instead of the in the initializer
68+
}
69+
```
70+
71+
Use calculated properies instead of getter and setter functions:
72+
73+
```swift
74+
class Component {
75+
let viewController = UIViewController()
76+
77+
var view: UIView {
78+
return viewController.view
79+
}
80+
}
81+
```
82+
83+
Dictionary and Array types
84+
==========================
85+
86+
Use shorthand type specifications:
87+
88+
```swift
89+
let names: [String]
90+
var names = [String]() // Creates the array of String as well
91+
```
92+
93+
```swift
94+
let populations: [String: Int]
95+
var populations = [String: Int]()
96+
```
97+
98+
Instead of:
99+
100+
```swift
101+
let names: Array<String> // No!
102+
```
103+
104+
White space
105+
===========
106+
107+
Indent: 4 spaces
108+
109+
In Xcode preferences under "Text Editing" select "Automatically trim trailing
110+
whitespace" and "Including whitespace-only lines".
111+
112+
Line lengths
113+
============
114+
115+
You are going to have long lines in Swift. Deal with it.
116+
117+
Try to wrap comments to 100 characters, though.
118+
119+
For Dictionary and Array literals, use python-style indentation:
120+
121+
```swift
122+
let nameMap: [String: String] = [
123+
"George": "Jetson",
124+
"Astro": "Boy",
125+
// etc.
126+
]
127+
```
128+
129+
Blocks / Closures
130+
=================
131+
132+
The common iOS name is "block".
133+
134+
Don't use types for parameters if not required:
135+
136+
```swift
137+
let callback: RpcMethodCallback = { result in
138+
// Do something with the result here
139+
}
140+
```
141+
142+
Place the block outside of the parenthesis when it is the last argument:
143+
144+
```swift
145+
// Calling UIView.animateWithDuration(duration: NSTimeInterval, animations: () -> Void)
146+
UIView.animateWithDuration(1.0) {
147+
label.alpha = 0.0 // Fade out the label
148+
}
149+
```
150+
151+
Place the closing `}` on the same line if the body of the block if empty:
152+
153+
```swift
154+
let callback: RpcMethodCallback = { result in } // Do nothing
155+
```
156+
157+
Objective-C interop
158+
===================
159+
160+
Do not use `@objc` or inherit from `NSObject` unless absolutely necessary.
161+
162+
Singletons
163+
==========
164+
165+
Avoid this design pattern.
166+
167+
`let` vs `var`
168+
==============
169+
170+
Use `let` unless the variable will be mutated.
171+
172+
delegates
173+
=========
174+
175+
Use the `weak` modifier for `delegate` properties.
176+
177+
Name the delegate property `delegate` unless your class supports multiple different delegates.
178+
179+
Protocols (aka Interfaces)
180+
==========================
181+
182+
Protocols are awesome. Use them to restrict the API surface area of an object
183+
being passed around.
184+
185+
typealiases
186+
===========
187+
188+
`typealias` standard types if used in specific contexts.
189+
190+
```swift
191+
typealias MessageAddress = String
192+
```
193+
194+
Also, you will often find it useful to typealias function types:
195+
196+
```swift
197+
typealias RpcMethodCallback = (RpcMethodResult) -> Void
198+
```
199+
200+
Access Control
201+
==============
202+
203+
Use either `private` or nothing (which defaults to module/framework internal).
204+
Only use `public` once you've written a feature and need to expose it outside
205+
of a module. Most things do not need to be exposed. Let the compiler guide
206+
you on what to make `public`.
207+
208+
Comments
209+
========
210+
211+
If you want your comments to be picked up by the documentation generation tool,
212+
[jazzy](https://github.com/Realm/jazzy), you must use a triple slash comment: `///`
213+
214+
Tests
215+
=====
216+
217+
This is the order of actual and expected values:
218+
219+
```swift
220+
XCTAssertEqual(actualValue, expectedValue)
221+
```
222+
223+
`if`/`else`
224+
===========
225+
226+
This is the style:
227+
228+
```swift
229+
if condition {
230+
231+
} else {
232+
233+
}
234+
```
235+
236+
`if let` variables
237+
==================
238+
239+
Use shadowing:
240+
241+
```swift
242+
let message: String?
243+
244+
if let message = message {
245+
// message is now a non-Optional String
246+
println(message)
247+
}
248+
```
249+
250+
**Don't** use variable names like `optionalMessage` or `unwrappedMessage`.
251+
252+
Global variables and functions
253+
==============================
254+
255+
Swift allows variables and functions to be defined in global scope. DO NOT USE THEM...unless you really need to.
256+
257+
Helper functions
258+
================
259+
260+
Group helper functions into a `struct` rather than a `class`.
261+
262+
Extensions
263+
==========
264+
265+
Create `extension`s to classes when you will use those functions in alot of places. Otherwise use [Helper functions](#Helper functions).
266+
267+
Layout
268+
======
269+
270+
Prefer Autolayout over springs+struts (autoresizing mask). Autolayout automatically handles many things that springs+struts doesn't (status bar hiding/showing, device rotation)
271+
272+
Other useful patterns
273+
=====================
274+
275+
Generic functions:
276+
277+
https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID181
278+
279+
```swift
280+
func getValue<T>(dictionary: [String: AnyObject], key: String, errorHandler: String -> Void)) -> T? {
281+
if let value = dictionary[key] {
282+
if let value = value as? T {
283+
return value
284+
} else {
285+
errorHandler("\(key) is not a \(T.self)")
286+
}
287+
} else {
288+
errorHandler("\(key) is not present")
289+
}
290+
291+
return nil
292+
}
293+
```
294+
295+
Note that the type T is inferred by what you assign `getValue()` to. So in this case getValue() infers String? because `message` is a String?.
296+
297+
```swift
298+
let message: String? = getValue(dictionary, "message") { error in
299+
println(error)
300+
}
301+
```

0 commit comments

Comments
 (0)