@aicgen/aicgen 1.0.0-beta.1 → 1.0.0

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.
Files changed (160) hide show
  1. package/{.claude/guidelines → .agent/rules}/api-design.md +5 -1
  2. package/{.claude/guidelines → .agent/rules}/architecture.md +5 -1
  3. package/{.claude/guidelines → .agent/rules}/best-practices.md +5 -1
  4. package/{.claude/guidelines → .agent/rules}/code-style.md +5 -1
  5. package/{.claude/guidelines → .agent/rules}/design-patterns.md +5 -1
  6. package/{.claude/guidelines → .agent/rules}/devops.md +5 -1
  7. package/{.claude/guidelines → .agent/rules}/error-handling.md +5 -1
  8. package/.agent/rules/instructions.md +28 -0
  9. package/{.claude/guidelines → .agent/rules}/language.md +5 -1
  10. package/{.claude/guidelines → .agent/rules}/performance.md +5 -1
  11. package/{.claude/guidelines → .agent/rules}/security.md +5 -1
  12. package/{.claude/guidelines → .agent/rules}/testing.md +5 -1
  13. package/.agent/workflows/add-documentation.md +10 -0
  14. package/.agent/workflows/generate-integration-tests.md +10 -0
  15. package/.agent/workflows/generate-unit-tests.md +11 -0
  16. package/.agent/workflows/performance-audit.md +11 -0
  17. package/.agent/workflows/refactor-extract-module.md +12 -0
  18. package/.agent/workflows/security-audit.md +12 -0
  19. package/.gemini/instructions.md +4843 -0
  20. package/.vs/ProjectSettings.json +2 -2
  21. package/.vs/VSWorkspaceState.json +15 -15
  22. package/.vs/aicgen.slnx/v18/DocumentLayout.json +53 -53
  23. package/AGENTS.md +9 -11
  24. package/assets/icon.svg +33 -33
  25. package/bun.lock +734 -26
  26. package/{CLAUDE.md → claude.md} +2 -2
  27. package/config.example.yml +129 -0
  28. package/config.yml +38 -0
  29. package/data/architecture/microservices/api-gateway.md +56 -56
  30. package/data/devops/observability.md +73 -73
  31. package/data/guideline-mappings.yml +128 -0
  32. package/data/language/dart/async.md +289 -0
  33. package/data/language/dart/basics.md +280 -0
  34. package/data/language/dart/error-handling.md +355 -0
  35. package/data/language/dart/index.md +10 -0
  36. package/data/language/dart/testing.md +352 -0
  37. package/data/language/swift/basics.md +477 -0
  38. package/data/language/swift/concurrency.md +654 -0
  39. package/data/language/swift/error-handling.md +679 -0
  40. package/data/language/swift/swiftui-mvvm.md +795 -0
  41. package/data/language/swift/testing.md +708 -0
  42. package/data/version.json +10 -8
  43. package/dist/index.js +50153 -28959
  44. package/jest.config.js +46 -0
  45. package/package.json +14 -3
  46. package/.claude/agents/architecture-reviewer.md +0 -88
  47. package/.claude/agents/guideline-checker.md +0 -73
  48. package/.claude/agents/security-auditor.md +0 -108
  49. package/.claude/settings.json +0 -98
  50. package/.claude/settings.local.json +0 -8
  51. package/.eslintrc.json +0 -28
  52. package/.github/workflows/release.yml +0 -180
  53. package/.github/workflows/test.yml +0 -81
  54. package/CONTRIBUTING.md +0 -821
  55. package/dist/commands/init.d.ts +0 -8
  56. package/dist/commands/init.d.ts.map +0 -1
  57. package/dist/commands/init.js +0 -46
  58. package/dist/commands/init.js.map +0 -1
  59. package/dist/config/profiles.d.ts +0 -4
  60. package/dist/config/profiles.d.ts.map +0 -1
  61. package/dist/config/profiles.js +0 -30
  62. package/dist/config/profiles.js.map +0 -1
  63. package/dist/config/settings.d.ts +0 -7
  64. package/dist/config/settings.d.ts.map +0 -1
  65. package/dist/config/settings.js +0 -7
  66. package/dist/config/settings.js.map +0 -1
  67. package/dist/index.d.ts +0 -3
  68. package/dist/index.d.ts.map +0 -1
  69. package/dist/index.js.map +0 -1
  70. package/dist/models/guideline.d.ts +0 -15
  71. package/dist/models/guideline.d.ts.map +0 -1
  72. package/dist/models/guideline.js +0 -2
  73. package/dist/models/guideline.js.map +0 -1
  74. package/dist/models/preference.d.ts +0 -9
  75. package/dist/models/preference.d.ts.map +0 -1
  76. package/dist/models/preference.js +0 -2
  77. package/dist/models/preference.js.map +0 -1
  78. package/dist/models/profile.d.ts +0 -9
  79. package/dist/models/profile.d.ts.map +0 -1
  80. package/dist/models/profile.js +0 -2
  81. package/dist/models/profile.js.map +0 -1
  82. package/dist/models/project.d.ts +0 -13
  83. package/dist/models/project.d.ts.map +0 -1
  84. package/dist/models/project.js +0 -2
  85. package/dist/models/project.js.map +0 -1
  86. package/dist/services/ai/anthropic.d.ts +0 -7
  87. package/dist/services/ai/anthropic.d.ts.map +0 -1
  88. package/dist/services/ai/anthropic.js +0 -39
  89. package/dist/services/ai/anthropic.js.map +0 -1
  90. package/dist/services/generator.d.ts +0 -2
  91. package/dist/services/generator.d.ts.map +0 -1
  92. package/dist/services/generator.js +0 -4
  93. package/dist/services/generator.js.map +0 -1
  94. package/dist/services/learner.d.ts +0 -2
  95. package/dist/services/learner.d.ts.map +0 -1
  96. package/dist/services/learner.js +0 -4
  97. package/dist/services/learner.js.map +0 -1
  98. package/dist/services/scanner.d.ts +0 -3
  99. package/dist/services/scanner.d.ts.map +0 -1
  100. package/dist/services/scanner.js +0 -54
  101. package/dist/services/scanner.js.map +0 -1
  102. package/dist/utils/errors.d.ts +0 -15
  103. package/dist/utils/errors.d.ts.map +0 -1
  104. package/dist/utils/errors.js +0 -27
  105. package/dist/utils/errors.js.map +0 -1
  106. package/dist/utils/file.d.ts +0 -7
  107. package/dist/utils/file.d.ts.map +0 -1
  108. package/dist/utils/file.js +0 -32
  109. package/dist/utils/file.js.map +0 -1
  110. package/dist/utils/logger.d.ts +0 -6
  111. package/dist/utils/logger.d.ts.map +0 -1
  112. package/dist/utils/logger.js +0 -17
  113. package/dist/utils/logger.js.map +0 -1
  114. package/dist/utils/path.d.ts +0 -6
  115. package/dist/utils/path.d.ts.map +0 -1
  116. package/dist/utils/path.js +0 -14
  117. package/dist/utils/path.js.map +0 -1
  118. package/docs/planning/memory-lane.md +0 -83
  119. package/packaging/linux/aicgen.spec +0 -23
  120. package/packaging/linux/control +0 -9
  121. package/packaging/macos/scripts/postinstall +0 -12
  122. package/packaging/windows/setup.nsi +0 -92
  123. package/scripts/add-categories.ts +0 -87
  124. package/scripts/build-binary.ts +0 -46
  125. package/scripts/embed-data.ts +0 -105
  126. package/scripts/generate-version.ts +0 -150
  127. package/scripts/test-decompress.ts +0 -27
  128. package/scripts/test-extract.ts +0 -31
  129. package/src/__tests__/services/assistant-file-writer.test.ts +0 -400
  130. package/src/__tests__/services/guideline-loader.test.ts +0 -281
  131. package/src/__tests__/services/tarball-extraction.test.ts +0 -125
  132. package/src/commands/add-guideline.ts +0 -296
  133. package/src/commands/clear.ts +0 -61
  134. package/src/commands/guideline-selector.ts +0 -123
  135. package/src/commands/init.ts +0 -645
  136. package/src/commands/quick-add.ts +0 -586
  137. package/src/commands/remove-guideline.ts +0 -152
  138. package/src/commands/stats.ts +0 -49
  139. package/src/commands/update.ts +0 -240
  140. package/src/config.ts +0 -82
  141. package/src/embedded-data.ts +0 -1492
  142. package/src/index.ts +0 -67
  143. package/src/models/profile.ts +0 -24
  144. package/src/models/project.ts +0 -43
  145. package/src/services/assistant-file-writer.ts +0 -612
  146. package/src/services/config-generator.ts +0 -150
  147. package/src/services/config-manager.ts +0 -70
  148. package/src/services/data-source.ts +0 -248
  149. package/src/services/first-run-init.ts +0 -148
  150. package/src/services/guideline-loader.ts +0 -311
  151. package/src/services/hook-generator.ts +0 -178
  152. package/src/services/subagent-generator.ts +0 -310
  153. package/src/utils/banner.ts +0 -66
  154. package/src/utils/errors.ts +0 -27
  155. package/src/utils/file.ts +0 -67
  156. package/src/utils/formatting.ts +0 -172
  157. package/src/utils/logger.ts +0 -89
  158. package/src/utils/path.ts +0 -17
  159. package/src/utils/wizard-state.ts +0 -132
  160. package/tsconfig.json +0 -25
