Enums + Payloads + switches are incredibly simple yet so effective. You can make a simple state object, isolate it with an actor, then stream it anywhere with Combine (or now Observability). You'll get full compiler safety too, forcing any new state to be handled everywhere.
You can even stack that with _generic_ payloads that conform to protocols, and if you're feel brave you can make those payloads equable completions for _typed returns_.
for example (I hate formatting on here, but I'll give it a shot)
// A user state enum that conforms to loggable.
// The state is generic, any object T that conforms to "Partner" protocol can be a partner
enum UserState<T: Partner>: Loggable {
// Your states
case loggedOut(error: String?)
case loggingIn(email: Email) // Email type is a string that's been validated
case loggedIn(authToken: String, user: User, partner: T)
case loggingOut(exampleBlock: (String) -> Bool) // You can embed a callback here
// Utility Functions
/// Is the user logged in?
func isLoggedIn() -> Bool {
switch self {
case .loggedOut, .loggingIn: return false
case .loggedIn: return true
}
}
/// Loggable conformance
func logText() -> String {
// switch that converts state into something that's safe for your logging service
}
}
In the above, any subscriber that gets a UserState object can switch on it, and for example if you're logged in you 100% get the auth token, user, etc. It's impossible to be logged in without those in-context.
It might look something like this in your UI:
/// Called by a subscriber that's hooked in to the state stream
func onUserStateChanged(newState: UserState) {
switch newState {
case let .loggedOut:
// Impossible? Error UI
case let .loggingIn, .loggingOut:
// loading UI
case let .loggedIn(authToken: _, user: user, parner: partner):
// set some text "hello \(user.userName)"
// display an image: \(MyRemoteLoader.url(url: partner.primaryLogoURL))
// Button to website: Button(url: partner.homePageURL)
}
}
add a new state later? The compiler will error in the 500 places in your codebase you didn't handle the new case with one build command.
Love this. Not only is it my favorite feature of swift, but I often say swift enums are my favorite feature of any programming language. They are just so elegant and powerful
It's by far my favorite feature of Swift.
Enums + Payloads + switches are incredibly simple yet so effective. You can make a simple state object, isolate it with an actor, then stream it anywhere with Combine (or now Observability). You'll get full compiler safety too, forcing any new state to be handled everywhere.
You can even stack that with _generic_ payloads that conform to protocols, and if you're feel brave you can make those payloads equable completions for _typed returns_.
for example (I hate formatting on here, but I'll give it a shot)
// A user state enum that conforms to loggable.
// The state is generic, any object T that conforms to "Partner" protocol can be a partner
enum UserState<T: Partner>: Loggable {
}In the above, any subscriber that gets a UserState object can switch on it, and for example if you're logged in you 100% get the auth token, user, etc. It's impossible to be logged in without those in-context.
It might look something like this in your UI:
/// Called by a subscriber that's hooked in to the state stream
func onUserStateChanged(newState: UserState) {
}add a new state later? The compiler will error in the 500 places in your codebase you didn't handle the new case with one build command.