SwiftTLA turns typed state rules into a typed Swift machine.
Write one Swift source model for state, actions, and invariants. compile()
validates declarations, binds names, links modules, lowers behavior, allocates
private identities, renders TLA+/PlusCal text, assembles the formal bundles,
and publishes one immutable compiled specification.
@TLAModel generates typed State, Action, and Transition values from that
meaning. SwiftUI stores the generated machine directly. The generated Actor
serializes access to that machine.
One source model. Typed application state. Bounded formal evidence.
Swift source model
│ compile()
▼
CompiledSpecification
├── generated State, Action, Transition, and machine
├── compiled runtime and bounded exploration
└── rendered TLA+ bundle and, for one authored Algorithm, PlusCal bundle
Generated machine
├── value stored in SwiftUI @State
└── generated Actor around the same value
Use #spec and Algorithm to define the behavior that matters. The DSL
expresses data, transitions, procedures, invariants, and temporal properties.
Example ID: readme-clock-model
import Foundation
import SwiftTLA
import SwiftTLAMacros
@TLAModel
public struct ClockModel: Sendable {
private enum Step: String, CaseIterable {
case tick
}
public static var spec: TLASpec {
#spec("Clock") {
Algorithm("Clock", scoped: { scope in
let hour = scope.sharedVar("hour", in: 0...23)
let minute = scope.sharedVar("minute", in: 0...59)
let second = scope.sharedVar("second", in: 0...59)
While(Step.tick, true) {
Either {
When(second < 59)
Assign(second, to: second + 1)
} or: {
Either {
When(second == 59)
When(minute < 59)
Assign(second, to: 0)
Assign(minute, to: minute + 1)
} or: {
Either {
When(second == 59)
When(minute == 59)
When(hour < 23)
Assign(second, to: 0)
Assign(minute, to: 0)
Assign(hour, to: hour + 1)
} or: {
When(second == 59)
When(minute == 59)
When(hour == 23)
Assign(second, to: 0)
Assign(minute, to: 0)
Assign(hour, to: 0)
}
}
}
}
Invariant("ValidTime") {
hour >= 0 && hour <= 23 &&
minute >= 0 && minute <= 59 &&
second >= 0 && second <= 59
}
})
}
}
}The generated API gives your application typed state and action cases.
var clock = try ClockModel.makeMachine(
.init(hour: 16, minute: 19, second: 59)
)
let transition = try clock.send(.tick)
print(transition.after)
// State(hour: 16, minute: 20, second: 0)The generated state is ordinary Swift value state. A view renders it and sends typed actions. The machine keeps each transition atomic.
Example ID: readme-clock-swiftui
import SwiftUI
struct ClockView: View {
@State private var machine: ClockModel?
@State private var diagnostic = ""
var body: some View {
VStack {
if let machine {
Text("Time: \(machine.state.hour):\(machine.state.minute):\(machine.state.second)")
Button("Tick") {
do {
var machine = machine
_ = try machine.send(.tick)
self.machine = machine
diagnostic = ""
} catch {
diagnostic = String(describing: error)
}
}
} else {
ProgressView()
}
if diagnostic.isEmpty == false {
Text(diagnostic)
}
}
.task {
guard machine == nil else { return }
do {
machine = try ClockModel.makeMachine(
.init(hour: 16, minute: 19, second: 59)
)
} catch {
diagnostic = String(describing: error)
}
}
}
}machine is the generated machine stored by the view. A failed action throws and
leaves the machine state unchanged. This view stores the diagnostic for display.
Use the generated actor for shared asynchronous state. It stores the same
generated machine behind actor isolation.
This clock's compiled specification renders direct TLA+ and its authored PlusCal algorithm. Finite graph comparison compares bounded SwiftTLA exploration with a pinned TLC run. See Finite graph comparison.
SwiftTLA applies to concurrent tasks, retries, cancellation, sync, background work, permissions, protocols, and distributed systems.
Use generated State and action cases for value-based state. For shared
running state, use the generated Actor. See
Generated Machines.
- Compiler design
- Production readiness
- Finite graph comparison
- Temporal and symmetry conformance
- SwiftTLA DocC
- Demonstrations app
- macOS 14+
- Xcode 16.4