swift-code-reviewer-skill 1.2.0 → 1.3.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 (95) hide show
  1. package/CHANGELOG.md +43 -169
  2. package/README.md +43 -2
  3. package/SKILL.md +194 -711
  4. package/bin/install.js +1 -1
  5. package/package.json +2 -1
  6. package/references/agent-loop-feedback.md +148 -0
  7. package/references/companion-skills.md +70 -0
  8. package/references/spec-adherence.md +157 -0
  9. package/skills/README.md +43 -0
  10. package/skills/swift-concurrency/NOTICE.md +18 -0
  11. package/skills/swift-concurrency/SKILL.md +235 -0
  12. package/skills/swift-concurrency/references/actors.md +640 -0
  13. package/skills/swift-concurrency/references/async-await-basics.md +249 -0
  14. package/skills/swift-concurrency/references/async-sequences.md +635 -0
  15. package/skills/swift-concurrency/references/core-data.md +533 -0
  16. package/skills/swift-concurrency/references/glossary.md +96 -0
  17. package/skills/swift-concurrency/references/linting.md +38 -0
  18. package/skills/swift-concurrency/references/memory-management.md +542 -0
  19. package/skills/swift-concurrency/references/migration.md +721 -0
  20. package/skills/swift-concurrency/references/performance.md +574 -0
  21. package/skills/swift-concurrency/references/sendable.md +578 -0
  22. package/skills/swift-concurrency/references/tasks.md +604 -0
  23. package/skills/swift-concurrency/references/testing.md +565 -0
  24. package/skills/swift-concurrency/references/threading.md +452 -0
  25. package/skills/swift-expert/NOTICE.md +18 -0
  26. package/skills/swift-expert/SKILL.md +226 -0
  27. package/skills/swift-expert/references/async-concurrency.md +363 -0
  28. package/skills/swift-expert/references/memory-performance.md +380 -0
  29. package/skills/swift-expert/references/protocol-oriented.md +357 -0
  30. package/skills/swift-expert/references/swiftui-patterns.md +294 -0
  31. package/skills/swift-expert/references/testing-patterns.md +402 -0
  32. package/skills/swift-testing/NOTICE.md +18 -0
  33. package/skills/swift-testing/SKILL.md +295 -0
  34. package/skills/swift-testing/references/async-testing.md +245 -0
  35. package/skills/swift-testing/references/dump-snapshot-testing.md +265 -0
  36. package/skills/swift-testing/references/fixtures.md +193 -0
  37. package/skills/swift-testing/references/integration-testing.md +189 -0
  38. package/skills/swift-testing/references/migration-xctest.md +301 -0
  39. package/skills/swift-testing/references/parameterized-tests.md +171 -0
  40. package/skills/swift-testing/references/snapshot-testing.md +201 -0
  41. package/skills/swift-testing/references/test-doubles.md +243 -0
  42. package/skills/swift-testing/references/test-organization.md +231 -0
  43. package/skills/swiftui-expert-skill/NOTICE.md +18 -0
  44. package/skills/swiftui-expert-skill/SKILL.md +281 -0
  45. package/skills/swiftui-expert-skill/references/accessibility-patterns.md +151 -0
  46. package/skills/swiftui-expert-skill/references/animation-advanced.md +403 -0
  47. package/skills/swiftui-expert-skill/references/animation-basics.md +284 -0
  48. package/skills/swiftui-expert-skill/references/animation-transitions.md +326 -0
  49. package/skills/swiftui-expert-skill/references/charts-accessibility.md +135 -0
  50. package/skills/swiftui-expert-skill/references/charts.md +602 -0
  51. package/skills/swiftui-expert-skill/references/image-optimization.md +203 -0
  52. package/skills/swiftui-expert-skill/references/latest-apis.md +464 -0
  53. package/skills/swiftui-expert-skill/references/layout-best-practices.md +266 -0
  54. package/skills/swiftui-expert-skill/references/liquid-glass.md +414 -0
  55. package/skills/swiftui-expert-skill/references/list-patterns.md +394 -0
  56. package/skills/swiftui-expert-skill/references/macos-scenes.md +318 -0
  57. package/skills/swiftui-expert-skill/references/macos-views.md +357 -0
  58. package/skills/swiftui-expert-skill/references/macos-window-styling.md +303 -0
  59. package/skills/swiftui-expert-skill/references/performance-patterns.md +403 -0
  60. package/skills/swiftui-expert-skill/references/scroll-patterns.md +293 -0
  61. package/skills/swiftui-expert-skill/references/sheet-navigation-patterns.md +363 -0
  62. package/skills/swiftui-expert-skill/references/state-management.md +417 -0
  63. package/skills/swiftui-expert-skill/references/view-structure.md +389 -0
  64. package/skills/swiftui-ui-patterns/NOTICE.md +18 -0
  65. package/skills/swiftui-ui-patterns/SKILL.md +95 -0
  66. package/skills/swiftui-ui-patterns/references/app-wiring.md +201 -0
  67. package/skills/swiftui-ui-patterns/references/async-state.md +96 -0
  68. package/skills/swiftui-ui-patterns/references/components-index.md +50 -0
  69. package/skills/swiftui-ui-patterns/references/controls.md +57 -0
  70. package/skills/swiftui-ui-patterns/references/deeplinks.md +66 -0
  71. package/skills/swiftui-ui-patterns/references/focus.md +90 -0
  72. package/skills/swiftui-ui-patterns/references/form.md +97 -0
  73. package/skills/swiftui-ui-patterns/references/grids.md +71 -0
  74. package/skills/swiftui-ui-patterns/references/haptics.md +71 -0
  75. package/skills/swiftui-ui-patterns/references/input-toolbar.md +51 -0
  76. package/skills/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
  77. package/skills/swiftui-ui-patterns/references/list.md +86 -0
  78. package/skills/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
  79. package/skills/swiftui-ui-patterns/references/macos-settings.md +71 -0
  80. package/skills/swiftui-ui-patterns/references/matched-transitions.md +59 -0
  81. package/skills/swiftui-ui-patterns/references/media.md +73 -0
  82. package/skills/swiftui-ui-patterns/references/menu-bar.md +101 -0
  83. package/skills/swiftui-ui-patterns/references/navigationstack.md +159 -0
  84. package/skills/swiftui-ui-patterns/references/overlay.md +45 -0
  85. package/skills/swiftui-ui-patterns/references/performance.md +62 -0
  86. package/skills/swiftui-ui-patterns/references/previews.md +48 -0
  87. package/skills/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
  88. package/skills/swiftui-ui-patterns/references/scrollview.md +87 -0
  89. package/skills/swiftui-ui-patterns/references/searchable.md +71 -0
  90. package/skills/swiftui-ui-patterns/references/sheets.md +155 -0
  91. package/skills/swiftui-ui-patterns/references/split-views.md +72 -0
  92. package/skills/swiftui-ui-patterns/references/tabview.md +114 -0
  93. package/skills/swiftui-ui-patterns/references/theming.md +71 -0
  94. package/skills/swiftui-ui-patterns/references/title-menus.md +93 -0
  95. package/skills/swiftui-ui-patterns/references/top-bar.md +49 -0
