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.
/plugin marketplace add daispacy/py-claude-marketplace/plugin install py-plugin@py-claude-marketplaceThis skill is limited to using the following tools:
examples.mdstandards.mdComprehensive code review for the Payoo Merchant iOS app following Clean Architecture with RxSwift and Swinject.
MVVM Pattern
ViewModelType protocolInput and Output nested typestransform(input:) -> Output methodRxSwift Memory Management
DisposeBag.disposed(by: disposeBag)[weak self] or [unowned self]Navigation & DI
ViewControllerFactorySession Error Handling
.catchSessionError(sessionUC).catchSessionError() → Session timeout won't logoutClean Architecture Rules
UseCase Pattern
UseCaseType).catchSessionError(sessionUC) for API callsService Protocols
Domain/Service/Repository Pattern
API Models
Data/Model/DomainConvertible or RealmRepresentableSwiftLint Compliance
./Pods/SwiftLint/swiftlint lint --reporter xcodeCommon Pitfalls
.catchSessionError() on API observablesdisposed(by: disposeBag).count > 0 instead of .isEmpty (SwiftLint)RxSwift Best Practices
Driver for UI bindings (never fails, main thread)Single for one-time operations (network calls)Observable for streamsMaybe for optional single values.bind(to:) over .subscribe(onNext:)Naming Conventions
[Feature]ViewModel (e.g., LoginViewModel, TransactionHistoryViewModel)[Feature]ViewController (e.g., LoginViewController)[Action]UseCase (e.g., GetProfileUseCase, LoginUseCase)[Action]UseCaseType (e.g., GetProfileUseCaseType)[Feature]Navigator (e.g., LoginNavigator, HomeNavigator)[Feature]NavigatorType[Name]Service (e.g., ApiService, LocalStorageService)Default[Name]Service or [Tech][Name]Service (e.g., DefaultApiService, RealmStorageService)[Name]Type suffix for main protocolsusrNm, use username)k prefix for global (e.g., kMaxRetryCount)loginButton, usernameTextField)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]
.catchSessionError() on API callsRun 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"
PayooMerchant/Controllers/Login/LoginViewModel.swiftLayer Dependencies
Presentation → Domain ← Data
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)
Always provide:
Reference: See standards.md for detailed coding standards and examples.md for review examples.