Skip to main content

CheckoutDraft - Geographic Normalization System

The CheckoutDraftsystem is a powerful component that handles address normalization, geographic data resolution, and checkout state management. It ensures consistent address formatting across multiple countries and languages.

Overview

CheckoutDraft provides:

  • Geographic Normalization - Converts country/province names to ISO codes
  • Multi-language Support - Handles English and Spanish variations
  • Address Validation - Resolves and validates geographic data
  • Checkout State - Centralized state management for checkout flow
  • Backend Integration - Generates proper payloads for API calls

Why CheckoutDraft?

Problem it Solves

Without CheckoutDraft, you might face:

  • Users entering "Estados Unidos" vs "USA" vs "United States"
  • Province names like "California" vs "CA" vs "Calif."
  • Inconsistent phone codes and country codes
  • Failed API calls due to format mismatches

Solution

CheckoutDraft normalizes all geographic data automatically:

// User enters: "Estados Unidos"
// CheckoutDraft resolves: ISO-2 = "US", Name = "United States"

// User enters: "Calif."
// CheckoutDraft resolves: Province Code = "CA", Name = "California"

Basic Setup

Step 1: Initialize in App

App.swift
import SwiftUI
import ReachuUI

@main
struct MyApp: App {
@StateObject private var checkoutDraft = CheckoutDraft()
@StateObject private var cartManager = CartManager()

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

Step 2: Use in Checkout Views

CheckoutAddressView.swift
import SwiftUI
import ReachuUI

struct CheckoutAddressView: View {
@EnvironmentObject private var checkoutDraft: CheckoutDraft

var body: some View {
Form {
Section("Personal Information") {
TextField("First Name", text: $checkoutDraft.firstName)
TextField("Last Name", text: $checkoutDraft.lastName)
TextField("Email", text: $checkoutDraft.email)
TextField("Phone", text: $checkoutDraft.phone)
}

Section("Address") {
TextField("Street Address", text: $checkoutDraft.address1)
TextField("Apt, Suite, etc.", text: $checkoutDraft.address2)
TextField("City", text: $checkoutDraft.city)
TextField("Province/State", text: $checkoutDraft.province)
TextField("Country", text: $checkoutDraft.countryName)
TextField("Postal Code", text: $checkoutDraft.zip)
}
}
}
}

Geographic Normalization

Country Resolution

CheckoutDraft automatically resolves country names to ISO-2 codes:

// Supported variations (examples):
"United States""US"
"USA""US"
"Estados Unidos""US"
"EEUU""US"

"United Kingdom""GB"
"UK""GB"
"Inglaterra""GB"

"Mexico""MX"
"México""MX"

Usage:

let iso2 = checkoutDraft.resolveISO2(fallback: "US")
// Returns: "US" even if user entered "Estados Unidos"

Province Resolution

Automatically converts province names to standard codes:

// United States examples:
"California""CA"
"Calif.""CA"
"New York""NY"
"Texas""TX"

// Canada examples:
"Ontario""ON"
"Quebec""QC"
"British Columbia""BC"

// Spain examples:
"Madrid""M"
"Barcelona""B"
"Valencia""V"

Phone Code Resolution

Resolves international phone codes by country:

let phoneCode = checkoutDraft.resolvePhoneCode(effectiveISO2: "US")
// Returns: "1" (for +1)

let phoneCode = checkoutDraft.resolvePhoneCode(effectiveISO2: "MX")
// Returns: "52" (for +52)

Generating API Payloads

Address Payload

CheckoutDraft generates properly formatted payloads for backend APIs:

SubmitCheckout.swift
let payload = checkoutDraft.addressPayload(fallbackCountryISO2: "US")

// Returns:
// {
// "address1": "123 Main St",
// "address2": "Apt 4B",
// "city": "San Francisco",
// "company": "",
// "country": "United States",
// "country_code": "US",
// "email": "user@example.com",
// "first_name": "John",
// "last_name": "Doe",
// "phone": "4155551234",
// "phone_code": "1",
// "province": "California",
// "province_code": "CA",
// "zip": "94102"
// }

Shipping Address

let shippingAddress = checkoutDraft.shippingAddressPayload(
fallbackCountryISO2: "US"
)

Billing Address

let billingAddress = checkoutDraft.billingAddressPayload(
fallbackCountryISO2: "US"
)

Supported Countries

CheckoutDraft includes built-in support for:

North America - United States (all 50 states + territories)

