Skip to content

Commit f301e15

Browse files
authored
Merge pull request #17 from Lickability/kpa/store-not-main-actor
Moving Store off main actor
2 parents 0d799e4 + 00724ca commit f301e15

6 files changed

Lines changed: 21 additions & 15 deletions

File tree

Example/Photos/With Stores/Banner Feature/MockBannerNetworkStateController.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
//
77

88
import Foundation
9-
import Combine
9+
@preconcurrency import Combine
1010

1111
/// A really contrived fake interface similar to networking state controller for updating `Banner` models on a nonexistent server.
12-
final class MockBannerNetworkStateController {
12+
final class MockBannerNetworkStateController: Sendable {
1313

1414
/// Represents the state of a network request for a banner.
1515
enum NetworkState {
@@ -67,22 +67,19 @@ final class MockBannerNetworkStateController {
6767
}
6868

6969
/// A publisher that sends updates of the `NetworkState`.
70-
public var publisher: PassthroughSubject<NetworkState, Never> = .init()
70+
public let publisher: PassthroughSubject<NetworkState, Never> = .init()
7171

7272
/// Uploads a `Banner` to a fake server.
7373
/// - Parameter banner: The `Banner` to upload.
74-
@MainActor
7574
func upload(banner: Banner) {
7675
self.publisher.send(.inProgress)
77-
76+
7877
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
7978

8079
// Pick whether you would like to get a successful (`.finished(.success...`) state or any error for this "network request".
8180

82-
//self.publisher.send(.finished(.success(banner)))
81+
self.publisher.send(.finished(.success(banner)))
8382

84-
self.publisher.send(.finished(.failure(.intentionalFailure)))
85-
8683
}
8784

8885
}

Example/Photos/With Stores/Banner Feature/View Level/BannerUpdateViewStore.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ final class BannerUpdateViewStore: Store {
1818
// MARK: - Store
1919

2020
/// Represents the state of the `BannerUpdateViewStore`
21-
struct State {
21+
struct State: Sendable {
2222
/// Stores the state of the nested `BannerDataStore`
2323
let bannerViewState: BannerDataStore.State
2424

@@ -51,7 +51,7 @@ final class BannerUpdateViewStore: Store {
5151
}
5252
}
5353

54-
enum Action {
54+
enum Action: Sendable {
5555
/// Action to update the title of the banner with a given string
5656
case updateTitle(String)
5757

@@ -106,11 +106,13 @@ final class BannerUpdateViewStore: Store {
106106

107107
extension BannerUpdateViewStoreType {
108108
/// Computed property that creates a binding for the working title
109+
@MainActor
109110
var workingTitle: Binding<String> {
110111
makeBinding(stateKeyPath: \.workingCopy.title, actionCasePath: /Action.updateTitle)
111112
}
112113

113114
/// Computed property that creates a binding for the error presentation state
115+
@MainActor
114116
var isErrorPresented: Binding<Bool> {
115117
.init(get: {
116118
return self.state.error != nil

Example/Photos/With Stores/PhotoListViewStore.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,13 @@ extension PhotoListViewStoreType {
165165
}
166166

167167
/// Computed property that creates a binding for the `showUpdateView` state
168+
@MainActor
168169
var showUpdateView: Binding<Bool> {
169170
makeBinding(stateKeyPath: \.showUpdateView, actionCasePath: /PhotoListViewStore.Action.showUpdateView)
170171
}
171172

172173
/// Computed property that creates a binding for the `showsPhotoCount` state
174+
@MainActor
173175
var showsPhotoCount: Binding<Bool> {
174176
//
175177
// return Binding<Bool> {
@@ -183,6 +185,7 @@ extension PhotoListViewStoreType {
183185
}
184186

185187
/// Computed property that creates a binding for the `searchText` state
188+
@MainActor
186189
var searchText: Binding<String> {
187190
makeBinding(stateKeyPath: \.searchText, actionCasePath: /Action.search)
188191
}

Sources/Store/MainQueueScheduler.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ import SwiftUI
1111

1212
/// Scheduler that allows you to either have a `default` implementation of the `DispatchQueue.main` scheduler, a `synchronous` implementation that will immediately call back, a `test` scheduler for fine grained control of time, and an `animated` scheduler to drive animations.
1313
/// You will want to use this to avoid the behavior of `DispatchQueue.main`'s scheduler to schedule work asynchronously by default.
14-
@MainActor
15-
public final class MainQueueScheduler: @preconcurrency Scheduler {
14+
public final class MainQueueScheduler: Scheduler {
1615

1716
/// Describes the characteristics of the scheduler.
1817
public enum SchedulerType: Equatable {

Sources/Store/Store+BindingAdditions.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
//
77

88
import SwiftUI
9-
import CasePaths
10-
import Combine
9+
@preconcurrency import CasePaths
10+
@preconcurrency import Combine
1111

1212
/// An extension on `Store` that provides conveniences for creating `Binding`s.
1313
public extension Store {
@@ -16,6 +16,7 @@ public extension Store {
1616
/// - Parameters:
1717
/// - stateKeyPath: The `KeyPath` to the `State` property that this binding wraps.
1818
/// - actionCasePath: The `CasePath` to the `Action` case associated with the `State` property.
19+
@MainActor
1920
func makeBinding<Value>(stateKeyPath: KeyPath<State, Value>, actionCasePath: CasePath<Action, Value>) -> Binding<Value> {
2021
return .init {
2122
self.state[keyPath: stateKeyPath]
@@ -28,6 +29,7 @@ public extension Store {
2829
/// - Parameters:
2930
/// - stateKeyPath: The `KeyPath` to the `State` property that this binding wraps.
3031
/// - actionCasePath: The `CasePath` to the `Action` case associated with the `State` property.
32+
@MainActor
3133
func makeBinding<Value>(stateKeyPath: KeyPath<State, Value>, actionCasePath: CasePath<Action, Void>) -> Binding<Value> {
3234
return .init {
3335
self.state[keyPath: stateKeyPath]
@@ -40,6 +42,7 @@ public extension Store {
4042
/// - Parameters:
4143
/// - stateKeyPath: The `KeyPath` to the optional `State` property whose existence determines the wrapped value.
4244
/// - actionCasePath: The `CasePath` to the `Action` case associated with the `State` property.
45+
@MainActor
4346
func makeBinding<Value>(stateKeyPath: KeyPath<State, Value?>, actionCasePath: CasePath<Action, Void>) -> Binding<Bool> {
4447
return .init {
4548
self.state[keyPath: stateKeyPath] != nil
@@ -52,6 +55,7 @@ public extension Store {
5255
/// - Parameters:
5356
/// - stateKeyPath: The `KeyPath` to the optional `State` property whose existence determines the wrapped value.
5457
/// - actionCasePath: The `CasePath` to the `Action` case associated with the `State` property.
58+
@MainActor
5559
func makeBinding<Value>(stateKeyPath: KeyPath<State, Value?>, actionCasePath: CasePath<Action, Value?>) -> Binding<Bool> {
5660
return .init {
5761
self.state[keyPath: stateKeyPath] != nil
@@ -68,6 +72,7 @@ public extension Store {
6872
/// - Parameters:
6973
/// - stateKeyPath: The `KeyPath` to the `State` property that this binding wraps.
7074
/// - publisher: The publisher to send the value to.
75+
@MainActor
7176
func makeBinding<Value>(stateKeyPath: KeyPath<State, Value>, publisher: PassthroughSubject<Value, Never>) -> Binding<Value> {
7277
return .init {
7378
self.state[keyPath: stateKeyPath]
@@ -80,6 +85,7 @@ public extension Store {
8085
/// - Parameters:
8186
/// - StateKeyPath: The `KeyPath` to the `State` property that this binding wraps.
8287
/// - publisher: The publisher to send the value to.
88+
@MainActor
8389
func makeBinding<Value>(stateKeyPath: KeyPath<State, Value>, publisher: CurrentValueSubject<Value, Never>) -> Binding<Value> {
8490
return .init {
8591
self.state[keyPath: stateKeyPath]

Sources/Store/Store.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import SwiftUI
99
import Combine
1010

1111
/// A store is an `ObservableObject` that allows us to separate business and/or view level logic and the rendering of views in a way that is repeatable, prescriptive, flexible, and testable by default.
12-
@MainActor
1312
public protocol Store<State, Action>: ObservableObject {
1413

1514
/// A container type for state associated with the corresponding domain.

0 commit comments

Comments
 (0)