Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Aug 3, 2024. It is now read-only.

StateKit 0.4.0 - SideEffects#1

Draft
BergerBytes wants to merge 31 commits into
main from
side-effects
Draft

StateKit 0.4.0 - SideEffects #1
BergerBytes wants to merge 31 commits into
main from
side-effects

Conversation

@BergerBytes

@BergerBytes BergerBytes commented Aug 5, 2022
edited
Loading

Copy link
Copy Markdown
Owner

StateKit 0.4

Goals

StateKit 0.4 aims to support SideEffects. One of the more consistent, albeit infrequent, gaps in StateKit that comes up is allowing Stores to communicate non-persistent state. Common use cases are, toast messages, alerts, optional prompts and non-destructive outcomes from requests.

One of the core principles of StateKit is Stores deliver their state down a single path, in a single object. This allows principle is what gives StateKit its power and ease of use. Adding additional state outputs is a risk and requires great care. StateKit also tries to be as simple and straightforward as possible; adding SideEffects eases the pain of a few situations, but it can't be at the cost of adding additional complexity to StateKit in every situation.

Proposal

Introduce a new protocol type, SideEffect

public protocol SideEffect {}

SideEffects have no requirements and can be fulfilled by any type. Enums are the most likely option as they allow for exhaustive switching.

Stores

Stores will require the SideEffect type definition.

class Store<State: StateContainer, Effect: SideEffect> { ... }

In an effort to keep SideEffects from overcomplicating all stores that don't have need for them, there is an "opt-out" type that can be used, NoSideEffects; we will explore examples of this later.

public enum NoSideEffects: SideEffect {}

Example:

// StateKit 0.3.x
class MyStore: Store<MyState> { ... } 
// StateKit 0.4.x
enum MySideEffects: SideEffect { ... }
class MyStore: Store<MyState, MySideEffects> { ... }
// or opt-out
class MyStore: Store<MyState, NoSideEffects> { ... }

Nothing else changes within the store with this change, except that there is a new function to send effects to subscribers.

// naming TBD
emit(.purchase(failed: error))

This is one of the things I least like about this approach. I have seen a lot of success with moving all the logic to the State struct. Testing State is very easy and they can be created mutated with transactions in a vacuum; if you ensure all queries and transactions are working as expected, there is very little in the store that needs to be tested. Moving a state emitter to the store separates this and could cause issues.

Subscription

With the goal of keeping a single path for the state to flow, the subscriptions now return both a StateContainer and an optional SideEffect

This means State is always provided with a SideEffect, an unneeded copy, but seen as a acceptable compromise over allowing optional State to have to be considered.

// StateKit 0.3.x
subscribe(to: myStore) { [weak self] state in
 self?.state.handle(my: state)
}
// StateKit 0.4.x
subscribe(to: myStore) { [weak self] state, sideEffect in
 // Use existing state handlers
 self?.state.handle(my: state)
 switch sideEffect {
 ...
 }
 // or update handlers to accept both the state and update
 self?.state.handle(myStore: state, effect: sideEffect)
}
// StateKit 0.4.x where SideEffect is NoSideEffects
// No changes needed!
subscribe(to: myStore) { [weak self] state in
 self?.state.handle(my: state)
}

# Conflicts:
#	Sources/StateKit/State/State.swift
#	Sources/StateKit/Store/ObservableViewStore.swift
#	Sources/StateKit/Store/StateSubscription.swift
#	Sources/StateKit/Store/Store.swift
#	Sources/StateKit/Store/ViewStore.swift
#	Sources/StateKit/UI/iOS/SwiftUI/UIKit Embedded/HostingController.swift
#	Sources/StateKit/UI/iOS/SwiftUI/UIKit Embedded/StateView.swift
#	Sources/StateKit/UI/iOS/SwiftUI/ViewWith.swift
#	Sources/StateKit/UI/iOS/UIKit/ViewController.swift
commit 125b551
Author: Michael Berger <michael@bergerbytes.io>
Date: Mon Nov 14 16:05:40 2022 -0600
 \
commit 87801ee
Author: Michael Berger <michael@bergerbytes.io>
Date: Mon Nov 14 15:54:40 2022 -0600
 Add force push to ObservableViewStore
# Conflicts:
#	Sources/StateKit/Store/ObservableViewStore.swift
#	Sources/StateKit/Store/Store.swift
#	Sources/StateKit/UI/iOS/SwiftUI/UIKit Embedded/HostingController.swift
#	Sources/StateKit/UI/iOS/SwiftUI/ViewWith.swift
#	Sources/StateKit/UI/iOS/UIKit/ViewController.swift
# Conflicts:
#	Sources/StateKit/UI/iOS/SwiftUI/UIKit Embedded/HostingController.swift
# Conflicts:
#	Sources/StateKit/State/State.swift
# Conflicts:
#	Sources/StateKit/Store/ObservableViewStore.swift
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /