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