Skip to content

Commit 58fddea

Browse files
committed
Merge remote-tracking branch 'origin/main' into core
2 parents 47d606b + 76c4411 commit 58fddea

File tree

7 files changed

+83
-49
lines changed

7 files changed

+83
-49
lines changed

.github/workflows/ci.yml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
matrix:
2222
command: [test, '']
2323
platform: [IOS, MACOS]
24-
xcode: ['16.0']
24+
xcode: ['16.2']
2525
steps:
2626
- uses: actions/checkout@v4
2727
- name: Select Xcode ${{ matrix.xcode }}
@@ -117,27 +117,27 @@ jobs:
117117
restore-keys: |
118118
deriveddata-examples-
119119
- name: Select Xcode 16
120-
run: sudo xcode-select -s /Applications/Xcode_16.0.app
120+
run: sudo xcode-select -s /Applications/Xcode_16.2.app
121121
- name: Set IgnoreFileSystemDeviceInodeChanges flag
122122
run: defaults write com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges -bool YES
123123
- name: Update mtime for incremental builds
124124
uses: chetan/git-restore-mtime-action@v2
125125
- name: CaseStudies (SwiftUI)
126-
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="CaseStudies (SwiftUI)" xcodebuild-raw
126+
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="CaseStudies (SwiftUI)" xcodebuild
127127
- name: CaseStudies (UIKit)
128-
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="CaseStudies (UIKit)" xcodebuild-raw
128+
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="CaseStudies (UIKit)" xcodebuild
129129
- name: Search
130-
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="Search" xcodebuild-raw
130+
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="Search" xcodebuild
131131
- name: SyncUps
132-
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="SyncUps" xcodebuild-raw
132+
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="SyncUps" xcodebuild
133133
- name: SpeechRecognition
134-
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="SpeechRecognition" xcodebuild-raw
134+
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="SpeechRecognition" xcodebuild
135135
- name: TicTacToe
136-
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="TicTacToe" xcodebuild-raw
136+
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="TicTacToe" xcodebuild
137137
- name: Todos
138-
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="Todos" xcodebuild-raw
138+
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="Todos" xcodebuild
139139
- name: VoiceMemos
140-
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="VoiceMemos" xcodebuild-raw
140+
run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="VoiceMemos" xcodebuild
141141

142142
check-macro-compatibility:
143143
name: Check Macro Compatibility

.github/workflows/documentation.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ jobs:
4141
4242
for tag in $(echo "main"; git tag -l --sort=-v:refname | grep -e "\d\+\.\d\+.0" | head -6);
4343
do
44-
if [ -d "docs-out/$tag/data/documentation/composablearchitecture" ]
45-
then
44+
if [ -d "docs-out/$tag/data/documentation/composablearchitecture" ]
45+
then
4646
echo "✅ Documentation for "$tag" already exists.";
47-
else
47+
else
4848
echo "⏳ Generating documentation for ComposableArchitecture @ "$tag" release.";
4949
rm -rf "docs-out/$tag";
5050

ComposableArchitecture.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 17 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ CONFIG = Debug
22

33
DERIVED_DATA_PATH = ~/.derivedData/$(CONFIG)
44

5-
PLATFORM_IOS = iOS Simulator,id=$(call udid_for,iOS,iPhone \d\+ Pro [^M])
5+
PLATFORM_IOS = iOS Simulator,id=$(call udid_for,iPhone)
66
PLATFORM_MACOS = macOS
77
PLATFORM_MAC_CATALYST = macOS,variant=Mac Catalyst
8-
PLATFORM_TVOS = tvOS Simulator,id=$(call udid_for,tvOS,TV)
9-
PLATFORM_VISIONOS = visionOS Simulator,id=$(call udid_for,visionOS,Vision)
10-
PLATFORM_WATCHOS = watchOS Simulator,id=$(call udid_for,watchOS,Watch)
8+
PLATFORM_TVOS = tvOS Simulator,id=$(call udid_for,TV)
9+
PLATFORM_VISIONOS = visionOS Simulator,id=$(call udid_for,Vision)
10+
PLATFORM_WATCHOS = watchOS Simulator,id=$(call udid_for,Watch)
1111

1212
PLATFORM = IOS
1313
DESTINATION = platform="$(PLATFORM_$(PLATFORM))"
@@ -47,7 +47,6 @@ warm-simulator:
4747
xcodebuild: warm-simulator
4848
$(XCODEBUILD)
4949

50-
# Workaround for debugging Swift Testing tests: https://github.com/cpisciotta/xcbeautify/issues/313
5150
xcodebuild-raw: warm-simulator
5251
$(XCODEBUILD_COMMAND)
5352

@@ -69,5 +68,5 @@ format:
6968
.PHONY: build-for-library-evolution format warm-simulator xcodebuild xcodebuild-raw
7069

7170
define udid_for
72-
$(shell xcrun simctl list devices available '$(1)' | grep '$(2)' | sort -r | head -1 | awk -F '[()]' '{ print $$(NF-3) }')
71+
$(shell xcrun simctl list --json devices available '$(1)' | jq -r '[.devices|to_entries|sort_by(.key)|reverse|.[].value|select(length > 0)|.[0]][0].udid')
7372
endef

