| name | review-case |
| description | Code review for API examples. Ensures examples follow project conventions, handle lifecycle correctly, manage threads safely, and use APIs properly.
|
| compatibility | ["Cursor","Kiro","Windsurf","Claude","Copilot"] |
| license | MIT |
| metadata | {"author":"APIExample Team","version":"1.0.0","platform":"macOS"} |
Review Case Skill — macOS
When to Use
Use this skill when you need to:
- Review a new or modified example for correctness
- Ensure the example follows project conventions
- Verify lifecycle management and thread safety
- Check API usage and error handling
Review Dimensions (Priority Order)
1. Engine Lifecycle (CRITICAL)
Check:
Correct Pattern:
override func viewDidLoad() {
super.viewDidLoad()
initializeAgoraEngine()
}
override func viewWillClose() {
leaveChannel()
agoraKit.destroy()
super.viewWillClose()
}
func joinChannel() {
agoraKit.joinChannel(byToken: token, channelName: channel, info: nil, uid: 0)
}
func leaveChannel() {
agoraKit.leaveChannel(nil)
}
Incorrect Pattern:
See references/incorrect-lifecycle.swift for common mistakes.
2. Thread Safety (CRITICAL)
Check:
Correct Pattern:
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
DispatchQueue.main.async {
self.statusLabel.stringValue = "Joined channel"
}
}
Incorrect Pattern:
See references/incorrect-thread-safety.swift for common mistakes.
3. Permission Handling (HIGH)
Check:
Correct Pattern:
func initializeAgoraEngine() {
AVCaptureDevice.requestAccess(for: .video) { granted in
if granted {
self.agoraKit.enableVideo()
}
}
AVCaptureDevice.requestAccess(for: .audio) { granted in
if granted {
self.agoraKit.enableAudio()
}
}
}
4. Error Handling (HIGH)
Check:
Correct Pattern:
func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurError errorCode: AgoraErrorCode) {
DispatchQueue.main.async {
self.showError("Error: \(errorCode.rawValue)")
}
}
func rtcEngine(_ engine: AgoraRtcEngineKit, tokenPrivilegeWillExpire token: String) {
let newToken = KeyCenter.Token(channelName: self.channelName)
self.agoraKit.renewToken(newToken)
}
5. Code Convention (MEDIUM)
Check:
Correct Pattern:
class ScreenShareMain: BaseViewController {
var agoraKit: AgoraRtcEngineKit!
var remoteUid: UInt = 0
@IBOutlet weak var Container: AGEVideoContainer!
override func viewDidLoad() { ... }
func initializeAgoraEngine() { ... }
@IBAction func joinButtonTapped(_ sender: Any) { ... }
}
6. API Usage Correctness (MEDIUM)
Check:
Correct Pattern:
agoraKit.enableVideo()
agoraKit.setupLocalVideo(AgoraRtcVideoCanvas(uid: 0))
agoraKit.joinChannel(byToken: token, channelName: channel, info: nil, uid: 0)
Incorrect Pattern:
agoraKit.joinChannel(...)
agoraKit.enableVideo()
7. Resource Cleanup (MEDIUM)
Check:
Correct Pattern:
func leaveChannel() {
agoraKit.stopAudioMixing()
agoraKit.stopScreenCapture()
agoraKit.leaveChannel(nil)
}
override func viewWillClose() {
leaveChannel()
agoraKit.destroy()
super.viewWillClose()
}
Review Output Format
When reviewing, provide feedback in this format:
## Review Results
### ✅ Passed
- Engine lifecycle correctly managed
- Thread safety ensured with DispatchQueue.main.async
- Permissions requested before device access
### ⚠️ Issues Found
**[HIGH] Thread Safety Issue**
- File: `ScreenShare.swift`
- Line: 45
- Issue: UI update in delegate callback without DispatchQueue.main.async
- Suggestion: Wrap UI update with `DispatchQueue.main.async { ... }`
**[MEDIUM] Missing Error Handling**
- File: `ScreenShare.swift`
- Line: 78
- Issue: joinChannel() result not checked
- Suggestion: Implement `rtcEngine(_:didOccurError:)` delegate method
### 🔧 Recommendations
- Add logging for debugging
- Consider adding retry logic for network failures
Platform-Specific Checks
macOS-Specific
Check:
Correct Pattern:
import Cocoa
import AgoraRtcKit
class ExampleMain: BaseViewController {
@IBOutlet weak var Container: AGEVideoContainer!
}
Incorrect Pattern:
import UIKit
class ExampleMain: UIViewController { }
NEVER List
Do NOT accept:
- Engine not destroyed (memory leak)
- UI updates from background threads without DispatchQueue.main.async
- Multiple engine instances in one example
- Hardcoded App ID or token (must use KeyCenter)
- Missing
leaveChannel() before destroy()
- Objective-C files (Swift only)
- UIKit or SwiftUI (Cocoa only)
- Examples outside
APIExample/Examples/[Basic|Advanced]/ structure
- Missing delegate implementation for event handling
- No error handling for joinChannel failures
Review Checklist
Use this checklist when reviewing an example:
Lifecycle:
Thread Safety:
Permissions:
Error Handling:
Code Quality:
API Usage:
Resources:
Platform:
Common Issues and Fixes
Issue: "Engine not initialized"
Cause: destroy() called without leaveChannel() first
Fix: Always call leaveChannel() before destroy()
Issue: "UI updates crash the app"
Cause: Direct UI update from background thread
Fix: Wrap with DispatchQueue.main.async { ... }
Issue: "Memory leak detected"
Cause: destroy() not called or engine recreated
Fix: Ensure destroy() in viewWillClose() and create engine once
Issue: "Token expired error"
Cause: No token refresh handling
Fix: Implement tokenPrivilegeWillExpire() delegate method
Issue: "No audio/video"
Cause: Permissions not requested
Fix: Request permissions before enableAudio() / enableVideo()
References
- Agora RTC SDK for macOS: Documentation
- Existing examples: Review
APIExample/Examples/Basic/JoinChannelVideo/ for reference
- BaseViewController: Check
APIExample/Common/ for base class implementation