PVCheevos/README.md
A Swift 6 library for communicating with the RetroAchievements API. This library provides a modern, async/await-based interface for accessing user profiles, achievements, games, and more.
Add the following to your Package.swift file:
dependencies: [
.package(url: "https://github.com/your-repo/PVCheevos.git", from: "1.0.0")
]
Or add it directly in Xcode:
The RetroAchievements API supports two authentication methods:
Option A: Web API Key (Recommended)
Option B: Username and Password
import PVCheevos
// Create a client with API key
let client = PVCheevos.client(
username: "your_username",
webAPIKey: "your_api_key"
)
// Validate credentials
let isValid = await client.validateCredentials()
if isValid {
print("✅ Credentials are valid!")
} else {
print("❌ Invalid credentials")
}
import PVCheevos
// Option 1: Create client and login manually
let client = PVCheevos.clientWithPassword(
username: "your_username",
password: "your_password"
)
let session = try await client.login(username: "your_username", password: "your_password")
print("✅ Logged in successfully! Token: \(session.token)")
// Option 2: Login and get client in one step
let (client, session) = try await PVCheevos.login(
username: "your_username",
password: "your_password"
)
print("✅ Logged in! User: \(session.username), Score: \(session.score ?? 0)")
// Get user profile
do {
let profile = try await client.getUserProfile(username: "MaxMilyin")
print("User: \(profile.user)")
print("Total Points: \(profile.totalPoints ?? 0)")
print("Member Since: \(profile.memberSince ?? "Unknown")")
} catch {
print("Error: \(error)")
}
// Get user awards
do {
let awards = try await client.getUserAwards(username: "MaxMilyin")
print("Total Awards: \(awards.totalAwardsCount ?? 0)")
print("Mastery Awards: \(awards.masteryAwardsCount ?? 0)")
} catch {
print("Error: \(error)")
}
// Get users you follow
do {
let following = try await client.getUsersIFollow()
print("Following \(following.users?.count ?? 0) users")
} catch {
print("Error: \(error)")
}
// Get achievement unlock information
do {
let unlocks = try await client.getAchievementUnlocks(achievementId: 12345)
print("Achievement: \(unlocks.achievement?.title ?? "Unknown")")
print("Total Unlocks: \(unlocks.totalUnlocks ?? 0)")
} catch {
print("Error: \(error)")
}
// Get game information
do {
let game = try await client.getGame(gameId: 1)
print("Game: \(game.title ?? "Unknown")")
print("Console: \(game.consoleName ?? "Unknown")")
print("Achievements: \(game.numAchievements ?? 0)")
} catch {
print("Error: \(error)")
}
// Get game with user progress
do {
let gameExtended = try await client.getGameExtended(gameId: 1, username: "MaxMilyin")
if let progress = gameExtended.userProgress {
print("Achieved: \(progress.numAchieved ?? 0)/\(progress.numPossibleAchievements ?? 0)")
print("Score: \(progress.scoreAchieved ?? 0)/\(progress.possibleScore ?? 0)")
}
} catch {
print("Error: \(error)")
}
// Get game rankings
do {
let rankings = try await client.getGameRankings(gameId: 1)
print("Top players:")
rankings.entries?.prefix(5).forEach { rank in
print("\(rank.user ?? "Unknown"): \(rank.totalScore ?? 0) points")
}
} catch {
print("Error: \(error)")
}
// Get comments on a user's wall
do {
let comments = try await client.getCommentsOnUserWall(username: "MaxMilyin")
print("Found \(comments.count ?? 0) comments")
comments.results?.forEach { comment in
print("\(comment.user ?? "Unknown"): \(comment.commentText ?? "")")
}
} catch {
print("Error: \(error)")
}
// Get comments on a game
do {
let comments = try await client.getCommentsOnGameWall(gameId: 1)
print("Game has \(comments.count ?? 0) comments")
} catch {
print("Error: \(error)")
}
// Get comments on an achievement
do {
let comments = try await client.getCommentsOnAchievementWall(achievementId: 12345)
print("Achievement has \(comments.count ?? 0) comments")
} catch {
print("Error: \(error)")
}
You can provide your own URLSession for custom networking configurations:
let customSession = URLSession(configuration: .ephemeral)
let client = PVCheevos.client(
username: "your_username",
webAPIKey: "your_api_key",
urlSession: customSession
)
The library provides comprehensive error handling through the RetroError enum:
do {
let profile = try await client.getUserProfile(username: "nonexistent")
} catch let error as RetroError {
switch error {
case .unauthorized:
print("Invalid credentials")
case .notFound:
print("User not found")
case .rateLimitExceeded:
print("Rate limit exceeded")
case .network(let networkError):
print("Network error: \(networkError)")
case .serverError(let message):
print("Server error: \(message ?? "Unknown")")
case .invalidResponse:
print("Invalid response format")
case .custom(let message):
print("Custom error: \(message)")
}
} catch {
print("Unexpected error: \(error)")
}
For convenience, the library provides shorter type aliases:
// Instead of RetroAchievementsClient
let client: RAClient = PVCheevos.client(username: "user", webAPIKey: "key")
// Instead of UserProfile
let profile: RAUserProfile = try await client.getUserProfile(username: "user")
// Instead of Achievement
let achievement: RAAchievement = // ...
RetroCredentials / RACredentials - Authentication credentialsRetroAchievementsClient / RAClient - Main API clientRetroError / RAError - Error typesUserProfile / RAUserProfile - Complete user profile informationUserAwards / RAUserAwards - User's awards and achievementsUserSummary / RAUserSummary - Basic user informationUserList / RAUserList - Collection of usersAchievement / RAAchievement - Individual achievement informationAchievementUnlocks / RAAchievementUnlocks - Achievement unlock dataUserProgress / RAUserProgress - User's progress on a gameGame / RAGame - Complete game informationGameExtended / RAGameExtended - Game with user progressGameSummary / RAGameSummary - Basic game informationGameConsole / RAGameConsole - Console informationComment / RAComment - Individual commentComments / RAComments - Collection of commentsRun tests using Xcode or Swift Package Manager:
swift test
The library includes comprehensive unit tests with mock network responses to ensure reliability.
The package includes a command-line tool (ra-cli) for testing and exploring the RetroAchievements API:
# Build and run the CLI tool
swift build
swift run ra-cli --help
# Test with your credentials (API key)
swift run ra-cli -u your_username -k your_api_key
# Test with username and password
swift run ra-cli -u your_username -p your_password
# Explore different data types
swift run ra-cli -u your_username -k your_api_key -c awards
swift run ra-cli -u your_username -p your_password -c social
swift run ra-cli -u your_username -k your_api_key -c game
swift run ra-cli -u your_username -p your_password -c all
# Use environment variables for convenience
export RA_USERNAME=your_username
export RA_API_KEY=your_api_key
swift run ra-cli -c profile
# Or with password
export RA_USERNAME=your_username
export RA_PASSWORD=your_password
swift run ra-cli -c awards
The CLI tool provides:
This library is designed to work seamlessly with the Provenance emulator app. Example integration:
class AchievementManager {
private let client: RetroAchievementsClient
init(username: String, apiKey: String) {
self.client = PVCheevos.client(username: username, webAPIKey: apiKey)
}
func setupForGame(gameId: Int) async {
do {
let game = try await client.getGame(gameId: gameId)
// Setup achievement tracking for this game
await setupAchievementTracking(for: game)
} catch {
print("Failed to setup achievements: \(error)")
}
}
private func setupAchievementTracking(for game: Game) async {
// Implementation depends on your core's achievement system
}
}
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.