ProductService API Reference
ProductService is a shared singleton service that provides a centralized way to load products across all SDK components. It eliminates code duplication and provides consistent error handling.
Overview
The ProductService class:
- Eliminates duplication - All components use the same service
- Caches SDK client - Improves performance by reusing connections
- Consistent error handling - Standardized error types across components
- Thread-safe - Safe to use from any thread with
@MainActor
Import
import ReachuUI
Singleton Access
let productService = ProductService.shared
Methods
Load Single Product
Load a single product by ID (as String or Int).
// Using String ID
let product = try await ProductService.shared.loadProduct(
productId: "123",
currency: "USD",
country: "US"
)
// Using Int ID
let product = try await ProductService.shared.loadProduct(
productId: 123,
currency: "USD",
country: "US"
)
Parameters: - productId: Product ID (String or Int)
currency: Currency code (e.g., "USD", "EUR")country: Country code for shipping (e.g., "US", "DE")
Returns:Product if found
Throws:ProductServiceError for various error conditions
Load Multiple Products
Load multiple products by IDs, or all products if IDs are empty/nil.
// Load specific products
let products = try await ProductService.shared.loadProducts(
productIds: [123, 456, 789],
currency: "USD",
country: "US"
)
// Load all products (pass nil or empty array)
let allProducts = try await ProductService.shared.loadProducts(
productIds: nil,
currency: "USD",
country: "US"
)
Parameters: - productIds: Array of product IDs (Int). If nil or empty, loads all products from channel
currency: Currency code (e.g., "USD", "EUR")country: Country code for shipping (e.g., "US", "DE")
**Returns:**Array of Product objects
Throws:ProductServiceError for various error conditions
Clear Cache
Clear the cached SDK client (useful for testing or reconfiguration).
ProductService.shared.clearCache()
Error Handling
ProductService throws ProductServiceError enum with the following cases:
public enum ProductServiceError: LocalizedError {
case invalidConfiguration(String)
case invalidProductId(String)
case productNotFound(Int)
case sdkError(SdkException)
case networkError(Error)
}
Example Error Handling
do {
let product = try await ProductService.shared.loadProduct(
productId: "123",
currency: "USD",
country: "US"
)
// Use product
} catch ProductServiceError.invalidProductId(let id) {
print("Invalid product ID: \(id)")
} catch ProductServiceError.productNotFound(let id) {
print("Product not found: \(id)")
} catch ProductServiceError.invalidConfiguration(let message) {
print("Configuration error: \(message)")
} catch ProductServiceError.sdkError(let error) {
print("SDK error: \(error.message ?? "Unknown")")
} catch ProductServiceError.networkError(let error) {
print("Network error: \(error.localizedDescription)")
} catch {
print("Unexpected error: \(error)")
}
Usage Examples
Basic Usage
import SwiftUI
import ReachuUI
struct ProductDetailView: View {
@State private var product: Product?
@State private var isLoading = false
@State private var errorMessage: String?
let productId: String
var body: some View {
Group {
if isLoading {
ProgressView()
} else if let product = product {
ProductView(product: product)
} else if let error = errorMessage {
Text("Error: \(error)")
}
}
.task {
await loadProduct()
}
}
@MainActor
private func loadProduct() async {
isLoading = true
errorMessage = nil
do {
product = try await ProductService.shared.loadProduct(
productId: productId,
currency: "USD",
country: "US"
)
} catch ProductServiceError.productNotFound(let id) {
errorMessage = "Product \(id) not found"
} catch {
errorMessage = error.localizedDescription
}
isLoading = false
}
}
Loading Multiple Products
import SwiftUI
import ReachuUI
struct ProductListView: View {
@State private var products: [Product] = []
@State private var isLoading = false
let productIds: [Int]
var body: some View {
List(products) { product in
ProductRow(product: product)
}
.task {
await loadProducts()
}
}
@MainActor
private func loadProducts() async {
isLoading = true
do {
products = try await ProductService.shared.loadProducts(
productIds: productIds,
currency: "USD",
country: "US"
)
} catch {
print("Failed to load products: \(error)")
}
isLoading = false
}
}
Loading All Products
@MainActor
private func loadAllProducts() async {
do {
// Pass nil or empty array to load all products
let allProducts = try await ProductService.shared.loadProducts(
productIds: nil,
currency: "USD",
country: "US"
)
print("Loaded \(allProducts.count) products")
} catch {
print("Error: \(error)")
}
}
Integration with Components
All SDK components (RProductBanner, RProductCarousel, RProductStore, RProductSpotlight) use ProductService internally. You don't need to use it directly unless you're building custom components.
Custom Component Example
import SwiftUI
import ReachuUI
struct CustomProductView: View {
@State private var products: [Product] = []
var body: some View {
ScrollView {
LazyVStack {
ForEach(products) { product in
ProductCard(product: product)
}
}
}
.task {
await loadProducts()
}
}
@MainActor
private func loadProducts() async {
do {
products = try await ProductService.shared.loadProducts(
productIds: nil, // Load all products
currency: "USD",
country: "US"
)
} catch {
print("Failed to load: \(error)")
}
}
}
Performance Considerations
- SDK Client Caching: The service caches the
SdkClientinstance to avoid recreating it on every call - Thread Safety: All methods are marked with
@MainActorfor safe UI updates - Error Handling: Errors are properly typed and localized for better debugging
Best Practices
- Always handle errors: Use
do-catchblocks when callingProductServicemethods - Use appropriate currency/country: Pass the correct values from your app's configuration
- Clear cache when needed: Call
clearCache()if you reconfigure the SDK - Use async/await: All methods are async and should be called with
await