.github/workflows/test.yml .github/workflows/update-docs.yml .gitignore .hound.yml .jazzy.json .scripts/doc-preprocessor .scripts/generate-docs .scripts/update-gh-pages .swiftlint.yml BuildPhases/run-swiftlint CHANGELOG.md CONTRIBUTING.md Cartfile.resolved Docs/Actions.md Docs/Getting Started Guide.md Docs/Reducers.md Docs/State.md Docs/Stores.md Docs/Utilities.md Docs/img/reswift_concept.graffle Docs/img/reswift_concept.png Docs/img/reswift_detail.png Docs/img/timetravel.gif Docs/jazzy-theme/assets/css/highlight.css.scss Docs/jazzy-theme/assets/css/jazzy.css.scss Docs/jazzy-theme/assets/img/carat.png Docs/jazzy-theme/assets/img/dash.png Docs/jazzy-theme/assets/img/gh.png Docs/jazzy-theme/assets/js/jazzy.js Docs/jazzy-theme/assets/js/jquery.min.js Docs/jazzy-theme/templates/doc.mustache Docs/jazzy-theme/templates/footer.mustache Docs/jazzy-theme/templates/header.mustache Docs/jazzy-theme/templates/nav.mustache Docs/jazzy-theme/templates/parameter.mustache Docs/jazzy-theme/templates/task.mustache Docs/jazzy-theme/templates/tasks.mustache Docs/templates/heading.md Docs/templates/toc.md LICENSE.md Package.swift Package@swift-5.0.swift Podfile Podfile.lock README.md ReSwift.podspec ReSwift.xcodeproj/project.pbxproj ReSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata ReSwift.xcodeproj/xcshareddata/xcbaselines/25DBCF861C30C4DB00D63A58.xcbaseline/8A5AF011-A17C-4780-8B4D-183154BA1DDA.plist ReSwift.xcodeproj/xcshareddata/xcbaselines/25DBCF861C30C4DB00D63A58.xcbaseline/CA98BC4B-4A03-4B07-BF9E-12C3F6A52195.plist ReSwift.xcodeproj/xcshareddata/xcbaselines/25DBCF861C30C4DB00D63A58.xcbaseline/Info.plist ReSwift.xcodeproj/xcshareddata/xcschemes/ReSwift-iOS.xcscheme ReSwift.xcodeproj/xcshareddata/xcschemes/ReSwift-macOS.xcscheme ReSwift.xcodeproj/xcshareddata/xcschemes/ReSwift-tvOS.xcscheme ReSwift.xcodeproj/xcshareddata/xcschemes/ReSwift-watchOS.xcscheme ReSwift.xcworkspace/contents.xcworkspacedata ReSwift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ReSwift/CoreTypes/Action.swift ReSwift/CoreTypes/DispatchingStoreType.swift ReSwift/CoreTypes/Middleware.swift ReSwift/CoreTypes/Reducer.swift ReSwift/CoreTypes/State.swift ReSwift/CoreTypes/Store.swift ReSwift/CoreTypes/StoreSubscriber.swift ReSwift/CoreTypes/StoreType.swift ReSwift/CoreTypes/Subscription.swift ReSwift/Info.plist ReSwift/ReSwift.h ReSwift/Utils/Assertions.swift ReSwift/Utils/Coding.swift ReSwift/Utils/Synchronized.swift ReSwift/Utils/TypeHelper.swift ReSwiftTests/AutomaticallySkipRepeatsTests.swift ReSwiftTests/Info.plist ReSwiftTests/PerformanceTests.swift ReSwiftTests/StoreDispatchTests.swift ReSwiftTests/StoreMiddlewareTests.swift ReSwiftTests/StoreSubscriberTests.swift ReSwiftTests/StoreSubscriptionTests.swift ReSwiftTests/StoreTests.swift ReSwiftTests/SynchronizedTests.swift ReSwiftTests/TestFakes.swift ReSwiftTests/TypeHelperTests.swift ReSwiftTests/XCTest+Assertions.swift ReSwiftTests/XCTest+Compatibility.swift SwiftLintIntegration/Info.plist SwiftLintIntegration/SwiftLintIntegration.swift codecov.yml <<<<<< network # path=.xcodecov/ReSwift-macOSTests.xctest.coverage.txt /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/PerformanceTests.swift: 1| |// Copyright © 2019 ReSwift Community. All rights reserved. 2| | 3| |import XCTest 4| |import ReSwift 5| | 6| |final class PerformanceTests: XCTestCase { 7| | struct MockState {} 8| | struct MockAction: Action {} 9| | 10| 0| let subscribers: [MockSubscriber] = (0..<3000).map { _ in MockSubscriber() } 11| 0| let store = Store( 12| 0| reducer: { _, state in return state ?? MockState() }, 13| 0| state: MockState(), 14| 0| automaticallySkipsRepeats: false 15| 0| ) 16| | 17| | class MockSubscriber: StoreSubscriber { 18| 0| func newState(state: MockState) { 19| 0| // Do nothing 20| 0| } 21| | } 22| | 23| 0| func testNotify() { 24| 0| self.subscribers.forEach(self.store.subscribe) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests16PerformanceTestsC10testNotifyyyFyAC14MockSubscriberCc0aB05StoreCyAC0I5StateVGcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests16PerformanceTestsC10testNotifyyyFyAC14MockSubscriberCc0aB05StoreCyAC0I5StateVGcfu_yAFcfu0_ ------------------ 25| 0| self.measure { 26| 0| self.store.dispatch(MockAction()) 27| 0| } 28| 0| } 29| | 30| 0| func testSubscribe() { 31| 0| self.measure { 32| 0| self.subscribers.forEach(self.store.subscribe) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests16PerformanceTestsC13testSubscribeyyFyyXEfU_yAC14MockSubscriberCc0aB05StoreCyAC0I5StateVGcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests16PerformanceTestsC13testSubscribeyyFyyXEfU_yAC14MockSubscriberCc0aB05StoreCyAC0I5StateVGcfu_yAFcfu0_ ------------------ 33| 0| } 34| 0| } 35| |} /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/StoreDispatchTests.swift: 1| |// 2| |// StoreDispatchTests.swift 3| |// ReSwift 4| |// 5| |// Created by Karl Bowden on 20/07/2016. 6| |// Copyright © 2016 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import ReSwift 11| | 12| |class StoreDispatchTests: XCTestCase { 13| | 14| | typealias TestSubscriber = TestStoreSubscriber 15| | typealias CallbackSubscriber = CallbackStoreSubscriber 16| | 17| | var store: Store! 18| | var reducer: TestReducer! 19| | 20| 0| override func setUp() { 21| 0| super.setUp() 22| 0| reducer = TestReducer() 23| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests18StoreDispatchTestsC5setUpyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0J7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests18StoreDispatchTestsC5setUpyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0J7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 24| 0| } 25| | 26| | /** 27| | it throws an exception when a reducer dispatches an action 28| | */ 29| 0| func testThrowsExceptionWhenReducersDispatch() { 30| 0| // Expectation lives in the `DispatchingReducer` class 31| 0| let reducer = DispatchingReducer() 32| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests18StoreDispatchTestsC031testThrowsExceptionWhenReducersF0yyFAA12TestAppStateV0aB06Action_p_AFSgtcAA18DispatchingReducerCcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests18StoreDispatchTestsC031testThrowsExceptionWhenReducersF0yyFAA12TestAppStateV0aB06Action_p_AFSgtcAA18DispatchingReducerCcfu_AfgH_p_AItcfu0_ ------------------ 33| 0| reducer.store = store 34| 0| store.dispatch(SetValueAction(10)) 35| 0| } 36| | 37| | /** 38| | it accepts action creators 39| | */ 40| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 41| 0| func testAcceptsActionCreators() { 42| 0| store.dispatch(SetValueAction(5)) 43| 0| 44| 0| let doubleValueActionCreator: Store.ActionCreator = { state, store in 45| 0| return SetValueAction(state.testValue! * 2) 46| 0| } 47| 0| 48| 0| store.dispatch(doubleValueActionCreator) 49| 0| 50| 0| XCTAssertEqual(store.state.testValue, 10) 51| 0| } 52| | 53| | /** 54| | it accepts async action creators 55| | */ 56| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 57| 0| func testAcceptsAsyncActionCreators() { 58| 0| 59| 0| let asyncExpectation = futureExpectation( 60| 0| withDescription: "It accepts async action creators") 61| 0| 62| 0| let asyncActionCreator: Store.AsyncActionCreator = { _, _, callback in 63| 0| dispatchAsync { 64| 0| // Provide the callback with an action creator 65| 0| callback { _, _ in 66| 0| return SetValueAction(5) 67| 0| } 68| 0| } 69| 0| } 70| 0| 71| 0| let subscriber = CallbackSubscriber { [unowned self] state in 72| 0| if self.store.state.testValue != nil { 73| 0| XCTAssertEqual(self.store.state.testValue, 5) 74| 0| asyncExpectation.fulfill() 75| 0| } 76| 0| } 77| 0| 78| 0| store.subscribe(subscriber) 79| 0| store.dispatch(asyncActionCreator) 80| 0| waitForFutureExpectations(withTimeout: 1) { error in 81| 0| if let error = error { 82| 0| XCTFail("waitForExpectationsWithTimeout errored: \(error)") 83| 0| } 84| 0| } 85| 0| } 86| | 87| | /** 88| | it calls the callback once state update from async action is complete 89| | */ 90| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 91| 0| func testCallsCallbackOnce() { 92| 0| let asyncExpectation = futureExpectation(withDescription: 93| 0| "It calls the callback once state update from async action is complete") 94| 0| 95| 0| let asyncActionCreator: Store.AsyncActionCreator = { _, _, callback in 96| 0| dispatchAsync { 97| 0| // Provide the callback with an action creator 98| 0| callback { _, _ in 99| 0| return SetValueAction(5) 100| 0| } 101| 0| } 102| 0| } 103| 0| 104| 0| store.dispatch(asyncActionCreator) { newState in 105| 0| XCTAssertEqual(self.store.state.testValue, 5) 106| 0| if newState.testValue == 5 { 107| 0| asyncExpectation.fulfill() 108| 0| } 109| 0| } 110| 0| 111| 0| waitForFutureExpectations(withTimeout: 1) { error in 112| 0| if let error = error { 113| 0| XCTFail("waitForExpectationsWithTimeout errored: \(error)") 114| 0| } 115| 0| } 116| 0| } 117| |} 118| | 119| |// Needs to be class so that shared reference can be modified to inject store 120| |class DispatchingReducer: XCTestCase { 121| | var store: Store? 122| | 123| 0| func handleAction(action: Action, state: TestAppState?) -> TestAppState { 124| 0| expectFatalError { 125| 0| self.store?.dispatch(SetValueAction(20)) 126| 0| } 127| 0| return state ?? TestAppState() 128| 0| } 129| |} /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/StoreMiddlewareTests.swift: 1| |// 2| |// StoreMiddlewareTests.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 12/24/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import ReSwift 11| | 12| 0|let firstMiddleware: Middleware = { dispatch, getState in 13| 0| return { next in 14| 0| return { action in 15| 0| 16| 0| if var action = action as? SetValueStringAction { 17| 0| action.value += " First Middleware" 18| 0| next(action) 19| 0| } else { 20| 0| next(action) 21| 0| } 22| 0| } 23| 0| } 24| 0|} 25| | 26| 0|let secondMiddleware: Middleware = { dispatch, getState in 27| 0| return { next in 28| 0| return { action in 29| 0| 30| 0| if var action = action as? SetValueStringAction { 31| 0| action.value += " Second Middleware" 32| 0| next(action) 33| 0| } else { 34| 0| next(action) 35| 0| } 36| 0| } 37| 0| } 38| 0|} 39| | 40| 0|let dispatchingMiddleware: Middleware = { dispatch, getState in 41| 0| return { next in 42| 0| return { action in 43| 0| 44| 0| if var action = action as? SetValueAction { 45| 0| dispatch(SetValueStringAction("\(action.value ?? 0)")) 46| 0| } 47| 0| 48| 0| next(action) 49| 0| } 50| 0| } 51| 0|} 52| | 53| 0|let stateAccessingMiddleware: Middleware = { dispatch, getState in 54| 0| return { next in 55| 0| return { action in 56| 0| 57| 0| let appState = getState(), 58| 0| stringAction = action as? SetValueStringAction 59| 0| 60| 0| // avoid endless recursion by checking if we've dispatched exactly this action 61| 0| if appState?.testValue == "OK" && stringAction?.value != "Not OK" { 62| 0| // dispatch a new action 63| 0| dispatch(SetValueStringAction("Not OK")) 64| 0| 65| 0| // and swallow the current one 66| 0| next(NoOpAction()) 67| 0| } else { 68| 0| next(action) 69| 0| } 70| 0| } 71| 0| } 72| 0|} 73| | 74| 0|func middleware(executing block: @escaping () -> Void) -> Middleware { 75| 0| return { dispatch, getState in 76| 0| return { next in 77| 0| return { action in 78| 0| block() 79| 0| } 80| 0| } 81| 0| } 82| 0|} 83| | 84| |class StoreMiddlewareTests: XCTestCase { 85| | 86| | /** 87| | it can decorate dispatch function 88| | */ 89| 0| func testDecorateDispatch() { 90| 0| let reducer = TestValueStringReducer() 91| 0| // Swift 4.1 fails to cast this from Middleware to Middleware 92| 0| // as expected during runtime, see: 93| 0| let middleware: [Middleware] = [ 94| 0| firstMiddleware, 95| 0| secondMiddleware 96| 0| ] 97| 0| let store = Store(reducer: reducer.handleAction, ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreMiddlewareTestsC20testDecorateDispatchyyFAA18TestStringAppStateV0aB06Action_p_AFSgtcAA0k5ValueL7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreMiddlewareTestsC20testDecorateDispatchyyFAA18TestStringAppStateV0aB06Action_p_AFSgtcAA0k5ValueL7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 98| 0| state: TestStringAppState(), 99| 0| middleware: middleware) 100| 0| 101| 0| let subscriber = TestStoreSubscriber() 102| 0| store.subscribe(subscriber) 103| 0| 104| 0| let action = SetValueStringAction("OK") 105| 0| store.dispatch(action) 106| 0| 107| 0| XCTAssertEqual(store.state.testValue, "OK First Middleware Second Middleware") 108| 0| } 109| | 110| | /** 111| | it can dispatch actions 112| | */ 113| 0| func testCanDispatch() { 114| 0| let reducer = TestValueStringReducer() 115| 0| // Swift 4.1 fails to cast this from Middleware to Middleware 116| 0| // as expected during runtime, see: 117| 0| let middleware: [Middleware] = [ 118| 0| firstMiddleware, 119| 0| secondMiddleware, 120| 0| dispatchingMiddleware 121| 0| ] 122| 0| let store = Store(reducer: reducer.handleAction, ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreMiddlewareTestsC15testCanDispatchyyFAA18TestStringAppStateV0aB06Action_p_AFSgtcAA0k5ValueL7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreMiddlewareTestsC15testCanDispatchyyFAA18TestStringAppStateV0aB06Action_p_AFSgtcAA0k5ValueL7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 123| 0| state: TestStringAppState(), 124| 0| middleware: middleware) 125| 0| 126| 0| let subscriber = TestStoreSubscriber() 127| 0| store.subscribe(subscriber) 128| 0| 129| 0| let action = SetValueAction(10) 130| 0| store.dispatch(action) 131| 0| 132| 0| XCTAssertEqual(store.state.testValue, "10 First Middleware Second Middleware") 133| 0| } 134| | 135| | /** 136| | it middleware can access the store's state 137| | */ 138| 0| func testMiddlewareCanAccessState() { 139| 0| let reducer = TestValueStringReducer() 140| 0| var state = TestStringAppState() 141| 0| state.testValue = "OK" 142| 0| 143| 0| let store = Store(reducer: reducer.handleAction, state: state, ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreMiddlewareTestsC04testF14CanAccessStateyyFAA013TestStringAppK0V0aB06Action_p_AFSgtcAA0l5ValueM7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreMiddlewareTestsC04testF14CanAccessStateyyFAA013TestStringAppK0V0aB06Action_p_AFSgtcAA0l5ValueM7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 144| 0| middleware: [stateAccessingMiddleware]) 145| 0| 146| 0| store.dispatch(SetValueStringAction("Action That Won't Go Through")) 147| 0| 148| 0| XCTAssertEqual(store.state.testValue, "Not OK") 149| 0| } 150| | 151| 0| func testCanMutateMiddlewareAfterInit() { 152| 0| 153| 0| let reducer = TestValueStringReducer() 154| 0| let state = TestStringAppState() 155| 0| let store = Store(reducer: reducer.handleAction, state: state, ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreMiddlewareTestsC013testCanMutateF9AfterInityyFAA18TestStringAppStateV0aB06Action_p_AFSgtcAA0m5ValueN7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreMiddlewareTestsC013testCanMutateF9AfterInityyFAA18TestStringAppStateV0aB06Action_p_AFSgtcAA0m5ValueN7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 156| 0| middleware: []) 157| 0| 158| 0| // Adding 159| 0| var added = false 160| 0| store.middleware.append(middleware(executing: { added = true })) 161| 0| store.dispatch(SetValueStringAction("")) 162| 0| XCTAssertTrue(added) 163| 0| 164| 0| // Removing 165| 0| added = false 166| 0| store.middleware = [] 167| 0| store.dispatch(SetValueStringAction("")) 168| 0| XCTAssertFalse(added) 169| 0| } 170| |} /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/StoreSubscriberTests.swift: 1| |// 2| |// StoreSubscriberTests.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 1/23/16. 6| |// Copyright © 2016 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |import ReSwift 11| | 12| |class StoreSubscriberTests: XCTestCase { 13| | 14| | /** 15| | it allows to pass a state selector closure 16| | */ 17| 0| func testAllowsSelectorClosure() { 18| 0| let reducer = TestReducer() 19| 0| let store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC25testAllowsSelectorClosureyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0L7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC25testAllowsSelectorClosureyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0L7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 20| 0| let subscriber = TestFilteredSubscriber() 21| 0| 22| 0| store.subscribe(subscriber) { 23| 0| $0.select { $0.testValue } 24| 0| } 25| 0| 26| 0| store.dispatch(SetValueAction(3)) 27| 0| 28| 0| XCTAssertEqual(subscriber.receivedValue, 3) 29| 0| 30| 0| store.dispatch(SetValueAction(nil)) 31| 0| 32| 0| XCTAssertEqual(subscriber.receivedValue, .some(.none)) 33| 0| } 34| | 35| | /** 36| | it allows to pass a state selector key path 37| | */ 38| 0| func testAllowsSelectorKeyPath() { 39| 0| let reducer = TestReducer() 40| 0| let store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC25testAllowsSelectorKeyPathyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0M7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC25testAllowsSelectorKeyPathyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0M7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 41| 0| let subscriber = TestFilteredSubscriber() 42| 0| 43| 0| store.subscribe(subscriber) { 44| 0| $0.select(\.testValue) 45| 0| } 46| 0| 47| 0| store.dispatch(SetValueAction(3)) 48| 0| 49| 0| XCTAssertEqual(subscriber.receivedValue, 3) 50| 0| 51| 0| store.dispatch(SetValueAction(nil)) 52| 0| 53| 0| #if swift(>=4.1) 54| 0| XCTAssertEqual(subscriber.receivedValue, .some(.none)) 55| 0| #else 56| 0| XCTAssertEqual(subscriber.receivedValue, nil) 57| 0| #endif 58| 0| } 59| | 60| | /** 61| | it supports complex state selector closures 62| | */ 63| 0| func testComplexStateSelector() { 64| 0| let reducer = TestComplexAppStateReducer() 65| 0| let store = Store(reducer: reducer.handleAction, state: TestComplexAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC24testComplexStateSelectoryyFAA04Testi3AppJ0V0aB06Action_p_AFSgtcAA0limJ7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC24testComplexStateSelectoryyFAA04Testi3AppJ0V0aB06Action_p_AFSgtcAA0limJ7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 66| 0| let subscriber = TestSelectiveSubscriber() 67| 0| 68| 0| store.subscribe(subscriber) { 69| 0| $0.select { 70| 0| ($0.testValue, $0.otherState?.name) 71| 0| } 72| 0| } 73| 0| store.dispatch(SetValueAction(5)) 74| 0| store.dispatch(SetOtherStateAction( 75| 0| otherState: OtherState(name: "TestName", age: 99) 76| 0| )) 77| 0| 78| 0| XCTAssertEqual(subscriber.receivedValue.0, 5) 79| 0| XCTAssertEqual(subscriber.receivedValue.1, "TestName") 80| 0| } 81| | 82| | /** 83| | it does not notify subscriber for unchanged substate state when using `skipRepeats`. 84| | */ 85| 0| func testUnchangedStateWithRegularSubstateSelection() { 86| 0| let reducer = TestReducer() 87| 0| var state = TestAppState() 88| 0| state.testValue = 3 89| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC46testUnchangedStateWithRegularSubstateSelectionyyFAA07TestAppJ0V0aB06Action_p_AFSgtcAA0O7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC46testUnchangedStateWithRegularSubstateSelectionyyFAA07TestAppJ0V0aB06Action_p_AFSgtcAA0O7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 90| 0| let subscriber = TestFilteredSubscriber() 91| 0| 92| 0| store.subscribe(subscriber) { 93| 0| $0 94| 0| .select { $0.testValue } 95| 0| .skipRepeats { $0 == $1 } 96| 0| } 97| 0| 98| 0| XCTAssertEqual(subscriber.receivedValue, 3) 99| 0| 100| 0| store.dispatch(SetValueAction(3)) 101| 0| 102| 0| XCTAssertEqual(subscriber.receivedValue, 3) 103| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 104| 0| } 105| | 106| 0| func testUnchangedStateWithKeyPath() { 107| 0| let reducer = TestReducer() 108| 0| var state = TestAppState() 109| 0| state.testValue = 3 110| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC29testUnchangedStateWithKeyPathyyFAA07TestAppJ0V0aB06Action_p_AFSgtcAA0N7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC29testUnchangedStateWithKeyPathyyFAA07TestAppJ0V0aB06Action_p_AFSgtcAA0N7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 111| 0| let subscriber = TestFilteredSubscriber() 112| 0| 113| 0| store.subscribe(subscriber) { 114| 0| $0 115| 0| .select(\.testValue) 116| 0| .skipRepeats { $0 == $1 } 117| 0| } 118| 0| 119| 0| XCTAssertEqual(subscriber.receivedValue, 3) 120| 0| 121| 0| store.dispatch(SetValueAction(3)) 122| 0| 123| 0| XCTAssertEqual(subscriber.receivedValue, 3) 124| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 125| 0| } 126| | 127| | /** 128| | it does not notify subscriber for unchanged substate state when using the default 129| | `skipRepeats` implementation. 130| | */ 131| 0| func testUnchangedStateDefaultSkipRepeatsWithRegularSubstateSelection() { 132| 0| let reducer = TestValueStringReducer() 133| 0| let state = TestStringAppState() 134| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC64testUnchangedStateDefaultSkipRepeatsWithRegularSubstateSelectionyyFAA013TestStringAppJ0V0aB06Action_p_AFSgtcAA0r5ValueS7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC64testUnchangedStateDefaultSkipRepeatsWithRegularSubstateSelectionyyFAA013TestStringAppJ0V0aB06Action_p_AFSgtcAA0r5ValueS7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 135| 0| let subscriber = TestFilteredSubscriber() 136| 0| 137| 0| store.subscribe(subscriber) { 138| 0| $0 139| 0| .select { $0.testValue } 140| 0| .skipRepeats() 141| 0| } 142| 0| 143| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 144| 0| 145| 0| store.dispatch(SetValueStringAction("Initial")) 146| 0| 147| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 148| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 149| 0| } 150| | 151| 0| func testUnchangedStateDefaultSkipRepeatsWithKeyPath() { 152| 0| let reducer = TestValueStringReducer() 153| 0| let state = TestStringAppState() 154| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC47testUnchangedStateDefaultSkipRepeatsWithKeyPathyyFAA013TestStringAppJ0V0aB06Action_p_AFSgtcAA0q5ValueR7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC47testUnchangedStateDefaultSkipRepeatsWithKeyPathyyFAA013TestStringAppJ0V0aB06Action_p_AFSgtcAA0q5ValueR7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 155| 0| let subscriber = TestFilteredSubscriber() 156| 0| 157| 0| store.subscribe(subscriber) { 158| 0| $0 159| 0| .select(\.testValue) 160| 0| .skipRepeats() 161| 0| } 162| 0| 163| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 164| 0| 165| 0| store.dispatch(SetValueStringAction("Initial")) 166| 0| 167| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 168| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 169| 0| } 170| | 171| | /** 172| | it skips repeated state values by when `skipRepeats` returns `true`. 173| | */ 174| 0| func testSkipsStateUpdatesForCustomEqualityChecksWithRegularSubstateSelection() { 175| 0| let reducer = TestCustomAppStateReducer() 176| 0| let state = TestCustomAppState(substateValue: 5) 177| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC72testSkipsStateUpdatesForCustomEqualityChecksWithRegularSubstateSelectionyyFAA04Testm3AppJ0V0aB06Action_p_AFSgtcAA0tmuJ7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC72testSkipsStateUpdatesForCustomEqualityChecksWithRegularSubstateSelectionyyFAA04Testm3AppJ0V0aB06Action_p_AFSgtcAA0tmuJ7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 178| 0| let subscriber = TestFilteredSubscriber() 179| 0| 180| 0| store.subscribe(subscriber) { 181| 0| $0 182| 0| .select { $0.substate } 183| 0| .skipRepeats { $0.value == $1.value } 184| 0| } 185| 0| 186| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 187| 0| 188| 0| store.dispatch(SetCustomSubstateAction(5)) 189| 0| 190| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 191| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 192| 0| } 193| | 194| 0| func testSkipsStateUpdatesForCustomEqualityChecksWithKeyPath() { 195| 0| let reducer = TestCustomAppStateReducer() 196| 0| let state = TestCustomAppState(substateValue: 5) 197| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC55testSkipsStateUpdatesForCustomEqualityChecksWithKeyPathyyFAA04Testm3AppJ0V0aB06Action_p_AFSgtcAA0smtJ7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC55testSkipsStateUpdatesForCustomEqualityChecksWithKeyPathyyFAA04Testm3AppJ0V0aB06Action_p_AFSgtcAA0smtJ7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 198| 0| let subscriber = TestFilteredSubscriber() 199| 0| 200| 0| store.subscribe(subscriber) { 201| 0| $0 202| 0| .select(\.substate) 203| 0| .skipRepeats { $0.value == $1.value } 204| 0| } 205| 0| 206| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 207| 0| 208| 0| store.dispatch(SetCustomSubstateAction(5)) 209| 0| 210| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 211| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 212| 0| } 213| | 214| 0| func testPassesOnDuplicateSubstateUpdatesByDefaultWithRegularSubstateSelection() { 215| 0| let reducer = TestNonEquatableReducer() 216| 0| let state = TestNonEquatable() 217| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC056testPassesOnDuplicateSubstateUpdatesByDefaultWithRegularL9SelectionyyFAA16TestNonEquatableV0aB06Action_p_AFSgtcAA0stU7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC056testPassesOnDuplicateSubstateUpdatesByDefaultWithRegularL9SelectionyyFAA16TestNonEquatableV0aB06Action_p_AFSgtcAA0stU7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 218| 0| let subscriber = TestFilteredSubscriber() 219| 0| 220| 0| store.subscribe(subscriber) { 221| 0| $0.select { $0.testValue } 222| 0| } 223| 0| 224| 0| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 225| 0| 226| 0| store.dispatch(SetNonEquatableAction(NonEquatable())) 227| 0| 228| 0| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 229| 0| XCTAssertEqual(subscriber.newStateCallCount, 2) 230| 0| } 231| | 232| 0| func testPassesOnDuplicateSubstateUpdatesByDefaultWithKeyPath() { 233| 0| let reducer = TestNonEquatableReducer() 234| 0| let state = TestNonEquatable() 235| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC56testPassesOnDuplicateSubstateUpdatesByDefaultWithKeyPathyyFAA16TestNonEquatableV0aB06Action_p_AFSgtcAA0stU7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC56testPassesOnDuplicateSubstateUpdatesByDefaultWithKeyPathyyFAA16TestNonEquatableV0aB06Action_p_AFSgtcAA0stU7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 236| 0| let subscriber = TestFilteredSubscriber() 237| 0| 238| 0| store.subscribe(subscriber) { 239| 0| $0.select(\.testValue) 240| 0| } 241| 0| 242| 0| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 243| 0| 244| 0| store.dispatch(SetNonEquatableAction(NonEquatable())) 245| 0| 246| 0| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 247| 0| XCTAssertEqual(subscriber.newStateCallCount, 2) 248| 0| } 249| | 250| 0| func testPassesOnDuplicateSubstateWhenSkipsFalseWithRegularSubstateSelection() { 251| 0| let reducer = TestValueStringReducer() 252| 0| let state = TestStringAppState() 253| 0| let store = Store(reducer: reducer.handleAction, state: state, middleware: [], automaticallySkipsRepeats: false) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC054testPassesOnDuplicateSubstateWhenSkipsFalseWithRegularL9SelectionyyFAA18TestStringAppStateV0aB06Action_p_AFSgtcAA0s5ValueT7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC054testPassesOnDuplicateSubstateWhenSkipsFalseWithRegularL9SelectionyyFAA18TestStringAppStateV0aB06Action_p_AFSgtcAA0s5ValueT7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 254| 0| let subscriber = TestFilteredSubscriber() 255| 0| 256| 0| store.subscribe(subscriber) { 257| 0| $0.select { $0.testValue } 258| 0| } 259| 0| 260| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 261| 0| 262| 0| store.dispatch(SetValueStringAction("Initial")) 263| 0| 264| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 265| 0| XCTAssertEqual(subscriber.newStateCallCount, 2) 266| 0| } 267| | 268| 0| func testPassesOnDuplicateSubstateWhenSkipsFalseWithKeyPath() { 269| 0| let reducer = TestValueStringReducer() 270| 0| let state = TestStringAppState() 271| 0| let store = Store(reducer: reducer.handleAction, state: state, middleware: [], automaticallySkipsRepeats: false) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC54testPassesOnDuplicateSubstateWhenSkipsFalseWithKeyPathyyFAA18TestStringAppStateV0aB06Action_p_AFSgtcAA0s5ValueT7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC54testPassesOnDuplicateSubstateWhenSkipsFalseWithKeyPathyyFAA18TestStringAppStateV0aB06Action_p_AFSgtcAA0s5ValueT7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 272| 0| let subscriber = TestFilteredSubscriber() 273| 0| 274| 0| store.subscribe(subscriber) { 275| 0| $0.select(\.testValue) 276| 0| } 277| 0| 278| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 279| 0| 280| 0| store.dispatch(SetValueStringAction("Initial")) 281| 0| 282| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 283| 0| XCTAssertEqual(subscriber.newStateCallCount, 2) 284| 0| } 285| | 286| 0| func testSkipsStateUpdatesForEquatableStateByDefault() { 287| 0| let reducer = TestValueStringReducer() 288| 0| let state = TestStringAppState() 289| 0| let store = Store(reducer: reducer.handleAction, state: state, middleware: []) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC033testSkipsStateUpdatesForEquatableJ9ByDefaultyyFAA013TestStringAppJ0V0aB06Action_p_AFSgtcAA0p5ValueQ7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC033testSkipsStateUpdatesForEquatableJ9ByDefaultyyFAA013TestStringAppJ0V0aB06Action_p_AFSgtcAA0p5ValueQ7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 290| 0| let subscriber = TestFilteredSubscriber() 291| 0| 292| 0| store.subscribe(subscriber) 293| 0| 294| 0| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 295| 0| 296| 0| store.dispatch(SetValueStringAction("Initial")) 297| 0| 298| 0| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 299| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 300| 0| } 301| | 302| 0| func testSkipsStateUpdatesForEquatableSubStateByDefaultWithRegularSubstateSelection() { 303| 0| let reducer = TestNonEquatableReducer() 304| 0| let state = TestNonEquatable() 305| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC036testSkipsStateUpdatesForEquatableSubJ37ByDefaultWithRegularSubstateSelectionyyFAA07TestNonM0V0aB06Action_p_AFSgtcAA0uvM7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC036testSkipsStateUpdatesForEquatableSubJ37ByDefaultWithRegularSubstateSelectionyyFAA07TestNonM0V0aB06Action_p_AFSgtcAA0uvM7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 306| 0| let subscriber = TestFilteredSubscriber() 307| 0| 308| 0| store.subscribe(subscriber) { 309| 0| $0.select { $0.testValue.testValue } 310| 0| } 311| 0| 312| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 313| 0| 314| 0| store.dispatch(SetValueStringAction("Initial")) 315| 0| 316| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 317| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 318| 0| } 319| | 320| 0| func testSkipsStateUpdatesForEquatableSubStateByDefaultWithKeyPathOnGenericStoreType() { 321| 0| let reducer = TestNonEquatableReducer() 322| 0| let state = TestNonEquatable() 323| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC036testSkipsStateUpdatesForEquatableSubj29ByDefaultWithKeyPathOnGenericE4TypeyyFAA07TestNonM0V0aB06Action_p_AFSgtcAA0wxM7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC036testSkipsStateUpdatesForEquatableSubj29ByDefaultWithKeyPathOnGenericE4TypeyyFAA07TestNonM0V0aB06Action_p_AFSgtcAA0wxM7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 324| 0| 325| 0| func runTests(store: S) where S.State == TestNonEquatable { 326| 0| let subscriber = TestFilteredSubscriber() 327| 0| 328| 0| store.subscribe(subscriber) { 329| 0| $0.select(\.testValue.testValue) 330| 0| } 331| 0| 332| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 333| 0| 334| 0| store.dispatch(SetValueStringAction("Initial")) 335| 0| 336| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 337| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 338| 0| } 339| 0| 340| 0| runTests(store: store) 341| 0| } 342| | 343| 0| func testSkipsStateUpdatesForEquatableSubStateByDefaultWithKeyPath() { 344| 0| let reducer = TestNonEquatableReducer() 345| 0| let state = TestNonEquatable() 346| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC036testSkipsStateUpdatesForEquatableSubJ20ByDefaultWithKeyPathyyFAA07TestNonM0V0aB06Action_p_AFSgtcAA0tuM7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC036testSkipsStateUpdatesForEquatableSubJ20ByDefaultWithKeyPathyyFAA07TestNonM0V0aB06Action_p_AFSgtcAA0tuM7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 347| 0| let subscriber = TestFilteredSubscriber() 348| 0| 349| 0| store.subscribe(subscriber) { 350| 0| $0.select(\.testValue.testValue) 351| 0| } 352| 0| 353| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 354| 0| 355| 0| store.dispatch(SetValueStringAction("Initial")) 356| 0| 357| 0| XCTAssertEqual(subscriber.receivedValue, "Initial") 358| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 359| 0| } 360| | 361| 0| func testPassesOnDuplicateStateUpdatesInCustomizedStore() { 362| 0| let reducer = TestValueStringReducer() 363| 0| let state = TestStringAppState() 364| 0| let store = Store(reducer: reducer.handleAction, state: state, middleware: [], automaticallySkipsRepeats: false) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC045testPassesOnDuplicateStateUpdatesInCustomizedE0yyFAA013TestStringAppL0V0aB06Action_p_AFSgtcAA0p5ValueQ7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC045testPassesOnDuplicateStateUpdatesInCustomizedE0yyFAA013TestStringAppL0V0aB06Action_p_AFSgtcAA0p5ValueQ7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 365| 0| let subscriber = TestFilteredSubscriber() 366| 0| 367| 0| store.subscribe(subscriber) 368| 0| 369| 0| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 370| 0| 371| 0| store.dispatch(SetValueStringAction("Initial")) 372| 0| 373| 0| XCTAssertEqual(subscriber.receivedValue.testValue, "Initial") 374| 0| XCTAssertEqual(subscriber.newStateCallCount, 2) 375| 0| } 376| | 377| 0| func testSkipWhenWithRegularSubstateSelection() { 378| 0| let reducer = TestCustomAppStateReducer() 379| 0| let state = TestCustomAppState(substateValue: 5) 380| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC40testSkipWhenWithRegularSubstateSelectionyyFAA18TestCustomAppStateV0aB06Action_p_AFSgtcAA0opqR7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC40testSkipWhenWithRegularSubstateSelectionyyFAA18TestCustomAppStateV0aB06Action_p_AFSgtcAA0opqR7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 381| 0| let subscriber = TestFilteredSubscriber() 382| 0| 383| 0| store.subscribe(subscriber) { 384| 0| $0 385| 0| .select { $0.substate } 386| 0| .skip { $0.value == $1.value } 387| 0| } 388| 0| 389| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 390| 0| 391| 0| store.dispatch(SetCustomSubstateAction(5)) 392| 0| 393| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 394| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 395| 0| } 396| | 397| 0| func testSkipWhenWithKeyPath() { 398| 0| let reducer = TestCustomAppStateReducer() 399| 0| let state = TestCustomAppState(substateValue: 5) 400| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC23testSkipWhenWithKeyPathyyFAA18TestCustomAppStateV0aB06Action_p_AFSgtcAA0nopQ7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC23testSkipWhenWithKeyPathyyFAA18TestCustomAppStateV0aB06Action_p_AFSgtcAA0nopQ7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 401| 0| let subscriber = TestFilteredSubscriber() 402| 0| 403| 0| store.subscribe(subscriber) { 404| 0| $0 405| 0| .select(\.substate) 406| 0| .skip { $0.value == $1.value } 407| 0| } 408| 0| 409| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 410| 0| 411| 0| store.dispatch(SetCustomSubstateAction(5)) 412| 0| 413| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 414| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 415| 0| } 416| | 417| 0| func testOnlyWhenWithRegularSubstateSelection() { 418| 0| let reducer = TestCustomAppStateReducer() 419| 0| let state = TestCustomAppState(substateValue: 5) 420| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC40testOnlyWhenWithRegularSubstateSelectionyyFAA18TestCustomAppStateV0aB06Action_p_AFSgtcAA0opqR7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC40testOnlyWhenWithRegularSubstateSelectionyyFAA18TestCustomAppStateV0aB06Action_p_AFSgtcAA0opqR7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 421| 0| let subscriber = TestFilteredSubscriber() 422| 0| 423| 0| store.subscribe(subscriber) { 424| 0| $0 425| 0| .select { $0.substate } 426| 0| .only { $0.value != $1.value } 427| 0| } 428| 0| 429| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 430| 0| 431| 0| store.dispatch(SetCustomSubstateAction(5)) 432| 0| 433| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 434| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 435| 0| } 436| | 437| 0| func testOnlyWhenWithKeyPath() { 438| 0| let reducer = TestCustomAppStateReducer() 439| 0| let state = TestCustomAppState(substateValue: 5) 440| 0| let store = Store(reducer: reducer.handleAction, state: state) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC23testOnlyWhenWithKeyPathyyFAA18TestCustomAppStateV0aB06Action_p_AFSgtcAA0nopQ7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests20StoreSubscriberTestsC23testOnlyWhenWithKeyPathyyFAA18TestCustomAppStateV0aB06Action_p_AFSgtcAA0nopQ7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 441| 0| let subscriber = TestFilteredSubscriber() 442| 0| 443| 0| store.subscribe(subscriber) { 444| 0| $0 445| 0| .select(\.substate) 446| 0| .only { $0.value != $1.value } 447| 0| } 448| 0| 449| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 450| 0| 451| 0| store.dispatch(SetCustomSubstateAction(5)) 452| 0| 453| 0| XCTAssertEqual(subscriber.receivedValue.value, 5) 454| 0| XCTAssertEqual(subscriber.newStateCallCount, 1) 455| 0| } 456| |} 457| | 458| |class TestFilteredSubscriber: StoreSubscriber { 459| | var receivedValue: T! 460| | var newStateCallCount = 0 461| | 462| 0| func newState(state: T) { 463| 0| receivedValue = state 464| 0| newStateCallCount += 1 465| 0| } 466| | 467| |} 468| | 469| |/** 470| | Example of how you can select a substate. The return value from 471| | `selectSubstate` and the argument for `newState` need to match up. 472| | */ 473| |class TestSelectiveSubscriber: StoreSubscriber { 474| | var receivedValue: (Int?, String?) 475| | 476| 0| func newState(state: (Int?, String?)) { 477| 0| receivedValue = state 478| 0| } 479| |} 480| | 481| |struct TestComplexAppState { 482| | var testValue: Int? 483| | var otherState: OtherState? 484| |} 485| | 486| |struct OtherState { 487| | var name: String? 488| | var age: Int? 489| |} 490| | 491| |struct TestComplexAppStateReducer { 492| 0| func handleAction(action: Action, state: TestComplexAppState?) -> TestComplexAppState { 493| 0| var state = state ?? TestComplexAppState() 494| 0| 495| 0| switch action { 496| 0| case let action as SetValueAction: 497| 0| state.testValue = action.value 498| 0| return state 499| 0| case let action as SetOtherStateAction: 500| 0| state.otherState = action.otherState 501| 0| default: 502| 0| break 503| 0| } 504| 0| 505| 0| return state 506| 0| } 507| |} 508| | 509| |struct SetOtherStateAction: Action { 510| | var otherState: OtherState 511| |} /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/StoreSubscriptionTests.swift: 1| |// 2| |// StoreSubscriptionTests.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 11/27/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |/** 11| | @testable import for testing of `Store.subscriptions` 12| | */ 13| |@testable import ReSwift 14| | 15| |class StoreSubscriptionTests: XCTestCase { 16| | 17| | typealias TestSubscriber = TestStoreSubscriber 18| | 19| | var store: Store! 20| | var reducer: TestReducer! 21| | 22| 0| override func setUp() { 23| 0| super.setUp() 24| 0| reducer = TestReducer() 25| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC5setUpyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0J7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC5setUpyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0J7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 26| 0| } 27| | 28| | /** 29| | It does not strongly capture an observer 30| | */ 31| 0| func testDoesNotCaptureStrongly() { 32| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC26testDoesNotCaptureStronglyyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0M7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC26testDoesNotCaptureStronglyyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0M7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 33| 0| var subscriber: TestSubscriber? = TestSubscriber() 34| 0| 35| 0| store.subscribe(subscriber!) 36| 0| XCTAssertEqual(store.subscriptions.compactMap({ $0.subscriber }).count, 1) 37| 0| // Ensure `subscriber` is accessed at least once to prevent it being optimised 38| 0| // away when tests are built using 'release' scheme. #459 refers. 39| 0| XCTAssertNotNil(subscriber) 40| 0| 41| 0| subscriber = nil 42| 0| XCTAssertEqual(store.subscriptions.compactMap({ $0.subscriber }).count, 0) 43| 0| } 44| | 45| | /** 46| | it removes deferenced subscribers before notifying state changes 47| | */ 48| 0| func testRemoveSubscribers() { 49| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC21testRemoveSubscribersyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0K7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC21testRemoveSubscribersyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0K7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 50| 0| var subscriber1: TestSubscriber? = TestSubscriber() 51| 0| var subscriber2: TestSubscriber? = TestSubscriber() 52| 0| 53| 0| store.subscribe(subscriber1!) 54| 0| store.subscribe(subscriber2!) 55| 0| store.dispatch(SetValueAction(3)) 56| 0| XCTAssertEqual(store.subscriptions.count, 2) 57| 0| XCTAssertEqual(subscriber1?.receivedStates.last?.testValue, 3) 58| 0| XCTAssertEqual(subscriber2?.receivedStates.last?.testValue, 3) 59| 0| 60| 0| subscriber1 = nil 61| 0| store.dispatch(SetValueAction(5)) 62| 0| XCTAssertEqual(store.subscriptions.count, 1) 63| 0| XCTAssertEqual(subscriber2?.receivedStates.last?.testValue, 5) 64| 0| 65| 0| subscriber2 = nil 66| 0| store.dispatch(SetValueAction(8)) 67| 0| XCTAssertEqual(store.subscriptions.count, 0) 68| 0| } 69| | 70| | /** 71| | it replaces the subscription of an existing subscriber with the new one. 72| | */ 73| 0| func testDuplicateSubscription() { 74| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC013testDuplicateF0yyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0J7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC013testDuplicateF0yyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0J7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 75| 0| let subscriber = TestSubscriber() 76| 0| 77| 0| // Initial subscription. 78| 0| store.subscribe(subscriber) 79| 0| // Subsequent subscription that skips repeated updates. 80| 0| store.subscribe(subscriber) { $0.skipRepeats { $0.testValue == $1.testValue } } 81| 0| 82| 0| // One initial state update for every subscription. 83| 0| XCTAssertEqual(subscriber.receivedStates.count, 2) 84| 0| 85| 0| store.dispatch(SetValueAction(3)) 86| 0| store.dispatch(SetValueAction(3)) 87| 0| store.dispatch(SetValueAction(3)) 88| 0| store.dispatch(SetValueAction(3)) 89| 0| 90| 0| // Only a single further state update, since latest subscription skips repeated values. 91| 0| XCTAssertEqual(subscriber.receivedStates.count, 3) 92| 0| } 93| | 94| | /** 95| | it dispatches initial value upon subscription 96| | */ 97| 0| func testDispatchInitialValue() { 98| 0| store = Store(reducer: reducer.handleAction, state: TestAppState(testValue: 7)) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC24testDispatchInitialValueyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0L7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC24testDispatchInitialValueyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0L7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 99| 0| let subscriber = TestSubscriber() 100| 0| 101| 0| store.subscribe(subscriber) 102| 0| 103| 0| XCTAssertEqual(subscriber.receivedStates.map(\.testValue), [7]) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC24testDispatchInitialValueyyFSaySiSgGyKXEfu1_AeA12TestAppStateVcs7KeyPathCyAhEGcfu2_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC24testDispatchInitialValueyyFSaySiSgGyKXEfu1_AeA12TestAppStateVcs7KeyPathCyAhEGcfu2_AeHcfu3_ ------------------ 104| 0| } 105| | 106| | /** 107| | it dispatches initial value upon subscription and subsequent state changes 108| | */ 109| 0| func testDispatchStateChanges() { 110| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC24testDispatchStateChangesyyFAA07TestAppJ0V0aB06Action_p_AFSgtcAA0L7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC24testDispatchStateChangesyyFAA07TestAppJ0V0aB06Action_p_AFSgtcAA0L7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 111| 0| let subscriber = TestSubscriber() 112| 0| 113| 0| store.subscribe(subscriber) 114| 0| store.dispatch(SetValueAction(9)) 115| 0| 116| 0| XCTAssertEqual(subscriber.receivedStates.map(\.testValue), [nil, 9]) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC24testDispatchStateChangesyyFSaySiSgGyKXEfu1_AeA07TestAppJ0Vcs7KeyPathCyAhEGcfu2_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC24testDispatchStateChangesyyFSaySiSgGyKXEfu1_AeA07TestAppJ0Vcs7KeyPathCyAhEGcfu2_AeHcfu3_ ------------------ 117| 0| } 118| | 119| | /** 120| | it dispatches initial value upon subscription and subsequent state changes 121| | */ 122| 0| func testDispatchInitialStateAfterStateChange() { 123| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC029testDispatchInitialStateAfterK6ChangeyyFAA07TestAppK0V0aB06Action_p_AFSgtcAA0N7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC029testDispatchInitialStateAfterK6ChangeyyFAA07TestAppK0V0aB06Action_p_AFSgtcAA0N7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 124| 0| let subscriber = TestSubscriber() 125| 0| 126| 0| // Change state first ... 127| 0| store.dispatch(SetValueAction(13)) 128| 0| // ... and then subscribe to receive the current state. 129| 0| store.subscribe(subscriber) 130| 0| 131| 0| XCTAssertEqual(subscriber.receivedStates.map(\.testValue), [13]) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC029testDispatchInitialStateAfterK6ChangeyyFSaySiSgGyKXEfu1_AeA07TestAppK0Vcs7KeyPathCyAhEGcfu2_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC029testDispatchInitialStateAfterK6ChangeyyFSaySiSgGyKXEfu1_AeA07TestAppK0Vcs7KeyPathCyAhEGcfu2_AeHcfu3_ ------------------ 132| 0| } 133| | 134| | /** 135| | it allows dispatching from within an observer 136| | */ 137| 0| func testAllowDispatchWithinObserver() { 138| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC31testAllowDispatchWithinObserveryyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0M7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC31testAllowDispatchWithinObserveryyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0M7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 139| 0| let subscriber = DispatchingSubscriber(store: store) 140| 0| 141| 0| store.subscribe(subscriber) 142| 0| store.dispatch(SetValueAction(2)) 143| 0| 144| 0| XCTAssertEqual(store.state.testValue, 5) 145| 0| } 146| | 147| | /** 148| | it does not dispatch value after subscriber unsubscribes 149| | */ 150| 0| func testDontDispatchToUnsubscribers() { 151| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC31testDontDispatchToUnsubscribersyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0M7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC31testDontDispatchToUnsubscribersyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0M7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 152| 0| let subscriber = TestSubscriber() 153| 0| 154| 0| store.dispatch(SetValueAction(5)) 155| 0| store.subscribe(subscriber) 156| 0| store.dispatch(SetValueAction(10)) 157| 0| 158| 0| store.unsubscribe(subscriber) 159| 0| // Following value is missed due to not being subscribed: 160| 0| store.dispatch(SetValueAction(15)) 161| 0| store.dispatch(SetValueAction(25)) 162| 0| 163| 0| store.subscribe(subscriber) 164| 0| 165| 0| store.dispatch(SetValueAction(20)) 166| 0| 167| 0| XCTAssertEqual(subscriber.receivedStates.count, 4) 168| 0| XCTAssertEqual(subscriber.receivedStates[subscriber.receivedStates.count - 4].testValue, 5) 169| 0| XCTAssertEqual(subscriber.receivedStates[subscriber.receivedStates.count - 3].testValue, 10) 170| 0| XCTAssertEqual(subscriber.receivedStates[subscriber.receivedStates.count - 2].testValue, 25) 171| 0| XCTAssertEqual(subscriber.receivedStates[subscriber.receivedStates.count - 1].testValue, 20) 172| 0| } 173| | 174| | /** 175| | it ignores identical subscribers 176| | */ 177| 0| func testIgnoreIdenticalSubscribers() { 178| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC30testIgnoreIdenticalSubscribersyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0L7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC30testIgnoreIdenticalSubscribersyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0L7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 179| 0| let subscriber = TestSubscriber() 180| 0| 181| 0| store.subscribe(subscriber) 182| 0| store.subscribe(subscriber) 183| 0| 184| 0| XCTAssertEqual(store.subscriptions.count, 1) 185| 0| } 186| | 187| | /** 188| | it ignores identical subscribers that provide substate selectors 189| | */ 190| 0| func testIgnoreIdenticalSubstateSubscribers() { 191| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC38testIgnoreIdenticalSubstateSubscribersyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0M7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC38testIgnoreIdenticalSubstateSubscribersyyFAA12TestAppStateV0aB06Action_p_AFSgtcAA0M7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 192| 0| let subscriber = TestSubscriber() 193| 0| 194| 0| store.subscribe(subscriber) { $0 } 195| 0| store.subscribe(subscriber) { $0 } 196| 0| 197| 0| XCTAssertEqual(store.subscriptions.count, 1) 198| 0| } 199| | 200| 0| func testNewStateModifyingSubscriptionsDoesNotDiscardNewSubscription() { 201| 0| // This was built as a failing test due to a bug introduced by #325 202| 0| // The bug occured by adding a subscriber during `newState` 203| 0| // The bug was caused by creating a copy of `subscriptions` before calling 204| 0| // `newState`, and then assigning that copy back to `subscriptions`, losing 205| 0| // the mutation that occured during `newState` 206| 0| 207| 0| store = Store(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC048testNewStateModifyingSubscriptionsDoesNotDiscardiF0yyFAA07TestAppJ0V0aB06Action_p_AFSgtcAA0P7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC048testNewStateModifyingSubscriptionsDoesNotDiscardiF0yyFAA07TestAppJ0V0aB06Action_p_AFSgtcAA0P7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 208| 0| 209| 0| let subscriber2 = BlockSubscriber { _ in 210| 0| self.store.dispatch(SetValueAction(2)) 211| 0| } 212| 0| 213| 0| let subscriber1 = BlockSubscriber { [unowned self] state in 214| 0| if state.testValue == 1 { 215| 0| self.store.subscribe(subscriber2) { 216| 0| $0.skip(when: { _, _ in return true }) 217| 0| } 218| 0| } 219| 0| } 220| 0| 221| 0| store.subscribe(subscriber1) { 222| 0| $0.only(when: { _, new in new.testValue.map { $0 == 1 } ?? false }) 223| 0| } 224| 0| 225| 0| store.dispatch(SetValueAction(1)) 226| 0| 227| 0| XCTAssertTrue(store.subscriptions.contains(where: { 228| 0| guard let subscriber = $0.subscriber else { 229| 0| XCTFail("expecting non-nil subscriber") 230| 0| return false 231| 0| } 232| 0| return subscriber === subscriber1 233| 0| })) 234| 0| XCTAssertTrue(store.subscriptions.contains(where: { 235| 0| guard let subscriber = $0.subscriber else { 236| 0| XCTFail("expecting non-nil subscriber") 237| 0| return false 238| 0| } 239| 0| return subscriber === subscriber2 240| 0| })) 241| 0| 242| 0| // Have a subscriber (#1) 243| 0| // #1 adds sub #2 in newState 244| 0| // #1 dispatches in newState 245| 0| // Test that store.subscribers == [#1, #2] // this should fail 246| 0| } 247| |} 248| | 249| |// MARK: Retain Cycle Detection 250| | 251| |private struct TracerAction: Action { } 252| | 253| |private class TestSubscriptionBox: SubscriptionBox { 254| | override init( 255| | originalSubscription: Subscription, 256| | transformedSubscription: Subscription?, 257| | subscriber: AnyStoreSubscriber 258| 0| ) { 259| 0| super.init(originalSubscription: originalSubscription, 260| 0| transformedSubscription: transformedSubscription, 261| 0| subscriber: subscriber) 262| 0| } 263| | 264| | var didDeinit: (() -> Void)? 265| 0| deinit { 266| 0| didDeinit?() 267| 0| } 268| |} 269| | 270| |private class TestStore: Store { 271| | override func subscriptionBox( 272| | originalSubscription: Subscription, 273| | transformedSubscription: Subscription?, 274| 0| subscriber: AnyStoreSubscriber) -> SubscriptionBox { 275| 0| return TestSubscriptionBox( 276| 0| originalSubscription: originalSubscription, 277| 0| transformedSubscription: transformedSubscription, 278| 0| subscriber: subscriber 279| 0| ) 280| 0| } 281| |} 282| | 283| |extension StoreSubscriptionTests { 284| | 285| 0| func testRetainCycle_OriginalSubscription() { 286| 0| 287| 0| var didDeinit = false 288| 0| 289| 0| autoreleasepool { 290| 0| 291| 0| store = TestStore(reducer: reducer.handleAction, state: TestAppState()) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC024testRetainCycle_OriginalF0yyFyyXEfU_AA12TestAppStateV0aB06Action_p_AFSgtcAA0L7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC024testRetainCycle_OriginalF0yyFyyXEfU_AA12TestAppStateV0aB06Action_p_AFSgtcAA0L7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 292| 0| let subscriber: TestSubscriber = TestSubscriber() 293| 0| 294| 0| // Preconditions 295| 0| XCTAssertEqual(subscriber.receivedStates.count, 0) 296| 0| XCTAssertEqual(store.subscriptions.count, 0) 297| 0| 298| 0| autoreleasepool { 299| 0| 300| 0| store.subscribe(subscriber) 301| 0| XCTAssertEqual(subscriber.receivedStates.count, 1) 302| 0| let subscriptionBox = store.subscriptions.first! as! TestSubscriptionBox 303| 0| subscriptionBox.didDeinit = { didDeinit = true } 304| 0| 305| 0| store.dispatch(TracerAction()) 306| 0| XCTAssertEqual(subscriber.receivedStates.count, 2) 307| 0| store.unsubscribe(subscriber) 308| 0| } 309| 0| 310| 0| XCTAssertEqual(store.subscriptions.count, 0) 311| 0| store.dispatch(TracerAction()) 312| 0| XCTAssertEqual(subscriber.receivedStates.count, 2) 313| 0| 314| 0| store = nil 315| 0| } 316| 0| 317| 0| XCTAssertTrue(didDeinit) 318| 0| } 319| | 320| 0| func testRetainCycle_TransformedSubscription() { 321| 0| 322| 0| var didDeinit = false 323| 0| 324| 0| autoreleasepool { 325| 0| 326| 0| store = TestStore(reducer: reducer.handleAction, state: TestAppState(), automaticallySkipsRepeats: false) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC027testRetainCycle_TransformedF0yyFyyXEfU_AA12TestAppStateV0aB06Action_p_AFSgtcAA0L7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests22StoreSubscriptionTestsC027testRetainCycle_TransformedF0yyFyyXEfU_AA12TestAppStateV0aB06Action_p_AFSgtcAA0L7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 327| 0| let subscriber = TestStoreSubscriber() 328| 0| 329| 0| // Preconditions 330| 0| XCTAssertEqual(subscriber.receivedStates.count, 0) 331| 0| XCTAssertEqual(store.subscriptions.count, 0) 332| 0| 333| 0| autoreleasepool { 334| 0| 335| 0| store.subscribe(subscriber, transform: { 336| 0| $0.select { $0.testValue } 337| 0| }) 338| 0| XCTAssertEqual(subscriber.receivedStates.count, 1) 339| 0| let subscriptionBox = store.subscriptions.first! as! TestSubscriptionBox 340| 0| subscriptionBox.didDeinit = { didDeinit = true } 341| 0| 342| 0| store.dispatch(TracerAction()) 343| 0| XCTAssertEqual(subscriber.receivedStates.count, 2) 344| 0| store.unsubscribe(subscriber) 345| 0| } 346| 0| 347| 0| XCTAssertEqual(store.subscriptions.count, 0) 348| 0| store.dispatch(TracerAction()) 349| 0| XCTAssertEqual(subscriber.receivedStates.count, 2) 350| 0| 351| 0| store = nil 352| 0| } 353| 0| 354| 0| XCTAssertTrue(didDeinit) 355| 0| } 356| |} /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/StoreTests.swift: 1| |// 2| |// StoreTests.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 11/27/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import ReSwift 11| | 12| |class StoreTests: XCTestCase { 13| | 14| | /** 15| | it dispatches an Init action when it doesn't receive an initial state 16| | */ 17| 0| func testInit() { 18| 0| let reducer = MockReducer() 19| 0| _ = Store(reducer: reducer.handleAction, state: nil) ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests10StoreTestsC8testInityyFAA12CounterStateV0aB06Action_p_AFSgtcAA11MockReducerCcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests10StoreTestsC8testInityyFAA12CounterStateV0aB06Action_p_AFSgtcAA11MockReducerCcfu_AfgH_p_AItcfu0_ ------------------ 20| 0| 21| 0| XCTAssert(reducer.calledWithAction[0] is ReSwiftInit) 22| 0| } 23| | 24| | /** 25| | it deinitializes when no reference is held 26| | */ 27| 0| func testDeinit() { 28| 0| var deInitCount = 0 29| 0| 30| 0| autoreleasepool { 31| 0| let reducer = TestReducer() 32| 0| _ = DeInitStore( 33| 0| reducer: reducer.handleAction, ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests10StoreTestsC10testDeinityyFyyXEfU_AA12TestAppStateV0aB06Action_p_AFSgtcAA0I7ReducerVcfu_ ------------------ | Unexecuted instantiation: $s18ReSwift_macOSTests10StoreTestsC10testDeinityyFyyXEfU_AA12TestAppStateV0aB06Action_p_AFSgtcAA0I7ReducerVcfu_AfgH_p_AItcfu0_ ------------------ 34| 0| state: TestAppState(), 35| 0| deInitAction: { deInitCount += 1 }) 36| 0| } 37| 0| 38| 0| XCTAssertEqual(deInitCount, 1) 39| 0| } 40| | 41| |} 42| | 43| |// Used for deinitialization test 44| |class DeInitStore: Store { 45| | var deInitAction: (() -> Void)? 46| | 47| 0| deinit { 48| 0| deInitAction?() 49| 0| } 50| | 51| | required convenience init( 52| | reducer: @escaping Reducer, 53| | state: State?, 54| 0| deInitAction: (() -> Void)?) { 55| 0| self.init( 56| 0| reducer: reducer, 57| 0| state: state, 58| 0| middleware: [], 59| 0| automaticallySkipsRepeats: false) 60| 0| self.deInitAction = deInitAction 61| 0| } 62| | 63| | required init( 64| | reducer: @escaping Reducer, 65| | state: State?, 66| | middleware: [Middleware], 67| 0| automaticallySkipsRepeats: Bool) { 68| 0| super.init( 69| 0| reducer: reducer, 70| 0| state: state, 71| 0| middleware: middleware, 72| 0| automaticallySkipsRepeats: automaticallySkipsRepeats) 73| 0| } 74| |} 75| | 76| |struct CounterState { 77| | var count: Int = 0 78| |} 79| | 80| |class MockReducer { 81| | 82| 0| var calledWithAction: [Action] = [] 83| | 84| 0| func handleAction(action: Action, state: CounterState?) -> CounterState { 85| 0| calledWithAction.append(action) 86| 0| 87| 0| return state ?? CounterState() 88| 0| } 89| | 90| |} /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/SynchronizedTests.swift: 1| |// 2| |// SynchronizedTests.swift 3| |// ReSwift 4| |// 5| |// Created by Basem Emara on 2020-08-18. 6| |// Copyright © 2020 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |/** 11| | @testable import for testing of `Utils.Synchronized` 12| | */ 13| |@testable import ReSwift 14| | 15| |class SynchronizedTests: XCTestCase { 16| | private let iterations = 100 // 1_000_000 17| | private let writeMultipleOf = 10 // 1000 18| |} 19| | 20| |extension SynchronizedTests { 21| 0| func testSharedVariable() { 22| 0| DispatchQueue.concurrentPerform(iterations: iterations) { _ in 23| 0| Database.shared.set(key: "test", value: true) 24| 0| } 25| 0| } 26| | private class Database { 27| | static let shared = Database() 28| 0| private var data = Synchronized<[String: Any]>([:]) 29| 0| func get(key: String) -> Any? { 30| 0| return data.value { $0[key] } 31| 0| } 32| 0| func set(key: String, value: Any) { 33| 0| data.value { $0[key] = value } 34| 0| } 35| | } 36| |} 37| | 38| |extension SynchronizedTests { 39| 0| func testWritePerformance() { 40| 0| var temp = Synchronized(0) 41| 0| measure { 42| 0| temp.value { $0 = 0 } // Reset 43| 0| DispatchQueue.concurrentPerform(iterations: iterations) { _ in 44| 0| temp.value { $0 += 1 } 45| 0| } 46| 0| XCTAssertEqual(temp.value, iterations) 47| 0| } 48| 0| } 49| |} 50| | 51| |extension SynchronizedTests { 52| 0| func testReadPerformance() { 53| 0| var temp = Synchronized(0) 54| 0| measure { 55| 0| temp.value { $0 = 0 } // Reset 56| 0| DispatchQueue.concurrentPerform(iterations: iterations) { 57| 0| guard $0 % writeMultipleOf != 0 else { return } 58| 0| temp.value { $0 += 1 } 59| 0| } 60| 0| XCTAssertGreaterThanOrEqual(temp.value, iterations / writeMultipleOf) 61| 0| } 62| 0| } 63| |} /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/TestFakes.swift: 1| |// 2| |// TestFakes.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 12/24/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import Foundation 10| |import ReSwift 11| | 12| |struct TestAppState { 13| | var testValue: Int? 14| | 15| 0| init(testValue: Int? = nil) { 16| 0| self.testValue = testValue 17| 0| } 18| |} 19| | 20| |struct TestStringAppState { 21| | var testValue: String 22| | 23| 0| init() { 24| 0| testValue = "Initial" 25| 0| } 26| |} 27| | 28| |extension TestStringAppState: Equatable { 29| 0| static func == (lhs: TestStringAppState, rhs: TestStringAppState) -> Bool { 30| 0| return lhs.testValue == rhs.testValue 31| 0| } 32| |} 33| | 34| |struct TestNonEquatable { 35| | var testValue: NonEquatable 36| | 37| 0| init() { 38| 0| testValue = NonEquatable() 39| 0| } 40| |} 41| | 42| |struct NonEquatable { 43| | var testValue: String 44| | 45| 0| init() { 46| 0| testValue = "Initial" 47| 0| } 48| |} 49| | 50| |struct TestCustomAppState { 51| | var substate: TestCustomSubstate 52| | 53| 0| init(substate: TestCustomSubstate) { 54| 0| self.substate = substate 55| 0| } 56| | 57| 0| init(substateValue value: Int = 0) { 58| 0| self.substate = TestCustomSubstate(value: value) 59| 0| } 60| | 61| | struct TestCustomSubstate { 62| | var value: Int 63| | } 64| |} 65| | 66| |struct NoOpAction: Action {} 67| | 68| |struct SetValueAction: Action { 69| | 70| | let value: Int? 71| | static let type = "SetValueAction" 72| | 73| 0| init (_ value: Int?) { 74| 0| self.value = value 75| 0| } 76| |} 77| | 78| |struct SetValueStringAction: Action { 79| | 80| | var value: String 81| | static let type = "SetValueStringAction" 82| | 83| 0| init (_ value: String) { 84| 0| self.value = value 85| 0| } 86| |} 87| | 88| |struct SetCustomSubstateAction: Action { 89| | 90| | var value: Int 91| | static let type = "SetCustomSubstateAction" 92| | 93| 0| init (_ value: Int) { 94| 0| self.value = value 95| 0| } 96| |} 97| | 98| |struct SetNonEquatableAction: Action { 99| | var value: NonEquatable 100| | static let type = "SetNonEquatableAction" 101| | 102| 0| init (_ value: NonEquatable) { 103| 0| self.value = value 104| 0| } 105| |} 106| | 107| |struct TestReducer { 108| 0| func handleAction(action: Action, state: TestAppState?) -> TestAppState { 109| 0| var state = state ?? TestAppState() 110| 0| 111| 0| switch action { 112| 0| case let action as SetValueAction: 113| 0| state.testValue = action.value 114| 0| return state 115| 0| default: 116| 0| return state 117| 0| } 118| 0| } 119| |} 120| | 121| |struct TestValueStringReducer { 122| 0| func handleAction(action: Action, state: TestStringAppState?) -> TestStringAppState { 123| 0| var state = state ?? TestStringAppState() 124| 0| 125| 0| switch action { 126| 0| case let action as SetValueStringAction: 127| 0| state.testValue = action.value 128| 0| return state 129| 0| default: 130| 0| return state 131| 0| } 132| 0| } 133| |} 134| | 135| |struct TestCustomAppStateReducer { 136| 0| func handleAction(action: Action, state: TestCustomAppState?) -> TestCustomAppState { 137| 0| var state = state ?? TestCustomAppState() 138| 0| 139| 0| switch action { 140| 0| case let action as SetCustomSubstateAction: 141| 0| state.substate.value = action.value 142| 0| return state 143| 0| default: 144| 0| return state 145| 0| } 146| 0| } 147| |} 148| | 149| |struct TestNonEquatableReducer { 150| | func handleAction(action: Action, state: TestNonEquatable?) -> 151| 0| TestNonEquatable { 152| 0| var state = state ?? TestNonEquatable() 153| 0| 154| 0| switch action { 155| 0| case let action as SetNonEquatableAction: 156| 0| state.testValue = action.value 157| 0| return state 158| 0| default: 159| 0| return state 160| 0| } 161| 0| } 162| |} 163| | 164| |class TestStoreSubscriber: StoreSubscriber { 165| 0| var receivedStates: [T] = [] 166| | 167| 0| func newState(state: T) { 168| 0| receivedStates.append(state) 169| 0| } 170| |} 171| | 172| |class BlockSubscriber: StoreSubscriber { 173| | typealias StoreSubscriberStateType = S 174| | private let block: (S) -> Void 175| | 176| 0| init(block: @escaping (S) -> Void) { 177| 0| self.block = block 178| 0| } 179| | 180| 0| func newState(state: S) { 181| 0| self.block(state) 182| 0| } 183| |} 184| | 185| |class DispatchingSubscriber: StoreSubscriber { 186| | var store: Store 187| | 188| 0| init(store: Store) { 189| 0| self.store = store 190| 0| } 191| | 192| 0| func newState(state: TestAppState) { 193| 0| // Test if we've already dispatched this action to 194| 0| // avoid endless recursion 195| 0| if state.testValue != 5 { 196| 0| self.store.dispatch(SetValueAction(5)) 197| 0| } 198| 0| } 199| |} 200| | 201| |class CallbackStoreSubscriber: StoreSubscriber { 202| | 203| | let handler: (T) -> Void 204| | 205| 0| init(handler: @escaping (T) -> Void) { 206| 0| self.handler = handler 207| 0| } 208| | 209| 0| func newState(state: T) { 210| 0| handler(state) 211| 0| } 212| |} /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/TypeHelperTests.swift: 1| |// 2| |// TypeHelperTests.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 12/20/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |/** 11| | @testable import for testing of `withSpecificTypes` 12| | */ 13| |@testable import ReSwift 14| | 15| |struct AppState1 {} 16| |struct AppState2 {} 17| | 18| |class TypeHelperTests: XCTestCase { 19| | 20| | /** 21| | it calls methods if the source type can be casted into the function signature type 22| | */ 23| 0| func testSourceTypeCasting() { 24| 0| var called = false 25| 0| let reducerFunction: (Action, AppState1?) -> AppState1 = { action, state in 26| 0| called = true 27| 0| 28| 0| return state ?? AppState1() 29| 0| } 30| 0| 31| 0| withSpecificTypes(NoOpAction(), state: AppState1(), function: reducerFunction) 32| 0| 33| 0| XCTAssertTrue(called) 34| 0| } 35| | 36| | /** 37| | it calls the method if the source type is nil 38| | */ 39| 0| func testCallsIfSourceTypeIsNil() { 40| 0| var called = false 41| 0| let reducerFunction: (Action, AppState1?) -> AppState1 = { action, state in 42| 0| called = true 43| 0| 44| 0| return state ?? AppState1() 45| 0| } 46| 0| 47| 0| withSpecificTypes(NoOpAction(), state: nil, function: reducerFunction) 48| 0| 49| 0| XCTAssertTrue(called) 50| 0| } 51| | 52| | /** 53| | it doesn't call if source type can't be casted to function signature type 54| | */ 55| 0| func testDoesntCallIfCastFails() { 56| 0| var called = false 57| 0| let reducerFunction: (Action, AppState1?) -> AppState1 = { action, state in 58| 0| called = true 59| 0| 60| 0| return state ?? AppState1() 61| 0| } 62| 0| 63| 0| withSpecificTypes(NoOpAction(), state: AppState2(), function: reducerFunction) 64| 0| 65| 0| XCTAssertFalse(called) 66| 0| } 67| |} /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/XCTest+Assertions.swift: 1| |// 2| |// Assertions 3| |// Copyright © 2015 mohamede1945. All rights reserved. 4| |// https://github.com/mohamede1945/AssertionsTestingExample 5| |// 6| | 7| |import Foundation 8| |import XCTest 9| |/** 10| | @testable import for testing of `Assertions.fatalErrorClosure` 11| | */ 12| |@testable import ReSwift 13| | 14| |private let noReturnFailureWaitTime = 0.1 15| | 16| |public extension XCTestCase { 17| | /** 18| | Expects an `fatalError` to be called. 19| | If `fatalError` not called, the test case will fail. 20| | 21| | - parameter expectedMessage: The expected message to be asserted to the one passed to the 22| | `fatalError`. If nil, then ignored. 23| | - parameter file: The file name that called the method. 24| | - parameter line: The line number that called the method. 25| | - parameter testCase: The test case to be executed that expected to fire the assertion 26| | method. 27| | */ 28| | func expectFatalError(expectedMessage: String? = nil, file: StaticString = #file, 29| 0| line: UInt = #line, testCase: @escaping () -> Void) { 30| 0| expectAssertionNoReturnFunction( 31| 0| functionName: "fatalError", 32| 0| file: file, 33| 0| line: line, 34| 0| function: { (caller: @escaping (String) -> Void) -> Void in 35| 0| Assertions.fatalErrorClosure = { message, _, _ in caller(message) } 36| 0| }, 37| 0| expectedMessage: expectedMessage, 38| 0| testCase: testCase, 39| 0| cleanUp: { 40| 0| Assertions.fatalErrorClosure = Assertions.swiftFatalErrorClosure 41| 0| }) 42| 0| } 43| | 44| | // MARK: Private Methods 45| | 46| | // swiftlint:disable function_parameter_count 47| | private func expectAssertionNoReturnFunction( 48| | functionName funcName: String, 49| | file: StaticString, 50| | line: UInt, 51| | function: (_ caller: @escaping (String) -> Void) -> Void, 52| | expectedMessage: String? = nil, 53| | testCase: @escaping () -> Void, 54| 0| cleanUp: @escaping () -> Void) { 55| 0| 56| 0| let asyncExpectation = futureExpectation(withDescription: funcName + "-Expectation") 57| 0| var assertionMessage: String? 58| 0| 59| 0| function { (message) -> Void in 60| 0| assertionMessage = message 61| 0| asyncExpectation.fulfill() 62| 0| } 63| 0| 64| 0| // act, perform on separate thread because a call to function runs forever 65| 0| dispatchUserInitiatedAsync(execute: testCase) 66| 0| 67| 0| waitForFutureExpectations(withTimeout: noReturnFailureWaitTime) { _ in 68| 0| defer { cleanUp() } 69| 0| guard let assertionMessage = assertionMessage else { 70| 0| XCTFail(funcName + " is expected to be called.", file: file, line: line) 71| 0| return 72| 0| } 73| 0| if let expectedMessage = expectedMessage { 74| 0| XCTAssertEqual(assertionMessage, expectedMessage, funcName + 75| 0| " called with incorrect message.", file: file, line: line) 76| 0| } 77| 0| } 78| 0| } 79| | // swiftlint:enable function_parameter_count 80| |} /Users/runner/work/ReSwift/ReSwift/ReSwiftTests/XCTest+Compatibility.swift: 1| |// Copyright © 2019 ReSwift Community. All rights reserved. 2| | 3| |import XCTest 4| | 5| 0|func dispatchAsync(execute work: @escaping @convention(block) () -> Swift.Void) { 6| 0| DispatchQueue.global(qos: .default).async(execute: work) 7| 0|} 8| | 9| |func dispatchUserInitiatedAsync 10| 0| (execute work: @escaping @convention(block) () -> Swift.Void) { 11| 0| DispatchQueue.global(qos: .userInitiated).async(execute: work) 12| 0|} 13| | 14| |extension XCTestCase { 15| | 16| 0| func futureExpectation(withDescription description: String) -> XCTestExpectation { 17| 0| return expectation(description: description) 18| 0| } 19| | 20| | func waitForFutureExpectations( 21| | withTimeout timeout: TimeInterval, 22| 0| handler: XCWaitCompletionHandler? = nil) { 23| 0| 24| 0| waitForExpectations(timeout: timeout, handler: handler) 25| 0| } 26| |} <<<<<< EOF # path=.xcodecov/ReSwift.framework.coverage.txt /Users/runner/work/ReSwift/ReSwift/ReSwift/CoreTypes/Store.swift: 1| |// 2| |// Store.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 11/11/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |/** 10| | This class is the default implementation of the `StoreType` protocol. You will use this store in most 11| | of your applications. You shouldn't need to implement your own store. 12| | You initialize the store with a reducer and an initial application state. If your app has multiple 13| | reducers you can combine them by initializing a `MainReducer` with all of your reducers as an 14| | argument. 15| | */ 16| |open class Store: StoreType { 17| | 18| | typealias SubscriptionType = SubscriptionBox 19| | 20| | private(set) public var state: State! { 21| 0| didSet { 22| 0| subscriptions.forEach { 23| 0| if $0.subscriber == nil { 24| 0| subscriptions.remove($0) 25| 0| } else { 26| 0| $0.newValues(oldState: oldValue, newState: state) 27| 0| } 28| 0| } 29| 0| } 30| | } 31| | 32| | public lazy var dispatchFunction: DispatchFunction! = createDispatchFunction() 33| | 34| | private var reducer: Reducer 35| | 36| 0| var subscriptions: Set = [] 37| | 38| 0| private var isDispatching = Synchronized(false) 39| | 40| | /// Indicates if new subscriptions attempt to apply `skipRepeats` 41| | /// by default. 42| | fileprivate let subscriptionsAutomaticallySkipRepeats: Bool 43| | 44| | public var middleware: [Middleware] { 45| 0| didSet { 46| 0| dispatchFunction = createDispatchFunction() 47| 0| } 48| | } 49| | 50| | /// Initializes the store with a reducer, an initial state and a list of middleware. 51| | /// 52| | /// Middleware is applied in the order in which it is passed into this constructor. 53| | /// 54| | /// - parameter reducer: Main reducer that processes incoming actions. 55| | /// - parameter state: Initial state, if any. Can be `nil` and will be 56| | /// provided by the reducer in that case. 57| | /// - parameter middleware: Ordered list of action pre-processors, acting 58| | /// before the root reducer. 59| | /// - parameter automaticallySkipsRepeats: If `true`, the store will attempt 60| | /// to skip idempotent state updates when a subscriber's state type 61| | /// implements `Equatable`. Defaults to `true`. 62| | public required init( 63| | reducer: @escaping Reducer, 64| | state: State?, 65| | middleware: [Middleware] = [], 66| | automaticallySkipsRepeats: Bool = true 67| 0| ) { 68| 0| self.subscriptionsAutomaticallySkipRepeats = automaticallySkipsRepeats 69| 0| self.reducer = reducer 70| 0| self.middleware = middleware 71| 0| 72| 0| if let state = state { 73| 0| self.state = state 74| 0| } else { 75| 0| dispatch(ReSwiftInit()) 76| 0| } 77| 0| } 78| | 79| 0| private func createDispatchFunction() -> DispatchFunction! { 80| 0| // Wrap the dispatch function with all middlewares 81| 0| return middleware 82| 0| .reversed() 83| 0| .reduce( 84| 0| { [unowned self] action in 85| 0| self._defaultDispatch(action: action) }, 86| 0| { dispatchFunction, middleware in 87| 0| // If the store get's deinitialized before the middleware is complete; drop 88| 0| // the action without dispatching. 89| 0| let dispatch: (Action) -> Void = { [weak self] in self?.dispatch($0) } 90| 0| let getState: () -> State? = { [weak self] in self?.state } 91| 0| return middleware(dispatch, getState)(dispatchFunction) 92| 0| }) 93| 0| } 94| | 95| | fileprivate func _subscribe( 96| | _ subscriber: S, originalSubscription: Subscription, 97| | transformedSubscription: Subscription?) 98| | where S.StoreSubscriberStateType == SelectedState 99| 0| { 100| 0| let subscriptionBox = self.subscriptionBox( 101| 0| originalSubscription: originalSubscription, 102| 0| transformedSubscription: transformedSubscription, 103| 0| subscriber: subscriber 104| 0| ) 105| 0| 106| 0| subscriptions.update(with: subscriptionBox) 107| 0| 108| 0| if let state = self.state { 109| 0| originalSubscription.newValues(oldState: nil, newState: state) 110| 0| } 111| 0| } 112| | 113| | open func subscribe(_ subscriber: S) 114| 0| where S.StoreSubscriberStateType == State { 115| 0| subscribe(subscriber, transform: nil) 116| 0| } 117| | 118| | open func subscribe( 119| | _ subscriber: S, transform: ((Subscription) -> Subscription)? 120| | ) where S.StoreSubscriberStateType == SelectedState 121| 0| { 122| 0| // Create a subscription for the new subscriber. 123| 0| let originalSubscription = Subscription() 124| 0| // Call the optional transformation closure. This allows callers to modify 125| 0| // the subscription, e.g. in order to subselect parts of the store's state. 126| 0| let transformedSubscription = transform?(originalSubscription) 127| 0| 128| 0| _subscribe(subscriber, originalSubscription: originalSubscription, 129| 0| transformedSubscription: transformedSubscription) 130| 0| } 131| | 132| | func subscriptionBox( 133| | originalSubscription: Subscription, 134| | transformedSubscription: Subscription?, 135| | subscriber: AnyStoreSubscriber 136| 0| ) -> SubscriptionBox { 137| 0| 138| 0| return SubscriptionBox( 139| 0| originalSubscription: originalSubscription, 140| 0| transformedSubscription: transformedSubscription, 141| 0| subscriber: subscriber 142| 0| ) 143| 0| } 144| | 145| 0| open func unsubscribe(_ subscriber: AnyStoreSubscriber) { 146| 0| #if swift(>=5.0) 147| 0| if let index = subscriptions.firstIndex(where: { return $0.subscriber === subscriber }) { 148| 0| subscriptions.remove(at: index) 149| 0| } 150| 0| #else 151| 0| if let index = subscriptions.index(where: { return $0.subscriber === subscriber }) { 152| 0| subscriptions.remove(at: index) 153| 0| } 154| 0| #endif 155| 0| } 156| | 157| | // swiftlint:disable:next identifier_name 158| 0| open func _defaultDispatch(action: Action) { 159| 0| guard !isDispatching.value else { 160| 0| raiseFatalError( 161| 0| "ReSwift:ConcurrentMutationError- Action has been dispatched while" + 162| 0| " a previous action is being processed. A reducer" + 163| 0| " is dispatching an action, or ReSwift is used in a concurrent context" + 164| 0| " (e.g. from multiple threads). Action: \(action)" 165| 0| ) 166| 0| } 167| 0| 168| 0| isDispatching.value { $0 = true } 169| 0| let newState = reducer(action, state) 170| 0| isDispatching.value { $0 = false } 171| 0| 172| 0| state = newState 173| 0| } 174| | 175| 0| open func dispatch(_ action: Action) { 176| 0| dispatchFunction(action) 177| 0| } 178| | 179| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 180| 0| open func dispatch(_ actionCreatorProvider: @escaping ActionCreator) { 181| 0| if let action = actionCreatorProvider(state, self) { 182| 0| dispatch(action) 183| 0| } 184| 0| } 185| | 186| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 187| 0| open func dispatch(_ asyncActionCreatorProvider: @escaping AsyncActionCreator) { 188| 0| dispatch(asyncActionCreatorProvider, callback: nil) 189| 0| } 190| | 191| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 192| | open func dispatch(_ actionCreatorProvider: @escaping AsyncActionCreator, 193| 0| callback: DispatchCallback?) { 194| 0| actionCreatorProvider(state, self) { actionProvider in 195| 0| let action = actionProvider(self.state, self) 196| 0| 197| 0| if let action = action { 198| 0| self.dispatch(action) 199| 0| callback?(self.state) 200| 0| } 201| 0| } 202| 0| } 203| | 204| | public typealias DispatchCallback = (State) -> Void 205| | 206| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 207| | public typealias ActionCreator = (_ state: State, _ store: Store) -> Action? 208| | 209| | @available(*, deprecated, message: "Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk") 210| | public typealias AsyncActionCreator = ( 211| | _ state: State, 212| | _ store: Store, 213| | _ actionCreatorCallback: @escaping ((ActionCreator) -> Void) 214| | ) -> Void 215| |} 216| | 217| |// MARK: Skip Repeats for Equatable States 218| | 219| |extension Store { 220| | open func subscribe( 221| | _ subscriber: S, transform: ((Subscription) -> Subscription)? 222| | ) where S.StoreSubscriberStateType == SelectedState 223| 0| { 224| 0| let originalSubscription = Subscription() 225| 0| 226| 0| var transformedSubscription = transform?(originalSubscription) 227| 0| if subscriptionsAutomaticallySkipRepeats { 228| 0| transformedSubscription = transformedSubscription?.skipRepeats() 229| 0| } 230| 0| _subscribe(subscriber, originalSubscription: originalSubscription, 231| 0| transformedSubscription: transformedSubscription) 232| 0| } 233| |} 234| | 235| |extension Store where State: Equatable { 236| | open func subscribe(_ subscriber: S) 237| 0| where S.StoreSubscriberStateType == State { 238| 0| guard subscriptionsAutomaticallySkipRepeats else { 239| 0| subscribe(subscriber, transform: nil) 240| 0| return 241| 0| } 242| 0| subscribe(subscriber, transform: { $0.skipRepeats() }) 243| 0| } 244| |} /Users/runner/work/ReSwift/ReSwift/ReSwift/CoreTypes/StoreSubscriber.swift: 1| |// 2| |// StoreSubscriber.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 12/14/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |public protocol AnyStoreSubscriber: AnyObject { 10| | // swiftlint:disable:next identifier_name 11| | func _newState(state: Any) 12| |} 13| | 14| |public protocol StoreSubscriber: AnyStoreSubscriber { 15| | associatedtype StoreSubscriberStateType 16| | 17| | func newState(state: StoreSubscriberStateType) 18| |} 19| | 20| |extension StoreSubscriber { 21| | // swiftlint:disable:next identifier_name 22| 0| public func _newState(state: Any) { 23| 0| if let typedState = state as? StoreSubscriberStateType { 24| 0| newState(state: typedState) 25| 0| } 26| 0| } 27| |} /Users/runner/work/ReSwift/ReSwift/ReSwift/CoreTypes/Subscription.swift: 1| |// 2| |// SubscriberWrapper.swift 3| |// ReSwift 4| |// 5| |// Created by Virgilio Favero Neto on 4/02/2016. 6| |// Copyright © 2016 ReSwift Community. All rights reserved. 7| |// 8| | 9| |/// A box around subscriptions and subscribers. 10| |/// 11| |/// Acts as a type-erasing wrapper around a subscription and its transformed subscription. 12| |/// The transformed subscription has a type argument that matches the selected substate of the 13| |/// subscriber; however that type cannot be exposed to the store. 14| |/// 15| |/// The box subscribes either to the original subscription, or if available to the transformed 16| |/// subscription and passes any values that come through this subscriptions to the subscriber. 17| |class SubscriptionBox: Hashable { 18| | 19| | private let originalSubscription: Subscription 20| | weak var subscriber: AnyStoreSubscriber? 21| | private let objectIdentifier: ObjectIdentifier 22| | 23| | #if swift(>=5.0) 24| 0| func hash(into hasher: inout Hasher) { 25| 0| hasher.combine(self.objectIdentifier) 26| 0| } 27| | #elseif swift(>=4.2) 28| | #if compiler(>=5.0) 29| | func hash(into hasher: inout Hasher) { 30| | hasher.combine(self.objectIdentifier) 31| | } 32| | #else 33| | var hashValue: Int { 34| | return self.objectIdentifier.hashValue 35| | } 36| | #endif 37| | #else 38| | var hashValue: Int { 39| | return self.objectIdentifier.hashValue 40| | } 41| | #endif 42| | 43| | init( 44| | originalSubscription: Subscription, 45| | transformedSubscription: Subscription?, 46| | subscriber: AnyStoreSubscriber 47| 0| ) { 48| 0| self.originalSubscription = originalSubscription 49| 0| self.subscriber = subscriber 50| 0| self.objectIdentifier = ObjectIdentifier(subscriber) 51| 0| 52| 0| // If we received a transformed subscription, we subscribe to that subscription 53| 0| // and forward all new values to the subscriber. 54| 0| if let transformedSubscription = transformedSubscription { 55| 0| transformedSubscription.observer = { [unowned self] _, newState in 56| 0| self.subscriber?._newState(state: newState as Any) 57| 0| } 58| 0| // If we haven't received a transformed subscription, we forward all values 59| 0| // from the original subscription. 60| 0| } else { 61| 0| originalSubscription.observer = { [unowned self] _, newState in 62| 0| self.subscriber?._newState(state: newState as Any) 63| 0| } 64| 0| } 65| 0| } 66| | 67| 0| func newValues(oldState: State, newState: State) { 68| 0| // We pass all new values through the original subscription, which accepts 69| 0| // values of type ``. If present, transformed subscriptions will 70| 0| // receive this update and transform it before passing it on to the subscriber. 71| 0| self.originalSubscription.newValues(oldState: oldState, newState: newState) 72| 0| } 73| | 74| 0| static func == (left: SubscriptionBox, right: SubscriptionBox) -> Bool { 75| 0| return left.objectIdentifier == right.objectIdentifier 76| 0| } 77| |} 78| | 79| |/// Represents a subscription of a subscriber to the store. The subscription determines which new 80| |/// values from the store are forwarded to the subscriber, and how they are transformed. 81| |/// The subscription acts as a very-light weight signal/observable that you might know from 82| |/// reactive programming libraries. 83| |public class Subscription { 84| | 85| | private func _select( 86| | _ selector: @escaping (State) -> Substate 87| | ) -> Subscription 88| 0| { 89| 0| return Subscription { sink in 90| 0| self.observer = { oldState, newState in 91| 0| sink(oldState.map(selector) ?? nil, selector(newState)) 92| 0| } 93| 0| } 94| 0| } 95| | 96| | // MARK: Public Interface 97| | 98| | /// Initializes a subscription with a sink closure. The closure provides a way to send 99| | /// new values over this subscription. 100| 0| public init(sink: @escaping (@escaping (State?, State) -> Void) -> Void) { 101| 0| // Provide the caller with a closure that will forward all values 102| 0| // to observers of this subscription. 103| 0| sink { old, new in 104| 0| self.newValues(oldState: old, newState: new) 105| 0| } 106| 0| } 107| | 108| | /// Provides a subscription that selects a substate of the state of the original subscription. 109| | /// - parameter selector: A closure that maps a state to a selected substate 110| | public func select( 111| | _ selector: @escaping (State) -> Substate 112| | ) -> Subscription 113| 0| { 114| 0| return self._select(selector) 115| 0| } 116| | 117| | /// Provides a subscription that selects a substate of the state of the original subscription. 118| | /// - parameter keyPath: A key path from a state to a substate 119| | public func select( 120| | _ keyPath: KeyPath 121| | ) -> Subscription 122| 0| { 123| 0| return self._select { $0[keyPath: keyPath] } 124| 0| } 125| | 126| | /// Provides a subscription that skips certain state updates of the original subscription. 127| | /// - parameter isRepeat: A closure that determines whether a given state update is a repeat and 128| | /// thus should be skipped and not forwarded to subscribers. 129| | /// - parameter oldState: The store's old state, before the action is reduced. 130| | /// - parameter newState: The store's new state, after the action has been reduced. 131| | public func skipRepeats(_ isRepeat: @escaping (_ oldState: State, _ newState: State) -> Bool) 132| 0| -> Subscription { 133| 0| return Subscription { sink in 134| 0| self.observer = { oldState, newState in 135| 0| switch (oldState, newState) { 136| 0| case let (old?, new): 137| 0| if !isRepeat(old, new) { 138| 0| sink(oldState, newState) 139| 0| } else { 140| 0| return 141| 0| } 142| 0| default: 143| 0| sink(oldState, newState) 144| 0| } 145| 0| } 146| 0| } 147| 0| } 148| | 149| | /// The closure called with changes from the store. 150| | /// This closure can be written to for use in extensions to Subscription similar to `skipRepeats` 151| | public var observer: ((State?, State) -> Void)? 152| | 153| | // MARK: Internals 154| | 155| 0| init() {} 156| | 157| | /// Sends new values over this subscription. Observers will be notified of these new values. 158| 0| func newValues(oldState: State?, newState: State) { 159| 0| self.observer?(oldState, newState) 160| 0| } 161| |} 162| | 163| |extension Subscription where State: Equatable { 164| 0| public func skipRepeats() -> Subscription{ 165| 0| return self.skipRepeats(==) ------------------ | Unexecuted instantiation: $s7ReSwift12SubscriptionCAASQRzlE11skipRepeatsACyxGyFSbx_xtcxmcfu_ ------------------ | Unexecuted instantiation: $s7ReSwift12SubscriptionCAASQRzlE11skipRepeatsACyxGyFSbx_xtcxmcfu_Sbx_xtcfu0_ ------------------ 166| 0| } 167| |} 168| | 169| |/// Subscription skipping convenience methods 170| |extension Subscription { 171| | 172| | /// Provides a subscription that skips certain state updates of the original subscription. 173| | /// 174| | /// This is identical to `skipRepeats` and is provided simply for convenience. 175| | /// - parameter when: A closure that determines whether a given state update is a repeat and 176| | /// thus should be skipped and not forwarded to subscribers. 177| | /// - parameter oldState: The store's old state, before the action is reduced. 178| | /// - parameter newState: The store's new state, after the action has been reduced. 179| 0| public func skip(when: @escaping (_ oldState: State, _ newState: State) -> Bool) -> Subscription { 180| 0| return self.skipRepeats(when) 181| 0| } 182| | 183| | /// Provides a subscription that only updates for certain state changes. 184| | /// 185| | /// This is effectively the inverse of `skip(when:)` / `skipRepeats(:)` 186| | /// - parameter when: A closure that determines whether a given state update should notify 187| | /// - parameter oldState: The store's old state, before the action is reduced. 188| | /// - parameter newState: The store's new state, after the action has been reduced. 189| | /// the subscriber. 190| 0| public func only(when: @escaping (_ oldState: State, _ newState: State) -> Bool) -> Subscription { 191| 0| return self.skipRepeats { oldState, newState in 192| 0| return !when(oldState, newState) 193| 0| } 194| 0| } 195| |} /Users/runner/work/ReSwift/ReSwift/ReSwift/Utils/Assertions.swift: 1| |// 2| |// Assertions 3| |// Copyright © 2015 mohamede1945. All rights reserved. 4| |// https://github.com/mohamede1945/AssertionsTestingExample 5| |// 6| | 7| |import Foundation 8| | 9| |/// drop-in fatalError replacement for testing 10| | 11| |/** 12| | Swift.fatalError wrapper for catching in tests 13| | 14| | - parameter message: Message to be wrapped 15| | - parameter file: Calling file 16| | - parameter line: Calling line 17| | */ 18| 0|func raiseFatalError(_ message: @autoclosure () -> String = "", 19| 0| file: StaticString = #file, line: UInt = #line) -> Never { 20| 0| Assertions.fatalErrorClosure(message(), file, line) 21| 0| repeat { 22| 0| RunLoop.current.run() 23| 0| } while (true) 24| 0|} 25| | 26| |/// Stores custom assertions closures, by default it points to Swift functions. But test target can 27| |/// override them. 28| |class Assertions { 29| | static var fatalErrorClosure = swiftFatalErrorClosure 30| | static let swiftFatalErrorClosure: (String, StaticString, UInt) -> Void 31| 0| = { Swift.fatalError($0, file: $1, line: $2) } 32| |} /Users/runner/work/ReSwift/ReSwift/ReSwift/Utils/Synchronized.swift: 1| |// 2| |// Synchronized.swift 3| |// ReSwift 4| |// 5| |// Created by Basem Emara on 2020-08-18. 6| |// https://basememara.com/creating-thread-safe-generic-values-in-swift/ 7| |// 8| |// Copyright © 2020 ReSwift Community. All rights reserved. 9| |// 10| | 11| |import Foundation 12| | 13| |/// An object that manages the execution of tasks atomically. 14| |struct Synchronized { 15| 0| private let mutex = DispatchQueue(label: "reswift.github.io.ReSwift.Utils.Synchronized", attributes: .concurrent) 16| | private var _value: Value 17| 0| init(_ value: Value) { 18| 0| self._value = value 19| 0| } 20| | /// Returns or modify the thread-safe value. 21| 0| var value: Value { return mutex.sync { return _value } } 22| | /// Submits a block for synchronous, thread-safe execution. 23| 0| mutating func value(execute task: (inout Value) throws -> T) rethrows -> T { 24| 0| return try mutex.sync(flags: .barrier) { return try task(&_value) } 25| 0| } 26| |} /Users/runner/work/ReSwift/ReSwift/ReSwift/Utils/TypeHelper.swift: 1| |// 2| |// TypeHelper.swift 3| |// ReSwift 4| |// 5| |// Created by Benjamin Encz on 11/27/15. 6| |// Copyright © 2015 ReSwift Community. All rights reserved. 7| |// 8| | 9| |/** 10| | Method is only used internally in ReSwift to cast the generic `StateType` to a specific 11| | type expected by reducers / store subscribers. 12| | 13| | - parameter action: An action that will be passed to `handleAction`. 14| | - parameter state: A generic state type that will be casted to `SpecificStateType`. 15| | - parameter function: The `handleAction` method. 16| | - returns: A `StateType` from `handleAction` or the original `StateType` if it cannot be 17| | casted to `SpecificStateType`. 18| | */ 19| |@discardableResult 20| |func withSpecificTypes( 21| | _ action: Action, 22| | state genericStateType: Any?, 23| | function: (_ action: Action, _ state: SpecificStateType?) -> SpecificStateType 24| 0| ) -> Any { 25| 0| guard let genericStateType = genericStateType else { 26| 0| return function(action, nil) as Any 27| 0| } 28| 0| 29| 0| guard let specificStateType = genericStateType as? SpecificStateType else { 30| 0| return genericStateType 31| 0| } 32| 0| 33| 0| return function(action, specificStateType) as Any 34| 0|} <<<<<< EOF