swift-code-reviewer-skill 1.1.1 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -162
- package/README.md +91 -21
- package/SKILL.md +107 -725
- package/bin/install.js +87 -22
- package/package.json +16 -2
- package/references/companion-skills.md +70 -0
- package/skills/README.md +43 -0
- package/skills/swift-concurrency/NOTICE.md +18 -0
- package/skills/swift-concurrency/SKILL.md +235 -0
- package/skills/swift-concurrency/references/actors.md +640 -0
- package/skills/swift-concurrency/references/async-await-basics.md +249 -0
- package/skills/swift-concurrency/references/async-sequences.md +635 -0
- package/skills/swift-concurrency/references/core-data.md +533 -0
- package/skills/swift-concurrency/references/glossary.md +96 -0
- package/skills/swift-concurrency/references/linting.md +38 -0
- package/skills/swift-concurrency/references/memory-management.md +542 -0
- package/skills/swift-concurrency/references/migration.md +721 -0
- package/skills/swift-concurrency/references/performance.md +574 -0
- package/skills/swift-concurrency/references/sendable.md +578 -0
- package/skills/swift-concurrency/references/tasks.md +604 -0
- package/skills/swift-concurrency/references/testing.md +565 -0
- package/skills/swift-concurrency/references/threading.md +452 -0
- package/skills/swift-expert/NOTICE.md +18 -0
- package/skills/swift-expert/SKILL.md +226 -0
- package/skills/swift-expert/references/async-concurrency.md +363 -0
- package/skills/swift-expert/references/memory-performance.md +380 -0
- package/skills/swift-expert/references/protocol-oriented.md +357 -0
- package/skills/swift-expert/references/swiftui-patterns.md +294 -0
- package/skills/swift-expert/references/testing-patterns.md +402 -0
- package/skills/swift-testing/NOTICE.md +18 -0
- package/skills/swift-testing/SKILL.md +295 -0
- package/skills/swift-testing/references/async-testing.md +245 -0
- package/skills/swift-testing/references/dump-snapshot-testing.md +265 -0
- package/skills/swift-testing/references/fixtures.md +193 -0
- package/skills/swift-testing/references/integration-testing.md +189 -0
- package/skills/swift-testing/references/migration-xctest.md +301 -0
- package/skills/swift-testing/references/parameterized-tests.md +171 -0
- package/skills/swift-testing/references/snapshot-testing.md +201 -0
- package/skills/swift-testing/references/test-doubles.md +243 -0
- package/skills/swift-testing/references/test-organization.md +231 -0
- package/skills/swiftui-expert-skill/NOTICE.md +18 -0
- package/skills/swiftui-expert-skill/SKILL.md +281 -0
- package/skills/swiftui-expert-skill/references/accessibility-patterns.md +151 -0
- package/skills/swiftui-expert-skill/references/animation-advanced.md +403 -0
- package/skills/swiftui-expert-skill/references/animation-basics.md +284 -0
- package/skills/swiftui-expert-skill/references/animation-transitions.md +326 -0
- package/skills/swiftui-expert-skill/references/charts-accessibility.md +135 -0
- package/skills/swiftui-expert-skill/references/charts.md +602 -0
- package/skills/swiftui-expert-skill/references/image-optimization.md +203 -0
- package/skills/swiftui-expert-skill/references/latest-apis.md +464 -0
- package/skills/swiftui-expert-skill/references/layout-best-practices.md +266 -0
- package/skills/swiftui-expert-skill/references/liquid-glass.md +414 -0
- package/skills/swiftui-expert-skill/references/list-patterns.md +394 -0
- package/skills/swiftui-expert-skill/references/macos-scenes.md +318 -0
- package/skills/swiftui-expert-skill/references/macos-views.md +357 -0
- package/skills/swiftui-expert-skill/references/macos-window-styling.md +303 -0
- package/skills/swiftui-expert-skill/references/performance-patterns.md +403 -0
- package/skills/swiftui-expert-skill/references/scroll-patterns.md +293 -0
- package/skills/swiftui-expert-skill/references/sheet-navigation-patterns.md +363 -0
- package/skills/swiftui-expert-skill/references/state-management.md +417 -0
- package/skills/swiftui-expert-skill/references/view-structure.md +389 -0
- package/skills/swiftui-ui-patterns/NOTICE.md +18 -0
- package/skills/swiftui-ui-patterns/SKILL.md +95 -0
- package/skills/swiftui-ui-patterns/references/app-wiring.md +201 -0
- package/skills/swiftui-ui-patterns/references/async-state.md +96 -0
- package/skills/swiftui-ui-patterns/references/components-index.md +50 -0
- package/skills/swiftui-ui-patterns/references/controls.md +57 -0
- package/skills/swiftui-ui-patterns/references/deeplinks.md +66 -0
- package/skills/swiftui-ui-patterns/references/focus.md +90 -0
- package/skills/swiftui-ui-patterns/references/form.md +97 -0
- package/skills/swiftui-ui-patterns/references/grids.md +71 -0
- package/skills/swiftui-ui-patterns/references/haptics.md +71 -0
- package/skills/swiftui-ui-patterns/references/input-toolbar.md +51 -0
- package/skills/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
- package/skills/swiftui-ui-patterns/references/list.md +86 -0
- package/skills/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
- package/skills/swiftui-ui-patterns/references/macos-settings.md +71 -0
- package/skills/swiftui-ui-patterns/references/matched-transitions.md +59 -0
- package/skills/swiftui-ui-patterns/references/media.md +73 -0
- package/skills/swiftui-ui-patterns/references/menu-bar.md +101 -0
- package/skills/swiftui-ui-patterns/references/navigationstack.md +159 -0
- package/skills/swiftui-ui-patterns/references/overlay.md +45 -0
- package/skills/swiftui-ui-patterns/references/performance.md +62 -0
- package/skills/swiftui-ui-patterns/references/previews.md +48 -0
- package/skills/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
- package/skills/swiftui-ui-patterns/references/scrollview.md +87 -0
- package/skills/swiftui-ui-patterns/references/searchable.md +71 -0
- package/skills/swiftui-ui-patterns/references/sheets.md +155 -0
- package/skills/swiftui-ui-patterns/references/split-views.md +72 -0
- package/skills/swiftui-ui-patterns/references/tabview.md +114 -0
- package/skills/swiftui-ui-patterns/references/theming.md +71 -0
- package/skills/swiftui-ui-patterns/references/title-menus.md +93 -0
- package/skills/swiftui-ui-patterns/references/top-bar.md +49 -0
- package/templates/agents/swift-code-reviewer.md +78 -0
- package/templates/commands/review.md +56 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# Memory & Performance
|
|
2
|
+
|
|
3
|
+
> Reference for: Swift Expert
|
|
4
|
+
> Load when: Memory management, ARC, performance optimization, profiling
|
|
5
|
+
|
|
6
|
+
## Automatic Reference Counting (ARC)
|
|
7
|
+
|
|
8
|
+
```swift
|
|
9
|
+
// Strong references (default)
|
|
10
|
+
class Person {
|
|
11
|
+
let name: String
|
|
12
|
+
var apartment: Apartment?
|
|
13
|
+
|
|
14
|
+
init(name: String) {
|
|
15
|
+
self.name = name
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
deinit {
|
|
19
|
+
print("\(name) is being deinitialized")
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class Apartment {
|
|
24
|
+
let unit: String
|
|
25
|
+
weak var tenant: Person? // Weak to break retain cycle
|
|
26
|
+
|
|
27
|
+
init(unit: String) {
|
|
28
|
+
self.unit = unit
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
deinit {
|
|
32
|
+
print("Apartment \(unit) is being deinitialized")
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var john: Person? = Person(name: "John")
|
|
37
|
+
var unit4A: Apartment? = Apartment(unit: "4A")
|
|
38
|
+
|
|
39
|
+
john?.apartment = unit4A
|
|
40
|
+
unit4A?.tenant = john
|
|
41
|
+
|
|
42
|
+
// Setting to nil will properly deallocate both
|
|
43
|
+
john = nil
|
|
44
|
+
unit4A = nil
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Weak and Unowned References
|
|
48
|
+
|
|
49
|
+
```swift
|
|
50
|
+
// Weak - optional reference that doesn't keep object alive
|
|
51
|
+
class ViewController: UIViewController {
|
|
52
|
+
weak var delegate: ViewControllerDelegate?
|
|
53
|
+
|
|
54
|
+
func performAction() {
|
|
55
|
+
delegate?.didPerformAction()
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Unowned - non-optional reference, assumes target outlives owner
|
|
60
|
+
class Customer {
|
|
61
|
+
let name: String
|
|
62
|
+
var card: CreditCard?
|
|
63
|
+
|
|
64
|
+
init(name: String) {
|
|
65
|
+
self.name = name
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class CreditCard {
|
|
70
|
+
let number: String
|
|
71
|
+
unowned let customer: Customer // Customer always outlives card
|
|
72
|
+
|
|
73
|
+
init(number: String, customer: Customer) {
|
|
74
|
+
self.number = number
|
|
75
|
+
self.customer = customer
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Unowned optional (Swift 5+)
|
|
80
|
+
class Department {
|
|
81
|
+
var courses: [Course] = []
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
class Course {
|
|
85
|
+
unowned var department: Department
|
|
86
|
+
unowned var nextCourse: Course?
|
|
87
|
+
|
|
88
|
+
init(department: Department) {
|
|
89
|
+
self.department = department
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Capture Lists in Closures
|
|
95
|
+
|
|
96
|
+
```swift
|
|
97
|
+
class DataManager {
|
|
98
|
+
var data: [String] = []
|
|
99
|
+
|
|
100
|
+
func loadData() {
|
|
101
|
+
// Strong reference cycle - DataManager won't be deallocated
|
|
102
|
+
NetworkManager.fetch { response in
|
|
103
|
+
self.data = response // self is captured strongly
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Weak self - breaks cycle
|
|
107
|
+
NetworkManager.fetch { [weak self] response in
|
|
108
|
+
guard let self = self else { return }
|
|
109
|
+
self.data = response
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Unowned self - when self definitely outlives closure
|
|
113
|
+
NetworkManager.fetch { [unowned self] response in
|
|
114
|
+
self.data = response // Crashes if self is deallocated
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Capturing specific values
|
|
118
|
+
let identifier = UUID()
|
|
119
|
+
NetworkManager.fetch { [identifier] response in
|
|
120
|
+
print("Request \(identifier) completed")
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Value Semantics
|
|
127
|
+
|
|
128
|
+
```swift
|
|
129
|
+
// Structs provide automatic copy-on-write for collections
|
|
130
|
+
struct User {
|
|
131
|
+
var name: String
|
|
132
|
+
var friends: [String] // Copy-on-write
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
var user1 = User(name: "Alice", friends: ["Bob"])
|
|
136
|
+
var user2 = user1 // Shallow copy
|
|
137
|
+
user2.friends.append("Charlie") // Now triggers deep copy
|
|
138
|
+
|
|
139
|
+
print(user1.friends) // ["Bob"]
|
|
140
|
+
print(user2.friends) // ["Bob", "Charlie"]
|
|
141
|
+
|
|
142
|
+
// Custom copy-on-write
|
|
143
|
+
final class Storage<T> {
|
|
144
|
+
var value: T
|
|
145
|
+
init(_ value: T) { self.value = value }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
struct MyArray<Element> {
|
|
149
|
+
private var storage: Storage<[Element]>
|
|
150
|
+
|
|
151
|
+
init(_ elements: [Element] = []) {
|
|
152
|
+
storage = Storage(elements)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
var value: [Element] {
|
|
156
|
+
get { storage.value }
|
|
157
|
+
set {
|
|
158
|
+
if !isKnownUniquelyReferenced(&storage) {
|
|
159
|
+
storage = Storage(newValue)
|
|
160
|
+
} else {
|
|
161
|
+
storage.value = newValue
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
mutating func append(_ element: Element) {
|
|
167
|
+
if !isKnownUniquelyReferenced(&storage) {
|
|
168
|
+
storage = Storage(storage.value)
|
|
169
|
+
}
|
|
170
|
+
storage.value.append(element)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Performance Optimization
|
|
176
|
+
|
|
177
|
+
```swift
|
|
178
|
+
// Use lazy properties for expensive computations
|
|
179
|
+
class Report {
|
|
180
|
+
let data: [DataPoint]
|
|
181
|
+
|
|
182
|
+
lazy var summary: String = {
|
|
183
|
+
// Expensive computation only when accessed
|
|
184
|
+
data.map { $0.description }.joined(separator: "\n")
|
|
185
|
+
}()
|
|
186
|
+
|
|
187
|
+
init(data: [DataPoint]) {
|
|
188
|
+
self.data = data
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Avoid repeated type casting
|
|
193
|
+
// Bad
|
|
194
|
+
for item in items {
|
|
195
|
+
if let user = item as? User {
|
|
196
|
+
processUser(user)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Good
|
|
201
|
+
let users = items.compactMap { $0 as? User }
|
|
202
|
+
for user in users {
|
|
203
|
+
processUser(user)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Use contiguous storage
|
|
207
|
+
// Slower - pointer indirection for each element
|
|
208
|
+
let arrayOfClasses: [MyClass] = [MyClass(), MyClass()]
|
|
209
|
+
|
|
210
|
+
// Faster - contiguous memory
|
|
211
|
+
let arrayOfStructs: [MyStruct] = [MyStruct(), MyStruct()]
|
|
212
|
+
|
|
213
|
+
// Avoid string concatenation in loops
|
|
214
|
+
// Bad
|
|
215
|
+
var result = ""
|
|
216
|
+
for item in items {
|
|
217
|
+
result += item.description // Allocates new string each time
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Good
|
|
221
|
+
let result = items.map { $0.description }.joined()
|
|
222
|
+
|
|
223
|
+
// Or
|
|
224
|
+
var result = ""
|
|
225
|
+
result.reserveCapacity(estimatedSize)
|
|
226
|
+
for item in items {
|
|
227
|
+
result.append(item.description)
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Collection Performance
|
|
232
|
+
|
|
233
|
+
```swift
|
|
234
|
+
// Choose the right collection type
|
|
235
|
+
// Array - ordered, random access O(1), append O(1) amortized
|
|
236
|
+
let ordered: [Int] = [1, 2, 3]
|
|
237
|
+
|
|
238
|
+
// Set - unique elements, contains O(1), no order
|
|
239
|
+
let unique: Set<Int> = [1, 2, 3]
|
|
240
|
+
|
|
241
|
+
// Dictionary - key-value pairs, lookup O(1)
|
|
242
|
+
let mapping: [String: Int] = ["a": 1, "b": 2]
|
|
243
|
+
|
|
244
|
+
// Use ContiguousArray for performance-critical code
|
|
245
|
+
let contiguous = ContiguousArray<MyStruct>(repeating: MyStruct(), count: 1000)
|
|
246
|
+
|
|
247
|
+
// Reserve capacity for known sizes
|
|
248
|
+
var numbers: [Int] = []
|
|
249
|
+
numbers.reserveCapacity(1000)
|
|
250
|
+
for i in 0..<1000 {
|
|
251
|
+
numbers.append(i)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Use enumerated() instead of indices
|
|
255
|
+
// Bad
|
|
256
|
+
for i in 0..<array.count {
|
|
257
|
+
process(index: i, value: array[i])
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Good
|
|
261
|
+
for (index, value) in array.enumerated() {
|
|
262
|
+
process(index: index, value: value)
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Memory Profiling with Instruments
|
|
267
|
+
|
|
268
|
+
```swift
|
|
269
|
+
// Add markers for profiling
|
|
270
|
+
import os.signpost
|
|
271
|
+
|
|
272
|
+
let log = OSLog(subsystem: "com.example.app", category: "Performance")
|
|
273
|
+
|
|
274
|
+
func processData() {
|
|
275
|
+
os_signpost(.begin, log: log, name: "Data Processing")
|
|
276
|
+
defer { os_signpost(.end, log: log, name: "Data Processing") }
|
|
277
|
+
|
|
278
|
+
// Processing code
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Autoreleasepool for memory-intensive loops
|
|
282
|
+
func processLargeDataset() {
|
|
283
|
+
for batch in dataBatches {
|
|
284
|
+
autoreleasepool {
|
|
285
|
+
// Process batch
|
|
286
|
+
// Memory released at end of each iteration
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Check for memory leaks
|
|
292
|
+
#if DEBUG
|
|
293
|
+
extension NSObject {
|
|
294
|
+
static func trackAllocations() {
|
|
295
|
+
let count = performSelector(
|
|
296
|
+
Selector(("instancesRespond:"))
|
|
297
|
+
)
|
|
298
|
+
print("\(self): \(count) instances")
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
#endif
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Optimization Levels
|
|
305
|
+
|
|
306
|
+
```swift
|
|
307
|
+
// Whole Module Optimization in Package.swift
|
|
308
|
+
let package = Package(
|
|
309
|
+
name: "MyApp",
|
|
310
|
+
products: [
|
|
311
|
+
.executable(name: "MyApp", targets: ["MyApp"])
|
|
312
|
+
],
|
|
313
|
+
targets: [
|
|
314
|
+
.target(
|
|
315
|
+
name: "MyApp",
|
|
316
|
+
swiftSettings: [
|
|
317
|
+
.unsafeFlags(["-O"], .when(configuration: .release))
|
|
318
|
+
]
|
|
319
|
+
)
|
|
320
|
+
]
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
// Inline optimization
|
|
324
|
+
@inline(__always)
|
|
325
|
+
func criticalPath() {
|
|
326
|
+
// Always inlined
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@inline(never)
|
|
330
|
+
func debugHelper() {
|
|
331
|
+
// Never inlined, good for debugging
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Optimization attributes
|
|
335
|
+
@_specialize(where T == Int)
|
|
336
|
+
@_specialize(where T == String)
|
|
337
|
+
func process<T>(_ value: T) {
|
|
338
|
+
// Specialized versions generated
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Memory Warnings
|
|
343
|
+
|
|
344
|
+
```swift
|
|
345
|
+
class ImageCache {
|
|
346
|
+
private var cache: [String: UIImage] = [:]
|
|
347
|
+
|
|
348
|
+
init() {
|
|
349
|
+
NotificationCenter.default.addObserver(
|
|
350
|
+
self,
|
|
351
|
+
selector: #selector(clearCache),
|
|
352
|
+
name: UIApplication.didReceiveMemoryWarningNotification,
|
|
353
|
+
object: nil
|
|
354
|
+
)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
@objc private func clearCache() {
|
|
358
|
+
cache.removeAll()
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
deinit {
|
|
362
|
+
NotificationCenter.default.removeObserver(self)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Best Practices
|
|
368
|
+
|
|
369
|
+
- Use value types (structs) by default
|
|
370
|
+
- Use weak references for delegates
|
|
371
|
+
- Use unowned when lifetime is guaranteed
|
|
372
|
+
- Always use capture lists in closures that reference self
|
|
373
|
+
- Profile before optimizing (use Instruments)
|
|
374
|
+
- Reserve collection capacity when size is known
|
|
375
|
+
- Use lazy properties for expensive computations
|
|
376
|
+
- Implement copy-on-write for custom types with reference storage
|
|
377
|
+
- Handle memory warnings in iOS apps
|
|
378
|
+
- Use autoreleasepool for memory-intensive loops
|
|
379
|
+
- Choose appropriate collection types
|
|
380
|
+
- Avoid premature optimization - measure first
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# Protocol-Oriented Programming
|
|
2
|
+
|
|
3
|
+
> Reference for: Swift Expert
|
|
4
|
+
> Load when: Protocol design, generics, associated types, type erasure
|
|
5
|
+
|
|
6
|
+
## Protocol Basics
|
|
7
|
+
|
|
8
|
+
```swift
|
|
9
|
+
// Protocol with requirements
|
|
10
|
+
protocol Drawable {
|
|
11
|
+
var boundingBox: CGRect { get }
|
|
12
|
+
func draw(in context: CGContext)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Protocol with default implementation
|
|
16
|
+
extension Drawable {
|
|
17
|
+
func draw(in context: CGContext) {
|
|
18
|
+
// Default drawing behavior
|
|
19
|
+
context.stroke(boundingBox)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Struct conforming to protocol
|
|
24
|
+
struct Circle: Drawable {
|
|
25
|
+
let center: CGPoint
|
|
26
|
+
let radius: CGFloat
|
|
27
|
+
|
|
28
|
+
var boundingBox: CGRect {
|
|
29
|
+
CGRect(
|
|
30
|
+
x: center.x - radius,
|
|
31
|
+
y: center.y - radius,
|
|
32
|
+
width: radius * 2,
|
|
33
|
+
height: radius * 2
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Associated Types
|
|
40
|
+
|
|
41
|
+
```swift
|
|
42
|
+
// Protocol with associated type
|
|
43
|
+
protocol Container {
|
|
44
|
+
associatedtype Item
|
|
45
|
+
var count: Int { get }
|
|
46
|
+
mutating func append(_ item: Item)
|
|
47
|
+
subscript(index: Int) -> Item { get }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Generic struct conforming
|
|
51
|
+
struct Stack<Element>: Container {
|
|
52
|
+
typealias Item = Element // Can be inferred
|
|
53
|
+
private var items: [Element] = []
|
|
54
|
+
|
|
55
|
+
var count: Int { items.count }
|
|
56
|
+
|
|
57
|
+
mutating func append(_ item: Element) {
|
|
58
|
+
items.append(item)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
subscript(index: Int) -> Element {
|
|
62
|
+
items[index]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Using where clause with associated types
|
|
67
|
+
extension Container where Item: Equatable {
|
|
68
|
+
func firstIndex(of item: Item) -> Int? {
|
|
69
|
+
for (index, current) in enumerated() where current == item {
|
|
70
|
+
return index
|
|
71
|
+
}
|
|
72
|
+
return nil
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Protocol Composition
|
|
78
|
+
|
|
79
|
+
```swift
|
|
80
|
+
// Multiple protocol conformance
|
|
81
|
+
protocol Named {
|
|
82
|
+
var name: String { get }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
protocol Aged {
|
|
86
|
+
var age: Int { get }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Composing protocols
|
|
90
|
+
typealias Person = Named & Aged
|
|
91
|
+
|
|
92
|
+
func greet(_ person: some Named & Aged) {
|
|
93
|
+
print("Hello \(person.name), age \(person.age)")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Protocol composition in constraints
|
|
97
|
+
func process<T: Codable & Hashable>(_ items: [T]) {
|
|
98
|
+
// T must conform to both Codable and Hashable
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Generics with Protocols
|
|
103
|
+
|
|
104
|
+
```swift
|
|
105
|
+
// Generic function with protocol constraint
|
|
106
|
+
func compare<T: Comparable>(_ a: T, _ b: T) -> T {
|
|
107
|
+
return a > b ? a : b
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Generic type with protocol constraint
|
|
111
|
+
class Repository<Model: Codable & Identifiable> {
|
|
112
|
+
private var items: [Model.ID: Model] = [:]
|
|
113
|
+
|
|
114
|
+
func save(_ model: Model) {
|
|
115
|
+
items[model.id] = model
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
func find(id: Model.ID) -> Model? {
|
|
119
|
+
items[id]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
func all() -> [Model] {
|
|
123
|
+
Array(items.values)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Using opaque return types
|
|
128
|
+
func makeCollection() -> some Collection {
|
|
129
|
+
return [1, 2, 3, 4, 5]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Primary associated types (Swift 5.7+)
|
|
133
|
+
protocol DataSource<Element> {
|
|
134
|
+
associatedtype Element
|
|
135
|
+
func fetch() async throws -> [Element]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
func loadData<T>(from source: some DataSource<T>) async throws -> [T] {
|
|
139
|
+
try await source.fetch()
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Type Erasure
|
|
144
|
+
|
|
145
|
+
```swift
|
|
146
|
+
// Problem: Can't use protocol with associated types as type
|
|
147
|
+
// protocol Storage {
|
|
148
|
+
// associatedtype Item
|
|
149
|
+
// func store(_ item: Item)
|
|
150
|
+
// }
|
|
151
|
+
// var storage: Storage // Error: protocol can only be used as constraint
|
|
152
|
+
|
|
153
|
+
// Solution: Type-erased wrapper
|
|
154
|
+
protocol Storage {
|
|
155
|
+
associatedtype Item
|
|
156
|
+
func store(_ item: Item)
|
|
157
|
+
func retrieve() -> Item?
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
struct AnyStorage<T>: Storage {
|
|
161
|
+
typealias Item = T
|
|
162
|
+
|
|
163
|
+
private let _store: (T) -> Void
|
|
164
|
+
private let _retrieve: () -> T?
|
|
165
|
+
|
|
166
|
+
init<S: Storage>(_ storage: S) where S.Item == T {
|
|
167
|
+
_store = storage.store
|
|
168
|
+
_retrieve = storage.retrieve
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
func store(_ item: T) {
|
|
172
|
+
_store(item)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
func retrieve() -> T? {
|
|
176
|
+
_retrieve()
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Now we can use it as a type
|
|
181
|
+
class MemoryStorage<T>: Storage {
|
|
182
|
+
private var item: T?
|
|
183
|
+
|
|
184
|
+
func store(_ item: T) {
|
|
185
|
+
self.item = item
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
func retrieve() -> T? {
|
|
189
|
+
item
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let storage: AnyStorage<String> = AnyStorage(MemoryStorage<String>())
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Protocol Inheritance
|
|
197
|
+
|
|
198
|
+
```swift
|
|
199
|
+
// Protocol inheriting from another
|
|
200
|
+
protocol Identifiable {
|
|
201
|
+
var id: UUID { get }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
protocol Timestampable {
|
|
205
|
+
var createdAt: Date { get }
|
|
206
|
+
var updatedAt: Date { get }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
protocol Entity: Identifiable, Timestampable {
|
|
210
|
+
var version: Int { get }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
struct User: Entity {
|
|
214
|
+
let id: UUID
|
|
215
|
+
let createdAt: Date
|
|
216
|
+
var updatedAt: Date
|
|
217
|
+
var version: Int
|
|
218
|
+
var name: String
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Conditional Conformance
|
|
223
|
+
|
|
224
|
+
```swift
|
|
225
|
+
// Make Array conform to protocol when elements conform
|
|
226
|
+
protocol Summarizable {
|
|
227
|
+
var summary: String { get }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
extension Array: Summarizable where Element: Summarizable {
|
|
231
|
+
var summary: String {
|
|
232
|
+
map { $0.summary }.joined(separator: ", ")
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
struct Task: Summarizable {
|
|
237
|
+
let title: String
|
|
238
|
+
var summary: String { title }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let tasks = [Task(title: "Buy milk"), Task(title: "Walk dog")]
|
|
242
|
+
print(tasks.summary) // "Buy milk, Walk dog"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Protocol Extensions
|
|
246
|
+
|
|
247
|
+
```swift
|
|
248
|
+
// Adding functionality to all conforming types
|
|
249
|
+
protocol Collection {
|
|
250
|
+
associatedtype Element
|
|
251
|
+
var count: Int { get }
|
|
252
|
+
subscript(index: Int) -> Element { get }
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
extension Collection {
|
|
256
|
+
var isEmpty: Bool {
|
|
257
|
+
count == 0
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
func map<T>(_ transform: (Element) -> T) -> [T] {
|
|
261
|
+
var result: [T] = []
|
|
262
|
+
for i in 0..<count {
|
|
263
|
+
result.append(transform(self[i]))
|
|
264
|
+
}
|
|
265
|
+
return result
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Constrained extensions
|
|
270
|
+
extension Collection where Element: Numeric {
|
|
271
|
+
func sum() -> Element {
|
|
272
|
+
var total: Element = 0
|
|
273
|
+
for i in 0..<count {
|
|
274
|
+
total += self[i]
|
|
275
|
+
}
|
|
276
|
+
return total
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Advanced Patterns
|
|
282
|
+
|
|
283
|
+
```swift
|
|
284
|
+
// Phantom types for type safety
|
|
285
|
+
enum Celsius {}
|
|
286
|
+
enum Fahrenheit {}
|
|
287
|
+
|
|
288
|
+
struct Temperature<Unit> {
|
|
289
|
+
let value: Double
|
|
290
|
+
|
|
291
|
+
init(_ value: Double) {
|
|
292
|
+
self.value = value
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
extension Temperature where Unit == Celsius {
|
|
297
|
+
func toFahrenheit() -> Temperature<Fahrenheit> {
|
|
298
|
+
Temperature<Fahrenheit>(value * 9/5 + 32)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
extension Temperature where Unit == Fahrenheit {
|
|
303
|
+
func toCelsius() -> Temperature<Celsius> {
|
|
304
|
+
Temperature<Celsius>((value - 32) * 5/9)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
let celsius = Temperature<Celsius>(100)
|
|
309
|
+
let fahrenheit = celsius.toFahrenheit()
|
|
310
|
+
|
|
311
|
+
// Witness tables pattern
|
|
312
|
+
protocol Encoder {
|
|
313
|
+
func encode<T: Encodable>(_ value: T) throws -> Data
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
protocol Decoder {
|
|
317
|
+
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
struct Codec<E: Encoder, D: Decoder> {
|
|
321
|
+
let encoder: E
|
|
322
|
+
let decoder: D
|
|
323
|
+
|
|
324
|
+
func roundtrip<T: Codable>(_ value: T) throws -> T {
|
|
325
|
+
let data = try encoder.encode(value)
|
|
326
|
+
return try decoder.decode(T.self, from: data)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Retroactive Modeling
|
|
332
|
+
|
|
333
|
+
```swift
|
|
334
|
+
// Adding protocol conformance to types you don't own
|
|
335
|
+
extension Int: Identifiable {
|
|
336
|
+
public var id: Int { self }
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Now Int can be used where Identifiable is required
|
|
340
|
+
let numbers: [Int] = [1, 2, 3]
|
|
341
|
+
ForEach(numbers) { number in
|
|
342
|
+
Text("\(number)")
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Best Practices
|
|
347
|
+
|
|
348
|
+
- Prefer protocols over base classes for abstraction
|
|
349
|
+
- Use protocol extensions for default implementations
|
|
350
|
+
- Design protocols with single responsibility
|
|
351
|
+
- Use associated types for generic protocols
|
|
352
|
+
- Apply type erasure when needed for storage
|
|
353
|
+
- Leverage conditional conformance
|
|
354
|
+
- Use opaque return types (some Protocol) for implementation hiding
|
|
355
|
+
- Compose small protocols rather than large ones
|
|
356
|
+
- Document protocol requirements and guarantees
|
|
357
|
+
- Consider protocol inheritance for layered abstraction
|