Concise iOS code review for Payoo Merchant app. Focuses on critical/high/medium issues - RxSwift memory leaks, retain cycles, naming conventions, Clean Architecture violations, and business logic placement. Use when reviewing Swift files, pull requests, ViewModels, ViewControllers, UseCases, and Repositories.
Expert iOS code reviewer for Payoo Merchant app that detects critical RxSwift memory leaks, retain cycles, Clean Architecture violations, and naming issues in Swift files, ViewModels, and UseCases. Activates on "review code", "check PR", or when examining Swift files.
/plugin marketplace add daispacy/py-claude-marketplace/plugin install py-plugin@py-claude-marketplaceThis skill is limited to using the following tools:
examples.mdExpert iOS code reviewer for Payoo Merchant application, focusing on CRITICAL, HIGH, and MEDIUM priority issues that impact app stability, maintainability, and architecture.
Determine what to review:
Use Read tool to examine files, focusing on CRITICAL, HIGH, and MEDIUM priority issues only.
Impact: Memory leaks, app crashes, performance degradation
Check for:
.subscribe() MUST have .disposed(by: disposeBag)[weak self] in all closures capturing selfCommon violations:
// ā CRITICAL - Memory leak
observable
.subscribe(onNext: { value in
self.updateUI(value) // Missing disposed(by:)
})
// ā CRITICAL - Retain cycle
observable
.subscribe(onNext: { [self] value in // Strong self!
self.updateUI(value)
})
.disposed(by: disposeBag)
// ā
GOOD
observable
.subscribe(onNext: { [weak self] value in
self?.updateUI(value)
})
.disposed(by: disposeBag)
Impact: Code readability, maintainability, team collaboration
Check for:
PaymentViewModel, not PmtVM)paymentAmount, not pmt_amt)is/has/should/can prefix (e.g., isLoading, not loading)amountTextField, not amount)Common violations:
// ā BAD
var usr: User?
var loading = false
@IBOutlet weak var amount: UITextField!
var pmtVM: PaymentViewModel?
// ā
GOOD
var user: User?
var isLoading = false
@IBOutlet weak var amountTextField: UITextField!
var paymentViewModel: PaymentViewModel?
Impact: Testability, maintainability, architecture integrity
Check for:
BaseViewModel<State>, NO business logicCommon violations:
// ā BAD - ViewModel calling Repository directly
class PaymentViewModel {
private let repository: PaymentRepository
func loadPayments() {
repository.getPayments() // ā Skip UseCase layer
.subscribe(onNext: { payments in
// ...
})
.disposed(by: disposeBag)
}
}
// ā BAD - Business logic in ViewModel
class PaymentViewModel {
func processPayment(amount: Double) {
if amount <= 0 { return } // ā Business logic!
let fee = amount * 0.02
let total = amount + fee
// ...
}
}
// ā
GOOD - Proper Clean Architecture
class PaymentViewModel: BaseViewModel<PaymentState> {
private let getPaymentsUseCase: GetPaymentsUseCase
private let processPaymentUseCase: ProcessPaymentUseCase
init(getPaymentsUseCase: GetPaymentsUseCase,
processPaymentUseCase: ProcessPaymentUseCase) {
self.getPaymentsUseCase = getPaymentsUseCase
self.processPaymentUseCase = processPaymentUseCase
super.init()
}
func loadPayments() {
getPaymentsUseCase.execute() // ā
Call UseCase
.subscribe(onNext: { [weak self] payments in
self?.state.accept(.success(payments))
})
.disposed(by: disposeBag)
}
}
Impact: UI freezes, background thread crashes
Check for:
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background)).observeOn(MainScheduler.instance) before UI workCommon violations:
// ā BAD - Heavy work on main thread
apiService.getData()
.subscribe(onNext: { data in
// Heavy processing on main thread
self.processLargeData(data)
})
.disposed(by: disposeBag)
// ā
GOOD - Proper scheduler usage
apiService.getData()
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
.map { data in
// Heavy processing on background
return self.processLargeData(data)
}
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] result in
// UI updates on main
self?.updateUI(result)
})
.disposed(by: disposeBag)
Impact: App crashes, poor UX
Check for:
.onError or use catchErrorCommon violations:
// ā BAD - No error handling
apiService.getData()
.subscribe(onNext: { data in
self.updateUI(data)
})
.disposed(by: disposeBag)
// ā
GOOD - Proper error handling
apiService.getData()
.subscribe(
onNext: { [weak self] data in
self?.updateUI(data)
},
onError: { [weak self] error in
self?.showError(error.localizedDescription)
}
)
.disposed(by: disposeBag)
// ā
BETTER - Using catchError
apiService.getData()
.catchError { error in
return Observable.just(defaultData)
}
.subscribe(onNext: { [weak self] data in
self?.updateUI(data)
})
.disposed(by: disposeBag)
Impact: Future compatibility, best practices
Check for:
Common violations:
// ā BAD - Using deprecated BehaviorSubject
private let loadingSubject = BehaviorSubject<Bool>(value: false)
// ā
GOOD - Use BehaviorRelay
private let loadingRelay = BehaviorRelay<Bool>(value: false)
Focus ONLY on CRITICAL (š“), HIGH (š ), and MEDIUM (š”) priority issues. Skip low priority findings.
Provide concise, actionable output with:
š“ CRITICAL - Fix immediately (blocks release)
.subscribe() without .disposed(by: disposeBag) ā Memory leakself in closures ā Memory leakš HIGH PRIORITY - Fix before merge
BaseViewModel<State>š” MEDIUM PRIORITY - Fix in current sprint
KEEP IT CONCISE - Focus on actionable findings only.
# iOS Code Review - Priority Issues
## Summary
š“ Critical: X | š High: X | š” Medium: X
## š“ CRITICAL ISSUES (Fix Immediately)
1. **Memory leak - Missing disposal** - `PaymentViewModel.swift:45`
```swift
// ā observable.subscribe(onNext: { ... }) // No disposal
// ā
.disposed(by: disposeBag)
TransactionViewController.swift:78
[weak self] instead of [self]Naming - Abbreviations - UserViewModel.swift:12
usr ā user, pmtVM ā paymentViewModelArchitecture - ViewModel calls Repository directly - PaymentViewModel.swift:34
GetPaymentsUseCase instead of PaymentRepositoryBusiness logic in ViewModel - PaymentViewModel.swift:56-60
ProcessPaymentUseCaseNo error handling - DashboardViewModel.swift:89
onError or catchError to API callWrong scheduler - TransactionListViewModel.swift:123
.observeOn(MainScheduler.instance) before UI updateDeprecated BehaviorSubject - SettingsViewModel.swift:23
BehaviorRelay insteadā Well Done: [If applicable, briefly acknowledge 1-2 good patterns]
## Quick Reference
**Focus on 6 Priority Areas**:
1. š“ **RxSwift Memory Leaks** - Missing disposal, retain cycles
2. š **Naming Conventions** - Abbreviations, wrong case, missing prefixes
3. š **Clean Architecture** - ViewModel ā UseCase ā Repository flow
4. š” **Schedulers** - Background work, main thread UI updates
5. š” **Error Handling** - onError, catchError in Observable chains
6. š” **Deprecated Patterns** - BehaviorRelay vs BehaviorSubject
**Skip**: Code style, docs, accessibility, security, performance (unless critical), UI/UX, low priority
## Tips
- **Be concise**: One-line issue descriptions when possible
- **Be specific**: Exact file paths and line numbers
- **Be actionable**: Clear fix instructions
- **Show code**: Only for Critical/High issues, keep minimal
- **Group issues**: Batch similar violations (e.g., "5 naming violations in PaymentViewModel.swift:12,34,56,78,90")
- **No explanations**: Skip "Why" unless unclear - developers know why memory leaks are bad