forked from RevenueCat/purchases-ios
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSwiftStyleGuide.swift
153 lines (116 loc) · 4.44 KB
/
SwiftStyleGuide.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// SwiftStyleGuide.swift
//
// Created by Andrés Boedo on 7/12/21.
//
// imports should be alphabetized
import Foundation
// keep one empty line after type declarations, and one before the end of it
protocol ValuePrintable {
// make protocol variables read-only unless needed
func foo() -> String
}
// use protocol extensions for default implementations of protocols for types
extension ValuePrintable where Self: NSNumber {
func foo() -> String {
return "the number as float is: \(self.floatValue)"
}
}
// prefer structs to classes whenever possible.
// keep in mind that structs are value types.
// More info here: https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes
// documentation is required for each public entity.
// Use jazzy-compatible syntax for documentation: https://github.com/realm/jazzy#supported-documentation-keywords
/// The MyStruct struct is responsible for ...
struct MyStruct {
// don't explicitly define types unless needed.
// prefer let to var whenever possible.
// public properties, then internal, then private.
/// mercury is used for ...
public let mercury = false
// use `maybe` prefix for optionals
/// maybeVenus is used for ...
public var maybeVenus: String?
/// eath is used for ...
public static var Earth = "earth"
// use public private(set) when a public var is only written to within the class scope
/// this variable will be readonly to the outside, but variable from inside the scope
public private(set) var onlyReadFromOutside = 2.0
// for internal properties, omit `internal` keyword since it's default
let mars = 4.0
private var jupiter = 2
// use the valuesByKey naming convention for dictionary types
private var productsByIdentifier: [String: Product]
// public methods in the main declaration
}
// separate protocol conformance into extension
// so methods from the protocol are easy to locate
extension MyStruct: MyProtocol {
// return can be omitted for one-liners
func foo() -> String { "foo" }
}
// use protocol-oriented programming to add behaviors to types
// https://developer.apple.com/videos/play/wwdc2015/408/
extension MyStruct: PrettyPrintable { }
// add error extensions to make it easy to map errors to situations
enum MyCustomError: Error {
case invalidDateComponents(_ dateComponents: DateComponents)
case networkError
}
// private methods in an extension
private extension MyStruct {
func somethingPrivate() -> String {
return Bool.random() ? .saturn : .nepturn
}
func someMethodThatThrows() throws {
throw Bool.random() ? MyCustomError.invalidDateComponents(DateComponents())
: MyCustomError.networkError
}
func methodThatNeedsToCaptureSelf() {
// great guide on when and when not to capture self strongly
// https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html#ID56
// no need to explicitly capture self if you need a strong reference
foo.methodThatNeedsStrongCapture {
// ...
self.bar()
}
// but of course we do add it for weak references
foo.methodThatNeedsWeakCapture { [weak self] in
// we need to make self strong again, because the object could be dealloc'ed while
// this completion block is running.
// so we capture it strongly only within the scope of this completion block.
guard let self = self else { return }
// from this point on, you can use self as usual
self.doThings()
// ...
}
}
}
// use private extensions of basic types to define constants
private extension String {
static let saturn = "saturn"
static let neptune = "neptune"
}
// swiftlint:disable identifier_name
// Use one line per let in a guard with multiple lets.
let maybe🌮 = restaurant.order("🥗")
let maybe🥤 = restaurant.order("☕️")
guard let veggieTaco = maybe🌮,
let coffee = maybe🥤 else {
return
}
// Also use one line per condition.
guard 1 == 1,
2 == 2,
3 == 3 else {
print("Universe is broken")
return
}