swift-code-reviewer-skill 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.
- package/CHANGELOG.md +214 -0
- package/CONTRIBUTING.md +271 -0
- package/LICENSE +21 -0
- package/README.md +536 -0
- package/SKILL.md +690 -0
- package/bin/install.js +173 -0
- package/package.json +41 -0
- package/references/architecture-patterns.md +862 -0
- package/references/custom-guidelines.md +852 -0
- package/references/feedback-templates.md +666 -0
- package/references/performance-review.md +914 -0
- package/references/review-workflow.md +1131 -0
- package/references/security-checklist.md +781 -0
- package/references/swift-quality-checklist.md +928 -0
- package/references/swiftui-review-checklist.md +909 -0
|
@@ -0,0 +1,1131 @@
|
|
|
1
|
+
# Swift/SwiftUI Code Review Workflow
|
|
2
|
+
|
|
3
|
+
This document provides a detailed, step-by-step workflow for reviewing Swift and SwiftUI code changes. Follow this process to ensure comprehensive, consistent, and actionable code reviews.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The review workflow consists of four main phases:
|
|
8
|
+
1. **Context Gathering**: Understand the project and scope
|
|
9
|
+
2. **Automated Analysis**: Run quality checks across categories
|
|
10
|
+
3. **Report Generation**: Aggregate and organize findings
|
|
11
|
+
4. **Delivery**: Present actionable feedback
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Phase 1: Context Gathering
|
|
16
|
+
|
|
17
|
+
### Step 1.1: Read Project-Specific Guidelines
|
|
18
|
+
|
|
19
|
+
**Objective**: Understand the project's coding standards and architecture
|
|
20
|
+
|
|
21
|
+
**Actions:**
|
|
22
|
+
|
|
23
|
+
1. **Check for .claude/CLAUDE.md**
|
|
24
|
+
```bash
|
|
25
|
+
# Check if project guidelines exist
|
|
26
|
+
if [ -f .claude/CLAUDE.md ]; then
|
|
27
|
+
echo "Project guidelines found"
|
|
28
|
+
fi
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
2. **Read the Guidelines File**
|
|
32
|
+
- Look for sections on:
|
|
33
|
+
- Coding standards
|
|
34
|
+
- Architecture patterns (MVVM, MVI, TCA)
|
|
35
|
+
- Dependency injection approach
|
|
36
|
+
- Error handling patterns
|
|
37
|
+
- Testing requirements
|
|
38
|
+
- Design system guidelines
|
|
39
|
+
- Navigation patterns
|
|
40
|
+
|
|
41
|
+
3. **Read Related Architecture Documents**
|
|
42
|
+
Common files to check:
|
|
43
|
+
- `.claude/DependencyInjection-Architecture.md`
|
|
44
|
+
- `.claude/Design System Structure.md`
|
|
45
|
+
- `.claude/Navigation-Architecture.md`
|
|
46
|
+
- `.claude/Testing-Guidelines.md`
|
|
47
|
+
|
|
48
|
+
4. **Extract Key Standards**
|
|
49
|
+
- Custom naming conventions
|
|
50
|
+
- Required property wrappers
|
|
51
|
+
- Error handling patterns
|
|
52
|
+
- ViewModel structure
|
|
53
|
+
- Repository patterns
|
|
54
|
+
- Testing coverage requirements
|
|
55
|
+
|
|
56
|
+
**Example Checklist:**
|
|
57
|
+
```markdown
|
|
58
|
+
Project Guidelines Summary:
|
|
59
|
+
- Architecture: MVVM with Coordinators
|
|
60
|
+
- DI: Constructor injection preferred
|
|
61
|
+
- State Management: @Observable for ViewModels
|
|
62
|
+
- Error Handling: Result<Success, Error> pattern
|
|
63
|
+
- Testing: Minimum 80% coverage
|
|
64
|
+
- Design System: Use AppColors, AppFonts, AppSpacing
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Step 1.2: Identify Review Scope
|
|
68
|
+
|
|
69
|
+
**Objective**: Determine which files to review and what changed
|
|
70
|
+
|
|
71
|
+
**Scenarios:**
|
|
72
|
+
|
|
73
|
+
#### Scenario A: Review Pull Request / Merge Request
|
|
74
|
+
|
|
75
|
+
**GitHub (using gh CLI):**
|
|
76
|
+
```bash
|
|
77
|
+
# Get PR details
|
|
78
|
+
gh pr view 123
|
|
79
|
+
|
|
80
|
+
# Get PR diff
|
|
81
|
+
gh pr diff 123 > pr_changes.diff
|
|
82
|
+
|
|
83
|
+
# List changed files
|
|
84
|
+
gh pr view 123 --json files -q '.files[].path'
|
|
85
|
+
|
|
86
|
+
# Get PR description
|
|
87
|
+
gh pr view 123 --json body -q '.body'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**GitLab (using glab CLI):**
|
|
91
|
+
```bash
|
|
92
|
+
# Get MR details
|
|
93
|
+
glab mr view 456
|
|
94
|
+
|
|
95
|
+
# Get MR diff
|
|
96
|
+
glab mr diff 456 > mr_changes.diff
|
|
97
|
+
|
|
98
|
+
# List changed files
|
|
99
|
+
glab mr view 456 --json
|
|
100
|
+
|
|
101
|
+
# Get MR description
|
|
102
|
+
glab mr view 456 --json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**What to Extract:**
|
|
106
|
+
- List of changed files
|
|
107
|
+
- Nature of changes (addition, modification, deletion)
|
|
108
|
+
- PR/MR description and context
|
|
109
|
+
- Related issue/ticket numbers
|
|
110
|
+
- Author comments
|
|
111
|
+
|
|
112
|
+
#### Scenario B: Review Uncommitted Changes
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Get all uncommitted changes
|
|
116
|
+
git diff > uncommitted_changes.diff
|
|
117
|
+
|
|
118
|
+
# Get staged changes only
|
|
119
|
+
git diff --cached > staged_changes.diff
|
|
120
|
+
|
|
121
|
+
# List modified files
|
|
122
|
+
git diff --name-only
|
|
123
|
+
|
|
124
|
+
# Get status for context
|
|
125
|
+
git status
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Scenario C: Review Specific Files
|
|
129
|
+
|
|
130
|
+
When user specifies files directly:
|
|
131
|
+
```bash
|
|
132
|
+
# User says: "Review LoginView.swift and LoginViewModel.swift"
|
|
133
|
+
# Simply read those files
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### Scenario D: Review Specific Directory
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# User says: "Review all ViewModels in Features/"
|
|
140
|
+
# Find all ViewModel files
|
|
141
|
+
find Features/ -name "*ViewModel.swift"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Categorize Changes:**
|
|
145
|
+
|
|
146
|
+
After identifying files, categorize by type:
|
|
147
|
+
- **UI Code**: Views, view modifiers, SwiftUI components
|
|
148
|
+
- **Business Logic**: ViewModels, services, use cases
|
|
149
|
+
- **Data Layer**: Repositories, network clients, database
|
|
150
|
+
- **Infrastructure**: Dependency injection, configuration
|
|
151
|
+
- **Tests**: Unit tests, UI tests, integration tests
|
|
152
|
+
- **Build**: Project configuration, build scripts
|
|
153
|
+
|
|
154
|
+
### Step 1.3: Parse Diff for Context
|
|
155
|
+
|
|
156
|
+
**Objective**: Understand what actually changed in each file
|
|
157
|
+
|
|
158
|
+
**Diff Format:**
|
|
159
|
+
```diff
|
|
160
|
+
diff --git a/Sources/Features/Login/LoginView.swift b/Sources/Features/Login/LoginView.swift
|
|
161
|
+
index abc123..def456 100644
|
|
162
|
+
--- a/Sources/Features/Login/LoginView.swift
|
|
163
|
+
+++ b/Sources/Features/Login/LoginView.swift
|
|
164
|
+
@@ -15,7 +15,8 @@ struct LoginView: View {
|
|
165
|
+
var body: some View {
|
|
166
|
+
VStack(spacing: 20) {
|
|
167
|
+
- TextField("Username", text: $username)
|
|
168
|
+
+ TextField("Email", text: $email)
|
|
169
|
+
+ .textInputAutocapitalization(.never)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Extraction Strategy:**
|
|
176
|
+
|
|
177
|
+
1. **Identify Changed Lines**
|
|
178
|
+
- Lines starting with `-` are removed
|
|
179
|
+
- Lines starting with `+` are added
|
|
180
|
+
- Lines with context (no prefix) are unchanged
|
|
181
|
+
|
|
182
|
+
2. **Track Line Numbers**
|
|
183
|
+
- `@@ -15,7 +15,8 @@` means:
|
|
184
|
+
- Original file: 7 lines starting at line 15
|
|
185
|
+
- New file: 8 lines starting at line 15
|
|
186
|
+
|
|
187
|
+
3. **Group Related Changes**
|
|
188
|
+
- Multiple changes in same function/method
|
|
189
|
+
- Changes that span logical blocks
|
|
190
|
+
- Related changes across files
|
|
191
|
+
|
|
192
|
+
4. **Determine Change Type**
|
|
193
|
+
- **Addition**: New functionality
|
|
194
|
+
- **Modification**: Behavior change
|
|
195
|
+
- **Deletion**: Removed code
|
|
196
|
+
- **Refactoring**: Structure change, same behavior
|
|
197
|
+
- **Bug Fix**: Error correction
|
|
198
|
+
|
|
199
|
+
### Step 1.4: Read Files for Full Context
|
|
200
|
+
|
|
201
|
+
**Objective**: Understand the complete file, not just the diff
|
|
202
|
+
|
|
203
|
+
**Why Read Full Files:**
|
|
204
|
+
- Diff doesn't show overall structure
|
|
205
|
+
- Need to understand surrounding code
|
|
206
|
+
- Verify consistency with rest of file
|
|
207
|
+
- Check for patterns used elsewhere
|
|
208
|
+
|
|
209
|
+
**What to Read:**
|
|
210
|
+
|
|
211
|
+
1. **Changed Files**
|
|
212
|
+
- Read complete file content
|
|
213
|
+
- Understand overall structure
|
|
214
|
+
- Note existing patterns
|
|
215
|
+
- Check for related code
|
|
216
|
+
|
|
217
|
+
2. **Related Files**
|
|
218
|
+
- Files imported by changed files
|
|
219
|
+
- Files that import changed files
|
|
220
|
+
- Protocol definitions
|
|
221
|
+
- Parent classes or base views
|
|
222
|
+
|
|
223
|
+
**Example:**
|
|
224
|
+
```swift
|
|
225
|
+
// If reviewing LoginView.swift, also read:
|
|
226
|
+
// - LoginViewModel.swift (referenced in view)
|
|
227
|
+
// - AuthService.swift (used by view model)
|
|
228
|
+
// - FormTextField.swift (component used in view)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Step 1.5: Understand Change Purpose
|
|
232
|
+
|
|
233
|
+
**Objective**: Know why the change was made
|
|
234
|
+
|
|
235
|
+
**Sources of Information:**
|
|
236
|
+
|
|
237
|
+
1. **PR/MR Description**
|
|
238
|
+
- Feature description
|
|
239
|
+
- Problem being solved
|
|
240
|
+
- Implementation approach
|
|
241
|
+
|
|
242
|
+
2. **Commit Messages**
|
|
243
|
+
```bash
|
|
244
|
+
# Get commit messages for PR
|
|
245
|
+
gh pr view 123 --json commits -q '.commits[].commit.message'
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
3. **Linked Issues/Tickets**
|
|
249
|
+
- User stories
|
|
250
|
+
- Bug reports
|
|
251
|
+
- Technical requirements
|
|
252
|
+
|
|
253
|
+
4. **Code Comments**
|
|
254
|
+
- New comments explaining changes
|
|
255
|
+
- TODO or FIXME markers
|
|
256
|
+
|
|
257
|
+
**Create a Context Summary:**
|
|
258
|
+
```markdown
|
|
259
|
+
Change Context:
|
|
260
|
+
- Purpose: Add email validation to login flow
|
|
261
|
+
- Scope: LoginView, LoginViewModel, ValidationService
|
|
262
|
+
- Type: Feature enhancement
|
|
263
|
+
- Risk: Medium (affects authentication flow)
|
|
264
|
+
- Testing: Unit tests added for ValidationService
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Phase 2: Automated Analysis
|
|
270
|
+
|
|
271
|
+
### Step 2.1: Swift Best Practices Check
|
|
272
|
+
|
|
273
|
+
**Reference**: `swift-best-practices` skill
|
|
274
|
+
|
|
275
|
+
**What to Check:**
|
|
276
|
+
|
|
277
|
+
#### Concurrency Safety
|
|
278
|
+
```swift
|
|
279
|
+
// ❌ Bad: Mutable state without synchronization
|
|
280
|
+
class ViewModel {
|
|
281
|
+
var data: [Item] = [] // Can be accessed from any thread
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ✅ Good: MainActor for UI state
|
|
285
|
+
@MainActor
|
|
286
|
+
class ViewModel: ObservableObject {
|
|
287
|
+
@Published var data: [Item] = []
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Checks:**
|
|
292
|
+
- [ ] All UI-related classes marked with `@MainActor`
|
|
293
|
+
- [ ] Mutable state properly isolated with actors
|
|
294
|
+
- [ ] Sendable conformance for types crossing actor boundaries
|
|
295
|
+
- [ ] No data races (shared mutable state)
|
|
296
|
+
- [ ] Async/await used instead of completion handlers
|
|
297
|
+
- [ ] Structured concurrency (Task, TaskGroup)
|
|
298
|
+
|
|
299
|
+
#### Error Handling
|
|
300
|
+
```swift
|
|
301
|
+
// ❌ Bad: Force try
|
|
302
|
+
let data = try! decoder.decode(Model.self, from: json)
|
|
303
|
+
|
|
304
|
+
// ✅ Good: Typed throws (Swift 6+)
|
|
305
|
+
func fetchUser() throws(NetworkError) -> User {
|
|
306
|
+
// Implementation
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ✅ Good: Result type
|
|
310
|
+
func fetchUser() async -> Result<User, NetworkError> {
|
|
311
|
+
// Implementation
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Checks:**
|
|
316
|
+
- [ ] No `try!` (force try)
|
|
317
|
+
- [ ] Proper error propagation
|
|
318
|
+
- [ ] Typed throws where appropriate
|
|
319
|
+
- [ ] Result type for recoverable errors
|
|
320
|
+
- [ ] Meaningful error messages
|
|
321
|
+
|
|
322
|
+
#### Optionals Handling
|
|
323
|
+
```swift
|
|
324
|
+
// ❌ Bad: Force unwrap
|
|
325
|
+
let user = userRepository.currentUser!
|
|
326
|
+
|
|
327
|
+
// ✅ Good: Guard or if-let
|
|
328
|
+
guard let user = userRepository.currentUser else {
|
|
329
|
+
logger.error("No current user")
|
|
330
|
+
return
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ✅ Good: Optional chaining
|
|
334
|
+
let username = userRepository.currentUser?.name ?? "Guest"
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Checks:**
|
|
338
|
+
- [ ] No force unwrapping (`!`)
|
|
339
|
+
- [ ] No forced casting (`as!`)
|
|
340
|
+
- [ ] Proper optional handling (guard, if-let, nil coalescing)
|
|
341
|
+
- [ ] Optional chaining used appropriately
|
|
342
|
+
|
|
343
|
+
#### Access Control
|
|
344
|
+
```swift
|
|
345
|
+
// ✅ Good: Explicit access control
|
|
346
|
+
public protocol AuthService {
|
|
347
|
+
func login(email: String, password: String) async throws -> User
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
internal final class DefaultAuthService: AuthService {
|
|
351
|
+
private let networkClient: NetworkClient
|
|
352
|
+
private let tokenStorage: TokenStorage
|
|
353
|
+
|
|
354
|
+
internal init(networkClient: NetworkClient, tokenStorage: TokenStorage) {
|
|
355
|
+
self.networkClient = networkClient
|
|
356
|
+
self.tokenStorage = tokenStorage
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**Checks:**
|
|
362
|
+
- [ ] Explicit access control (not relying on defaults)
|
|
363
|
+
- [ ] Private for internal implementation details
|
|
364
|
+
- [ ] Internal for module-level sharing
|
|
365
|
+
- [ ] Public only for API surface
|
|
366
|
+
- [ ] Final classes when inheritance not needed
|
|
367
|
+
|
|
368
|
+
#### Naming Conventions
|
|
369
|
+
```swift
|
|
370
|
+
// ✅ Good: Swift API Design Guidelines
|
|
371
|
+
func fetchUser(withID id: UUID) async throws -> User
|
|
372
|
+
func validate(email: String) -> Bool
|
|
373
|
+
var isLoading: Bool
|
|
374
|
+
let maximumRetryCount: Int
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
**Checks:**
|
|
378
|
+
- [ ] Clear, descriptive names
|
|
379
|
+
- [ ] Proper parameter labels (argument labels + parameter names)
|
|
380
|
+
- [ ] Bool properties start with `is`, `has`, `should`
|
|
381
|
+
- [ ] Methods start with verbs
|
|
382
|
+
- [ ] Types use UpperCamelCase
|
|
383
|
+
- [ ] Properties/variables use lowerCamelCase
|
|
384
|
+
|
|
385
|
+
### Step 2.2: SwiftUI Quality Check
|
|
386
|
+
|
|
387
|
+
**Reference**: `swiftui-expert-skill`
|
|
388
|
+
|
|
389
|
+
#### State Management
|
|
390
|
+
```swift
|
|
391
|
+
// ✅ Good: @Observable for view models
|
|
392
|
+
@Observable
|
|
393
|
+
final class LoginViewModel {
|
|
394
|
+
var email: String = ""
|
|
395
|
+
var isLoading: Bool = false
|
|
396
|
+
|
|
397
|
+
func login() async {
|
|
398
|
+
// Implementation
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
struct LoginView: View {
|
|
403
|
+
let viewModel: LoginViewModel
|
|
404
|
+
|
|
405
|
+
var body: some View {
|
|
406
|
+
// View updates automatically when viewModel properties change
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Checks:**
|
|
412
|
+
- [ ] @Observable used for view models (iOS 17+)
|
|
413
|
+
- [ ] @State for view-local state
|
|
414
|
+
- [ ] @Binding for two-way bindings
|
|
415
|
+
- [ ] @Environment for dependency injection
|
|
416
|
+
- [ ] No @StateObject with @Observable
|
|
417
|
+
- [ ] No @ObservedObject with @Observable
|
|
418
|
+
- [ ] Correct state ownership
|
|
419
|
+
|
|
420
|
+
#### Property Wrapper Selection Guide
|
|
421
|
+
| Scenario | Property Wrapper |
|
|
422
|
+
|----------|-----------------|
|
|
423
|
+
| View-local state (private to view) | `@State` |
|
|
424
|
+
| Two-way binding from parent | `@Binding` |
|
|
425
|
+
| Observable view model (iOS 17+) | `@Observable` class |
|
|
426
|
+
| Legacy view model (iOS 16-) | `@StateObject` or `@ObservedObject` |
|
|
427
|
+
| Environment dependency | `@Environment` |
|
|
428
|
+
| App-wide shared state | `@Environment` with custom key |
|
|
429
|
+
| UserDefaults-backed | `@AppStorage` |
|
|
430
|
+
| Keychain-backed | Custom property wrapper |
|
|
431
|
+
|
|
432
|
+
**Checks:**
|
|
433
|
+
- [ ] Correct property wrapper for use case
|
|
434
|
+
- [ ] No unnecessary state (derived values computed)
|
|
435
|
+
- [ ] State changes trigger appropriate view updates
|
|
436
|
+
- [ ] No state in static views
|
|
437
|
+
|
|
438
|
+
#### Modern API Usage
|
|
439
|
+
```swift
|
|
440
|
+
// ❌ Bad: Deprecated API
|
|
441
|
+
.onAppear {
|
|
442
|
+
viewModel.load()
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// ✅ Good: Modern task modifier
|
|
446
|
+
.task {
|
|
447
|
+
await viewModel.load()
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ❌ Bad: GeometryReader for simple cases
|
|
451
|
+
GeometryReader { geometry in
|
|
452
|
+
Text("Hello")
|
|
453
|
+
.frame(width: geometry.size.width)
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ✅ Good: Use frame with maxWidth
|
|
457
|
+
Text("Hello")
|
|
458
|
+
.frame(maxWidth: .infinity)
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
**Checks:**
|
|
462
|
+
- [ ] No deprecated APIs (if modern alternatives exist)
|
|
463
|
+
- [ ] `.task` instead of `.onAppear` for async work
|
|
464
|
+
- [ ] `.onChange` with new syntax (iOS 17+)
|
|
465
|
+
- [ ] `NavigationStack` instead of `NavigationView`
|
|
466
|
+
- [ ] `@Observable` instead of `ObservableObject` (iOS 17+)
|
|
467
|
+
- [ ] Avoid GeometryReader unless necessary
|
|
468
|
+
|
|
469
|
+
#### View Composition
|
|
470
|
+
```swift
|
|
471
|
+
// ❌ Bad: Monolithic view
|
|
472
|
+
struct LoginView: View {
|
|
473
|
+
var body: some View {
|
|
474
|
+
VStack {
|
|
475
|
+
// 200 lines of UI code
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ✅ Good: Extracted subviews
|
|
481
|
+
struct LoginView: View {
|
|
482
|
+
var body: some View {
|
|
483
|
+
VStack {
|
|
484
|
+
LoginHeaderView()
|
|
485
|
+
LoginFormView()
|
|
486
|
+
LoginActionsView()
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
**Checks:**
|
|
493
|
+
- [ ] View body < 50 lines (guideline)
|
|
494
|
+
- [ ] Logical subviews extracted
|
|
495
|
+
- [ ] Reusable components identified
|
|
496
|
+
- [ ] Proper view hierarchy depth
|
|
497
|
+
- [ ] No excessive nesting (< 5 levels)
|
|
498
|
+
|
|
499
|
+
#### Accessibility
|
|
500
|
+
```swift
|
|
501
|
+
// ✅ Good: Accessibility support
|
|
502
|
+
TextField("Email", text: $email)
|
|
503
|
+
.accessibilityLabel("Email address")
|
|
504
|
+
.accessibilityHint("Enter your email to log in")
|
|
505
|
+
.textContentType(.emailAddress)
|
|
506
|
+
.keyboardType(.emailAddress)
|
|
507
|
+
.textInputAutocapitalization(.never)
|
|
508
|
+
|
|
509
|
+
Button("Log In") {
|
|
510
|
+
login()
|
|
511
|
+
}
|
|
512
|
+
.accessibilityLabel("Log in button")
|
|
513
|
+
.accessibilityAddTraits(.isButton)
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**Checks:**
|
|
517
|
+
- [ ] Accessibility labels for non-text elements
|
|
518
|
+
- [ ] Accessibility hints for complex interactions
|
|
519
|
+
- [ ] Proper traits (button, header, etc.)
|
|
520
|
+
- [ ] Support for Dynamic Type
|
|
521
|
+
- [ ] Keyboard navigation support
|
|
522
|
+
- [ ] VoiceOver tested (if critical UI)
|
|
523
|
+
|
|
524
|
+
### Step 2.3: Performance Check
|
|
525
|
+
|
|
526
|
+
**Reference**: `swiftui-performance-audit`
|
|
527
|
+
|
|
528
|
+
#### View Optimization
|
|
529
|
+
```swift
|
|
530
|
+
// ❌ Bad: Heavy computation in body
|
|
531
|
+
struct ItemListView: View {
|
|
532
|
+
let items: [Item]
|
|
533
|
+
|
|
534
|
+
var body: some View {
|
|
535
|
+
let sortedItems = items.sorted { $0.date > $1.date } // ❌ Computed every render
|
|
536
|
+
List(sortedItems) { item in
|
|
537
|
+
ItemRow(item: item)
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// ✅ Good: Computed property or view model
|
|
543
|
+
struct ItemListView: View {
|
|
544
|
+
let viewModel: ItemListViewModel
|
|
545
|
+
|
|
546
|
+
var body: some View {
|
|
547
|
+
List(viewModel.sortedItems) { item in // ✅ Cached in view model
|
|
548
|
+
ItemRow(item: item)
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**Checks:**
|
|
555
|
+
- [ ] No heavy computation in `body`
|
|
556
|
+
- [ ] No synchronous network calls in `body`
|
|
557
|
+
- [ ] No database queries in `body`
|
|
558
|
+
- [ ] Expensive computations cached or memoized
|
|
559
|
+
- [ ] View models have Equatable conformance
|
|
560
|
+
|
|
561
|
+
#### ForEach Performance
|
|
562
|
+
```swift
|
|
563
|
+
// ❌ Bad: Unstable identity
|
|
564
|
+
List {
|
|
565
|
+
ForEach(items.indices, id: \.self) { index in // ❌ Index-based
|
|
566
|
+
ItemRow(item: items[index])
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// ✅ Good: Stable identity
|
|
571
|
+
List {
|
|
572
|
+
ForEach(items) { item in // ✅ Using Identifiable
|
|
573
|
+
ItemRow(item: item)
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
**Checks:**
|
|
579
|
+
- [ ] ForEach uses stable IDs (Identifiable or explicit id)
|
|
580
|
+
- [ ] No index-based iteration when data changes
|
|
581
|
+
- [ ] No array enumerated() in ForEach
|
|
582
|
+
- [ ] Proper identity for animations
|
|
583
|
+
|
|
584
|
+
#### Layout Performance
|
|
585
|
+
```swift
|
|
586
|
+
// ❌ Bad: Excessive GeometryReader
|
|
587
|
+
GeometryReader { geometry in
|
|
588
|
+
VStack {
|
|
589
|
+
ForEach(items) { item in
|
|
590
|
+
GeometryReader { itemGeometry in // ❌ Nested GeometryReader
|
|
591
|
+
ItemView(item: item, width: itemGeometry.size.width)
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// ✅ Good: Use layout protocol or simple frames
|
|
598
|
+
VStack {
|
|
599
|
+
ForEach(items) { item in
|
|
600
|
+
ItemView(item: item)
|
|
601
|
+
.frame(maxWidth: .infinity) // ✅ Simple frame
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
**Checks:**
|
|
607
|
+
- [ ] Minimal GeometryReader usage
|
|
608
|
+
- [ ] No nested GeometryReaders
|
|
609
|
+
- [ ] Layout protocol for custom layouts
|
|
610
|
+
- [ ] Efficient frame calculations
|
|
611
|
+
|
|
612
|
+
#### Resource Management
|
|
613
|
+
```swift
|
|
614
|
+
// ❌ Bad: Loading large image synchronously
|
|
615
|
+
struct PhotoView: View {
|
|
616
|
+
let imageURL: URL
|
|
617
|
+
|
|
618
|
+
var body: some View {
|
|
619
|
+
if let data = try? Data(contentsOf: imageURL), // ❌ Synchronous load
|
|
620
|
+
let image = UIImage(data: data) {
|
|
621
|
+
Image(uiImage: image)
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// ✅ Good: Async loading with caching
|
|
627
|
+
struct PhotoView: View {
|
|
628
|
+
let imageURL: URL
|
|
629
|
+
|
|
630
|
+
var body: some View {
|
|
631
|
+
AsyncImage(url: imageURL) { phase in // ✅ Async with built-in caching
|
|
632
|
+
switch phase {
|
|
633
|
+
case .success(let image):
|
|
634
|
+
image.resizable().aspectRatio(contentMode: .fit)
|
|
635
|
+
case .failure:
|
|
636
|
+
Image(systemName: "photo")
|
|
637
|
+
case .empty:
|
|
638
|
+
ProgressView()
|
|
639
|
+
@unknown default:
|
|
640
|
+
EmptyView()
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
**Checks:**
|
|
648
|
+
- [ ] AsyncImage for remote images
|
|
649
|
+
- [ ] Image caching implemented
|
|
650
|
+
- [ ] No synchronous I/O on main thread
|
|
651
|
+
- [ ] Lazy loading for large lists
|
|
652
|
+
- [ ] Proper memory management (no retain cycles)
|
|
653
|
+
|
|
654
|
+
### Step 2.4: Security & Safety Check
|
|
655
|
+
|
|
656
|
+
#### Force Unwrap Audit
|
|
657
|
+
```bash
|
|
658
|
+
# Search for force unwraps
|
|
659
|
+
grep -n "!" *.swift | grep -v "// " # Exclude comments
|
|
660
|
+
grep -n "as!" *.swift
|
|
661
|
+
grep -n "try!" *.swift
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
**Checks:**
|
|
665
|
+
- [ ] No `!` force unwraps
|
|
666
|
+
- [ ] No `as!` forced casts
|
|
667
|
+
- [ ] No `try!` forced try
|
|
668
|
+
- [ ] Justify exceptions with comments
|
|
669
|
+
|
|
670
|
+
#### Sensitive Data
|
|
671
|
+
```swift
|
|
672
|
+
// ❌ Bad: Storing password in UserDefaults
|
|
673
|
+
UserDefaults.standard.set(password, forKey: "password")
|
|
674
|
+
|
|
675
|
+
// ✅ Good: Using Keychain
|
|
676
|
+
KeychainService.shared.save(password, forKey: "user_password")
|
|
677
|
+
|
|
678
|
+
// ❌ Bad: Logging sensitive data
|
|
679
|
+
logger.debug("User password: \(password)")
|
|
680
|
+
|
|
681
|
+
// ✅ Good: Sanitized logging
|
|
682
|
+
logger.debug("User logged in successfully")
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
**Checks:**
|
|
686
|
+
- [ ] No sensitive data in UserDefaults
|
|
687
|
+
- [ ] Keychain used for passwords, tokens
|
|
688
|
+
- [ ] No sensitive data in logs
|
|
689
|
+
- [ ] Secure network communication (HTTPS)
|
|
690
|
+
- [ ] No hardcoded credentials
|
|
691
|
+
|
|
692
|
+
#### Input Validation
|
|
693
|
+
```swift
|
|
694
|
+
// ✅ Good: Email validation
|
|
695
|
+
func isValid(email: String) -> Bool {
|
|
696
|
+
let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
|
|
697
|
+
let predicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
|
|
698
|
+
return predicate.evaluate(with: email)
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// ✅ Good: Bounds checking
|
|
702
|
+
func fetchItem(at index: Int) -> Item? {
|
|
703
|
+
guard items.indices.contains(index) else {
|
|
704
|
+
return nil
|
|
705
|
+
}
|
|
706
|
+
return items[index]
|
|
707
|
+
}
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
**Checks:**
|
|
711
|
+
- [ ] User input validated
|
|
712
|
+
- [ ] Bounds checking for array access
|
|
713
|
+
- [ ] Type safety enforced
|
|
714
|
+
- [ ] SQL injection prevention (if using SQL)
|
|
715
|
+
- [ ] XSS prevention (if rendering web content)
|
|
716
|
+
|
|
717
|
+
### Step 2.5: Architecture & Maintainability Check
|
|
718
|
+
|
|
719
|
+
#### Project Architecture Compliance
|
|
720
|
+
```swift
|
|
721
|
+
// Example: MVVM Pattern
|
|
722
|
+
|
|
723
|
+
// ✅ Good: Clear separation
|
|
724
|
+
// View - Presentation only
|
|
725
|
+
struct LoginView: View {
|
|
726
|
+
let viewModel: LoginViewModel
|
|
727
|
+
|
|
728
|
+
var body: some View {
|
|
729
|
+
// UI only, no business logic
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// ViewModel - Business logic
|
|
734
|
+
@Observable
|
|
735
|
+
final class LoginViewModel {
|
|
736
|
+
private let authService: AuthService
|
|
737
|
+
|
|
738
|
+
var email: String = ""
|
|
739
|
+
var isLoading: Bool = false
|
|
740
|
+
|
|
741
|
+
func login() async {
|
|
742
|
+
// Business logic
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Service - Data operations
|
|
747
|
+
protocol AuthService {
|
|
748
|
+
func login(email: String, password: String) async throws -> User
|
|
749
|
+
}
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
**Checks:**
|
|
753
|
+
- [ ] Follows project architecture (MVVM, MVI, TCA)
|
|
754
|
+
- [ ] Clear layer separation (View/ViewModel/Service)
|
|
755
|
+
- [ ] No business logic in views
|
|
756
|
+
- [ ] No UI code in view models
|
|
757
|
+
- [ ] Dependency injection used
|
|
758
|
+
- [ ] Protocol-oriented design
|
|
759
|
+
|
|
760
|
+
#### Code Organization
|
|
761
|
+
```swift
|
|
762
|
+
// ✅ Good: Organized with extensions
|
|
763
|
+
struct LoginView: View {
|
|
764
|
+
// MARK: - Properties
|
|
765
|
+
let viewModel: LoginViewModel
|
|
766
|
+
|
|
767
|
+
// MARK: - Body
|
|
768
|
+
var body: some View {
|
|
769
|
+
// Implementation
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// MARK: - Subviews
|
|
774
|
+
private extension LoginView {
|
|
775
|
+
var headerView: some View {
|
|
776
|
+
// Implementation
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// MARK: - Actions
|
|
781
|
+
private extension LoginView {
|
|
782
|
+
func handleLogin() {
|
|
783
|
+
// Implementation
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
**Checks:**
|
|
789
|
+
- [ ] Logical file organization
|
|
790
|
+
- [ ] MARK comments for sections
|
|
791
|
+
- [ ] Extensions for protocol conformances
|
|
792
|
+
- [ ] Private extensions for private members
|
|
793
|
+
- [ ] Consistent file structure across project
|
|
794
|
+
|
|
795
|
+
#### Testability
|
|
796
|
+
```swift
|
|
797
|
+
// ✅ Good: Testable design
|
|
798
|
+
protocol AuthService {
|
|
799
|
+
func login(email: String, password: String) async throws -> User
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
final class LoginViewModel {
|
|
803
|
+
private let authService: AuthService // ✅ Protocol for mocking
|
|
804
|
+
|
|
805
|
+
init(authService: AuthService) {
|
|
806
|
+
self.authService = authService
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Test
|
|
811
|
+
final class LoginViewModelTests: XCTestCase {
|
|
812
|
+
func testLogin() async throws {
|
|
813
|
+
let mockAuthService = MockAuthService() // ✅ Easy to mock
|
|
814
|
+
let viewModel = LoginViewModel(authService: mockAuthService)
|
|
815
|
+
// Test
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
**Checks:**
|
|
821
|
+
- [ ] Dependencies are protocols (mockable)
|
|
822
|
+
- [ ] Constructor injection used
|
|
823
|
+
- [ ] No singletons (or testable singletons)
|
|
824
|
+
- [ ] Pure functions where possible
|
|
825
|
+
- [ ] Tests exist for critical paths
|
|
826
|
+
|
|
827
|
+
### Step 2.6: Project-Specific Standards Check
|
|
828
|
+
|
|
829
|
+
**Load Custom Guidelines:**
|
|
830
|
+
1. Read `.claude/CLAUDE.md`
|
|
831
|
+
2. Extract project-specific rules
|
|
832
|
+
3. Validate code against custom patterns
|
|
833
|
+
|
|
834
|
+
**Common Custom Standards:**
|
|
835
|
+
|
|
836
|
+
#### Example: Custom Error Handling
|
|
837
|
+
```swift
|
|
838
|
+
// Project standard: All errors must conform to AppError
|
|
839
|
+
protocol AppError: Error {
|
|
840
|
+
var message: String { get }
|
|
841
|
+
var code: Int { get }
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// ✅ Good: Follows project standard
|
|
845
|
+
enum LoginError: AppError {
|
|
846
|
+
case invalidCredentials
|
|
847
|
+
case networkFailure
|
|
848
|
+
|
|
849
|
+
var message: String {
|
|
850
|
+
switch self {
|
|
851
|
+
case .invalidCredentials: return "Invalid email or password"
|
|
852
|
+
case .networkFailure: return "Network connection failed"
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
var code: Int {
|
|
857
|
+
switch self {
|
|
858
|
+
case .invalidCredentials: return 1001
|
|
859
|
+
case .networkFailure: return 2001
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
**Checks:**
|
|
866
|
+
- [ ] Custom error protocol conformance
|
|
867
|
+
- [ ] Project naming conventions
|
|
868
|
+
- [ ] Required ViewParsing patterns
|
|
869
|
+
- [ ] Design system usage (colors, fonts, spacing)
|
|
870
|
+
- [ ] Navigation patterns
|
|
871
|
+
- [ ] Testing coverage requirements
|
|
872
|
+
|
|
873
|
+
---
|
|
874
|
+
|
|
875
|
+
## Phase 3: Report Generation
|
|
876
|
+
|
|
877
|
+
### Step 3.1: Categorize Findings
|
|
878
|
+
|
|
879
|
+
**Severity Levels:**
|
|
880
|
+
|
|
881
|
+
#### Critical
|
|
882
|
+
- Security vulnerabilities
|
|
883
|
+
- Data races and concurrency issues
|
|
884
|
+
- Force unwraps that can crash
|
|
885
|
+
- Memory leaks
|
|
886
|
+
- Breaking API changes
|
|
887
|
+
|
|
888
|
+
#### High
|
|
889
|
+
- Performance problems (O(n²) algorithms, excessive renders)
|
|
890
|
+
- Anti-patterns (improper state management, retain cycles)
|
|
891
|
+
- Major architecture violations
|
|
892
|
+
- Missing error handling
|
|
893
|
+
|
|
894
|
+
#### Medium
|
|
895
|
+
- Code quality issues (complex methods, duplication)
|
|
896
|
+
- Missing documentation
|
|
897
|
+
- Minor architecture inconsistencies
|
|
898
|
+
- Accessibility gaps
|
|
899
|
+
|
|
900
|
+
#### Low
|
|
901
|
+
- Style inconsistencies
|
|
902
|
+
- Naming improvements
|
|
903
|
+
- Refactoring suggestions
|
|
904
|
+
- Minor optimizations
|
|
905
|
+
|
|
906
|
+
### Step 3.2: Include Positive Feedback
|
|
907
|
+
|
|
908
|
+
**Acknowledge Good Practices:**
|
|
909
|
+
```markdown
|
|
910
|
+
✅ **Excellent State Management** (LoginView.swift:23)
|
|
911
|
+
- Proper use of @Observable for view model
|
|
912
|
+
- Clean separation of concerns
|
|
913
|
+
- Immutable state design
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
**Types of Positive Feedback:**
|
|
917
|
+
- Modern API adoption
|
|
918
|
+
- Strong architecture adherence
|
|
919
|
+
- Excellent test coverage
|
|
920
|
+
- Clear documentation
|
|
921
|
+
- Performance optimizations
|
|
922
|
+
- Security best practices
|
|
923
|
+
|
|
924
|
+
### Step 3.3: Add Refactoring Suggestions
|
|
925
|
+
|
|
926
|
+
**Proactive Improvements:**
|
|
927
|
+
```markdown
|
|
928
|
+
💡 **Consider Extracting Subview** (LoginView.swift:45-78)
|
|
929
|
+
- The login form could be extracted into a reusable component
|
|
930
|
+
- Benefits: Better testability, improved reusability
|
|
931
|
+
- Suggested: Create `LoginFormView` component
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
### Step 3.4: Organize by File and Category
|
|
935
|
+
|
|
936
|
+
**Structure:**
|
|
937
|
+
```markdown
|
|
938
|
+
## File: LoginView.swift
|
|
939
|
+
|
|
940
|
+
### ✅ Positive Feedback
|
|
941
|
+
1. [positive item]
|
|
942
|
+
|
|
943
|
+
### 🔴 Critical Issues
|
|
944
|
+
1. [critical item]
|
|
945
|
+
|
|
946
|
+
### 🟡 High Priority
|
|
947
|
+
1. [high item]
|
|
948
|
+
|
|
949
|
+
### 🟠 Medium Priority
|
|
950
|
+
1. [medium item]
|
|
951
|
+
|
|
952
|
+
### 🔵 Low Priority
|
|
953
|
+
1. [low item]
|
|
954
|
+
|
|
955
|
+
### 💡 Refactoring Suggestions
|
|
956
|
+
1. [suggestion item]
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
---
|
|
960
|
+
|
|
961
|
+
## Phase 4: Delivery
|
|
962
|
+
|
|
963
|
+
### Step 4.1: Generate Summary
|
|
964
|
+
|
|
965
|
+
```markdown
|
|
966
|
+
# Code Review Report
|
|
967
|
+
|
|
968
|
+
## Summary
|
|
969
|
+
- **Files Reviewed**: 5
|
|
970
|
+
- **Total Findings**: 15
|
|
971
|
+
- **Critical**: 1
|
|
972
|
+
- **High**: 3
|
|
973
|
+
- **Medium**: 6
|
|
974
|
+
- **Low**: 5
|
|
975
|
+
- **Positive Feedback**: 12
|
|
976
|
+
- **Refactoring Suggestions**: 4
|
|
977
|
+
|
|
978
|
+
## Executive Summary
|
|
979
|
+
This PR adds email validation to the login flow. Overall code quality is good
|
|
980
|
+
with modern SwiftUI patterns and proper architecture. One critical concurrency
|
|
981
|
+
issue must be fixed before merge. Several opportunities for performance
|
|
982
|
+
optimization identified.
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
### Step 4.2: Format with Code Examples
|
|
986
|
+
|
|
987
|
+
**Before/After Examples:**
|
|
988
|
+
```markdown
|
|
989
|
+
#### 🔴 Data Race Risk (LoginViewModel.swift:45)
|
|
990
|
+
**Severity**: Critical
|
|
991
|
+
**Category**: Concurrency
|
|
992
|
+
|
|
993
|
+
**Issue**: Mutable state accessed without synchronization
|
|
994
|
+
|
|
995
|
+
**Current Code:**
|
|
996
|
+
```swift
|
|
997
|
+
class LoginViewModel {
|
|
998
|
+
var isLoading = false // ❌ Can be accessed from any thread
|
|
999
|
+
}
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
**Recommended Fix:**
|
|
1003
|
+
```swift
|
|
1004
|
+
@MainActor
|
|
1005
|
+
class LoginViewModel: ObservableObject {
|
|
1006
|
+
@Published var isLoading = false // ✅ MainActor-isolated
|
|
1007
|
+
}
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
**Reference**: swift-best-practices/references/concurrency.md
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
### Step 4.3: Provide Actionable Items
|
|
1014
|
+
|
|
1015
|
+
```markdown
|
|
1016
|
+
## Prioritized Action Items
|
|
1017
|
+
|
|
1018
|
+
### Must Fix (Before Merge)
|
|
1019
|
+
- [ ] Fix data race in LoginViewModel.swift:45
|
|
1020
|
+
- [ ] Remove force unwrap in LoginView.swift:89
|
|
1021
|
+
|
|
1022
|
+
### Should Fix (This Sprint)
|
|
1023
|
+
- [ ] Add documentation to AuthService protocol
|
|
1024
|
+
- [ ] Improve error handling in NetworkClient.swift
|
|
1025
|
+
- [ ] Add unit tests for ValidationService
|
|
1026
|
+
|
|
1027
|
+
### Consider (Future)
|
|
1028
|
+
- [ ] Extract login form into separate view
|
|
1029
|
+
- [ ] Implement retry logic for network failures
|
|
1030
|
+
- [ ] Add loading states for better UX
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
---
|
|
1034
|
+
|
|
1035
|
+
## Tips for Effective Reviews
|
|
1036
|
+
|
|
1037
|
+
### Do's
|
|
1038
|
+
✅ Read project guidelines first
|
|
1039
|
+
✅ Understand the context and purpose
|
|
1040
|
+
✅ Provide balanced feedback (positive + negative)
|
|
1041
|
+
✅ Be specific with file:line references
|
|
1042
|
+
✅ Include code examples for fixes
|
|
1043
|
+
✅ Explain *why* something is an issue
|
|
1044
|
+
✅ Prioritize by severity
|
|
1045
|
+
✅ Suggest improvements, not just problems
|
|
1046
|
+
✅ Link to documentation and resources
|
|
1047
|
+
|
|
1048
|
+
### Don'ts
|
|
1049
|
+
❌ Review without reading .claude/CLAUDE.md
|
|
1050
|
+
❌ Only provide criticism (no positive feedback)
|
|
1051
|
+
❌ Be vague ("this could be better")
|
|
1052
|
+
❌ Nitpick style without real impact
|
|
1053
|
+
❌ Ignore project-specific standards
|
|
1054
|
+
❌ Review code you haven't read completely
|
|
1055
|
+
❌ Suggest fixes without examples
|
|
1056
|
+
|
|
1057
|
+
---
|
|
1058
|
+
|
|
1059
|
+
## Appendix: Git Commands Reference
|
|
1060
|
+
|
|
1061
|
+
### Common Git Operations
|
|
1062
|
+
|
|
1063
|
+
```bash
|
|
1064
|
+
# View diff
|
|
1065
|
+
git diff # Uncommitted changes
|
|
1066
|
+
git diff --cached # Staged changes
|
|
1067
|
+
git diff HEAD~1 # Last commit
|
|
1068
|
+
git diff branch1..branch2 # Between branches
|
|
1069
|
+
|
|
1070
|
+
# View specific file diff
|
|
1071
|
+
git diff path/to/file.swift
|
|
1072
|
+
|
|
1073
|
+
# View commit
|
|
1074
|
+
git show <commit-hash>
|
|
1075
|
+
|
|
1076
|
+
# View file at specific commit
|
|
1077
|
+
git show <commit-hash>:path/to/file.swift
|
|
1078
|
+
|
|
1079
|
+
# List changed files
|
|
1080
|
+
git diff --name-only
|
|
1081
|
+
git diff --name-status # With status (M, A, D)
|
|
1082
|
+
|
|
1083
|
+
# View log
|
|
1084
|
+
git log --oneline
|
|
1085
|
+
git log --graph --oneline --all
|
|
1086
|
+
|
|
1087
|
+
# View file history
|
|
1088
|
+
git log --follow -- path/to/file.swift
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
### GitHub CLI (gh)
|
|
1092
|
+
|
|
1093
|
+
```bash
|
|
1094
|
+
# View PR
|
|
1095
|
+
gh pr view 123
|
|
1096
|
+
gh pr view 123 --json title,body,files
|
|
1097
|
+
|
|
1098
|
+
# View PR diff
|
|
1099
|
+
gh pr diff 123
|
|
1100
|
+
|
|
1101
|
+
# View PR checks
|
|
1102
|
+
gh pr checks 123
|
|
1103
|
+
|
|
1104
|
+
# List PR files
|
|
1105
|
+
gh pr view 123 --json files -q '.files[].path'
|
|
1106
|
+
|
|
1107
|
+
# View PR comments
|
|
1108
|
+
gh pr view 123 --json comments
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
### GitLab CLI (glab)
|
|
1112
|
+
|
|
1113
|
+
```bash
|
|
1114
|
+
# View MR
|
|
1115
|
+
glab mr view 456
|
|
1116
|
+
|
|
1117
|
+
# View MR diff
|
|
1118
|
+
glab mr diff 456
|
|
1119
|
+
|
|
1120
|
+
# List MR files
|
|
1121
|
+
glab mr view 456 --json
|
|
1122
|
+
|
|
1123
|
+
# View MR notes
|
|
1124
|
+
glab mr note list 456
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
---
|
|
1128
|
+
|
|
1129
|
+
## Version
|
|
1130
|
+
**Last Updated**: 2026-02-10
|
|
1131
|
+
**Version**: 1.0.0
|