Payoo iOS Code Review
Comprehensive code review for the Payoo Merchant iOS app following Clean Architecture with RxSwift and Swinject.
When to Activate
"review code", "check code", "code review"
"review PR", "review pull request", "check pull request"
"review this file", "check this ViewModel"
"is this code correct", "any issues with this code"
When analyzing Swift files in PayooMerchant, Domain, Data, or Analytics layers
Review Process
Step 1: Identify Scope
Single file review → Read the file
Multiple files → Use Glob to find related files
Pull request → Check git diff for changed files
Full feature → Grep for related ViewModels/UseCases
Step 2: Layer-Specific Checks
For Presentation Layer (PayooMerchant/)
MVVM Pattern
✓ ViewModel implements ViewModelType protocol
✓ Has Input and Output nested types
✓ Has transform(input:) -> Output method
✓ ViewControllers bind to Input/Output only
✓ No business logic in ViewControllers
RxSwift Memory Management
✓ Every ViewController/ViewModel has DisposeBag
✓ All subscriptions use .disposed(by: disposeBag)
✓ Closures capturing self use [weak self] or [unowned self]
✓ No retain cycles in Observable chains
Navigation & DI
✓ Navigator passed as dependency (never created directly)
✓ UseCases injected via constructor
✓ No direct ViewController instantiation
✓ Uses factory methods from ViewControllerFactory
Session Error Handling
✓ CRITICAL : All API calls have .catchSessionError(sessionUC)
✗ Missing .catchSessionError() → Session timeout won't logout
For Domain Layer (Domain/)
Clean Architecture Rules
✓ Pure Swift only (no UIKit imports)
✓ No imports from Data or Presentation layers
✓ Only protocols for services (no implementations)
✓ Models are simple structs/classes
UseCase Pattern
✓ Protocol defines interface (UseCaseType)
✓ Implementation injected with dependencies
✓ Single responsibility per UseCase
✓ Returns RxSwift Observables/Singles/Maybes
✓ Uses .catchSessionError(sessionUC) for API calls
Service Protocols
✓ Defined in Domain/Service/
✓ Implemented in Data layer
✓ Injected via Swinject
For Data Layer (Data/)
Repository Pattern
✓ Implements Domain service protocols
✓ Uses Moya for network calls
✓ Uses Realm for local storage
✓ Converters transform DTOs ↔ Domain models
API Models
✓ DTOs in Data/Model/
✓ Conform to DomainConvertible or RealmRepresentable
✓ Use ObjectMapper for JSON parsing
✓ Don't leak to Domain/Presentation layers
Step 3: Project-Wide Checks
SwiftLint Compliance
Run: ./Pods/SwiftLint/swiftlint lint --reporter xcode
Check: Type body length (300/400), file length (800/1200)
Check: Opt-in rules (empty_count, yoda_condition, todo, etc.)
Common Pitfalls
RxSwift Best Practices
Use Driver for UI bindings (never fails, main thread)
Use Single for one-time operations (network calls)
Use Observable for streams
Use Maybe for optional single values
Prefer .bind(to:) over .subscribe(onNext:)
Naming Conventions
ViewModels: [Feature]ViewModel (e.g., LoginViewModel, TransactionHistoryViewModel)
ViewControllers: [Feature]ViewController (e.g., LoginViewController)
UseCases: [Action]UseCase (e.g., GetProfileUseCase, LoginUseCase)
UseCase protocols: [Action]UseCaseType (e.g., GetProfileUseCaseType)
Navigators: [Feature]Navigator (e.g., LoginNavigator, HomeNavigator)
Navigator protocols: [Feature]NavigatorType
Services (protocols): [Name]Service (e.g., ApiService, LocalStorageService)
Services (impl): Default[Name]Service or [Tech][Name]Service (e.g., DefaultApiService, RealmStorageService)
Protocols: [Name]Type suffix for main protocols
Variables: camelCase, descriptive (avoid abbreviations like usrNm, use username)
Constants: camelCase for local, or k prefix for global (e.g., kMaxRetryCount)
IBOutlets: Descriptive names with type suffix (e.g., loginButton, usernameTextField)
Avoid single letters except in loops (i, j) or common conventions (x, y)
Step 4: Generate Report
Format:
## Code Review: [File/Feature Name]
### 📋 Summary
Files: X | 🔴 Critical: X | 🟡 Warning: X | 🔵 Info: X | Status: [✅ Approved / ⚠️ Needs fixes / ❌ Blocked]
### ✅ Strengths
- [List good patterns found]
### ⚠️ Issues Found
#### 🔴 Critical (Must Fix)
**[Issue]** at [file:line]
- **Problem**: [Description]
- **Impact**: [Why critical]
- **Fix**:
\`\`\`swift
// Corrected code
\`\`\`
#### 🟡 Warning (Should Fix)
**[Issue]** at [file:line]
- **Problem**: [Description]
- **Suggestion**: [How to fix]
#### 🔵 Info (Consider)
**[Issue]** at [file:line]
- **Note**: [Observation]
- **Suggestion**: [Optional improvement]
Review Categories
Critical Issues (Must Fix)
Missing .catchSessionError() on API calls
Retain cycles / memory leaks
Breaking Clean Architecture layer boundaries
Missing DisposeBag disposal
Force unwraps in unsafe contexts
Warnings (Should Fix)
Manual ViewController instantiation
Missing DependencyContainer registration
SwiftLint violations
Non-descriptive variable names
Large type bodies (>300 lines)
Info (Consider)
Potential optimizations
Code duplication
Missing unit tests
Outdated comments
TODO/FIXME comments
Quick Commands
Run SwiftLint:
./Pods/SwiftLint/swiftlint lint --reporter xcode
Find files without DisposeBag:
grep -L "DisposeBag" PayooMerchant/**/*ViewModel.swift
Find API calls without catchSessionError:
grep -r "apiService\." --include="*.swift" | grep -v "catchSessionError"
Example Review Flow
User: "Review LoginViewModel"
Read PayooMerchant/Controllers/Login/LoginViewModel.swift
Check MVVM pattern, RxSwift, DI
Grep for related files (LoginViewController, LoginUseCase)
Run SwiftLint on the file
Generate detailed report with line numbers
Provide fix recommendations
Key Architectural Rules
Layer Dependencies
Presentation → Domain ← Data
Presentation can import Domain
Data can import Domain
Domain imports nothing (pure Swift)
NEVER: Domain imports Data/Presentation
RxSwift Pattern
// ViewModel transform pattern
func transform(input: Input) -> Output {
let result = input.trigger
.flatMapLatest { [weak self] _ -> Observable<Data> in
guard let self = self else { return .empty() }
return self.useCase.execute()
.catchSessionError(self.sessionUC) // CRITICAL!
}
return Output(result: result.asDriver(onErrorJustReturn: .empty))
}
Memory Management
// CORRECT
.subscribe(onNext: { [weak self] value in
self?.updateUI(value)
}).disposed(by: disposeBag)
// WRONG - Retain cycle!
.subscribe(onNext: { value in
self.updateUI(value)
}).disposed(by: disposeBag)
Output Format
Always provide:
Clear issue categorization (Critical/Warning/Info)
File paths with line numbers for clickable links
Code snippets showing the problem
Concrete fix recommendations
Summary with metrics
Reference: See standards.md for detailed coding standards and examples.md for review examples.
1 --- 2 name: payoo-ios-code-review 3 description: Comprehensive iOS code review for Payoo Merchant app. Checks Clean Architecture patterns, MVVM with RxSwift, memory management, Swinject DI, session error handling, layer separation, naming conventions, and SwiftLint compliance. Use when "review code", "check code", "code review", "review PR", "check pull request", or analyzing Swift files in this project. 4 --- 5
6 # Payoo iOS Code Review
7
8 Comprehensive code review for the Payoo Merchant iOS app following Clean Architecture with RxSwift and Swinject.
9
10 ## When to Activate
11
12 - "review code", "check code", "code review"
13 - "review PR", "review pull request", "check pull request"
14 - "review this file", "check this ViewModel"
15 - "is this code correct", "any issues with this code"
16 - When analyzing Swift files in PayooMerchant, Domain, Data, or Analytics layers
17
18 ## Review Process
19
20 ### Step 1: Identify Scope
21 - Single file review → Read the file
22 - Multiple files → Use Glob to find related files
23 - Pull request → Check git diff for changed files
24 - Full feature → Grep for related ViewModels/UseCases
25
26 ### Step 2: Layer-Specific Checks
27
28 #### For Presentation Layer (PayooMerchant/)
29 1. **MVVM Pattern**
30 - ✓ ViewModel implements `ViewModelType` protocol
31 - ✓ Has `Input` and `Output` nested types
32 - ✓ Has `transform(input:) -> Output` method
33 - ✓ ViewControllers bind to Input/Output only
34 - ✓ No business logic in ViewControllers
35
36 2. **RxSwift Memory Management**
37 - ✓ Every ViewController/ViewModel has `DisposeBag`
38 - ✓ All subscriptions use `.disposed(by: disposeBag)`
39 - ✓ Closures capturing self use `[weak self]` or `[unowned self]`
40 - ✓ No retain cycles in Observable chains
41
42 3. **Navigation & DI**
43 - ✓ Navigator passed as dependency (never created directly)
44 - ✓ UseCases injected via constructor
45 - ✓ No direct ViewController instantiation
46 - ✓ Uses factory methods from `ViewControllerFactory`
47
48 4. **Session Error Handling**
49 - ✓ **CRITICAL**: All API calls have `.catchSessionError(sessionUC)`
50 - ✗ Missing `.catchSessionError()` → Session timeout won't logout
51
52 #### For Domain Layer (Domain/)
53 1. **Clean Architecture Rules**
54 - ✓ Pure Swift only (no UIKit imports)
55 - ✓ No imports from Data or Presentation layers
56 - ✓ Only protocols for services (no implementations)
57 - ✓ Models are simple structs/classes
58
59 2. **UseCase Pattern**
60 - ✓ Protocol defines interface (`UseCaseType`)
61 - ✓ Implementation injected with dependencies
62 - ✓ Single responsibility per UseCase
63 - ✓ Returns RxSwift Observables/Singles/Maybes
64 - ✓ Uses `.catchSessionError(sessionUC)` for API calls
65
66 3. **Service Protocols**
67 - ✓ Defined in `Domain/Service/`
68 - ✓ Implemented in Data layer
69 - ✓ Injected via Swinject
70
71 #### For Data Layer (Data/)
72 1. **Repository Pattern**
73 - ✓ Implements Domain service protocols
74 - ✓ Uses Moya for network calls
75 - ✓ Uses Realm for local storage
76 - ✓ Converters transform DTOs ↔ Domain models
77
78 2. **API Models**
79 - ✓ DTOs in `Data/Model/`
80 - ✓ Conform to `DomainConvertible` or `RealmRepresentable`
81 - ✓ Use ObjectMapper for JSON parsing
82 - ✓ Don't leak to Domain/Presentation layers
83
84 ### Step 3: Project-Wide Checks
85
86 1. **SwiftLint Compliance**
87 - Run: `./Pods/SwiftLint/swiftlint lint --reporter xcode`
88 - Check: Type body length (300/400), file length (800/1200)
89 - Check: Opt-in rules (empty_count, yoda_condition, todo, etc.)
90
91 2. **Common Pitfalls**
92 - [ ] Missing `.catchSessionError()` on API observables
93 - [ ] Manual ViewController instantiation (should use factory)
94 - [ ] Missing DependencyContainer registration
95 - [ ] Breaking layer boundaries (e.g., Data imported in Domain)
96 - [ ] Missing `disposed(by: disposeBag)`
97 - [ ] Strong self in closures causing retain cycles
98 - [ ] Using `.count > 0` instead of `.isEmpty` (SwiftLint)
99 - [ ] Force unwraps without justification
100 - [ ] Magic numbers without constants
101
102 3. **RxSwift Best Practices**
103 - Use `Driver` for UI bindings (never fails, main thread)
104 - Use `Single` for one-time operations (network calls)
105 - Use `Observable` for streams
106 - Use `Maybe` for optional single values
107 - Prefer `.bind(to:)` over `.subscribe(onNext:)`
108
109 4. **Naming Conventions**
110 - [ ] ViewModels: `[Feature]ViewModel` (e.g., `LoginViewModel`, `TransactionHistoryViewModel`)
111 - [ ] ViewControllers: `[Feature]ViewController` (e.g., `LoginViewController`)
112 - [ ] UseCases: `[Action]UseCase` (e.g., `GetProfileUseCase`, `LoginUseCase`)
113 - [ ] UseCase protocols: `[Action]UseCaseType` (e.g., `GetProfileUseCaseType`)
114 - [ ] Navigators: `[Feature]Navigator` (e.g., `LoginNavigator`, `HomeNavigator`)
115 - [ ] Navigator protocols: `[Feature]NavigatorType`
116 - [ ] Services (protocols): `[Name]Service` (e.g., `ApiService`, `LocalStorageService`)
117 - [ ] Services (impl): `Default[Name]Service` or `[Tech][Name]Service` (e.g., `DefaultApiService`, `RealmStorageService`)
118 - [ ] Protocols: `[Name]Type` suffix for main protocols
119 - [ ] Variables: camelCase, descriptive (avoid abbreviations like `usrNm`, use `username`)
120 - [ ] Constants: camelCase for local, or `k` prefix for global (e.g., `kMaxRetryCount`)
121 - [ ] IBOutlets: Descriptive names with type suffix (e.g., `loginButton`, `usernameTextField`)
122 - [ ] Avoid single letters except in loops (i, j) or common conventions (x, y)
123
124 ### Step 4: Generate Report
125
126 Format:
127 ```markdown
128 ## Code Review: [File/Feature Name]
129
130 ### 📋 Summary
131 Files: X | 🔴 Critical: X | 🟡 Warning: X | 🔵 Info: X | Status: [✅ Approved / ⚠️ Needs fixes / ❌ Blocked]
132
133 ### ✅ Strengths
134 - [List good patterns found]
135
136 ### ⚠️ Issues Found
137
138 #### 🔴 Critical (Must Fix)
139 **[Issue]** at [file:line]
140 - **Problem**: [Description]
141 - **Impact**: [Why critical]
142 - **Fix**:
143 \`\`\`swift
144 // Corrected code
145 \`\`\`
146
147 #### 🟡 Warning (Should Fix)
148 **[Issue]** at [file:line]
149 - **Problem**: [Description]
150 - **Suggestion**: [How to fix]
151
152 #### 🔵 Info (Consider)
153 **[Issue]** at [file:line]
154 - **Note**: [Observation]
155 - **Suggestion**: [Optional improvement]
156 ```
157
158 ## Review Categories
159
160 ### Critical Issues (Must Fix)
161 - Missing `.catchSessionError()` on API calls
162 - Retain cycles / memory leaks
163 - Breaking Clean Architecture layer boundaries
164 - Missing DisposeBag disposal
165 - Force unwraps in unsafe contexts
166
167 ### Warnings (Should Fix)
168 - Manual ViewController instantiation
169 - Missing DependencyContainer registration
170 - SwiftLint violations
171 - Non-descriptive variable names
172 - Large type bodies (>300 lines)
173
174 ### Info (Consider)
175 - Potential optimizations
176 - Code duplication
177 - Missing unit tests
178 - Outdated comments
179 - TODO/FIXME comments
180
181 ## Quick Commands
182
183 Run SwiftLint:
184 ```bash
185 ./Pods/SwiftLint/swiftlint lint --reporter xcode
186 ```
187
188 Find files without DisposeBag:
189 ```bash
190 grep -L "DisposeBag" PayooMerchant/**/*ViewModel.swift
191 ```
192
193 Find API calls without catchSessionError:
194 ```bash
195 grep -r "apiService\." --include="*.swift" | grep -v "catchSessionError"
196 ```
197
198 ## Example Review Flow
199
200 1. User: "Review LoginViewModel"
201 2. Read `PayooMerchant/Controllers/Login/LoginViewModel.swift`
202 3. Check MVVM pattern, RxSwift, DI
203 4. Grep for related files (LoginViewController, LoginUseCase)
204 5. Run SwiftLint on the file
205 6. Generate detailed report with line numbers
206 7. Provide fix recommendations
207
208 ## Key Architectural Rules
209
210 1. **Layer Dependencies**
211 ```
212 Presentation → Domain ← Data
213 ```
214 - Presentation can import Domain
215 - Data can import Domain
216 - Domain imports nothing (pure Swift)
217 - NEVER: Domain imports Data/Presentation
218
219 2. **RxSwift Pattern**
220 ```swift
221 // ViewModel transform pattern
222 func transform(input: Input) -> Output {
223 let result = input.trigger
224 .flatMapLatest { [weak self] _ -> Observable<Data> in
225 guard let self = self else { return .empty() }
226 return self.useCase.execute()
227 .catchSessionError(self.sessionUC) // CRITICAL!
228 }
229 return Output(result: result.asDriver(onErrorJustReturn: .empty))
230 }
231 ```
232
233 3. **Memory Management**
234 ```swift
235 // CORRECT
236 .subscribe(onNext: { [weak self] value in
237 self?.updateUI(value)
238 }).disposed(by: disposeBag)
239
240 // WRONG - Retain cycle!
241 .subscribe(onNext: { value in
242 self.updateUI(value)
243 }).disposed(by: disposeBag)
244 ```
245
246 ## Output Format
247
248 Always provide:
249 1. Clear issue categorization (Critical/Warning/Info)
250 2. File paths with line numbers for clickable links
251 3. Code snippets showing the problem
252 4. Concrete fix recommendations
253 5. Summary with metrics
254
255 Reference: See `standards.md` for detailed coding standards and `examples.md` for review examples.