This repository demonstrates how to implement a type-safe, compile-time verified order flow in Swift using phantom types, with three usage styles:
Order<State>β Classic compile-time checked order flowAsyncOrder<State>β Async/Await-based progressive flowCombineOrder<State>β Reactive Combine pipeline
Phantom types allow you to enforce business logic at compile time by encoding state transitions into generic types. In this example, a simple e-commerce order process is implemented as:
EmptyCart β ItemsAdded β PaymentProvided β OrderPlaced
Each state has its own type, preventing invalid transitions like:
- Placing an order before providing payment
- Providing payment before adding an item
enum EmptyCart {}
enum ItemsAdded {}
enum PaymentProvided {}
enum OrderPlaced {}let empty = Order<EmptyCart>.start()
let withItems = empty.addItem("MacBook Pro", price: 2499.99)
let withPayment = withItems.enterPaymentDetails("Visa **** 1234")
let placed = withPayment.placeOrder()Attempting to call .placeOrder() directly on empty or withItems will cause a compile-time error.
Task {
let empty = AsyncOrder<EmptyCart>.start()
let withItems = await empty.addItem("iPhone 15 Pro", price: 1799.99)
let withPayment = await withItems.enterPaymentDetails("MasterCard **** 4321")
let placed = await withPayment.placeOrder()
}Simulates delay using Task.sleep to mimic API latency.
import Combine
var cancellables = Set<AnyCancellable>()
CombineOrder<EmptyCart>.start()
.addItem("Magic Keyboard", price: 299)
.flatMap { $0.enterPaymentDetails("Amex **** 9876") }
.flatMap { $0.placeOrder() }
.sink(receiveValue: { _ in
print("π Combine order placed.")
})
.store(in: &cancellables)Uses Just + delay(for:) + handleEvents to simulate asynchronous Combine streams.
- Compile-time safety: Invalid flows are impossible
- Cleaner API: You model state transitions explicitly
- Flexible design: Works with async/await, Combine, or traditional code
- Zero runtime state errors