Skip to content

Commit 0d13440

Browse files
Allow initializing first update from closure (#30)
Introduce `init(create:environment:` convenience initializer. Remove `init(update:action:environment:` convenience initializer. This is an alternative to #29 for cases where we need to initialize store from an `Update`. Instead of introducing a `ModelFactoryProtocol`, we introduce a convenience initializer that takes a `create` closure. An advantage here is that we do not have to create a `flags` type for customizing the model construction process. Instead, because we can choose which factory function is passed to `create`, we can create one-off closure factories that set the necessary properties on the model for a given view. Additionally, this approach does not require us to specify the type signature of the Store, since Swift is able to infer the store type from the function's return type. Fixes #28 Alternatives considered: - #31 - #29.
1 parent 2ae7450 commit 0d13440

2 files changed

Lines changed: 27 additions & 16 deletions

File tree

Sources/ObservableStore/ObservableStore.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,16 @@ where Model: ModelProtocol
196196
self._actions = PassthroughSubject<Model.Action, Never>()
197197
}
198198

199-
/// Initialize and send an initial action to the store.
200-
/// Useful when performing actions once and only once upon creation
201-
/// of the store.
199+
/// Initialize with a closure that receives environment.
200+
/// Useful for initializing model properties from environment, and for
201+
/// kicking off actions once at store creation.
202202
public convenience init(
203-
state: Model,
204-
action: Model.Action,
203+
create: (Model.Environment) -> Update<Model>,
205204
environment: Model.Environment
206205
) {
207-
self.init(state: state, environment: environment)
208-
self.send(action)
206+
let update = create(environment)
207+
self.init(state: update.state, environment: environment)
208+
self.subscribe(to: update.fx)
209209
}
210210

211211
/// Subscribe to a publisher of actions, piping them through to

Tests/ObservableStoreTests/ObservableStoreTests.swift

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -318,23 +318,34 @@ final class ObservableStoreTests: XCTestCase {
318318
wait(for: [expectation], timeout: 0.2)
319319
}
320320

321-
func testInitialActionInit() throws {
321+
func testCreateInit() throws {
322322
let store = Store(
323-
state: AppModel(),
324-
action: .increment,
323+
create: { environment in
324+
let model = AppModel(count: 1)
325+
let fx = Just(AppModel.Action.increment).eraseToAnyPublisher()
326+
return Update(state: model, fx: fx)
327+
},
325328
environment: AppModel.Environment()
326329
)
327-
XCTAssertEqual(
328-
store.state.count,
329-
1,
330-
"action was sent to store during init"
330+
let expectation = XCTestExpectation(
331+
description: "Sent fx"
331332
)
333+
DispatchQueue.main.async {
334+
// Publisher should fire twice: once for initial state,
335+
// once for state change.
336+
XCTAssertEqual(
337+
store.state.count,
338+
2,
339+
"Sent fx"
340+
)
341+
expectation.fulfill()
342+
}
343+
wait(for: [expectation], timeout: 0.1)
332344
}
333-
345+
334346
func testActionsPublisher() throws {
335347
let store = Store(
336348
state: AppModel(),
337-
action: .increment,
338349
environment: AppModel.Environment()
339350
)
340351

0 commit comments

Comments
 (0)