Skip to content

Commit

Permalink
Merge pull request #4 from dannyhertz/if_let_store_fixes
Browse files Browse the repository at this point in the history
If let store fixes from core TCA repo
  • Loading branch information
dannyhertz authored Aug 4, 2020
2 parents 765a403 + 2399e9e commit d8524e9
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 48 deletions.
19 changes: 2 additions & 17 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,12 @@ jobs:
strategy:
matrix:
xcode:
- 11.3
- 11.4
- 11.5
- 11.6
steps:
- uses: actions/checkout@v2
- name: Select Xcode ${{ matrix.xcode }}
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
- name: Run tests
run: make test-swift

examples:
runs-on: macOS-latest
strategy:
matrix:
xcode:
- 11.3
- 11.4
- 11.5
steps:
- uses: actions/checkout@v2
- name: Select Xcode ${{ matrix.xcode }}
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
- name: Run tests
run: make test-workspace
run: make test
3 changes: 3 additions & 0 deletions ComposableArchitecture.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions Examples/CaseStudies/CaseStudies.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/* Begin PBXBuildFile section */
7854FF92249318910094D8A8 /* UIKitCaseStudiesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7854FF91249318910094D8A8 /* UIKitCaseStudiesTests.swift */; };
78634FE924930375006E231F /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 78634FE824930375006E231F /* RxCocoa */; };
78B64BD42492F5EC00A47CE0 /* ComposableArchitecture in Frameworks */ = {isa = PBXBuildFile; productRef = 78B64BD32492F5EC00A47CE0 /* ComposableArchitecture */; };
78BC6CAC24D9BD200061AB2D /* ComposableArchitecture in Frameworks */ = {isa = PBXBuildFile; productRef = 78BC6CAB24D9BD200061AB2D /* ComposableArchitecture */; };
DC25DC5F2450F13200082E81 /* IfLetStoreController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC25DC5E2450F13200082E81 /* IfLetStoreController.swift */; };
DC25DC612450F2B000082E81 /* LoadThenNavigate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC25DC602450F2B000082E81 /* LoadThenNavigate.swift */; };
DC25DC642450F2DF00082E81 /* ActivityIndicatorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC25DC632450F2DF00082E81 /* ActivityIndicatorViewController.swift */; };
Expand Down Expand Up @@ -82,8 +82,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
78BC6CAC24D9BD200061AB2D /* ComposableArchitecture in Frameworks */,
78634FE924930375006E231F /* RxCocoa in Frameworks */,
78B64BD42492F5EC00A47CE0 /* ComposableArchitecture in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -187,8 +187,8 @@
);
name = UIKitCaseStudies;
packageProductDependencies = (
78B64BD32492F5EC00A47CE0 /* ComposableArchitecture */,
78634FE824930375006E231F /* RxCocoa */,
78BC6CAB24D9BD200061AB2D /* ComposableArchitecture */,
);
productName = UIKitCaseStudies;
productReference = DC4C6EA72450DD380066A05D /* UIKitCaseStudies.app */;
Expand Down Expand Up @@ -245,8 +245,8 @@
);
mainGroup = DC89C40A24460F95006900B9;
packageReferences = (
78B64BD22492F5EC00A47CE0 /* XCRemoteSwiftPackageReference "rx-swift-composable-architecture" */,
78634FE724930375006E231F /* XCRemoteSwiftPackageReference "RxSwift" */,
78BC6CAA24D9BD200061AB2D /* XCRemoteSwiftPackageReference "rxswift-composable-architecture" */,
);
productRefGroup = DC89C41424460F95006900B9 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -560,12 +560,12 @@
minimumVersion = 5.1.1;
};
};
78B64BD22492F5EC00A47CE0 /* XCRemoteSwiftPackageReference "rx-swift-composable-architecture" */ = {
78BC6CAA24D9BD200061AB2D /* XCRemoteSwiftPackageReference "rxswift-composable-architecture" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "git@github.com:dannyhertz/rx-swift-composable-architecture.git";
repositoryURL = "https://github.com/dannyhertz/rxswift-composable-architecture.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.3.0;
minimumVersion = 0.6.0;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand All @@ -576,9 +576,9 @@
package = 78634FE724930375006E231F /* XCRemoteSwiftPackageReference "RxSwift" */;
productName = RxCocoa;
};
78B64BD32492F5EC00A47CE0 /* ComposableArchitecture */ = {
78BC6CAB24D9BD200061AB2D /* ComposableArchitecture */ = {
isa = XCSwiftPackageProductDependency;
package = 78B64BD22492F5EC00A47CE0 /* XCRemoteSwiftPackageReference "rx-swift-composable-architecture" */;
package = 78BC6CAA24D9BD200061AB2D /* XCRemoteSwiftPackageReference "rxswift-composable-architecture" */;
productName = ComposableArchitecture;
};
/* End XCSwiftPackageProductDependency section */
Expand Down
18 changes: 7 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,10 @@ PLATFORM_IOS = iOS Simulator,name=iPhone 11 Pro Max
PLATFORM_MACOS = macOS
PLATFORM_TVOS = tvOS Simulator,name=Apple TV 4K (at 1080p)

default: test-all
default: test

test-all: test-swift test-workspace

test-swift:
swift test \
--enable-pubgrub-resolver \
--enable-test-discovery \
--parallel

test-workspace:
test:
instruments -s devices
xcodebuild test \
-scheme ComposableArchitecture \
-destination platform="$(PLATFORM_IOS)"
Expand All @@ -22,8 +15,11 @@ test-workspace:
xcodebuild test \
-scheme ComposableArchitecture \
-destination platform="$(PLATFORM_TVOS)"
xcodebuild test \
-scheme "CaseStudies (UIKit)" \
-destination platform="$(PLATFORM_IOS)"

format:
swift format --in-place --recursive ./Package.swift ./Sources ./Tests

.PHONY: format test-all test-swift test-workspace
.PHONY: format test
32 changes: 21 additions & 11 deletions Sources/ComposableArchitecture/UIKit/IfLetUIKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,27 @@ extension Store {
then unwrap: @escaping (Store<Wrapped, Action>) -> Void,
else: @escaping () -> Void
) -> Disposable where State == Wrapped? {
self.scope(
state: { state in
return
state
.distinctUntilChanged({ ($0 != nil) == ($1 != nil) })
.do(onNext: { if $0 == nil { `else`() } })
.compactMap { $0 }
},
action: { $0 }
)
.subscribe(onNext: unwrap)

let elseDisposable = self
.scope(
state: { state in
state.distinctUntilChanged({ ($0 != nil) == ($1 != nil) })
}
)
.subscribe(onNext: { store in
if store.state == nil { `else`() }
})

let unwrapDisposable = self
.scope(
state: { state in
state.distinctUntilChanged({ ($0 != nil) == ($1 != nil) })
.compactMap { $0 }
}
)
.subscribe(onNext: unwrap)

return CompositeDisposable(elseDisposable, unwrapDisposable)
}

/// An overload of `ifLet(then:else:)` for the times that you do not want to handle the `else`
Expand Down
116 changes: 116 additions & 0 deletions Tests/ComposableArchitectureTests/StoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,120 @@ final class StoreTests: XCTestCase {
store.send(.incr)
XCTAssertEqual(ViewStore(store).state, 100_000)
}

func testPublisherScope() {
let appReducer = Reducer<Int, Bool, Void> { state, action, _ in
state += action ? 1 : 0
return .none
}

let parentStore = Store(initialState: 0, reducer: appReducer, environment: ())

var outputs: [Int] = []

parentStore
.scope { $0.distinctUntilChanged() }
.subscribe(onNext: {
outputs.append($0.state)
})
.disposed(by: disposeBag)

XCTAssertEqual(outputs, [0])

parentStore.send(true)
XCTAssertEqual(outputs, [0, 1])

parentStore.send(false)
XCTAssertEqual(outputs, [0, 1])
parentStore.send(false)
XCTAssertEqual(outputs, [0, 1])
parentStore.send(false)
XCTAssertEqual(outputs, [0, 1])
parentStore.send(false)
XCTAssertEqual(outputs, [0, 1])
}

func testIfLetAfterScope() {
struct AppState {
var count: Int?
}

let appReducer = Reducer<AppState, Int?, Void> { state, action, _ in
state.count = action
return .none
}

let parentStore = Store(initialState: AppState(), reducer: appReducer, environment: ())

// NB: This test needs to hold a strong reference to the emitted stores
var outputs: [Int?] = []
var stores: [Any] = []

parentStore
.scope(state: \.count)
.ifLet(
then: { store in
stores.append(store)
outputs.append(store.state)
},
else: {
outputs.append(nil)
})
.disposed(by: disposeBag)

XCTAssertEqual(outputs, [nil])

parentStore.send(1)
XCTAssertEqual(outputs, [nil, 1])

parentStore.send(nil)
XCTAssertEqual(outputs, [nil, 1, nil])

parentStore.send(1)
XCTAssertEqual(outputs, [nil, 1, nil, 1])

parentStore.send(nil)
XCTAssertEqual(outputs, [nil, 1, nil, 1, nil])

parentStore.send(1)
XCTAssertEqual(outputs, [nil, 1, nil, 1, nil, 1])

parentStore.send(nil)
XCTAssertEqual(outputs, [nil, 1, nil, 1, nil, 1, nil])
}

func testIfLetTwo() {
let parentStore = Store(
initialState: 0,
reducer: Reducer<Int?, Bool, Void> { state, action, _ in
if action {
state? += 1
return .none
} else {
return Effect(value: true)
.observeOn(MainScheduler.instance)
.eraseToEffect()
}
},
environment: ()
)

parentStore.ifLet { childStore in
let vs = ViewStore(childStore)

vs
.publisher
.subscribe(onNext: { _ in })
.disposed(by: self.disposeBag)

vs.send(false)
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
vs.send(false)
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
vs.send(false)
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
XCTAssertEqual(vs.state, 3)
}
.disposed(by: disposeBag)
}
}

0 comments on commit d8524e9

Please sign in to comment.