@@ -0,0 +1,679 @@
1
+ # Swift Error Handling
2
+
3
+ ## Error Types
4
+
5
+ Define custom error types using enums conforming to Error:
6
+
7
+ ```swift
8
+ // ✅ Simple error enum
9
+ enum NetworkError: Error {
10
+ case connectionFailed
11
+ case timeout
12
+ case invalidResponse
13
+ case unauthorized
14
+ case serverError(code: Int)
15
+ }
16
+
17
+ // ✅ Error with associated values
18
+ enum ValidationError: Error {
19
+ case emptyField(fieldName: String)
20
+ case invalidFormat(fieldName: String, reason: String)
21
+ case tooShort(fieldName: String, minimumLength: Int)
22
+ case tooLong(fieldName: String, maximumLength: Int)
23
+ }
24
+
25
+ // ✅ Error with localized descriptions
26
+ enum AuthError: Error {
27
+ case invalidCredentials
28
+ case accountLocked
29
+ case sessionExpired
30
+
31
+ var localizedDescription: String {
32
+ switch self {
33
+ case .invalidCredentials:
34
+ return "The email or password you entered is incorrect."
35
+ case .accountLocked:
36
+ return "Your account has been temporarily locked. Please try again later."
37
+ case .sessionExpired:
38
+ return "Your session has expired. Please log in again."
39
+ }
40
+ }
41
+ }
42
+
43
+ // ❌ Avoid overly generic errors
44
+ enum GenericError: Error {
45
+ case error
46
+ case failed
47
+ }
48
+ ```
49
+
50
+ ## Throwing Functions
51
+
52
+ Use `throws` to indicate a function can throw an error:
53
+
54
+ ```swift
55
+ // ✅ Throwing function
56
+ func fetchUser(id: String) throws -> User {
57
+ guard !id.isEmpty else {
58
+ throw ValidationError.emptyField(fieldName: "id")
59
+ }
60
+
61
+ guard let user = database.findUser(id: id) else {
62
+ throw DatabaseError.notFound
63
+ }
64
+
65
+ return user
66
+ }
67
+
68
+ // ✅ Async throwing function
69
+ func fetchUserAsync(id: String) async throws -> User {
70
+ let response = try await URLSession.shared.data(from: url)
71
+ return try JSONDecoder().decode(User.self, from: response.0)
72
+ }
73
+
74
+ // ✅ Rethrows - propagates errors from closure
75
+ func transform<T, U>(_ items: [T], using: (T) throws -> U) rethrows -> [U] {
76
+ var results: [U] = []
77
+ for item in items {
78
+ let transformed = try using(item)
79
+ results.append(transformed)
80
+ }
81
+ return results
82
+ }
83
+ ```
84
+
85
+ ## Handling Errors with do-catch
86
+
87
+ ```swift
88
+ // ✅ Basic error handling
89
+ func loadUser() {
90
+ do {
91
+ let user = try fetchUser(id: "123")
92
+ print("User: \(user.name)")
93
+ } catch {
94
+ print("Error: \(error)")
95
+ }
96
+ }
97
+
98
+ // ✅ Catch specific errors
99
+ func handleSpecificErrors() {
100
+ do {
101
+ let user = try fetchUser(id: "123")
102
+ print(user.name)
103
+ } catch NetworkError.connectionFailed {
104
+ print("No internet connection")
105
+ } catch NetworkError.timeout {
106
+ print("Request timed out")
107
+ } catch NetworkError.serverError(let code) {
108
+ print("Server error: \(code)")
109
+ } catch {
110
+ print("Unknown error: \(error)")
111
+ }
112
+ }
113
+
114
+ // ✅ Async error handling
115
+ func loadUserAsync() async {
116
+ do {
117
+ let user = try await fetchUserAsync(id: "123")
118
+ print("User: \(user.name)")
119
+ } catch {
120
+ print("Failed to load user: \(error)")
121
+ }
122
+ }
123
+
124
+ // ❌ Don't catch and ignore errors silently
125
+ func badErrorHandling() {
126
+ do {
127
+ try riskyOperation()
128
+ } catch {
129
+ // Silent failure - bad practice!
130
+ }
131
+ }
132
+ ```
133
+
134
+ ## Try Variants
135
+
136
+ Swift provides three variants of `try`:
137
+
138
+ ```swift
139
+ // ✅ try - Must be in do-catch or throwing function
140
+ func normalTry() throws {
141
+ let user = try fetchUser(id: "123")
142
+ process(user)
143
+ }
144
+
145
+ // ✅ try? - Converts error to optional (nil on error)
146
+ func optionalTry() {
147
+ if let user = try? fetchUser(id: "123") {
148
+ print("User: \(user.name)")
149
+ } else {
150
+ print("Failed to fetch user")
151
+ }
152
+ }
153
+
154
+ // ⚠️ try! - Force try (crashes on error) - Use sparingly
155
+ func forceTry() {
156
+ let config = try! loadConfig() // Only if error is truly impossible
157
+ print(config.apiKey)
158
+ }
159
+
160
+ // ✅ Good use of try! - Error is truly impossible
161
+ func loadBundledFile() -> String {
162
+ guard let url = Bundle.main.url(forResource: "data", withExtension: "json"),
163
+ let data = try? Data(contentsOf: url),
164
+ let string = String(data: data, encoding: .utf8) else {
165
+ fatalError("Bundled file must exist")
166
+ }
167
+ return string
168
+ }
169
+
170
+ // ❌ Bad use of try! - Error is possible
171
+ func badForceTry() {
172
+ let user = try! fetchUser(id: "123") // Crashes if fetch fails!
173
+ }
174
+ ```
175
+
176
+ ## Result Type
177
+
178
+ Use Result for explicit success/failure without exceptions:
179
+
180
+ ```swift
181
+ // ✅ Return Result instead of throwing
182
+ func fetchUser(id: String) -> Result<User, Error> {
183
+ guard !id.isEmpty else {
184
+ return .failure(ValidationError.emptyField(fieldName: "id"))
185
+ }
186
+
187
+ do {
188
+ let user = try database.findUser(id: id)
189
+ return .success(user)
190
+ } catch {
191
+ return .failure(error)
192
+ }
193
+ }
194
+
195
+ // ✅ Handle Result with switch
196
+ func handleResult() {
197
+ let result = fetchUser(id: "123")
198
+
199
+ switch result {
200
+ case .success(let user):
201
+ print("User: \(user.name)")
202
+ case .failure(let error):
203
+ print("Error: \(error)")
204
+ }
205
+ }
206
+
207
+ // ✅ Use Result methods
208
+ func useResultMethods() {
209
+ let result = fetchUser(id: "123")
210
+
211
+ // Get value or provide default
212
+ let user = result.get(default: User.guest)
213
+
214
+ // Map success value
215
+ let userName = result.map { $0.name }
216
+
217
+ // Map error
218
+ let friendly = result.mapError { error in
219
+ "Failed to load user: \(error.localizedDescription)"
220
+ }
221
+ }
222
+
223
+ // ✅ Convert between Result and throws
224
+ func convertToThrowing() throws -> User {
225
+ let result = fetchUser(id: "123")
226
+ return try result.get() // Throws if failure
227
+ }
228
+
229
+ func convertFromThrowing() -> Result<User, Error> {
230
+ Result { try fetchUserThrowing(id: "123") }
231
+ }
232
+ ```
233
+
234
+ ## When to Use Throws vs Result
235
+
236
+ ### Use `throws` when:
237
+
238
+ ```swift
239
+ // ✅ Synchronous operations where errors are exceptional
240
+ func parseJSON<T: Decodable>(_ data: Data) throws -> T {
241
+ try JSONDecoder().decode(T.self, from: data)
242
+ }
243
+
244
+ // ✅ Async operations
245
+ func fetchData() async throws -> Data {
246
+ try await URLSession.shared.data(from: url).0
247
+ }
248
+
249
+ // ✅ Errors should propagate automatically
250
+ func processOrder() throws {
251
+ try validateOrder()
252
+ try chargePayment()
253
+ try shipOrder()
254
+ }
255
+ ```
256
+
257
+ ### Use `Result` when:
258
+
259
+ ```swift
260
+ // ✅ Storing error state
261
+ class ViewModel: ObservableObject {
262
+ @Published var userResult: Result<User, Error>?
263
+
264
+ func loadUser() async {
265
+ userResult = await fetchUserResult(id: "123")
266
+ }
267
+ }
268
+
269
+ // ✅ Callback-based APIs
270
+ func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {
271
+ // Async operation
272
+ DispatchQueue.global().async {
273
+ let result = self.performFetch()
274
+ DispatchQueue.main.async {
275
+ completion(result)
276
+ }
277
+ }
278
+ }
279
+
280
+ // ✅ When you want to delay error handling
281
+ func loadUsers() -> [Result<User, Error>] {
282
+ userIDs.map { id in
283
+ Result { try fetchUser(id: id) }
284
+ }
285
+ }
286
+ ```
287
+
288
+ ## Error Recovery Patterns
289
+
290
+ ### Retry with Exponential Backoff
291
+
292
+ ```swift
293
+ // ✅ Retry failing operations
294
+ func fetchWithRetry<T>(
295
+ maxAttempts: Int = 3,
296
+ operation: @escaping () async throws -> T
297
+ ) async throws -> T {
298
+ var lastError: Error?
299
+
300
+ for attempt in 0..<maxAttempts {
301
+ do {
302
+ return try await operation()
303
+ } catch {
304
+ lastError = error
305
+
306
+ if attempt < maxAttempts - 1 {
307
+ let delay = UInt64(pow(2.0, Double(attempt)) * 1_000_000_000)
308
+ try await Task.sleep(nanoseconds: delay)
309
+ }
310
+ }
311
+ }
312
+
313
+ throw lastError!
314
+ }
315
+
316
+ // Usage
317
+ let user = try await fetchWithRetry {
318
+ try await fetchUser(id: "123")
319
+ }
320
+ ```
321
+
322
+ ### Fallback Values
323
+
324
+ ```swift
325
+ // ✅ Provide fallback on error
326
+ func loadUserWithFallback(id: String) -> User {
327
+ do {
328
+ return try fetchUser(id: id)
329
+ } catch {
330
+ return User.guest
331
+ }
332
+ }
333
+
334
+ // ✅ Try multiple sources
335
+ func loadConfiguration() -> Configuration {
336
+ // Try remote config first
337
+ if let config = try? fetchRemoteConfig() {
338
+ return config
339
+ }
340
+
341
+ // Fall back to cached config
342
+ if let config = try? loadCachedConfig() {
343
+ return config
344
+ }
345
+
346
+ // Last resort: default config
347
+ return Configuration.default
348
+ }
349
+ ```
350
+
351
+ ### Partial Success
352
+
353
+ ```swift
354
+ // ✅ Return partial results with errors
355
+ func fetchAllUsers(ids: [String]) async -> (users: [User], errors: [Error]) {
356
+ var users: [User] = []
357
+ var errors: [Error] = []
358
+
359
+ await withTaskGroup(of: Result<User, Error>.self) { group in
360
+ for id in ids {
361
+ group.addTask {
362
+ do {
363
+ return .success(try await fetchUser(id: id))
364
+ } catch {
365
+ return .failure(error)
366
+ }
367
+ }
368
+ }
369
+
370
+ for await result in group {
371
+ switch result {
372
+ case .success(let user):
373
+ users.append(user)
374
+ case .failure(let error):
375
+ errors.append(error)
376
+ }
377
+ }
378
+ }
379
+
380
+ return (users, errors)
381
+ }
382
+ ```
383
+
384
+ ## Custom Error Context
385
+
386
+ Add context to errors:
387
+
388
+ ```swift
389
+ // ✅ Wrap errors with context
390
+ struct ContextualError: Error {
391
+ let underlyingError: Error
392
+ let context: String
393
+ let metadata: [String: Any]
394
+
395
+ init(_ error: Error, context: String, metadata: [String: Any] = [:]) {
396
+ self.underlyingError = error
397
+ self.context = context
398
+ self.metadata = metadata
399
+ }
400
+ }
401
+
402
+ func fetchUserWithContext(id: String) throws -> User {
403
+ do {
404
+ return try fetchUser(id: id)
405
+ } catch {
406
+ throw ContextualError(
407
+ error,
408
+ context: "Failed to fetch user",
409
+ metadata: ["userId": id, "timestamp": Date()]
410
+ )
411
+ }
412
+ }
413
+
414
+ // ✅ Add context without wrapping
415
+ extension Error {
416
+ func addingContext(_ context: String) -> Error {
417
+ ContextualError(self, context: context)
418
+ }
419
+ }
420
+
421
+ func processOrder() throws {
422
+ do {
423
+ try chargePayment()
424
+ } catch {
425
+ throw error.addingContext("Payment processing failed")
426
+ }
427
+ }
428
+ ```
429
+
430
+ ## Error Logging
431
+
432
+ ```swift
433
+ // ✅ Structured error logging
434
+ func logError(_ error: Error, context: String, metadata: [String: Any] = [:]) {
435
+ var logData: [String: Any] = [
436
+ "error": String(describing: error),
437
+ "context": context,
438
+ "timestamp": Date().ISO8601Format()
439
+ ]
440
+
441
+ logData.merge(metadata) { (_, new) in new }
442
+
443
+ // Log to your logging system
444
+ print("ERROR: \(logData)")
445
+ }
446
+
447
+ // Usage
448
+ func handleError() {
449
+ do {
450
+ try riskyOperation()
451
+ } catch {
452
+ logError(error, context: "Risk operation failed", metadata: [
453
+ "userId": currentUserId,
454
+ "operation": "riskyOperation"
455
+ ])
456
+ }
457
+ }
458
+
459
+ // ✅ Error boundary pattern
460
+ func performWithErrorBoundary<T>(
461
+ operation: () throws -> T,
462
+ onError: (Error) -> Void
463
+ ) -> T? {
464
+ do {
465
+ return try operation()
466
+ } catch {
467
+ logError(error, context: "Operation failed")
468
+ onError(error)
469
+ return nil
470
+ }
471
+ }
472
+ ```
473
+
474
+ ## Preconditions and Assertions
475
+
476
+ For programmer errors, use preconditions instead of throwing:
477
+
478
+ ```swift
479
+ // ✅ Precondition for programmer errors
480
+ func divide(_ a: Int, by b: Int) -> Int {
481
+ precondition(b != 0, "Division by zero")
482
+ return a / b
483
+ }
484
+
485
+ // ✅ Assert in debug builds only
486
+ func process(_ items: [Item]) {
487
+ assert(!items.isEmpty, "Items should not be empty")
488
+ // Process items
489
+ }
490
+
491
+ // ✅ Fatal error for unrecoverable errors
492
+ func loadRequiredConfiguration() -> Configuration {
493
+ guard let config = try? loadConfiguration() else {
494
+ fatalError("Failed to load required configuration")
495
+ }
496
+ return config
497
+ }
498
+
499
+ // ❌ Don't use throwing for programmer errors
500
+ func badDivide(_ a: Int, by b: Int) throws -> Int {
501
+ guard b != 0 else {
502
+ throw MathError.divisionByZero // Should be precondition
503
+ }
504
+ return a / b
505
+ }
506
+ ```
507
+
508
+ ## Error Handling Best Practices
509
+
510
+ ### Be Specific with Error Types
511
+
512
+ ```swift
513
+ // ❌ Generic error
514
+ enum AppError: Error {
515
+ case error
516
+ case somethingWentWrong
517
+ }
518
+
519
+ // ✅ Specific errors
520
+ enum UserServiceError: Error {
521
+ case userNotFound(id: String)
522
+ case invalidEmail(String)
523
+ case duplicateEmail(String)
524
+ case insufficientPermissions(required: Permission, actual: Permission)
525
+ }
526
+ ```
527
+
528
+ ### Handle Errors at the Right Level
529
+
530
+ ```swift
531
+ // ✅ Handle errors where you have context
532
+ func loadUserProfile() async {
533
+ do {
534
+ let user = try await fetchUser(id: userId)
535
+ self.user = user
536
+ } catch NetworkError.unauthorized {
537
+ // Handle at this level - we know what to do
538
+ showLoginScreen()
539
+ } catch {
540
+ // Let other errors propagate or show generic error
541
+ showErrorAlert(error.localizedDescription)
542
+ }
543
+ }
544
+
545
+ // ❌ Don't catch errors you can't handle
546
+ func badErrorHandling() {
547
+ do {
548
+ try fetchUser(id: "123")
549
+ } catch {
550
+ print("Error!") // What now? No useful handling
551
+ }
552
+ }
553
+ ```
554
+
555
+ ### Provide User-Friendly Messages
556
+
557
+ ```swift
558
+ // ✅ Convert technical errors to user messages
559
+ extension Error {
560
+ var userFriendlyMessage: String {
561
+ switch self {
562
+ case NetworkError.connectionFailed:
563
+ return "Please check your internet connection and try again."
564
+ case NetworkError.timeout:
565
+ return "The request took too long. Please try again."
566
+ case NetworkError.unauthorized:
567
+ return "Your session has expired. Please log in again."
568
+ case let ValidationError.invalidFormat(field, _):
569
+ return "The \(field) format is invalid. Please check and try again."
570
+ default:
571
+ return "Something went wrong. Please try again later."
572
+ }
573
+ }
574
+ }
575
+
576
+ // Usage
577
+ do {
578
+ try performOperation()
579
+ } catch {
580
+ showAlert(message: error.userFriendlyMessage)
581
+ }
582
+ ```
583
+
584
+ ### Document Error Conditions
585
+
586
+ ```swift
587
+ // ✅ Document what errors can be thrown
588
+ /// Fetches a user from the API.
589
+ ///
590
+ /// - Parameter id: The unique identifier for the user
591
+ /// - Returns: The user object
592
+ /// - Throws:
593
+ /// - `NetworkError.connectionFailed` if no internet connection
594
+ /// - `NetworkError.unauthorized` if authentication fails
595
+ /// - `NetworkError.notFound` if user doesn't exist
596
+ /// - `DecodingError` if response parsing fails
597
+ func fetchUser(id: String) async throws -> User {
598
+ // Implementation
599
+ }
600
+ ```
601
+
602
+ ## Async Error Handling Patterns
603
+
604
+ ### Structured Concurrency Errors
605
+
606
+ ```swift
607
+ // ✅ First error cancels sibling tasks
608
+ func fetchDashboard() async throws -> Dashboard {
609
+ try await withThrowingTaskGroup(of: DashboardData.self) { group in
610
+ group.addTask { try await fetchUserData() }
611
+ group.addTask { try await fetchPosts() }
612
+ group.addTask { try await fetchStats() }
613
+
614
+ var results: [DashboardData] = []
615
+ for try await result in group {
616
+ results.append(result)
617
+ }
618
+
619
+ return Dashboard(data: results)
620
+ }
621
+ // If any task throws, others are automatically cancelled
622
+ }
623
+
624
+ // ✅ Collect all results, including errors
625
+ func fetchAllData() async -> [Result<Data, Error>] {
626
+ await withTaskGroup(of: Result<Data, Error>.self) { group in
627
+ for url in urls {
628
+ group.addTask {
629
+ do {
630
+ return .success(try await fetch(from: url))
631
+ } catch {
632
+ return .failure(error)
633
+ }
634
+ }
635
+ }
636
+
637
+ var results: [Result<Data, Error>] = []
638
+ for await result in group {
639
+ results.append(result)
640
+ }
641
+ return results
642
+ }
643
+ }
644
+ ```
645
+
646
+ ### Cancellation as Error
647
+
648
+ ```swift
649
+ // ✅ Handle task cancellation
650
+ func performLongOperation() async throws -> Result {
651
+ for i in 0..<1000 {
652
+ // Check for cancellation
653
+ try Task.checkCancellation()
654
+
655
+ await processItem(i)
656
+ }
657
+
658
+ return result
659
+ }
660
+
661
+ // ✅ Graceful cancellation handling
662
+ func downloadWithCancellation() async throws -> Data {
663
+ do {
664
+ return try await performDownload()
665
+ } catch is CancellationError {
666
+ // Clean up partial download
667
+ cleanupPartialData()
668
+ throw DownloadError.cancelled
669
+ }
670
+ }
671
+ ```
672
+
673
+ ---
674
+
675
+ **Sources:**
676
+ - [Swift.org: Error Handling](https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html)
677
+ - [Apple Documentation: Error Handling](https://developer.apple.com/documentation/swift/error)
678
+ - [Swift by Sundell: Error Handling](https://www.swiftbysundell.com/basics/error-handling/)
679
+ - [WWDC: Modern Swift Error Handling](https://developer.apple.com/videos/play/wwdc2020/10672/)