Skip to main content

Global Checkout System

A complete, production-ready checkout solution that provides global cart management and seamless checkout flow across your entire iOS/macOS application. The checkout system works as a modal overlay that appears over any screen, allowing users to complete purchases without interrupting their navigation flow.

Overview

The Reachu Swift SDK includes a comprehensive checkout system built around two main components:

  • CartManager - Global state management for shopping cart operations
  • RCheckoutOverlay - Modal checkout interface that handles the complete purchase flow

This system is designed to integrate seamlessly with any iOS/macOS app, providing a consistent checkout experience regardless of where products are added to the cart.

✨ UX Enhancement Components

The Reachu Swift SDK includes advanced UX components that provide professional, polished interactions:

RFloatingCartIndicator - Persistent Cart Access

A floating cart indicator that appears on all screens when the cart contains items, providing quick access to checkout from anywhere in your app.

Key Features:

  • Global Persistence - Appears on every screen when cart has items
  • Real-time Updates - Shows current item count and total price
  • Quick Checkout - One-tap access to checkout overlay
  • Elegant Design - Gradient background with smooth animations
  • Auto-hide - Disappears when cart is empty

RToastNotification - User Feedback System

Elegant, non-intrusive notifications that provide instant feedback for user actions.

Key Features:

  • Auto-notifications - Appear automatically when products are added/removed
  • Multiple Types - Success, info, warning, error variants
  • Product Names - Include specific product information
  • Auto-dismiss - Disappear automatically with smooth animations
  • Haptic Feedback - Tactile confirmation on iOS devices

Enhanced Product Animations

Both RProductCard and RProductSlider include sophisticated animations that provide visual feedback.

Key Features:

  • Button Animations - Scale effects and loading states
  • Success Indicators - Temporary "Added!" state with checkmarks
  • Product Highlighting - Green borders and scale effects in sliders
  • Slider Feedback - Subtle scale animation for the entire slider
  • Spring Animations - Natural, bouncy feel for all interactions

Core Components

CartManager (Global State)

The CartManager is an ObservableObject that manages the global shopping cart state across your entire application.

Key Features:

  • Global State Management - Single source of truth for cart data
  • Reactive Updates - Real-time UI updates via @Published properties
  • Quantity Management - Add, remove, and update item quantities
  • Checkout Coordination - Controls checkout overlay presentation
  • Error Handling - Comprehensive error management with user feedback

RCheckoutOverlay (Modal Interface)

A full-screen modal that handles the complete checkout process from cart review to payment completion.

Key Features:

  • Multi-Step Flow - Cart → Shipping → Payment → Confirmation
  • Progress Indicator - Visual progress through checkout steps
  • Cross-Platform - Works on iOS, macOS, tvOS, and watchOS
  • Responsive Design - Adapts to different screen sizes
  • Error Handling - User-friendly error messages and recovery

Basic Setup

Step 1: Initialize CartManager

Add the CartManager as a global environment object in your app:

App.swift
import SwiftUI
import ReachuUI

@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(CartManager())
}
}
}

Step 2: Add UX Components and Checkout Overlay

In your main content view, add the checkout overlay and UX enhancement components:

ContentView.swift
import SwiftUI
import ReachuCore
import ReachuUI

struct ContentView: View {
@EnvironmentObject var cartManager: CartManager

var body: some View {
NavigationView {
// Your main app content
YourMainContentView()
}
// Checkout overlay - appears when user initiates checkout
.sheet(isPresented: $cartManager.isCheckoutPresented) {
RCheckoutOverlay()
.environmentObject(cartManager)
}
// Floating cart indicator - persistent across all screens
.overlay {
RFloatingCartIndicator()
.environmentObject(cartManager)
}
// Global toast notifications - automatic feedback
.overlay {
RToastOverlay()
}
}
}

What each component does:

  • RCheckoutOverlay - Modal checkout flow when user taps "Proceed to Checkout"
  • RFloatingCartIndicator - Shows cart count/total, provides quick checkout access
  • RToastOverlay - Displays automatic notifications when products are added/removed

