Documentation Index
Fetch the complete documentation index at: https://docs.privacyboost.io/llms.txt
Use this file to discover all available pages before exploring further.
Error Handling
This guide covers error handling patterns for the Privacy Boost iOS SDK. For the cross-platform error code reference, see Error Handling.
SDKError
All SDK errors are thrown as SDKError enum cases:
enum SDKError: Error {
case notConnected // Wallet not connected
case notAuthenticated // Not logged in
case invalidConfig // Invalid SDK configuration
case networkError(String) // Network/connectivity issues
case walletError(String) // Wallet operation failed
case signatureRejected // User rejected signing
case insufficientBalance // Not enough balance
case invalidAddress // Invalid address format
case invalidAmount // Invalid amount format
case serializationError(String) // Data encoding/decoding failed
case authServerError(code: String, message: String)
case shieldError(code: String, message: String)
case transferError(code: String, message: String)
case noteError(code: String, message: String)
case merkleError(code: String, message: String)
case apiError(code: String, message: String, retryable: Bool)
case rateLimited(retryAfterMs: UInt64) // Too many requests
case forbidden(String) // Access denied
case resourceNotFound(String) // Resource not found
case internalError(String) // Internal SDK error
}
Type-Safe Error Handling
Use Swift’s pattern matching to handle specific error cases:
do {
let result = try await sdk.shield(
tokenAddress: tokenAddress,
amount: amount
)
} catch SDKError.signatureRejected {
// User cancelled — do nothing
return
} catch SDKError.insufficientBalance {
showAlert("Not enough balance")
} catch SDKError.walletError(let message) {
showAlert("Wallet error: \(message)")
} catch SDKError.networkError(let message) {
showAlert("Network error. Please try again.")
} catch SDKError.rateLimited(let retryAfterMs) {
let seconds = retryAfterMs / 1000
showAlert("Too many requests. Try again in \(seconds) seconds.")
} catch SDKError.apiError(_, let message, let retryable) {
if retryable {
// Retry the operation
} else {
showAlert(message)
}
} catch {
showAlert("An unexpected error occurred")
}
Retry with Exponential Backoff
For retryable errors, use exponential backoff:
func withRetry<T>(
maxRetries: Int = 3,
operation: () async throws -> T
) async throws -> T {
for attempt in 1...maxRetries {
do {
return try await operation()
} catch SDKError.networkError(_) where attempt < maxRetries {
let delay = UInt64(1_000_000_000 * attempt) // 1s, 2s, 3s
try await Task.sleep(nanoseconds: delay)
continue
} catch SDKError.apiError(_, _, let retryable) where retryable && attempt < maxRetries {
let delay = UInt64(1_000_000_000 * attempt)
try await Task.sleep(nanoseconds: delay)
continue
} catch SDKError.rateLimited(let retryAfterMs) where attempt < maxRetries {
try await Task.sleep(nanoseconds: retryAfterMs * 1_000_000)
continue
}
}
// Last attempt — let errors propagate
return try await operation()
}
// Usage
let balance = try await withRetry {
try await sdk.getBalance(tokenAddress: token)
}
Auto Re-Authentication
Handle expired sessions by re-authenticating automatically:
func withAuth<T>(
wallet: WalletDelegate,
operation: () async throws -> T
) async throws -> T {
do {
return try await operation()
} catch SDKError.notAuthenticated {
// Session expired — re-authenticate
_ = try await sdk.authenticate(wallet: wallet)
return try await operation()
}
}
// Usage
let balance = try await withAuth(wallet: myWallet) {
try await sdk.getBalance(tokenAddress: token)
}
User-Friendly Error Messages
Map SDK errors to messages suitable for showing to users:
func userMessage(for error: Error) -> String {
guard let sdkError = error as? SDKError else {
return "An unexpected error occurred"
}
switch sdkError {
case .notConnected:
return "Please connect your wallet"
case .notAuthenticated:
return "Session expired. Please log in again."
case .signatureRejected:
return "Request cancelled"
case .insufficientBalance:
return "Not enough balance"
case .invalidAddress:
return "Invalid address"
case .invalidAmount:
return "Invalid amount"
case .networkError:
return "Connection failed. Please check your internet and try again."
case .rateLimited:
return "Too many requests. Please wait a moment."
case .walletError(let message):
return "Wallet error: \(message)"
default:
return "Something went wrong. Please try again."
}
}
Categorizing Errors
Group errors by whether user action can resolve them:
enum ErrorCategory {
case userAction // User can fix (e.g., connect wallet, add funds)
case retryable // Transient, retry may work
case fatal // Requires developer attention
}
func categorize(_ error: SDKError) -> ErrorCategory {
switch error {
case .notConnected, .notAuthenticated, .signatureRejected,
.insufficientBalance, .invalidAddress, .invalidAmount:
return .userAction
case .networkError, .rateLimited:
return .retryable
case .apiError(_, _, let retryable):
return retryable ? .retryable : .fatal
default:
return .fatal
}
}
SwiftUI Error Handling Pattern
class VaultViewModel: ObservableObject {
@Published var errorMessage: String?
@Published var isLoading = false
func deposit(tokenAddress: String, amount: String) async {
isLoading = true
errorMessage = nil
do {
let result = try await withRetry {
try await sdk.shield(
tokenAddress: tokenAddress,
amount: amount
)
}
print("Deposit successful: \(result.txHash)")
} catch {
errorMessage = userMessage(for: error)
}
isLoading = false
}
}
Best Practices
1. Always Handle signatureRejected Silently
When a user cancels a wallet signature, don’t show an error — just return to the previous state:
do {
let result = try await sdk.shield(tokenAddress: token, amount: amount)
} catch SDKError.signatureRejected {
return // User cancelled, nothing to report
} catch {
showAlert(userMessage(for: error))
}
2. Log Errors for Debugging
do {
let result = try await sdk.shield(tokenAddress: token, amount: amount)
} catch {
// Log full error for debugging
print("[PrivacyBoost] Deposit failed: \(error)")
// Show user-friendly message
showAlert(userMessage(for: error))
}
3. Use Error Categories for UI Decisions
do {
try await someOperation()
} catch let error as SDKError {
switch categorize(error) {
case .userAction:
showAlert(userMessage(for: error))
case .retryable:
showRetryButton()
case .fatal:
showContactSupport()
}
} catch {
showContactSupport()
}
Next Steps