ReachuCore Module
The ReachuCore
module provides the essential functionality for ecommerce operations in your Swift applications. It includes models, business logic, API clients, and core services for managing products, carts, checkouts, and payments.
Overview
ReachuCore is the foundation module that all other Reachu SDK modules depend on. It provides:
- 📱 Core Models - Product, Cart, Checkout, Payment, and related data structures
- 🌐 API Clients - GraphQL and REST API integration
- 🔧 Business Logic - Cart management, checkout flows, payment processing
- ⚙️ Configuration - SDK setup and environment management
Installation
ReachuCore is automatically included when you install any Reachu SDK module:
dependencies: [
.product(name: "ReachuCore", package: "ReachuSwiftSDK"),
// or any other module (ReachuUI, ReachuLiveShow) which includes Core
]
SDK Configuration
Basic Setup
import ReachuCore
@main
struct MyApp: App {
init() {
ReachuSDK.configure(
baseURL: "https://graph-ql-dev.reachu.io",
apiKey: "your-api-key",
environment: .development
)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Advanced Configuration
import ReachuCore
// Custom configuration with additional options
ReachuSDK.configure(
baseURL: "https://graph-ql-dev.reachu.io",
apiKey: "your-api-key",
environment: .development,
options: ReachuSDKOptions(
enableLogging: true,
cachePolicy: .cacheFirst,
timeout: 30.0,
retryAttempts: 3
)
)
Core Models
Product
Represents a product in the Reachu catalog.
public struct Product: Identifiable, Codable {
public let id: String
public let title: String
public let brand: String?
public let description: String?
public let sku: String
public let quantity: Int?
public let price: Price
public let images: [ProductImage]
public let variants: [Variant]
}
Usage Example:
// Access product properties
let productTitle = product.title
let productPrice = product.price.displayAmount
let isInStock = (product.quantity ?? 0) > 0
// Work with product images
let primaryImage = product.images.first { $0.order == 0 }
let allImages = product.images.sorted { $0.order < $1.order }
// Check for variants
if !product.variants.isEmpty {
print("Product has \(product.variants.count) variants")
}
Price
Represents pricing information with currency support.
public struct Price: Codable {
public let amount: Float
public let currencyCode: String
public let compareAt: Float?
// Computed properties
public var displayAmount: String { get }
public var displayCompareAtAmount: String? { get }
}
Usage Example:
// Display formatted prices
Text(product.price.displayAmount) // "USD 49.99"
// Show comparison pricing
if let comparePrice = product.price.displayCompareAtAmount {
Text(comparePrice)
.strikethrough()
.foregroundColor(.secondary)
}
// Check for discounts
let hasDiscount = product.price.compareAt != nil &&
product.price.compareAt! > product.price.amount
ProductImage
Represents product images with ordering support.
public struct ProductImage: Identifiable, Codable {
public let id: String
public let url: String
public let order: Int
}
Usage Example:
// Get primary image (order 0 or lowest order)
let primaryImage = product.images.sorted { $0.order < $1.order }.first
// Create image gallery
let sortedImages = product.images.sorted {
// Prioritize order 0 and 1
if $0.order == 0 { return true }
if $1.order == 0 { return false }
if $0.order == 1 { return true }
if $1.order == 1 { return false }
return $0.order < $1.order
}
Cart
Represents a shopping cart with line items.
public struct Cart: Identifiable, Codable {
public let id: String
public let customerSessionId: String
public let currency: String
public let lineItems: [LineItem]
public let totals: CartTotals
public let createdAt: Date
public let updatedAt: Date
}
Checkout
Represents a checkout session.
public struct Checkout: Identifiable, Codable {
public let id: String
public let cartId: String
public let status: CheckoutStatus
public let shippingAddress: Address?
public let billingAddress: Address?
public let paymentMethod: PaymentMethod?
public let totals: CheckoutTotals
}
API Clients
Product API
Fetch and search products from the Reachu catalog.
import ReachuCore
class ProductService {
private let sdk = ReachuSDK.shared
// Get all products
func loadProducts() async throws -> [Product] {
return try await sdk.channel.product.getAll(
currency: "USD",
limit: 50,
offset: 0
)
}
// Get products by category
func loadProductsByCategory(categoryId: String) async throws -> [Product] {
return try await sdk.channel.product.getByCategory(
categoryId: categoryId,
currency: "USD"
)
}
// Search products
func searchProducts(query: String) async throws -> [Product] {
return try await sdk.channel.product.search(
query: query,
currency: "USD",
limit: 20
)
}
// Get single product
func getProduct(id: String) async throws -> Product {
return try await sdk.channel.product.getById(
id: id,
currency: "USD"
)
}
}
Cart API
Manage shopping cart operations.
import ReachuCore
class CartService: ObservableObject {
@Published var cart: Cart?
private let sdk = ReachuSDK.shared
// Create new cart
func createCart() async throws {
self.cart = try await sdk.cart.create(
customerSessionId: UUID().uuidString,
currency: "USD"
)
}
// Add item to cart
func addItem(productId: String, quantity: Int = 1, variantId: String? = nil) async throws {
guard let cartId = cart?.id else { return }
self.cart = try await sdk.cart.addItem(
cartId: cartId,
productId: productId,
quantity: quantity,
variantId: variantId
)
}
// Update item quantity
func updateItem(lineItemId: String, quantity: Int) async throws {
guard let cartId = cart?.id else { return }
self.cart = try await sdk.cart.updateItem(
cartId: cartId,
lineItemId: lineItemId,
quantity: quantity
)
}
// Remove item from cart
func removeItem(lineItemId: String) async throws {
guard let cartId = cart?.id else { return }
self.cart = try await sdk.cart.removeItem(
cartId: cartId,
lineItemId: lineItemId
)
}
// Clear cart
func clearCart() async throws {
guard let cartId = cart?.id else { return }
try await sdk.cart.clear(cartId: cartId)
self.cart = nil
}
}
Checkout API
Handle checkout and payment processing.
import ReachuCore
class CheckoutService: ObservableObject {
@Published var checkout: Checkout?
private let sdk = ReachuSDK.shared
// Create checkout from cart
func createCheckout(cartId: String) async throws {
self.checkout = try await sdk.checkout.create(cartId: cartId)
}
// Update shipping address
func updateShippingAddress(_ address: Address) async throws {
guard let checkoutId = checkout?.id else { return }
self.checkout = try await sdk.checkout.updateShippingAddress(
checkoutId: checkoutId,
address: address
)
}
// Update billing address
func updateBillingAddress(_ address: Address) async throws {
guard let checkoutId = checkout?.id else { return }
self.checkout = try await sdk.checkout.updateBillingAddress(
checkoutId: checkoutId,
address: address
)
}
// Apply discount code
func applyDiscount(code: String) async throws {
guard let checkoutId = checkout?.id else { return }
self.checkout = try await sdk.checkout.applyDiscount(
checkoutId: checkoutId,
discountCode: code
)
}
// Initialize payment
func initializePayment(method: PaymentMethodType) async throws -> PaymentIntent {
guard let checkoutId = checkout?.id else {
throw ReachuError.checkoutNotFound
}
switch method {
case .stripe:
return try await sdk.payment.createStripeIntent(checkoutId: checkoutId)
case .klarna:
return try await sdk.payment.createKlarnaSession(checkoutId: checkoutId)
case .vipps:
return try await sdk.payment.createVippsSession(checkoutId: checkoutId)
}
}
}
Error Handling
The SDK provides comprehensive error handling through the ReachuError
enum:
import ReachuCore
enum ReachuError: Error, LocalizedError {
case networkError(underlying: Error)
case invalidAPIKey
case productNotFound(id: String)
case cartNotFound(id: String)
case checkoutNotFound(id: String)
case insufficientStock(productId: String, available: Int)
case invalidDiscountCode(code: String)
case paymentFailed(reason: String)
case configurationError(message: String)
var errorDescription: String? {
switch self {
case .networkError(let error):
return "Network error: \(error.localizedDescription)"
case .invalidAPIKey:
return "Invalid API key provided"
case .productNotFound(let id):
return "Product with ID '\(id)' not found"
case .cartNotFound(let id):
return "Cart with ID '\(id)' not found"
case .checkoutNotFound:
return "Checkout session not found"
case .insufficientStock(let productId, let available):
return "Insufficient stock for product \(productId). Available: \(available)"
case .invalidDiscountCode(let code):
return "Invalid discount code: \(code)"
case .paymentFailed(let reason):
return "Payment failed: \(reason)"
case .configurationError(let message):
return "Configuration error: \(message)"
}
}
}
Usage Example:
class ProductViewModel: ObservableObject {
@Published var products: [Product] = []
@Published var error: ReachuError?
@Published var isLoading = false
func loadProducts() async {
isLoading = true
defer { isLoading = false }
do {
self.products = try await ReachuSDK.shared.channel.product.getAll()
self.error = nil
} catch let reachuError as ReachuError {
self.error = reachuError
print("Reachu error: \(reachuError.localizedDescription)")
} catch {
// Handle unexpected errors
self.error = .networkError(underlying: error)
}
}
}
Environment Management
Configure different environments for development and production:
public enum ReachuEnvironment {
case development
case staging
case production
var baseURL: String {
switch self {
case .development:
return "https://graph-ql-dev.reachu.io"
case .staging:
return "https://graph-ql-staging.reachu.io"
case .production:
return "https://graph-ql.reachu.io"
}
}
}
// Configuration helper
extension ReachuSDK {
static func configureForEnvironment(_ environment: ReachuEnvironment, apiKey: String) {
configure(
baseURL: environment.baseURL,
apiKey: apiKey,
environment: environment
)
}
}
Thread Safety
All ReachuCore APIs are designed to be thread-safe and can be called from any queue:
// Safe to call from any queue
Task {
do {
let products = try await ReachuSDK.shared.channel.product.getAll()
// Update UI on main queue
await MainActor.run {
self.products = products
}
} catch {
print("Error loading products: \(error)")
}
}
- Always handle errors gracefully with proper user feedback
- Use async/await for all API calls to maintain responsive UI
- Configure the SDK once at app launch
- Implement proper loading states for better UX