Package.resolved

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/ComposableArchitecture/Core.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,14 @@ final class RootCore<Root: Reducer>: Core {
9292
defer { index += 1 }
9393
let action = self.bufferedActions[index]
9494
let effect = reducer.reduce(into: &currentState, action: action)
95+
let uuid = UUID()
9596

9697
switch effect.operation {
9798
case .none:
9899
break
99100
case let .publisher(publisher):
100101
var didComplete = false
101102
let boxedTask = Box<Task<Void, Never>?>(wrappedValue: nil)
102-
let uuid = UUID()
103103
let effectCancellable = withEscapedDependencies { continuation in
104104
publisher
105105
.receive(on: UIScheduler.shared)
@@ -128,11 +128,13 @@ final class RootCore<Root: Reducer>: Core {
128128
}
129129
boxedTask.wrappedValue = task
130130
tasks.withValue { $0.append(task) }
131-
self.effectCancellables[uuid] = effectCancellable
131+
self.effectCancellables[uuid] = AnyCancellable {
132+
task.cancel()
133+
}
132134
}
133135
case let .run(priority, operation):
134136
withEscapedDependencies { continuation in
135-
let task = Task(priority: priority) { @MainActor in
137+
let task = Task(priority: priority) { @MainActor [weak self] in
136138
let isCompleted = LockIsolated(false)
137139
defer { isCompleted.setValue(true) }
138140
await operation(
@@ -158,14 +160,18 @@ final class RootCore<Root: Reducer>: Core {
158160
)
159161
}
160162
if let task = continuation.yield({
161-
self.send(effectAction)
163+
self?.send(effectAction)
162164
}) {
163165
tasks.withValue { $0.append(task) }
164166
}
165167
}
166168
)
169+
self?.effectCancellables[uuid] = nil
167170
}
168171
tasks.withValue { $0.append(task) }
172+
self.effectCancellables[uuid] = AnyCancellable {
173+
task.cancel()
174+
}
169175
}
170176
}
171177
}

Tests/ComposableArchitectureTests/StoreLifetimeTests.swift

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Combine
22
@_spi(Logging) import ComposableArchitecture
33
import XCTest
44

5+
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
56
final class StoreLifetimeTests: BaseTCATestCase {
67
@available(*, deprecated)
78
@MainActor
@@ -69,9 +70,6 @@ final class StoreLifetimeTests: BaseTCATestCase {
6970

7071
@MainActor
7172
func testStoreDeinit_RunningEffect() async {
72-
XCTTODO(
73-
"We would like for this to pass, but it requires full deprecation of uncached child stores"
74-
)
7573
Logger.shared.isEnabled = true
7674
let effectFinished = self.expectation(description: "Effect finished")
7775
do {
@@ -99,9 +97,6 @@ final class StoreLifetimeTests: BaseTCATestCase {
9997

10098
@MainActor
10199
func testStoreDeinit_RunningCombineEffect() async {
102-
XCTTODO(
103-
"We would like for this to pass, but it requires full deprecation of uncached child stores"
104-
)
105100
Logger.shared.isEnabled = true
106101
let effectFinished = self.expectation(description: "Effect finished")
107102
do {
@@ -129,28 +124,61 @@ final class StoreLifetimeTests: BaseTCATestCase {
129124
await self.fulfillment(of: [effectFinished], timeout: 0.5)
130125
}
131126
#endif
127+
128+
@MainActor
129+
@available(*, deprecated)
130+
func testUnCachedStores() async {
131+
Logger.shared.isEnabled = true
132+
let clock = TestClock()
133+
let store = Store(initialState: Parent.State()) {
134+
Parent()
135+
} withDependencies: {
136+
$0.continuousClock = clock
137+
}
138+
do {
139+
let child = store.scope(state: { $0.child }, action: { .child($0) })
140+
child.send(.start)
141+
XCTAssertEqual(store.withState(\.child.count), 1)
142+
}
143+
await clock.run()
144+
XCTAssertEqual(store.withState(\.child.count), 2)
145+
}
132146
}
133147

134148
@Reducer
149+
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
135150
private struct Child {
136151
struct State: Equatable {
137152
var count = 0
138153
}
139154
enum Action {
140155
case tap
156+
case start
157+
case response
141158
}
159+
@Dependency(\.continuousClock) var clock
142160
var body: some ReducerOf<Self> {
143161
Reduce { state, action in
144162
switch action {
145163
case .tap:
146164
state.count += 1
147165
return .none
166+
case .start:
167+
state.count += 1
168+
return .run { send in
169+
try await clock.sleep(for: .seconds(0))
170+
await send(.response)
171+
}
172+
case .response:
173+
state.count += 1
174+
return .none
148175
}
149176
}
150177
}
151178
}
152179

153180
@Reducer
181+
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
154182
private struct Parent {
155183
struct State: Equatable {
156184
var child = Child.State()
@@ -166,6 +194,7 @@ private struct Parent {
166194
}
167195

168196
@Reducer
197+
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
169198
private struct Grandparent {
170199
struct State: Equatable {
171200
var child = Parent.State()

0 commit comments

Comments
 (0)