Step 3: Add Products to Cart

Use the cart manager anywhere in your app to add products:

ProductView.swift
import SwiftUI
import ReachuUI
import ReachuCore

struct ProductView: View {
@EnvironmentObject var cartManager: CartManager
let product: Product

var body: some View {
VStack {
// Product details...

RButton(
title: "Add to Cart",
style: .primary,
size: .large,
isLoading: cartManager.isLoading
) {
Task {
await cartManager.addProduct(product)
}
}
}
}
}

✨ Automatic UX Enhancements

When you use RProductCard and RProductSlider with the global cart system, you automatically get sophisticated animations and feedback:

Product Card Animations

Every RProductCard includes built-in animations when products are added to cart:

Automatic Animations in RProductCard
RProductCard(
product: product,
variant: .grid,
onAddToCart: {
Task {
await cartManager.addProduct(product) // Triggers automatic animations
}
}
)

What happens automatically:

  1. Button Scale Animation - Button scales down (0.9x) then back to normal
  2. Loading State - Shows loading spinner during cart operation
  3. Success State - Button shows "Added!" with checkmark for 1.5 seconds
  4. Toast Notification - Automatic "Added [Product Name] to cart" message
  5. Haptic Feedback - Light vibration on iOS devices
  6. Floating Cart Update - Cart indicator updates in real-time

Product Slider Animations

RProductSlider includes enhanced feedback when products are added:

Enhanced Slider Experience
RProductSlider(
title: "Featured Products",
products: products,
layout: .featured,
onAddToCart: { product in
Task {
await cartManager.addProduct(product) // Enhanced slider animations
}
}
)

Additional slider animations:

  1. Product Highlighting - Added product gets green border and scales to 1.05x
  2. Slider Feedback - Entire slider subtly scales to 1.02x for tactile feedback
  3. Coordinated Timing - All animations are perfectly synchronized
  4. Spring Animations - Natural, bouncy feel using spring curves

Manual Toast Notifications

You can also trigger custom toast notifications manually:

Custom Toast Examples
// Success notification
ToastManager.shared.showSuccess("Product added to wishlist!")

// Error notification
ToastManager.shared.showError("Failed to load product details")

// Info notification
ToastManager.shared.showInfo("Product updated successfully")

// Warning notification
ToastManager.shared.showWarning("Low stock - only 2 items remaining")

Floating Cart Customization

The floating cart indicator automatically appears when cart has items, but you can customize its behavior:

Floating Cart Integration
// The floating cart automatically:
// - Shows when cart.itemCount > 0
// - Displays current item count and total price
// - Provides one-tap access to checkout
// - Bounces when new items are added
// - Hides when cart becomes empty

// Access from anywhere in your app:
Button("Quick Checkout") {
cartManager.showCheckout() // Opens checkout overlay
}

Complete Implementation Example

Main App Structure

ReachuShoppingApp.swift
import SwiftUI
import ReachuCore
import ReachuUI

@main
struct ReachuShoppingApp: App {

init() {
// Configure Reachu SDK
ReachuSDK.configure(
baseURL: "https://graph-ql.reachu.io",
apiKey: "your-api-key"
)
}

var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(CartManager())
}
}
}

Main Content View with Checkout Integration

ContentView.swift
import SwiftUI
import ReachuCore
import ReachuDesignSystem
import ReachuUI

struct ContentView: View {
@EnvironmentObject var cartManager: CartManager

var body: some View {
TabView {
// Products tab
ProductCatalogView()
.tabItem {
Image(systemName: "bag.fill")
Text("Products")
}

// Cart tab with badge
ShoppingCartView()
.tabItem {
Image(systemName: "cart.fill")
Text("Cart")
}
.badge(cartManager.itemCount > 0 ? cartManager.itemCount : nil)
}
// Global checkout overlay
.sheet(isPresented: $cartManager.isCheckoutPresented) {
RCheckoutOverlay()
.environmentObject(cartManager)
}
}
}

Product Catalog with Cart Integration

