Skip to content

Commit

Permalink
Improve store implementation (#856)
Browse files Browse the repository at this point in the history
  • Loading branch information
tinder-cfuller authored Oct 3, 2024
1 parent f482e7e commit 5e1953a
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 32 deletions.
9 changes: 5 additions & 4 deletions Customization/Perception.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ private class PerceptibleViewStateStoreBase<

// MARK: - Preview

@Perceptible
@preconcurrency
@MainActor
public final class PerceptiblePreviewStore<ViewState: Equatable>: PerceptibleViewStateStore {
Expand Down Expand Up @@ -183,7 +184,7 @@ private final class PerceptibleScope<
@Perceptible
@preconcurrency
@MainActor
open class PerceptibleStore<
public final class PerceptibleStore<
State: Equatable,
ViewState: Equatable
>: PerceptibleStateStore, PerceptibleViewStateStore {
Expand Down Expand Up @@ -254,9 +255,9 @@ extension PerceptibleViewStateStore {
to keyPath: KeyPath<ViewState, T>,
onChange: (@MainActor (T) -> Void)?
) -> Binding<T> {
guard let onChange: @MainActor (T) -> Void
else { return bind(to: keyPath) { _ in } }
return bind(to: keyPath, onChange: onChange)
bind(to: keyPath) { value in
onChange?(value)
}
}
}
```
9 changes: 5 additions & 4 deletions Sources/Nodes/StateManagement/ObservableStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ private class ObservableViewStateStoreBase<
@MainActor
public final class ObservablePreviewStore<ViewState: Equatable>: ObservableViewStateStore {

@Published
public var viewState: ViewState

public init(viewState: ViewState) {
Expand Down Expand Up @@ -189,7 +190,7 @@ private final class ObservableScope<

@preconcurrency
@MainActor
open class ObservableStore<
public final class ObservableStore<
State: Equatable,
ViewState: Equatable
>: ObservableStateStore, ObservableViewStateStore {
Expand Down Expand Up @@ -260,9 +261,9 @@ extension ObservableViewStateStore {
to keyPath: KeyPath<ViewState, T>,
onChange: (@MainActor (T) -> Void)?
) -> Binding<T> {
guard let onChange: @MainActor (T) -> Void
else { return bind(to: keyPath) { _ in } }
return bind(to: keyPath, onChange: onChange)
bind(to: keyPath) { value in
onChange?(value)
}
}
}

Expand Down
9 changes: 5 additions & 4 deletions Sources/Nodes/StateManagement/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ private class ViewStateStoreBase<

// MARK: - Preview

@Observable
@preconcurrency
@MainActor
@available(macOS 14.0, macCatalyst 17.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
Expand Down Expand Up @@ -191,7 +192,7 @@ private final class Scope<
@preconcurrency
@MainActor
@available(macOS 14.0, macCatalyst 17.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
open class Store<
public final class Store<
State: Equatable,
ViewState: Equatable
>: StateStore, ViewStateStore {
Expand Down Expand Up @@ -263,9 +264,9 @@ extension ViewStateStore {
to keyPath: KeyPath<ViewState, T>,
onChange: (@MainActor (T) -> Void)?
) -> Binding<T> {
guard let onChange: @MainActor (T) -> Void
else { return bind(to: keyPath) { _ in } }
return bind(to: keyPath, onChange: onChange)
bind(to: keyPath) { value in
onChange?(value)
}
}
}

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

import Nodes

public final class StateStoreMock<State: Equatable>: StateStore, ObservableStateStore {
@preconcurrency
@MainActor
public final class ObservableStateStoreMock<State: Equatable>: ObservableStateStore {

public var state: State {
didSet { stateSetCallCount += 1 }
Expand Down
26 changes: 26 additions & 0 deletions Sources/NodesTesting/Mocks/ObservableViewStateStoreMock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// Copyright © 2024 Tinder (Match Group, LLC)
//

#if canImport(SwiftUI)

import Nodes
import SwiftUI

@preconcurrency
@MainActor
public final class ObservableViewStateStoreMock<ViewState: Equatable>: ObservableViewStateStore {

@Published
public var viewState: ViewState {
didSet { viewStateSetCallCount += 1 }
}

public private(set) var viewStateSetCallCount: Int = 0

public init(viewState: ViewState) {
self.viewState = viewState
}
}

#endif
27 changes: 27 additions & 0 deletions Sources/NodesTesting/Mocks/StateStoreMock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// Copyright © 2024 Tinder (Match Group, LLC)
//

#if canImport(Observation)

import Nodes
import Observation

@Observable
@preconcurrency
@MainActor
@available(macOS 14.0, macCatalyst 17.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
public final class StateStoreMock<State: Equatable>: StateStore {

public var state: State {
didSet { stateSetCallCount += 1 }
}

public private(set) var stateSetCallCount: Int = 0

public init(state: State) {
self.state = state
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
// Copyright © 2024 Tinder (Match Group, LLC)
//

#if canImport(Observation)

import Nodes
import Observation

public final class ViewStateStoreMock<ViewState: Equatable>: ViewStateStore, ObservableViewStateStore {
@Observable
@preconcurrency
@MainActor
@available(macOS 14.0, macCatalyst 17.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
public final class ViewStateStoreMock<ViewState: Equatable>: ViewStateStore {

public var viewState: ViewState {
didSet { viewStateSetCallCount += 1 }
Expand All @@ -16,3 +23,5 @@ public final class ViewStateStoreMock<ViewState: Equatable>: ViewStateStore, Obs
self.viewState = viewState
}
}

#endif
File renamed without changes.
16 changes: 7 additions & 9 deletions Tests/NodesTests/StateManagementTests/ObservableStoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,10 @@ import XCTest

final class ObservableStoreTests: XCTestCase {

private final class ObservableStore: Nodes.ObservableStore<Int, String> {

init() {
super.init(state: 1) { $0 == 0 ? "23" : "\($0)" }
}
}

@MainActor
func testObservableStore() {

let store: ObservableStore = .init()
let store: ObservableStore = givenObservableStore()

expect(store).to(notBeNilAndToDeallocateAfterTest())

Expand Down Expand Up @@ -86,7 +79,7 @@ final class ObservableStoreTests: XCTestCase {

@MainActor
func testObservableStoreBindings() {
let store: ObservableStore = .init()
let store: ObservableStore = givenObservableStore()
expect(store).to(notBeNilAndToDeallocateAfterTest())
let onChangeA: (String) -> Void = { value in
let sanitized: String = value.replacingOccurrences(of: "|", with: "")
Expand All @@ -102,4 +95,9 @@ final class ObservableStoreTests: XCTestCase {
expect(bindingA.wrappedValue) == "23"
expect(bindingB.wrappedValue) == "23"
}

@MainActor
private func givenObservableStore() -> ObservableStore<Int, String> {
ObservableStore(state: 1) { $0 == 0 ? "23" : "\($0)" }
}
}
16 changes: 7 additions & 9 deletions Tests/NodesTests/StateManagementTests/StoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,10 @@ import XCTest
@available(macOS 14.0, macCatalyst 17.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
final class StoreTests: XCTestCase {

private final class Store: Nodes.Store<Int, String> {

init() {
super.init(state: 1) { $0 == 0 ? "23" : "\($0)" }
}
}

@MainActor
func testStore() {

let store: Store = .init()
let store: Store = givenStore()

expect(store).to(notBeNilAndToDeallocateAfterTest())

Expand Down Expand Up @@ -87,7 +80,7 @@ final class StoreTests: XCTestCase {

@MainActor
func testStoreBindings() {
let store: Store = .init()
let store: Store = givenStore()
expect(store).to(notBeNilAndToDeallocateAfterTest())
let onChangeA: (String) -> Void = { value in
let sanitized: String = value.replacingOccurrences(of: "|", with: "")
Expand All @@ -103,4 +96,9 @@ final class StoreTests: XCTestCase {
expect(bindingA.wrappedValue) == "23"
expect(bindingB.wrappedValue) == "23"
}

@MainActor
private func givenStore() -> Store<Int, String> {
Store(state: 1) { $0 == 0 ? "23" : "\($0)" }
}
}

0 comments on commit 5e1953a

Please sign in to comment.