@@ -0,0 +1,294 @@
1
+ # SwiftUI Patterns
2
+
3
+ > Reference for: Swift Expert
4
+ > Load when: Building SwiftUI views, state management, custom modifiers
5
+
6
+ ## State Management
7
+
8
+ ```swift
9
+ import SwiftUI
10
+
11
+ // @State for local view state
12
+ struct CounterView: View {
13
+ @State private var count = 0
14
+
15
+ var body: some View {
16
+ VStack {
17
+ Text("Count: \(count)")
18
+ Button("Increment") { count += 1 }
19
+ }
20
+ }
21
+ }
22
+
23
+ // @Binding for two-way data flow
24
+ struct ToggleView: View {
25
+ @Binding var isOn: Bool
26
+
27
+ var body: some View {
28
+ Toggle("Enable Feature", isOn: $isOn)
29
+ }
30
+ }
31
+
32
+ // @StateObject for observable objects (view owns it)
33
+ class ViewModel: ObservableObject {
34
+ @Published var items: [String] = []
35
+ @Published var isLoading = false
36
+ }
37
+
38
+ struct ContentView: View {
39
+ @StateObject private var viewModel = ViewModel()
40
+
41
+ var body: some View {
42
+ List(viewModel.items, id: \.self) { item in
43
+ Text(item)
44
+ }
45
+ }
46
+ }
47
+
48
+ // @ObservedObject for passed-in observable objects
49
+ struct DetailView: View {
50
+ @ObservedObject var viewModel: ViewModel
51
+ }
52
+
53
+ // @EnvironmentObject for dependency injection
54
+ struct AppView: View {
55
+ @EnvironmentObject var appState: AppState
56
+ }
57
+ ```
58
+
59
+ ## Modern View Composition
60
+
61
+ ```swift
62
+ // View builder for custom containers
63
+ struct ConditionalView<Content: View>: View {
64
+ let condition: Bool
65
+ @ViewBuilder let content: () -> Content
66
+
67
+ var body: some View {
68
+ if condition {
69
+ content()
70
+ } else {
71
+ EmptyView()
72
+ }
73
+ }
74
+ }
75
+
76
+ // Custom ViewModifier
77
+ struct CardModifier: ViewModifier {
78
+ func body(content: Content) -> some View {
79
+ content
80
+ .padding()
81
+ .background(Color.white)
82
+ .cornerRadius(12)
83
+ .shadow(radius: 4)
84
+ }
85
+ }
86
+
87
+ extension View {
88
+ func cardStyle() -> some View {
89
+ modifier(CardModifier())
90
+ }
91
+ }
92
+
93
+ // Usage
94
+ Text("Hello")
95
+ .cardStyle()
96
+ ```
97
+
98
+ ## Environment Values
99
+
100
+ ```swift
101
+ // Custom environment key
102
+ private struct ThemeKey: EnvironmentKey {
103
+ static let defaultValue: Theme = .light
104
+ }
105
+
106
+ extension EnvironmentValues {
107
+ var theme: Theme {
108
+ get { self[ThemeKey.self] }
109
+ set { self[ThemeKey.self] = newValue }
110
+ }
111
+ }
112
+
113
+ extension View {
114
+ func theme(_ theme: Theme) -> some View {
115
+ environment(\.theme, theme)
116
+ }
117
+ }
118
+
119
+ // Usage
120
+ struct ThemedView: View {
121
+ @Environment(\.theme) var theme
122
+
123
+ var body: some View {
124
+ Text("Themed")
125
+ .foregroundColor(theme.textColor)
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## Preference Keys
131
+
132
+ ```swift
133
+ // Collecting data from child views
134
+ struct SizePreferenceKey: PreferenceKey {
135
+ static var defaultValue: CGSize = .zero
136
+
137
+ static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
138
+ value = nextValue()
139
+ }
140
+ }
141
+
142
+ struct MeasurableView: View {
143
+ @State private var size: CGSize = .zero
144
+
145
+ var body: some View {
146
+ Text("Measure me")
147
+ .background(
148
+ GeometryReader { geometry in
149
+ Color.clear
150
+ .preference(key: SizePreferenceKey.self, value: geometry.size)
151
+ }
152
+ )
153
+ .onPreferenceChange(SizePreferenceKey.self) { newSize in
154
+ size = newSize
155
+ }
156
+ }
157
+ }
158
+ ```
159
+
160
+ ## Animations
161
+
162
+ ```swift
163
+ // Implicit animations
164
+ struct AnimatedView: View {
165
+ @State private var scale: CGFloat = 1.0
166
+
167
+ var body: some View {
168
+ Circle()
169
+ .scaleEffect(scale)
170
+ .animation(.spring(response: 0.5, dampingFraction: 0.6), value: scale)
171
+ .onTapGesture {
172
+ scale = scale == 1.0 ? 1.5 : 1.0
173
+ }
174
+ }
175
+ }
176
+
177
+ // Explicit animations
178
+ struct ExplicitAnimationView: View {
179
+ @State private var offset: CGFloat = 0
180
+
181
+ var body: some View {
182
+ Text("Slide")
183
+ .offset(x: offset)
184
+ .onTapGesture {
185
+ withAnimation(.easeInOut(duration: 0.3)) {
186
+ offset = offset == 0 ? 100 : 0
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ // Custom transitions
193
+ extension AnyTransition {
194
+ static var slideAndFade: AnyTransition {
195
+ AnyTransition.slide.combined(with: .opacity)
196
+ }
197
+ }
198
+ ```
199
+
200
+ ## Async/Await Integration
201
+
202
+ ```swift
203
+ struct AsyncDataView: View {
204
+ @State private var data: [Item] = []
205
+ @State private var isLoading = false
206
+
207
+ var body: some View {
208
+ List(data) { item in
209
+ Text(item.title)
210
+ }
211
+ .task {
212
+ await loadData()
213
+ }
214
+ .refreshable {
215
+ await loadData()
216
+ }
217
+ }
218
+
219
+ private func loadData() async {
220
+ isLoading = true
221
+ defer { isLoading = false }
222
+
223
+ do {
224
+ data = try await API.fetchItems()
225
+ } catch {
226
+ print("Error: \(error)")
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ ## Custom Layouts (iOS 16+)
233
+
234
+ ```swift
235
+ struct WaterfallLayout: Layout {
236
+ var columns: Int = 2
237
+ var spacing: CGFloat = 8
238
+
239
+ func sizeThatFits(
240
+ proposal: ProposedViewSize,
241
+ subviews: Subviews,
242
+ cache: inout ()
243
+ ) -> CGSize {
244
+ // Calculate total size needed
245
+ let columnWidth = (proposal.width! - spacing * CGFloat(columns - 1)) / CGFloat(columns)
246
+ var columnHeights = Array(repeating: CGFloat(0), count: columns)
247
+
248
+ for subview in subviews {
249
+ let column = columnHeights.enumerated().min(by: { $0.element < $1.element })!.offset
250
+ let size = subview.sizeThatFits(.init(width: columnWidth, height: nil))
251
+ columnHeights[column] += size.height + spacing
252
+ }
253
+
254
+ return CGSize(
255
+ width: proposal.width!,
256
+ height: columnHeights.max()! - spacing
257
+ )
258
+ }
259
+
260
+ func placeSubviews(
261
+ in bounds: CGRect,
262
+ proposal: ProposedViewSize,
263
+ subviews: Subviews,
264
+ cache: inout ()
265
+ ) {
266
+ let columnWidth = (bounds.width - spacing * CGFloat(columns - 1)) / CGFloat(columns)
267
+ var columnHeights = Array(repeating: CGFloat(0), count: columns)
268
+
269
+ for subview in subviews {
270
+ let column = columnHeights.enumerated().min(by: { $0.element < $1.element })!.offset
271
+ let x = bounds.minX + CGFloat(column) * (columnWidth + spacing)
272
+ let y = bounds.minY + columnHeights[column]
273
+
274
+ subview.place(
275
+ at: CGPoint(x: x, y: y),
276
+ proposal: .init(width: columnWidth, height: nil)
277
+ )
278
+
279
+ columnHeights[column] += subview.dimensions(in: .init(width: columnWidth, height: nil)).height + spacing
280
+ }
281
+ }
282
+ }
283
+ ```
284
+
285
+ ## Performance Tips
286
+
287
+ - Use `@State` for simple value types
288
+ - Use `@StateObject` for reference types you create
289
+ - Use `@ObservedObject` for reference types passed in
290
+ - Prefer `@Environment` over prop drilling
291
+ - Use `equatable()` modifier for expensive views
292
+ - Leverage `id()` modifier to control view identity
293
+ - Use `task(id:)` to cancel and restart async work
294
+ - Avoid computing expensive values in body - use `@State` or computed properties
@@ -0,0 +1,402 @@
1
+ # Testing Patterns
2
+
3
+ > Reference for: Swift Expert
4
+ > Load when: XCTest, async testing, mocking, test strategies
5
+
6
+ ## XCTest Basics
7
+
8
+ ```swift
9
+ import XCTest
10
+ @testable import MyApp
11
+
12
+ final class UserTests: XCTestCase {
13
+ var sut: UserManager!
14
+
15
+ override func setUp() {
16
+ super.setUp()
17
+ sut = UserManager()
18
+ }
19
+
20
+ override func tearDown() {
21
+ sut = nil
22
+ super.tearDown()
23
+ }
24
+
25
+ func testUserCreation() {
26
+ // Given
27
+ let name = "John Doe"
28
+ let email = "john@example.com"
29
+
30
+ // When
31
+ let user = sut.createUser(name: name, email: email)
32
+
33
+ // Then
34
+ XCTAssertEqual(user.name, name)
35
+ XCTAssertEqual(user.email, email)
36
+ XCTAssertNotNil(user.id)
37
+ }
38
+
39
+ func testValidation() throws {
40
+ // Unwrapping optionals in tests
41
+ let user = try XCTUnwrap(sut.findUser(id: 123))
42
+ XCTAssertEqual(user.name, "Test User")
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Async Testing
48
+
49
+ ```swift
50
+ final class AsyncTests: XCTestCase {
51
+ func testAsyncFunction() async throws {
52
+ // Test async/await code directly
53
+ let result = try await fetchData()
54
+ XCTAssertEqual(result.count, 10)
55
+ }
56
+
57
+ func testAsyncSequence() async throws {
58
+ var results: [Int] = []
59
+
60
+ for try await value in numberStream() {
61
+ results.append(value)
62
+ if results.count >= 5 {
63
+ break
64
+ }
65
+ }
66
+
67
+ XCTAssertEqual(results.count, 5)
68
+ }
69
+
70
+ func testWithTimeout() async throws {
71
+ // Test with timeout
72
+ try await withTimeout(seconds: 5) {
73
+ try await longRunningOperation()
74
+ }
75
+ }
76
+
77
+ func testConcurrentOperations() async throws {
78
+ async let result1 = fetchData(id: 1)
79
+ async let result2 = fetchData(id: 2)
80
+
81
+ let (data1, data2) = try await (result1, result2)
82
+
83
+ XCTAssertNotNil(data1)
84
+ XCTAssertNotNil(data2)
85
+ }
86
+ }
87
+
88
+ // Helper for timeout
89
+ func withTimeout<T>(
90
+ seconds: TimeInterval,
91
+ operation: @escaping () async throws -> T
92
+ ) async throws -> T {
93
+ try await withThrowingTaskGroup(of: T.self) { group in
94
+ group.addTask {
95
+ try await operation()
96
+ }
97
+
98
+ group.addTask {
99
+ try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
100
+ throw TimeoutError()
101
+ }
102
+
103
+ let result = try await group.next()!
104
+ group.cancelAll()
105
+ return result
106
+ }
107
+ }
108
+ ```
109
+
110
+ ## Mocking
111
+
112
+ ```swift
113
+ // Protocol for dependency injection
114
+ protocol DataService {
115
+ func fetch(id: Int) async throws -> Data
116
+ func save(_ data: Data) async throws
117
+ }
118
+
119
+ // Production implementation
120
+ class APIDataService: DataService {
121
+ func fetch(id: Int) async throws -> Data {
122
+ // Real API call
123
+ }
124
+
125
+ func save(_ data: Data) async throws {
126
+ // Real save operation
127
+ }
128
+ }
129
+
130
+ // Mock for testing
131
+ class MockDataService: DataService {
132
+ var fetchCalled = false
133
+ var fetchID: Int?
134
+ var fetchResult: Data?
135
+ var fetchError: Error?
136
+
137
+ var saveCalled = false
138
+ var savedData: Data?
139
+ var saveError: Error?
140
+
141
+ func fetch(id: Int) async throws -> Data {
142
+ fetchCalled = true
143
+ fetchID = id
144
+
145
+ if let error = fetchError {
146
+ throw error
147
+ }
148
+
149
+ return fetchResult ?? Data()
150
+ }
151
+
152
+ func save(_ data: Data) async throws {
153
+ saveCalled = true
154
+ savedData = data
155
+
156
+ if let error = saveError {
157
+ throw error
158
+ }
159
+ }
160
+ }
161
+
162
+ // Using mock in tests
163
+ final class DataManagerTests: XCTestCase {
164
+ func testDataFetch() async throws {
165
+ // Given
166
+ let mockService = MockDataService()
167
+ mockService.fetchResult = "test data".data(using: .utf8)
168
+ let manager = DataManager(service: mockService)
169
+
170
+ // When
171
+ let result = try await manager.loadData(id: 123)
172
+
173
+ // Then
174
+ XCTAssertTrue(mockService.fetchCalled)
175
+ XCTAssertEqual(mockService.fetchID, 123)
176
+ XCTAssertNotNil(result)
177
+ }
178
+ }
179
+ ```
180
+
181
+ ## Test Doubles
182
+
183
+ ```swift
184
+ // Spy - records interactions
185
+ class SpyDelegate: UserManagerDelegate {
186
+ private(set) var didUpdateUserCalled = false
187
+ private(set) var updatedUser: User?
188
+ private(set) var callCount = 0
189
+
190
+ func didUpdateUser(_ user: User) {
191
+ didUpdateUserCalled = true
192
+ updatedUser = user
193
+ callCount += 1
194
+ }
195
+ }
196
+
197
+ // Stub - provides predetermined responses
198
+ class StubNetworkService: NetworkService {
199
+ var stubbedResponse: Result<Data, Error> = .success(Data())
200
+
201
+ func fetch(url: URL) async throws -> Data {
202
+ try stubbedResponse.get()
203
+ }
204
+ }
205
+
206
+ // Fake - working implementation with shortcuts
207
+ class FakeDatabase: Database {
208
+ private var storage: [String: Data] = [:]
209
+
210
+ func save(key: String, value: Data) {
211
+ storage[key] = value
212
+ }
213
+
214
+ func load(key: String) -> Data? {
215
+ storage[key]
216
+ }
217
+
218
+ func clear() {
219
+ storage.removeAll()
220
+ }
221
+ }
222
+ ```
223
+
224
+ ## Performance Testing
225
+
226
+ ```swift
227
+ final class PerformanceTests: XCTestCase {
228
+ func testSortingPerformance() {
229
+ let numbers = (0..<10000).shuffled()
230
+
231
+ measure {
232
+ _ = numbers.sorted()
233
+ }
234
+ }
235
+
236
+ func testCustomMetrics() {
237
+ let metrics: [XCTMetric] = [
238
+ XCTClockMetric(),
239
+ XCTCPUMetric(),
240
+ XCTMemoryMetric(),
241
+ XCTStorageMetric()
242
+ ]
243
+
244
+ let options = XCTMeasureOptions()
245
+ options.iterationCount = 10
246
+
247
+ measure(metrics: metrics, options: options) {
248
+ performExpensiveOperation()
249
+ }
250
+ }
251
+ }
252
+ ```
253
+
254
+ ## UI Testing
255
+
256
+ ```swift
257
+ final class AppUITests: XCTestCase {
258
+ var app: XCUIApplication!
259
+
260
+ override func setUp() {
261
+ super.setUp()
262
+ continueAfterFailure = false
263
+ app = XCUIApplication()
264
+ app.launch()
265
+ }
266
+
267
+ func testLoginFlow() {
268
+ // Test UI interactions
269
+ let emailField = app.textFields["Email"]
270
+ emailField.tap()
271
+ emailField.typeText("test@example.com")
272
+
273
+ let passwordField = app.secureTextFields["Password"]
274
+ passwordField.tap()
275
+ passwordField.typeText("password123")
276
+
277
+ app.buttons["Login"].tap()
278
+
279
+ // Verify navigation
280
+ XCTAssertTrue(app.navigationBars["Dashboard"].exists)
281
+ }
282
+
283
+ func testButtonEnabled() {
284
+ let button = app.buttons["Submit"]
285
+ XCTAssertFalse(button.isEnabled)
286
+
287
+ app.textFields["Username"].tap()
288
+ app.textFields["Username"].typeText("testuser")
289
+
290
+ XCTAssertTrue(button.isEnabled)
291
+ }
292
+ }
293
+ ```
294
+
295
+ ## Testing Actors
296
+
297
+ ```swift
298
+ final class ActorTests: XCTestCase {
299
+ func testActorIsolation() async throws {
300
+ actor Counter {
301
+ private var value = 0
302
+
303
+ func increment() -> Int {
304
+ value += 1
305
+ return value
306
+ }
307
+
308
+ func reset() {
309
+ value = 0
310
+ }
311
+ }
312
+
313
+ let counter = Counter()
314
+
315
+ // Test concurrent access
316
+ await withTaskGroup(of: Int.self) { group in
317
+ for _ in 0..<100 {
318
+ group.addTask {
319
+ await counter.increment()
320
+ }
321
+ }
322
+ }
323
+
324
+ let finalValue = await counter.increment()
325
+ XCTAssertEqual(finalValue, 101)
326
+ }
327
+ }
328
+ ```
329
+
330
+ ## Snapshot Testing
331
+
332
+ ```swift
333
+ import SnapshotTesting
334
+
335
+ final class ViewSnapshotTests: XCTestCase {
336
+ func testButtonAppearance() {
337
+ let button = UIButton()
338
+ button.setTitle("Tap Me", for: .normal)
339
+ button.backgroundColor = .blue
340
+ button.frame = CGRect(x: 0, y: 0, width: 200, height: 50)
341
+
342
+ assertSnapshot(matching: button, as: .image)
343
+ }
344
+
345
+ func testViewControllerLayout() {
346
+ let vc = MyViewController()
347
+ assertSnapshot(matching: vc, as: .image(on: .iPhone13))
348
+ }
349
+
350
+ func testDarkMode() {
351
+ let view = MyView()
352
+ assertSnapshot(matching: view, as: .image(traits: .init(userInterfaceStyle: .dark)))
353
+ }
354
+ }
355
+ ```
356
+
357
+ ## Test Organization
358
+
359
+ ```swift
360
+ // MARK: - Test Cases
361
+ extension UserManagerTests {
362
+ // MARK: Creation Tests
363
+ func testUserCreation() { }
364
+ func testUserCreationWithInvalidData() { }
365
+
366
+ // MARK: Validation Tests
367
+ func testEmailValidation() { }
368
+ func testPasswordValidation() { }
369
+
370
+ // MARK: Persistence Tests
371
+ func testUserSave() { }
372
+ func testUserLoad() { }
373
+ }
374
+
375
+ // MARK: - Test Helpers
376
+ extension UserManagerTests {
377
+ func makeTestUser() -> User {
378
+ User(name: "Test", email: "test@example.com")
379
+ }
380
+
381
+ func setupMockData() {
382
+ // Common test setup
383
+ }
384
+ }
385
+ ```
386
+
387
+ ## Best Practices
388
+
389
+ - Use `@testable import` to test internal types
390
+ - One assertion concept per test (can have multiple XCTAssert calls)
391
+ - Use Given-When-Then pattern for clarity
392
+ - Name tests descriptively: `test_methodName_condition_expectedResult`
393
+ - Use setUp/tearDown for common test setup
394
+ - Prefer dependency injection for testability
395
+ - Use protocols to enable mocking
396
+ - Test edge cases and error conditions
397
+ - Use async/await for testing async code
398
+ - Measure performance with XCTest metrics
399
+ - Use UI testing for critical user flows
400
+ - Mock external dependencies
401
+ - Keep tests fast and independent
402
+ - Use test doubles appropriately (mock, stub, spy, fake)
@@ -0,0 +1,18 @@
1
+ # swift-testing — Attribution Notice
2
+
3
+ Bundled into `swift-code-reviewer-skill` on 2026-04-21 from
4
+ `~/.claude/skills/swift-testing` (resolved from symlink `~/.agents/skills/swift-testing`).
5
+
6
+ Primary author (best-effort attribution): **Antoine van der Lee ([@AvdLee](https://github.com/AvdLee))**
7
+ Also credited: [@Dimillian](https://github.com/Dimillian), [@bocato](https://github.com/bocato)
8
+
9
+ These three authors' public Swift/SwiftUI content — including SwiftLee, IceCubesApp,
10
+ and their various open-source contributions — informed the skills bundled here.
11
+
12
+ License: the upstream folder did not contain a LICENSE file at the time of vendoring.
13
+ Content is reproduced here in good faith for reference alongside this MIT-licensed
14
+ project. If you are an upstream author and want the attribution corrected, the license
15
+ clarified, or the content removed, please open an issue at:
16
+ https://github.com/Viniciuscarvalho/swift-code-reviewer-skill/issues
17
+
18
+ Changes from upstream: none (verbatim copy; `.DS_Store` files excluded).