ProductCatalogView.swift
import SwiftUI
import ReachuUI
import ReachuCore

struct ProductCatalogView: View {
@EnvironmentObject var cartManager: CartManager
@StateObject private var viewModel = ProductCatalogViewModel()

var body: some View {
NavigationView {
ScrollView {
LazyVGrid(columns: [
GridItem(.flexible()),
GridItem(.flexible())
], spacing: ReachuSpacing.md) {
ForEach(viewModel.products) { product in
RProductCard(
product: product,
variant: .grid,
onTap: {
viewModel.showProductDetail(product)
},
onAddToCart: {
Task {
await cartManager.addProduct(product)
}
}
)
}
}
.padding(ReachuSpacing.lg)
}
.navigationTitle("Products")
.task {
await viewModel.loadProducts()
}
}
}
}

Shopping Cart View

ShoppingCartView.swift
import SwiftUI
import ReachuUI
import ReachuDesignSystem

struct ShoppingCartView: View {
@EnvironmentObject var cartManager: CartManager

var body: some View {
NavigationView {
VStack(spacing: 0) {
if cartManager.items.isEmpty {
// Empty cart state
VStack(spacing: ReachuSpacing.lg) {
Spacer()

Image(systemName: "cart")
.font(.system(size: 48))
.foregroundColor(ReachuColors.textSecondary)

Text("Your cart is empty")
.font(ReachuTypography.headline)
.foregroundColor(ReachuColors.textSecondary)

Text("Add some products to get started")
.font(ReachuTypography.body)
.foregroundColor(ReachuColors.textTertiary)

Spacer()
}
} else {
// Cart items
ScrollView {
LazyVStack(spacing: ReachuSpacing.md) {
ForEach(cartManager.items) { item in
CartItemRowView(item: item)
.environmentObject(cartManager)
}
}
.padding(ReachuSpacing.lg)
}

Spacer()

// Checkout section
VStack(spacing: 0) {
Divider()

VStack(spacing: ReachuSpacing.md) {
// Total
HStack {
Text("Total")
.font(ReachuTypography.headline)

Spacer()

Text("\(cartManager.currency) \(String(format: "%.2f", cartManager.cartTotal))")
.font(ReachuTypography.headline)
.foregroundColor(ReachuColors.primary)
}

// Checkout button
RButton(
title: "Proceed to Checkout",
style: .primary,
size: .large,
isLoading: cartManager.isLoading
) {
cartManager.showCheckout()
}
}
.padding(ReachuSpacing.lg)
}
.background(ReachuColors.surface)
}
}
.navigationTitle("Cart (\(cartManager.itemCount))")
.toolbar {
if !cartManager.items.isEmpty {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Clear") {
Task {
await cartManager.clearCart()
}
}
.foregroundColor(ReachuColors.error)
}
}
}
}
}
}

Cart Item Row Component

CartItemRowView.swift
import SwiftUI
import ReachuUI
import ReachuDesignSystem

