├── README.md
├── cocoa_swift_style_guide.md
└── swift_style_guide.md
/README.md:
--------------------------------------------------------------------------------
1 | swift-style-guide
2 | =================
3 |
4 | This repo contains style guides for using Swift in iOS apps at SlideShare.
5 |
--------------------------------------------------------------------------------
/cocoa_swift_style_guide.md:
--------------------------------------------------------------------------------
1 | Official SlideShare Cocoa/Swift Style Guide
2 | ===========================
3 | This is the SlideShare Cocoa/Swift Style Guide we are using for our iOS 8 only app written in Swift. Please feel free to submit pull requests, so that this may be helpful for anyone else using Swift. This document lays out our conventions for using Swift with the Cocoa APIs for iOS.
4 |
5 | *You can download the app at http://lnkd.in/ssios*
6 |
7 | ### Table Of Contents
8 |
9 | * [Protocols](#protocols)
10 | * [UITableView/UICollectionView](#uitableview)
11 | * [Strings](#strings)
12 | * [NSNotification](#nsnotification)
13 | * [App Delegate](#app-delegate)
14 | * [Core Foundation](#core-foundation)
15 | * [View Controllers](#view-controllers)
16 | * [UIView](#uiview)
17 | * [Objective-C Interoperability](#objective-c-interoperability)
18 |
19 | ---
20 |
21 | #### Protocols
22 | - The ReusableView Protocol should be used by any view used by a UICollectionView or UITableView that needs a reuse identifier. You will see how this is used in the UITableView section.
23 |
24 | ```swift
25 | protocol ReusableView {
26 | static var ReuseIdentifier: String { get }
27 | static var NibName: String { get }
28 | }
29 | ```
30 |
31 |
32 | #### UITableView & UICollectionView
33 | - In a UITableViewCell/UICollectionViewCell subclass, create a read-only computed property for the reuse identifier for the cell. Use camel case with first letter uppercase, because it is a constant. **Note**: Please use the protocol listed in the conformance.
34 |
35 | ```swift
36 | class TableViewCell: UITableViewCell, ReusableView {
37 | static let ReuseIdentifier: String = "TableViewCellIdentifier"
38 | static let NibName: String = "CustomTableViewCell"
39 | }
40 | ```
41 | > **Reasoning**: When registering cells for reuse in a UITableView or UICollectionView, you need the nib name to load the nib and the reuse identifier.
42 |
43 | #### Strings
44 | - Put any user-facing string in the Localizable.strings file with a key in snake case. Use NSLocalizedString when accessing the strings in code.
45 |
46 | ```swift
47 | // Localizable.strings //
48 | //
49 | "user_facing_string_key" = "This is a user-facing string."
50 |
51 | // Someting.swift //
52 | var userFacing = NSLocalizedString("user_facing_string_key", comment: "")
53 | ```
54 |
55 | #### NSNotification
56 | - Name notifications in reverse domain format with the notification name in snake case.
57 |
58 | ```swift
59 | com.linkedin.slideshare.notification_name
60 | ```
61 |
62 | - Create a struct to hold all notification names as constants.
63 |
64 | ```swift
65 | struct GlobalNotifications {
66 | static let ABC = ""
67 | }
68 | ```
69 |
70 | - Create notification handlers as lazy closures.
71 |
72 | ```swift
73 | private lazy var handleNotification: (NSNotification!) -> Void { [weak self] notification in
74 | // Handle the notification
75 | }
76 | ```
77 | > **Reasoning**: This way you can define capture semantics for self and also use the identifier as the selector in the addObserver method (see below) instead of a string. This gives you the safety of the compiler.
78 |
79 | - Create a registerNotifications() method and deregisterNotifications().
80 |
81 | ```swift
82 | func registerNotifications() {
83 | let notificationCenter = NSNotificationCenter.defaultCenter()
84 |
85 | notificationCenter.addObserver(self, selector: handleNotificationABC, name: GlobalNotifications.ABC, object: nil)
86 | }
87 |
88 | func deregisterNotifications() {
89 | NSNotificationCenter.defaultCenter().removeObserver(self)
90 | }
91 | ```
92 |
93 | #### App Delegate
94 | - Abstract initialization of the application into a separate class.
95 |
96 | ```swift
97 | class AppInitializer {
98 | func onAppStart() {
99 | // content
100 | }
101 | }
102 | ```
103 |
104 | #### Core Foundation
105 | - When using Core Graphics structs, such as CGRect, use the initializers instead of the older CGRectMake method.
106 |
107 | ```swift
108 | var rect = CGRect(x: 10, y: 10, width: 45, height: 300)
109 | ```
110 |
111 | - If you need to make an instance of a struct zeroed out, utilize the class constant.
112 |
113 | ```swift
114 | var zeroRect = CGRect.zeroRect
115 | ```
116 |
117 | #### View Controllers
118 | - If the view controller is associated with a Storyboard, create a class method named createInstance to return an initialized instance of the view controller from the Storyboard.
119 |
120 | ```swift
121 | static func createInstance() -> MasterViewController {
122 | return UIStoryboard.initialControllerFromStoryboard("Master") as! MasterViewController
123 | }
124 | ```
125 | > **Reasoning**: Use static if you are not going to make a subclass of this class. If you are going to make a subclass, change it to class func.
126 |
127 | - If you have the situation described above, but have properties that need to be initialized, also create helper methods following the designated/convenience initializer type pattern like so.
128 |
129 | ```swift
130 | static func createInstanceWithId(id: Int) -> MasterViewController {
131 | let masterViewController = createInstance()
132 | masterViewController.id = id
133 | return masterViewController
134 | }
135 | ```
136 |
137 | #### UIView
138 | - If you have a class that inherits from UIView and has a XIB file where it is layed out, create a class method named createInstance similar to the example in the View Controllers section.
139 |
140 | ```swift
141 | class CustomView: UIView {
142 |
143 | private static let NibName: String = "CustomView"
144 |
145 | static func createInstance() -> CustomView {
146 | return NSBundle.mainBundle().loadNibNamed(nibName, owner: nil, options: nil)[0] as! CustomView
147 | }
148 | }
149 | ```
150 |
151 | #### Objective-C Interoperability
152 | - You must have a single Objective-C bridging header for Object-C interoperability. However, if a certain set of code you are importing has multiple header files; group them into another header file.
153 |
154 | ```objc
155 | // -Bridging-Header.h
156 | #import "SDWebImageHeader.h"
157 |
158 | // SDWebImageHeader.h
159 | #import
160 | #import
161 | #import
162 | ```
163 |
--------------------------------------------------------------------------------
/swift_style_guide.md:
--------------------------------------------------------------------------------
1 | Official SlideShare Swift Style Guide
2 | ===========================
3 | This is the SlideShare Swift Style Guide we are using for our upcoming iOS 8 only app written in Swift. Please feel free to submit pull requests, so that this may be helpful for anyone else using Swift.
4 |
5 | *You can download the app at http://lnkd.in/ssios*
6 |
7 | ### Table Of Contents
8 |
9 | * [Xcode Preferences](#xcode-preferences)
10 | * [Switch](#switch)
11 | * [Properties](#properties)
12 | * [Closures](#closures)
13 | * [Identifiers](#identifiers)
14 | * [Singleton](#singleton)
15 | * [Optionals](#optionals)
16 | * [Strings](#strings)
17 | * [Enums](#enums)
18 | * [Documentation](#documentation)
19 |
20 | ---
21 |
22 |
23 | #### Xcode Preferences
24 | - Use spaces for tabs and 4 spaces per tab (Change the default in Xcode->Preferences->Text Editing->Indentation)
25 |
26 |
27 | #### Switch
28 | - Switch statements should have each case statement not indented and all code executed for that case indented below:
29 |
30 | ```swift
31 | var value = 2
32 | var test: String?
33 |
34 | switch value {
35 | case 1:
36 | test = "abc"
37 | default:
38 | test = "xyz"
39 | }
40 | ```
41 |
42 | - If you want to match multiple values within an object or struct, create a tuple with the two values:
43 |
44 | ```swift
45 | struct TestValue {
46 | enum Val {
47 | case A
48 | case B
49 | }
50 | var value: Val = .A
51 | var detail: String = "Test"
52 | }
53 | var testValue = TestValue()
54 |
55 | switch (testValue.value, testValue.detail) {
56 | case (.A, "Test"):
57 | print("This is printed")
58 | default:
59 | print("This is not printed")
60 | }
61 | ```
62 |
63 | - If you have a default case that shouldn't be reached, use an assert.
64 |
65 | ```swift
66 | var test = "Hello"
67 |
68 | switch test {
69 | case "Hello"
70 | print("It prints")
71 | case "World"
72 | print("It doesn't")
73 | default:
74 | assert(false, "Useful message for developer")
75 | }
76 | ```
77 |
78 | #### Properties
79 | - If making a read-only computed variable, provide the getter without the get {} around it:
80 |
81 | ```swift
82 | var computedProp: String {
83 | if someBool {
84 | return "Hello"
85 | }
86 | }
87 | ```
88 |
89 | - If making a computed variable that is readwrite, have get {} and set{} indented:
90 |
91 | ```swift
92 | var computedProp: String {
93 | get {
94 | if someBool {
95 | return "Hello"
96 | }
97 | }
98 | set {
99 | print(newValue)
100 | }
101 | }
102 | ```
103 |
104 | - Same rule as above but for willSet and didSet:
105 |
106 | ```swift
107 | var property = 10 {
108 | willSet {
109 | print("willSet")
110 | }
111 | didSet {
112 | print("didSet")
113 | }
114 | }
115 | ```
116 |
117 | - Though you can create a custom name for the new or old value for willSet/didSet and set, use the standard newValue/oldValue identifiers that are provided by default:
118 |
119 | ```swift
120 | var property = 10 {
121 | willSet {
122 | if newValue == 10 {
123 | print("It’s 10")
124 | }
125 | }
126 | didSet {
127 | if oldValue == 10 {
128 | print("It was 10")
129 | }
130 | }
131 | }
132 | ```
133 |
134 | - Create class constants as static for any strings or constant values.
135 |
136 | ```swift
137 | class Test {
138 | static let ConstantValue: String = "TestString"
139 | }
140 | ```
141 |
142 | #### Closures
143 | - Do not use parameter types when declaring parameter names to use in a closure. Also, keep parameter names on same line as opening brace for closures:
144 |
145 | ```swift
146 | doSomethingWithCompletion() { param1 in
147 | print("\(param1)")
148 | }
149 | ```
150 |
151 | - Always use trailing closure syntax if there is a single closure as the last parameter of a method:
152 |
153 | ```swift
154 | // Definition
155 | func newMethod(input: Int, onComplete methodToRun: (input: Int) -> Void) {
156 | // content
157 | }
158 |
159 | // Usage
160 | newMethod(10) { param in
161 | print("output: \(param)"")
162 | }
163 | ```
164 |
165 | - However, if there are 2 closures as the last parameters, do not use trailing closure syntax for the last one as this is ambiguous. Also, when creating a closure inline as a method parameter, put the parameter name on a new line and follow the following indentation rules:
166 |
167 | ```swift
168 | testMethod(param: 2.5,
169 | success: {
170 | print("success")
171 | },
172 | failure: {
173 | print("failure")
174 | })
175 | ```
176 |
177 | - Use trailing closure syntax if a closure is the only parameter:
178 |
179 | ```swift
180 | array1.map { /* content */ }
181 | ```
182 |
183 | - If declaring the type of a function or closure with no return type, specify this by using Void as the return type. Also, put a space before and after -> when declaring a closure type:
184 |
185 | ```swift
186 | func takeClosure(aClosure: () -> Void) {
187 | // content
188 | }
189 | ```
190 |
191 | - If creating a function or closure with no return type, do not specify one:
192 |
193 | ```swift
194 | func noReturn() {
195 | // content
196 | }
197 | ```
198 |
199 | - If creating a closure that seems to be large (use your best judgement) do not declare inline; create a local variable.
200 |
201 | ```swift
202 | func foo(something: () -> Void) {
203 | something()
204 | }
205 |
206 | func doEverything() {
207 | let doSomething = {
208 | var x = 1
209 | for 1...3 {
210 | x++
211 | }
212 | print(x)
213 | }
214 | foo(doSomething)
215 | }
216 | ```
217 |
218 | #### Identifiers
219 | - Only use self. if you need to, which is when you have a parameter of the same name as the instance variable, or in closures:
220 |
221 | ```swift
222 | class Test {
223 |
224 | var a: (() -> Void)?
225 | var b: Int = 3
226 |
227 | func foo(a: () -> Void) {
228 | self.a = a
229 | }
230 |
231 | func foo1() {
232 | foo() {
233 | print(self.b)
234 | }
235 | }
236 | }
237 | ```
238 |
239 | - If declaring a variable with its type, place the colon directly after the identifier with a space and then the type:
240 |
241 | ```swift
242 | static var testVar: String
243 | ```
244 |
245 | - When declaring dictionary types, include a space before the key type and after the colon:
246 |
247 | ```swift
248 | var someDictionary: [String : Int]
249 | ```
250 |
251 | - When declaring a constant, use camel case with the first letter uppercase.
252 |
253 | ```swift
254 | class TestClass {
255 | let ConstantValue = 3
256 | }
257 | ```
258 |
259 | - To declare a set of constants not to be used for switching, use a struct:
260 |
261 | ```swift
262 | struct Constants {
263 | static let A = "A"
264 | static let B = "B"
265 | }
266 | ```
267 |
268 | #### Singleton
269 | - Implement a singleton by having this at the top of your class definition and a private initializer:
270 |
271 | ```swift
272 | class ClassA {
273 |
274 | static let sharedInstance: ClassA = ClassA()
275 |
276 | private init() {
277 | // ...
278 | }
279 | }
280 | ```
281 | Note: Xcode currently (6.0.1) does not indent properly in this case. Use the indentation specified above.
282 |
283 | #### Optionals
284 | - When unwrapping optionals, rebind the optional to the same name, unless there is a reason not to. This example shows this, but in this case it should be done within the binding.
285 |
286 | ```swift
287 | func possibleBike() -> Bike? {
288 | // content
289 | }
290 |
291 | let bike = possibleBike()
292 | if let bike = bike {
293 | // content
294 | }
295 | ```
296 |
297 | #### Strings
298 | - When appending to a string, always use the += operator.
299 |
300 | ```swift
301 | var newString = "Hello"
302 | newString += " world!"
303 | ```
304 |
305 | *Note: do not concatenate user-facing strings as the ordering could change in different languages.*
306 |
307 | #### Enums
308 | - When using an enum, always prefer the shorthand syntax when possible. The shorthand syntax should be possible whenever the type does not need to be inferred from the assigned value. Note: there are certain bugs that don't allow them to be used everywhere they should be possible.
309 |
310 | ```swift
311 | enum TestEnum {
312 | case A
313 | case B
314 | }
315 |
316 | var theValue: TestEnum?
317 | var shouldBeA = true
318 |
319 | if shouldBeA {
320 | theValue = .A
321 | } else {
322 | theValue = .B
323 | }
324 | ```
325 |
326 | - When declaring and setting a variable/constant of an enum type, do so in the following manner.
327 |
328 | ```swift
329 | var testValue: TestEnum = .A
330 | ```
331 |
332 | #### Documentation
333 | - When documenting a method, use /// if it is only a single line
334 |
335 | ```swift
336 | /// This method does nothing
337 | func foo() {
338 | // content
339 | }
340 | ```
341 |
342 | - If you are documenting a method, use /\*\* to begin and */ to end if it is multiline; do not indent.
343 |
344 | ```swift
345 | /**
346 | This method does something.
347 | It's very useful.
348 | */
349 | func foo2() {
350 | // content
351 | }
352 | ```
353 |
354 | - Use the standard Swift Documentation syntax (reST) in order to enable Quick Documentation. Follow the formatting below, exactly. Using Control+I (Re-indent) should follow this same formatting.
355 |
356 | Note: Make sure to test your documentation by checking it's Quick Documentation by option-clicking on the method name.
357 |
358 | ```swift
359 | /**
360 | This method has parameters and a return type.
361 |
362 | - Parameter input: This is an input parameter.
363 | - Returns: True if it worked; false otherwise.
364 | */
365 | func foo3(input: String) -> Bool {
366 | // content
367 | }
368 | ```
369 |
370 | > *Note: The following section refers to marks, which are Swift's version of #pragma mark in Objective-C to separate code. There are 2 types of marks: section marks and sub-section marks.*
371 |
372 | > Section Marks:
373 |
374 | > // MARK: - Section Name
375 |
376 | > Sub-section Marks:
377 |
378 | > // MARK: Sub-section Name
379 |
380 | - Use marks to indicate new sections in code. Separate the mark comment with a new line.
381 |
382 | ```swift
383 | class Stuff {
384 |
385 | // MARK: - Instance methods
386 |
387 | func newMethod() {
388 | // content
389 | }
390 | }
391 | ```
392 |
393 | - The class/struct file layout should be ordered as follows with respect to marks and code organization (Note: See naming convention specified in above note):
394 |
395 | - Section: Declared Types (Enums, Structs, etc.), Type Aliases
396 | - Section: Constants
397 | - Section: Class Properties
398 | - Section: Instance Properties
399 | - Sub-section: Stored
400 | - Sub-section: Computed
401 | - Section: Init/Deinit
402 | - Section: Class Methods
403 | - Sub-section: Public
404 | - Sub-section: Private
405 | - Section: Instance Methods
406 | - Sub-section: Public
407 | - Sub-section: Private
408 | - Section: Protocols
409 | - Sub-section:
410 |
411 | - When a class implements a protocol, an extension should be created at the bottom of the file that declares the protocol conformance and implements the protocol. 1 extension per protocol:
412 |
413 | ```swift
414 | // NewProtocol.swift //
415 | protocol NewProtocol {
416 | func reqMethod()
417 | }
418 |
419 | // Test.swift //
420 | class Test {
421 |
422 | // content
423 | }
424 |
425 | // MARK: - Protocols
426 | // MARK: NewProtocol
427 |
428 | extension Test: NewProtocol {
429 | func reqMethod() {
430 | // content
431 | }
432 | }
433 | ```
434 |
--------------------------------------------------------------------------------