| name | payoo-ios-code-review |
| 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. |
| allowed-tools | Read, Grep, Glob, Bash |
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
ViewModelTypeprotocol - ✓ Has
InputandOutputnested types - ✓ Has
transform(input:) -> Outputmethod - ✓ ViewControllers bind to Input/Output only
- ✓ No business logic in ViewControllers
- ✓ ViewModel implements
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
- ✓ Every ViewController/ViewModel has
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
- ✓ CRITICAL: All API calls have
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
- ✓ Protocol defines interface (
Service Protocols
- ✓ Defined in
Domain/Service/ - ✓ Implemented in Data layer
- ✓ Injected via Swinject
- ✓ Defined in
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
DomainConvertibleorRealmRepresentable - ✓ Use ObjectMapper for JSON parsing
- ✓ Don't leak to Domain/Presentation layers
- ✓ DTOs in
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.)
- Run:
Common Pitfalls
- Missing
.catchSessionError()on API observables - Manual ViewController instantiation (should use factory)
- Missing DependencyContainer registration
- Breaking layer boundaries (e.g., Data imported in Domain)
- Missing
disposed(by: disposeBag) - Strong self in closures causing retain cycles
- Using
.count > 0instead of.isEmpty(SwiftLint) - Force unwraps without justification
- Magic numbers without constants
- Missing
RxSwift Best Practices
- Use
Driverfor UI bindings (never fails, main thread) - Use
Singlefor one-time operations (network calls) - Use
Observablefor streams - Use
Maybefor optional single values - Prefer
.bind(to:)over.subscribe(onNext:)
- Use
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]Serviceor[Tech][Name]Service(e.g.,DefaultApiService,RealmStorageService) - Protocols:
[Name]Typesuffix for main protocols - Variables: camelCase, descriptive (avoid abbreviations like
usrNm, useusername) - Constants: camelCase for local, or
kprefix 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)
- ViewModels:
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.