struct CartItemRowView: View {
let item: CartManager.CartItem
@EnvironmentObject var cartManager: CartManager

var body: some View {
HStack(spacing: ReachuSpacing.md) {
// Product image
AsyncImage(url: URL(string: item.imageUrl ?? "")) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
Rectangle()
.fill(ReachuColors.background)
.overlay {
Image(systemName: "photo")
.foregroundColor(ReachuColors.textSecondary)
}
}
.frame(width: 60, height: 60)
.cornerRadius(ReachuBorderRadius.medium)

// Product info
VStack(alignment: .leading, spacing: ReachuSpacing.xs) {
Text(item.title)
.font(ReachuTypography.bodyBold)
.lineLimit(2)

if let brand = item.brand {
Text(brand)
.font(ReachuTypography.caption1)
.foregroundColor(ReachuColors.textSecondary)
}

Text("\(item.currency) \(String(format: "%.2f", item.price))")
.font(ReachuTypography.body)
.foregroundColor(ReachuColors.primary)
}

Spacer()

// Quantity controls
VStack(spacing: ReachuSpacing.xs) {
HStack(spacing: ReachuSpacing.xs) {
// Decrease quantity
Button("-") {
if item.quantity > 1 {
Task {
await cartManager.updateQuantity(for: item, to: item.quantity - 1)
}
}
}
.frame(width: 32, height: 32)
.background(ReachuColors.background)
.cornerRadius(ReachuBorderRadius.small)

// Current quantity
Text("\(item.quantity)")
.font(ReachuTypography.bodyBold)
.frame(minWidth: 24)

// Increase quantity
Button("+") {
Task {
await cartManager.updateQuantity(for: item, to: item.quantity + 1)
}
}
.frame(width: 32, height: 32)
.background(ReachuColors.background)
.cornerRadius(ReachuBorderRadius.small)
}

// Remove item
Button("Remove") {
Task {
await cartManager.removeItem(item)
}
}
.font(ReachuTypography.caption1)
.foregroundColor(ReachuColors.error)
}
}
.padding(ReachuSpacing.md)
.background(ReachuColors.surface)
.cornerRadius(ReachuBorderRadius.medium)
.shadow(color: ReachuColors.textPrimary.opacity(0.05), radius: 2, x: 0, y: 1)
}
}

Advanced Features

Custom Checkout Triggers

You can trigger checkout from anywhere in your app:

QuickCheckoutButton.swift
import SwiftUI
import ReachuUI

struct QuickCheckoutButton: View {
@EnvironmentObject var cartManager: CartManager

var body: some View {
Button(action: {
cartManager.showCheckout()
}) {
HStack {
Image(systemName: "cart.fill")
Text("Checkout (\(cartManager.itemCount))")
}
}
.disabled(cartManager.items.isEmpty)
}
}

Cart Badge for Tab Bar

CartBadgeView.swift
import SwiftUI
import ReachuUI

struct CartBadgeView: View {
@EnvironmentObject var cartManager: CartManager

var body: some View {
TabView {
ProductsView()
.tabItem {
Image(systemName: "bag.fill")
Text("Products")
}

CartView()
.tabItem {
Image(systemName: "cart.fill")
Text("Cart")
}
.badge(cartManager.itemCount > 0 ? cartManager.itemCount : nil)
}
}
}

Floating Cart Button

FloatingCartButton.swift
import SwiftUI
import ReachuUI
import ReachuDesignSystem

struct FloatingCartButton: View {
@EnvironmentObject var cartManager: CartManager

var body: some View {
if cartManager.itemCount > 0 {
VStack {
Spacer()
HStack {
Spacer()

Button(action: {
cartManager.showCheckout()
}) {
HStack {
Image(systemName: "cart.fill")
Text("\(cartManager.itemCount)")
Text("•")
Text("\(cartManager.currency) \(String(format: "%.0f", cartManager.cartTotal))")
}
.padding(.horizontal, ReachuSpacing.lg)
.padding(.vertical, ReachuSpacing.md)
.background(ReachuColors.primary)
.foregroundColor(.white)
.cornerRadius(ReachuBorderRadius.circle)
.shadow(radius: 4)
}

Spacer()
}
.padding(.bottom, ReachuSpacing.xl)
}
}
}
}

API Reference

CartManager

Properties

@Published public var items: [CartItem] = []
@Published public var isCheckoutPresented = false
@Published public var isLoading = false
@Published public var cartTotal: Double = 0.0
@Published public var currency: String = "USD"
@Published public var errorMessage: String?

public var itemCount: Int // Computed property

Methods

// Add product to cart
public func addProduct(_ product: Product, quantity: Int = 1) async

// Remove specific item from cart
public func removeItem(_ item: CartItem) async

// Update item quantity
public func updateQuantity(for item: CartItem, to newQuantity: Int) async

// Clear entire cart
public func clearCart() async

// Show/hide checkout overlay
public func showCheckout()
public func hideCheckout()

CartItem Model

public struct CartItem: Identifiable, Equatable {
public let id: String
public let productId: Int
public let variantId: String?
public let title: String
public let brand: String?
public let imageUrl: String?
public let price: Double
public let currency: String
public var quantity: Int
public let sku: String?
}

