@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,654 @@
1
+ # Swift Concurrency
2
+
3
+ ## Async/Await Fundamentals
4
+
5
+ Swift's modern concurrency model uses async/await for asynchronous operations:
6
+
7
+ ```swift
8
+ // ✅ Async function definition
9
+ func fetchUser(id: String) async throws -> User {
10
+ let url = URL(string: "https://api.example.com/users/\(id)")!
11
+ let (data, _) = try await URLSession.shared.data(from: url)
12
+ return try JSONDecoder().decode(User.self, from: data)
13
+ }
14
+
15
+ // ✅ Calling async functions
16
+ func loadUserData() async {
17
+ do {
18
+ let user = try await fetchUser(id: "123")
19
+ print("User: \(user.name)")
20
+ } catch {
21
+ print("Error: \(error)")
22
+ }
23
+ }
24
+
25
+ // ❌ Cannot call async function without await
26
+ func badExample() {
27
+ let user = fetchUser(id: "123") // Compile error
28
+ }
29
+ ```
30
+
31
+ ## Actors for Thread Safety
32
+
33
+ Actors ensure data race safety by serializing access to mutable state:
34
+
35
+ ```swift
36
+ // ✅ Actor protects mutable state
37
+ actor Counter {
38
+ private var value = 0
39
+
40
+ func increment() {
41
+ value += 1
42
+ }
43
+
44
+ func getValue() -> Int {
45
+ return value
46
+ }
47
+ }
48
+
49
+ // Usage
50
+ let counter = Counter()
51
+ await counter.increment()
52
+ let value = await counter.getValue()
53
+
54
+ // ✅ Actor with async methods
55
+ actor ImageCache {
56
+ private var cache: [String: UIImage] = [:]
57
+
58
+ func image(for key: String) async -> UIImage? {
59
+ if let cached = cache[key] {
60
+ return cached
61
+ }
62
+
63
+ let image = await downloadImage(for: key)
64
+ cache[key] = image
65
+ return image
66
+ }
67
+
68
+ private func downloadImage(for key: String) async -> UIImage? {
69
+ // Download implementation
70
+ return nil
71
+ }
72
+ }
73
+
74
+ // ❌ Don't use class with locks when actor is safer
75
+ class UnsafeCache {
76
+ private var cache: [String: UIImage] = [:]
77
+ private let lock = NSLock()
78
+
79
+ func image(for key: String) -> UIImage? {
80
+ lock.lock()
81
+ defer { lock.unlock() }
82
+ return cache[key]
83
+ }
84
+ }
85
+ ```
86
+
87
+ ## @MainActor for UI Updates
88
+
89
+ Use @MainActor to ensure code runs on the main thread:
90
+
91
+ ```swift
92
+ // ✅ Mark entire class as @MainActor
93
+ @MainActor
94
+ class UserViewModel: ObservableObject {
95
+ @Published var user: User?
96
+ @Published var isLoading = false
97
+
98
+ func loadUser(id: String) async {
99
+ isLoading = true
100
+ defer { isLoading = false }
101
+
102
+ do {
103
+ user = try await fetchUser(id: id)
104
+ } catch {
105
+ print("Error: \(error)")
106
+ }
107
+ }
108
+ }
109
+
110
+ // ✅ Mark specific methods as @MainActor
111
+ class DataService {
112
+ func fetchData() async -> Data {
113
+ // Background work
114
+ return Data()
115
+ }
116
+
117
+ @MainActor
118
+ func updateUI(with data: Data) {
119
+ // UI updates guaranteed on main thread
120
+ }
121
+ }
122
+
123
+ // ✅ Mark properties as @MainActor
124
+ class ViewModel {
125
+ @MainActor var title: String = ""
126
+
127
+ func loadData() async {
128
+ let data = await fetchData()
129
+ await updateTitle(data.title)
130
+ }
131
+
132
+ @MainActor
133
+ private func updateTitle(_ newTitle: String) {
134
+ title = newTitle
135
+ }
136
+ }
137
+
138
+ // ❌ Avoid manual dispatch to main queue
139
+ func badUpdate() async {
140
+ let data = await fetchData()
141
+ DispatchQueue.main.async {
142
+ // Update UI
143
+ }
144
+ }
145
+ ```
146
+
147
+ ## Structured Concurrency with Task
148
+
149
+ Use Task for structured concurrent operations:
150
+
151
+ ```swift
152
+ // ✅ Simple Task creation
153
+ Task {
154
+ let user = try await fetchUser(id: "123")
155
+ print(user)
156
+ }
157
+
158
+ // ✅ Task with priority
159
+ Task(priority: .high) {
160
+ let urgentData = try await fetchUrgentData()
161
+ process(urgentData)
162
+ }
163
+
164
+ // ✅ Detached task (unstructured)
165
+ Task.detached {
166
+ // Runs independently of current context
167
+ await performBackgroundWork()
168
+ }
169
+
170
+ // ✅ Task cancellation
171
+ let task = Task {
172
+ try await longRunningOperation()
173
+ }
174
+
175
+ // Later, cancel the task
176
+ task.cancel()
177
+
178
+ // ✅ Check for cancellation
179
+ func processItems(_ items: [Item]) async throws {
180
+ for item in items {
181
+ try Task.checkCancellation() // Throw if cancelled
182
+ await process(item)
183
+ }
184
+ }
185
+
186
+ // ✅ Handle cancellation gracefully
187
+ func fetchWithCancellation() async throws -> Data {
188
+ guard !Task.isCancelled else {
189
+ throw CancellationError()
190
+ }
191
+
192
+ return try await URLSession.shared.data(from: url).0
193
+ }
194
+ ```
195
+
196
+ ## TaskGroup for Parallel Operations
197
+
198
+ Use TaskGroup to run multiple async operations in parallel:
199
+
200
+ ```swift
201
+ // ✅ Parallel data fetching
202
+ func fetchAllUsers(ids: [String]) async throws -> [User] {
203
+ try await withThrowingTaskGroup(of: User.self) { group in
204
+ for id in ids {
205
+ group.addTask {
206
+ try await fetchUser(id: id)
207
+ }
208
+ }
209
+
210
+ var users: [User] = []
211
+ for try await user in group {
212
+ users.append(user)
213
+ }
214
+ return users
215
+ }
216
+ }
217
+
218
+ // ✅ Process results as they complete
219
+ func processImages(_ urls: [URL]) async {
220
+ await withTaskGroup(of: UIImage?.self) { group in
221
+ for url in urls {
222
+ group.addTask {
223
+ await downloadImage(from: url)
224
+ }
225
+ }
226
+
227
+ for await image in group {
228
+ if let image = image {
229
+ await display(image)
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ // ✅ Limit concurrency with semaphore pattern
236
+ func processWithLimit(_ items: [Item], limit: Int) async {
237
+ await withTaskGroup(of: Void.self) { group in
238
+ var iterator = items.makeIterator()
239
+
240
+ // Start initial batch
241
+ for _ in 0..<min(limit, items.count) {
242
+ if let item = iterator.next() {
243
+ group.addTask {
244
+ await process(item)
245
+ }
246
+ }
247
+ }
248
+
249
+ // As tasks complete, start new ones
250
+ for await _ in group {
251
+ if let item = iterator.next() {
252
+ group.addTask {
253
+ await process(item)
254
+ }
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ // ❌ Don't use unstructured concurrency for related tasks
261
+ func badParallelFetch(ids: [String]) async -> [User] {
262
+ var users: [User] = []
263
+ for id in ids {
264
+ Task { // Unstructured - loses parent context
265
+ if let user = try? await fetchUser(id: id) {
266
+ users.append(user) // Data race!
267
+ }
268
+ }
269
+ }
270
+ return users
271
+ }
272
+ ```
273
+
274
+ ## AsyncSequence for Streaming Data
275
+
276
+ Use AsyncSequence for processing streams of data:
277
+
278
+ ```swift
279
+ // ✅ Consuming AsyncSequence
280
+ func processNotifications() async {
281
+ for await notification in notificationStream {
282
+ handle(notification)
283
+ }
284
+ }
285
+
286
+ // ✅ Creating custom AsyncSequence
287
+ struct Countdown: AsyncSequence {
288
+ typealias Element = Int
289
+ let start: Int
290
+
291
+ struct AsyncIterator: AsyncIteratorProtocol {
292
+ var current: Int
293
+
294
+ mutating func next() async -> Int? {
295
+ guard current >= 0 else { return nil }
296
+
297
+ let value = current
298
+ current -= 1
299
+ try? await Task.sleep(nanoseconds: 1_000_000_000)
300
+ return value
301
+ }
302
+ }
303
+
304
+ func makeAsyncIterator() -> AsyncIterator {
305
+ AsyncIterator(current: start)
306
+ }
307
+ }
308
+
309
+ // Usage
310
+ for await number in Countdown(start: 5) {
311
+ print(number)
312
+ }
313
+
314
+ // ✅ Transform AsyncSequence
315
+ func processStream() async {
316
+ let stream = URLSession.shared.bytes(from: url)
317
+
318
+ for try await byte in stream {
319
+ process(byte)
320
+ }
321
+ }
322
+
323
+ // ✅ Async sequence with map/filter
324
+ extension AsyncSequence {
325
+ func mapAsync<T>(_ transform: @escaping (Element) async throws -> T) -> AsyncThrowingStream<T, Error> {
326
+ AsyncThrowingStream { continuation in
327
+ Task {
328
+ do {
329
+ for try await element in self {
330
+ let transformed = try await transform(element)
331
+ continuation.yield(transformed)
332
+ }
333
+ continuation.finish()
334
+ } catch {
335
+ continuation.finish(throwing: error)
336
+ }
337
+ }
338
+ }
339
+ }
340
+ }
341
+ ```
342
+
343
+ ## Continuations for Bridging Callback APIs
344
+
345
+ Use continuations to wrap callback-based APIs:
346
+
347
+ ```swift
348
+ // ✅ Wrap completion handler with continuation
349
+ func fetchData() async throws -> Data {
350
+ try await withCheckedThrowingContinuation { continuation in
351
+ legacyFetchData { result in
352
+ switch result {
353
+ case .success(let data):
354
+ continuation.resume(returning: data)
355
+ case .failure(let error):
356
+ continuation.resume(throwing: error)
357
+ }
358
+ }
359
+ }
360
+ }
361
+
362
+ // ✅ Wrap delegate callback
363
+ func requestPermission() async -> Bool {
364
+ await withCheckedContinuation { continuation in
365
+ permissionManager.request { granted in
366
+ continuation.resume(returning: granted)
367
+ }
368
+ }
369
+ }
370
+
371
+ // ⚠️ Use unsafe continuation only when necessary
372
+ func unsafeExample() async -> String {
373
+ await withUnsafeContinuation { continuation in
374
+ // Must call resume exactly once!
375
+ continuation.resume(returning: "value")
376
+ }
377
+ }
378
+
379
+ // ❌ Never resume continuation multiple times
380
+ func badContinuation() async {
381
+ await withCheckedContinuation { continuation in
382
+ continuation.resume(returning: 1)
383
+ continuation.resume(returning: 2) // Runtime error!
384
+ }
385
+ }
386
+ ```
387
+
388
+ ## Sendable Protocol
389
+
390
+ Use Sendable to ensure types can be safely passed across concurrency boundaries:
391
+
392
+ ```swift
393
+ // ✅ Value types are implicitly Sendable
394
+ struct User: Sendable {
395
+ let id: String
396
+ let name: String
397
+ }
398
+
399
+ // ✅ Immutable reference types can be Sendable
400
+ final class Configuration: Sendable {
401
+ let apiKey: String
402
+ let baseURL: URL
403
+
404
+ init(apiKey: String, baseURL: URL) {
405
+ self.apiKey = apiKey
406
+ self.baseURL = baseURL
407
+ }
408
+ }
409
+
410
+ // ✅ Actor types are implicitly Sendable
411
+ actor DatabaseManager: Sendable {
412
+ func save(_ item: Item) async throws {
413
+ // Implementation
414
+ }
415
+ }
416
+
417
+ // ❌ Mutable classes should not be Sendable
418
+ class MutableState: Sendable { // Warning!
419
+ var count = 0 // Unsafe across concurrency boundaries
420
+ }
421
+
422
+ // ✅ Use actor instead
423
+ actor SafeState {
424
+ var count = 0
425
+
426
+ func increment() {
427
+ count += 1
428
+ }
429
+ }
430
+ ```
431
+
432
+ ## Best Practices
433
+
434
+ ### Avoid Data Races
435
+
436
+ ```swift
437
+ // ❌ Data race with shared mutable state
438
+ class Counter {
439
+ var value = 0
440
+
441
+ func increment() {
442
+ value += 1 // Unsafe if called from multiple tasks
443
+ }
444
+ }
445
+
446
+ // ✅ Use actor for shared mutable state
447
+ actor Counter {
448
+ var value = 0
449
+
450
+ func increment() {
451
+ value += 1 // Safe - actor serializes access
452
+ }
453
+ }
454
+ ```
455
+
456
+ ### Prefer Structured Concurrency
457
+
458
+ ```swift
459
+ // ❌ Unstructured tasks lose parent context
460
+ func badExample() {
461
+ Task {
462
+ await work1()
463
+ }
464
+ Task {
465
+ await work2()
466
+ }
467
+ // Returns immediately, tasks run independently
468
+ }
469
+
470
+ // ✅ Structured concurrency with async let
471
+ func goodExample() async {
472
+ async let result1 = work1()
473
+ async let result2 = work2()
474
+
475
+ let (r1, r2) = await (result1, result2)
476
+ // Both complete before function returns
477
+ }
478
+ ```
479
+
480
+ ### Use async let for Independent Operations
481
+
482
+ ```swift
483
+ // ✅ Parallel execution with async let
484
+ func loadDashboard() async throws -> Dashboard {
485
+ async let user = fetchUser()
486
+ async let posts = fetchPosts()
487
+ async let stats = fetchStats()
488
+
489
+ return try await Dashboard(
490
+ user: user,
491
+ posts: posts,
492
+ stats: stats
493
+ )
494
+ }
495
+
496
+ // ❌ Sequential execution (slower)
497
+ func slowDashboard() async throws -> Dashboard {
498
+ let user = try await fetchUser()
499
+ let posts = try await fetchPosts()
500
+ let stats = try await fetchStats()
501
+
502
+ return Dashboard(user: user, posts: posts, stats: stats)
503
+ }
504
+ ```
505
+
506
+ ### Handle Task Cancellation
507
+
508
+ ```swift
509
+ // ✅ Respect cancellation
510
+ func processItems(_ items: [Item]) async throws {
511
+ for item in items {
512
+ try Task.checkCancellation()
513
+ await process(item)
514
+ }
515
+ }
516
+
517
+ // ✅ Clean up on cancellation
518
+ func downloadFile() async throws -> URL {
519
+ let task = Task {
520
+ try await actualDownload()
521
+ }
522
+
523
+ defer {
524
+ if Task.isCancelled {
525
+ cleanupPartialDownload()
526
+ }
527
+ }
528
+
529
+ return try await task.value
530
+ }
531
+ ```
532
+
533
+ ### Avoid Blocking the Main Thread
534
+
535
+ ```swift
536
+ // ❌ Blocking UI thread
537
+ @MainActor
538
+ func loadData() {
539
+ let data = fetchDataSynchronously() // Blocks UI!
540
+ updateUI(data)
541
+ }
542
+
543
+ // ✅ Use async/await
544
+ @MainActor
545
+ func loadData() async {
546
+ let data = await fetchDataAsynchronously()
547
+ updateUI(data) // Already on main actor
548
+ }
549
+ ```
550
+
551
+ ## Common Patterns
552
+
553
+ ### Retry with Backoff
554
+
555
+ ```swift
556
+ func fetchWithRetry<T>(
557
+ maxAttempts: Int = 3,
558
+ operation: @escaping () async throws -> T
559
+ ) async throws -> T {
560
+ var lastError: Error?
561
+
562
+ for attempt in 0..<maxAttempts {
563
+ do {
564
+ return try await operation()
565
+ } catch {
566
+ lastError = error
567
+
568
+ if attempt < maxAttempts - 1 {
569
+ let delay = UInt64(pow(2.0, Double(attempt)) * 1_000_000_000)
570
+ try await Task.sleep(nanoseconds: delay)
571
+ }
572
+ }
573
+ }
574
+
575
+ throw lastError!
576
+ }
577
+
578
+ // Usage
579
+ let user = try await fetchWithRetry {
580
+ try await fetchUser(id: "123")
581
+ }
582
+ ```
583
+
584
+ ### Timeout
585
+
586
+ ```swift
587
+ func withTimeout<T>(
588
+ seconds: TimeInterval,
589
+ operation: @escaping () async throws -> T
590
+ ) async throws -> T {
591
+ try await withThrowingTaskGroup(of: T.self) { group in
592
+ group.addTask {
593
+ try await operation()
594
+ }
595
+
596
+ group.addTask {
597
+ try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
598
+ throw TimeoutError()
599
+ }
600
+
601
+ let result = try await group.next()!
602
+ group.cancelAll()
603
+ return result
604
+ }
605
+ }
606
+
607
+ // Usage
608
+ let user = try await withTimeout(seconds: 5) {
609
+ try await fetchUser(id: "123")
610
+ }
611
+ ```
612
+
613
+ ### Debounce
614
+
615
+ ```swift
616
+ actor Debouncer<T: Sendable> {
617
+ private var task: Task<T, Error>?
618
+ private let delay: Duration
619
+ private let operation: @Sendable () async throws -> T
620
+
621
+ init(delay: Duration, operation: @escaping @Sendable () async throws -> T) {
622
+ self.delay = delay
623
+ self.operation = operation
624
+ }
625
+
626
+ func call() async throws -> T {
627
+ task?.cancel()
628
+
629
+ let newTask = Task {
630
+ try await Task.sleep(for: delay)
631
+ return try await operation()
632
+ }
633
+
634
+ task = newTask
635
+ return try await newTask.value
636
+ }
637
+ }
638
+
639
+ // Usage
640
+ let searchDebouncer = Debouncer(delay: .milliseconds(300)) {
641
+ await searchAPI(query: currentQuery)
642
+ }
643
+
644
+ // Only last call within 300ms executes
645
+ try await searchDebouncer.call()
646
+ ```
647
+
648
+ ---
649
+
650
+ **Sources:**
651
+ - [Swift Evolution: Concurrency](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md)
652
+ - [Swift.org: Concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html)
653
+ - [WWDC21: Meet async/await in Swift](https://developer.apple.com/videos/play/wwdc2021/10132/)
654
+ - [WWDC21: Protect mutable state with Swift actors](https://developer.apple.com/videos/play/wwdc2021/10133/)