  • Canada (all provinces/territories)
  • Mexico (all states)

Europe - Spain (all provinces)

  • United Kingdom (England, Scotland, Wales, N. Ireland)
  • France (all regions)
  • Germany (all states)
  • Italy (all regions)
  • And 20+ more European countries

Latin America - Argentina

  • Brazil
  • Chile
  • Colombia
  • Peru
  • And more LATAM countries

Asia Pacific - Australia

  • Japan
  • South Korea
  • China
  • India

Integration with RCheckoutOverlay

The RCheckoutOverlay component automatically uses CheckoutDraft:

ContentView.swift
import SwiftUI
import ReachuUI

struct ContentView: View {
@EnvironmentObject private var cartManager: CartManager
@EnvironmentObject private var checkoutDraft: CheckoutDraft

var body: some View {
MyAppContent()
// RCheckoutOverlay automatically accesses checkoutDraft
.sheet(isPresented: $cartManager.isCheckoutPresented) {
RCheckoutOverlay()
.environmentObject(cartManager)
.environmentObject(checkoutDraft)
}
}
}

Advanced Usage

Custom Validation

Add custom validation logic:

extension CheckoutDraft {
var isAddressComplete: Bool {
!firstName.isEmpty &&
!lastName.isEmpty &&
!email.isEmpty &&
!address1.isEmpty &&
!city.isEmpty &&
!countryName.isEmpty &&
!zip.isEmpty
}

var isEmailValid: Bool {
email.contains("@") && email.contains(".")
}

var isPhoneValid: Bool {
phone.count >= 10
}
}

Pre-fill User Data

func loadUserProfile() async {
let user = await fetchUserProfile()

checkoutDraft.firstName = user.firstName
checkoutDraft.lastName = user.lastName
checkoutDraft.email = user.email
checkoutDraft.phone = user.phone

if let address = user.defaultAddress {
checkoutDraft.address1 = address.street
checkoutDraft.city = address.city
checkoutDraft.province = address.province
checkoutDraft.countryName = address.country
checkoutDraft.zip = address.postalCode
}
}

Save for Future Use

func saveCheckoutData() {
UserDefaults.standard.set(checkoutDraft.firstName, forKey: "checkout_firstName")
UserDefaults.standard.set(checkoutDraft.lastName, forKey: "checkout_lastName")
UserDefaults.standard.set(checkoutDraft.email, forKey: "checkout_email")
// ... save other fields
}

func loadSavedCheckoutData() {
checkoutDraft.firstName = UserDefaults.standard.string(forKey: "checkout_firstName") ?? ""
checkoutDraft.lastName = UserDefaults.standard.string(forKey: "checkout_lastName") ?? ""
checkoutDraft.email = UserDefaults.standard.string(forKey: "checkout_email") ?? ""
// ... load other fields
}

Error Handling

CheckoutDraft includes built-in error handling for common issues:

Invalid Country

let iso2 = checkoutDraft.resolveISO2(fallback: "US")
// If countryName is invalid, returns fallback "US"

Missing Province Code

let provinceCode = checkoutDraft.resolveProvinceCode(
effectiveISO2: "US",
provinceName: "UnknownProvince"
)
// Returns empty string if province not found

Phone Number Formatting

// Automatically removes "+" from phone code
checkoutDraft.phoneCountryCode = "+1"
// Stored as: "1"

Best Practices

1. Initialize Early

// Good: Initialize in App struct
@StateObject private var checkoutDraft = CheckoutDraft()

// Bad: Create new instance in each view
let checkoutDraft = CheckoutDraft() // State lost between views

2. Use Environment Object

// Good: Inject as environment object
.environmentObject(checkoutDraft)

// Bad: Pass as parameter through multiple views
MyView(checkoutDraft: checkoutDraft)

3. Let CheckoutDraft Handle Normalization

// Good: Let user type naturally
TextField("Country", text: $checkoutDraft.countryName)

// Bad: Force specific format
TextField("Country (ISO-2 only)", text: $checkoutDraft.countryCode)

4. Use Fallbacks

// Good: Always provide fallback
let iso2 = checkoutDraft.resolveISO2(fallback: "US")

// Bad: No fallback
let iso2 = checkoutDraft.countryCode // Might be empty

Next Steps


Geographic Support

CheckoutDraft supports 50+ countries out of the box. If you need support for additional countries, contact us.