RCheckoutOverlay

Initialization

public init()

The checkout overlay automatically accesses the CartManager from the environment and presents a multi-step checkout flow.

Checkout Steps

  1. Cart Review - Review items, quantities, and totals
  2. Shipping Information - Address and delivery options
  3. Payment - Payment method selection and processing
  4. Confirmation - Order confirmation and tracking info

Best Practices

1. Environment Object Setup

Always provide CartManager at the highest level of your app hierarchy:

App.swift
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(CartManager()) // ✅ Global access
}
}
}

2. Error Handling

Always handle async operations properly:

Proper Error Handling
Button("Add to Cart") {
Task {
do {
await cartManager.addProduct(product)
// Success feedback
} catch {
// Handle error appropriately
}
}
}

3. Loading States

Use the built-in loading states for better UX:

Loading State Usage
RButton(
title: "Add to Cart",
style: .primary,
isLoading: cartManager.isLoading // ✅ Shows loading indicator
) {
Task {
await cartManager.addProduct(product)
}
}

4. Conditional UI

Show/hide elements based on cart state:

Conditional UI
// Only show cart tab badge if items exist
.badge(cartManager.itemCount > 0 ? cartManager.itemCount : nil)

// Only show checkout button if cart has items
.disabled(cartManager.items.isEmpty)

Platform Considerations

iOS Specific

iOS Optimizations
#if os(iOS)
// Use iOS-specific features
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
#endif

macOS Adaptations

macOS Adjustments
#if os(macOS)
// Adjust for macOS interface guidelines
.frame(minWidth: 600, minHeight: 400)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Close") { cartManager.hideCheckout() }
}
}
#endif

Integration Examples

With Existing E-commerce Apps

Migration Example
// Before: Custom cart management
class OldCartManager {
var items: [Item] = []
}

// After: Reachu integration
import ReachuUI

struct MigratedApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(CartManager()) // ✅ Replace old system
}
}
}

With Navigation Flows

Navigation Integration
struct ProductDetailView: View {
@EnvironmentObject var cartManager: CartManager
let product: Product

var body: some View {
VStack {
// Product details...

RButton(
title: "Add to Cart & Checkout",
style: .primary
) {
Task {
await cartManager.addProduct(product)
cartManager.showCheckout() // Immediate checkout
}
}
}
}
}

Testing

Unit Testing CartManager

CartManagerTests.swift
import XCTest
@testable import ReachuUI

class CartManagerTests: XCTestCase {
var cartManager: CartManager!

override func setUp() {
cartManager = CartManager()
}

func testAddProduct() async {
let product = MockProduct.sample
await cartManager.addProduct(product)

XCTAssertEqual(cartManager.items.count, 1)
XCTAssertEqual(cartManager.itemCount, 1)
XCTAssertGreaterThan(cartManager.cartTotal, 0)
}

func testUpdateQuantity() async {
let product = MockProduct.sample
await cartManager.addProduct(product)

let item = cartManager.items.first!
await cartManager.updateQuantity(for: item, to: 3)

XCTAssertEqual(cartManager.itemCount, 3)
}
}

SwiftUI Previews

CheckoutPreviews.swift
#if DEBUG
import SwiftUI
import ReachuUI
import ReachuTesting

struct Checkout_Previews: PreviewProvider {
static var previews: some View {
Group {
// Empty cart state
RCheckoutOverlay()
.environmentObject(CartManager())
.previewDisplayName("Empty Cart")

// Cart with items
RCheckoutOverlay()
.environmentObject(CartManager.withMockItems())
.previewDisplayName("With Items")
}
}
}

extension CartManager {
static func withMockItems() -> CartManager {
let manager = CartManager()
// Add mock items for preview
return manager
}
}
#endif

Production Ready

This checkout system is production-ready and handles real-world scenarios including error states, loading indicators, accessibility, and cross-platform compatibility. Use it as your complete checkout solution.

Integration Support

Need help integrating the checkout system into your existing app? Contact our team for personalized assistance.