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.
/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