Error Handling
Handle Hyperbasis errors gracefully.
Error Types
HBStorageError
The main error type for storage operations:
public enum HBStorageError: LocalizedError {
case notFound(type: String, id: UUID)
case encodingFailed(underlying: Error)
case decodingFailed(underlying: Error)
case fileSystemError(underlying: Error)
case compressionFailed(underlying: Error)
case decompressionFailed(underlying: Error)
case cloudNotConfigured
case cloudSyncFailed(underlying: Error)
case networkError(underlying: Error)
case unauthorized
case serverError(statusCode: Int, message: String?)
// Versioning errors
case versionNotFound(anchorId: UUID, version: Int)
case reconstructionFailed(anchorId: UUID)
case eventLogCorrupted(spaceId: UUID)
}HBSpaceError
Errors specific to space operations:
public enum HBSpaceError: LocalizedError {
case serializationFailed(underlying: Error?)
case deserializationFailed(underlying: Error?)
case invalidWorldMapData
}HBAnchorError
Errors specific to anchor operations:
public enum HBAnchorError: LocalizedError {
case invalidTransform(count: Int)
case alreadyDeleted
case metadataKeyNotFound(key: String)
}Handling Patterns
Basic Error Handling
do {
try await storage.save(space)
} catch {
print("Failed to save: \(error.localizedDescription)")
}Pattern Matching
do {
let space = try await storage.loadSpace(id: spaceId)
} catch HBStorageError.notFound(let type, let id) {
print("\(type) with id \(id) not found")
} catch HBStorageError.decompressionFailed {
print("World map data is corrupted")
} catch {
print("Unexpected error: \(error)")
}Versioning Errors
Handle errors when working with timeline and rollback:
do {
try await storage.rollback(anchorId: anchorId, toVersion: 5)
} catch HBStorageError.versionNotFound(let id, let version) {
print("Version \(version) doesn't exist for anchor \(id)")
} catch HBStorageError.reconstructionFailed(let id) {
print("Could not reconstruct anchor \(id) from events")
} catch HBStorageError.eventLogCorrupted(let spaceId) {
print("Event log for space \(spaceId) is corrupted")
}Cloud Sync Errors
Cloud sync errors don't prevent local operations:
do {
try await storage.save(space)
} catch HBStorageError.cloudSyncFailed(let underlying) {
// Local save succeeded, cloud sync failed
// Operation is queued for retry
print("Will retry cloud sync: \(underlying.localizedDescription)")
} catch HBStorageError.networkError {
// Local save succeeded, no network
print("Saved locally, will sync when online")
} catch {
// Local save failed - this is critical
print("Critical error: \(error)")
}Error Recovery
Retry Logic
func saveWithRetry(_ space: HBSpace, maxAttempts: Int = 3) async throws {
var lastError: Error?
for attempt in 1...maxAttempts {
do {
try await storage.save(space)
return
} catch HBStorageError.networkError {
lastError = error
// Wait before retry
try await Task.sleep(nanoseconds: UInt64(attempt) * 1_000_000_000)
} catch {
throw error // Don't retry non-network errors
}
}
throw lastError!
}Fallback Strategies
func loadSpaceWithFallback() async throws -> HBSpace? {
do {
return try await storage.loadSpace(id: preferredSpaceId)
} catch HBStorageError.notFound {
// Try loading any available space
let spaces = try await storage.loadAllSpaces()
return spaces.first
} catch HBStorageError.decompressionFailed {
// Corrupted space, delete and start fresh
try? await storage.deleteSpace(id: preferredSpaceId)
return nil
}
}SwiftUI Integration
Error State
@MainActor
class ARViewModel: ObservableObject {
@Published var error: AppError?
enum AppError: LocalizedError {
case loadFailed(Error)
case saveFailed(Error)
case relocalizationFailed
var errorDescription: String? {
switch self {
case .loadFailed(let error):
return "Failed to load: \(error.localizedDescription)"
case .saveFailed(let error):
return "Failed to save: \(error.localizedDescription)"
case .relocalizationFailed:
return "Could not relocalize. Try moving to a different area."
}
}
var recoverySuggestion: String? {
switch self {
case .loadFailed:
return "Try starting a new session."
case .saveFailed:
return "Check your storage space and try again."
case .relocalizationFailed:
return "Look around the room to help find matching features."
}
}
}
}Error Alert
struct ContentView: View {
@StateObject var viewModel = ARViewModel()
var body: some View {
ARViewContainer()
.alert(
"Error",
isPresented: Binding(
get: { viewModel.error != nil },
set: { if !$0 { viewModel.error = nil } }
),
presenting: viewModel.error
) { error in
Button("OK") { viewModel.error = nil }
if let suggestion = error.recoverySuggestion {
Button("Try Again") {
viewModel.error = nil
Task { await viewModel.retry() }
}
}
} message: { error in
VStack {
Text(error.localizedDescription)
if let suggestion = error.recoverySuggestion {
Text(suggestion)
.font(.caption)
}
}
}
}
}Logging
Structured Logging
import os.log
extension Logger {
static let hyperbasis = Logger(subsystem: "com.yourapp", category: "hyperbasis")
}
func saveSpace(_ space: HBSpace) async {
Logger.hyperbasis.info("Saving space: \(space.id)")
do {
try await storage.save(space)
Logger.hyperbasis.info("Space saved successfully")
} catch {
Logger.hyperbasis.error("Failed to save space: \(error.localizedDescription)")
}
}Debug Information
extension HBStorageError {
var debugInfo: String {
switch self {
case .notFound(let type, let id):
return "NotFound: \(type) \(id)"
case .encodingFailed(let error):
return "Encoding: \(error)"
case .decodingFailed(let error):
return "Decoding: \(error)"
case .fileSystemError(let error):
return "FileSystem: \(error)"
case .cloudSyncFailed(let error):
return "CloudSync: \(error)"
case .networkError(let error):
return "Network: \(error)"
case .serverError(let code, let message):
return "Server(\(code)): \(message ?? "No message")"
default:
return String(describing: self)
}
}
}Best Practices
- •Always handle errors - Don't ignore errors, especially for save operations
- •Distinguish critical vs non-critical - Cloud sync failures are recoverable; local failures are critical
- •Provide user feedback - Show appropriate error messages and recovery options
- •Log for debugging - Log errors with context for troubleshooting
- •Test error paths - Use mock storage to test error handling