Skip to main content

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:

Package.swift
dependencies: [
.product(name: "ReachuCore", package: "ReachuSwiftSDK"),
// or any other module (ReachuUI, ReachuLiveShow) which includes Core
]

SDK Configuration

Basic Setup

App.swift
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

Configuration.swift
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.

Product Model
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:

Product Usage
// 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.

Price Model
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:

Price Usage
// 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.

ProductImage Model
public struct ProductImage: Identifiable, Codable {
public let id: String
public let url: String
public let order: Int
}

Usage Example:

ProductImage Usage
// 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.

Cart Model
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.

Checkout Model
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.

Product API
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.

Cart API
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.

Checkout API
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:

Error Handling
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:

Error Handling Usage
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:

Environment Configuration
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:

Async/Await Usage
// 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)")
}
}

Best Practices
  • 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