Skip to content

Soft-deprecate Effect.task and Effect.fireAndForget #2099

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

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

Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ struct EffectsBasics: ReducerProtocol {
// Return an effect that re-increments the count after 1 second if the count is negative
return state.count >= 0
? .none
: .task {
: .run { send in
try await self.clock.sleep(for: .seconds(1))
return .decrementDelayResponse
await send(.decrementDelayResponse)
}
.cancellable(id: CancelID.delay)

Expand All @@ -71,8 +71,8 @@ struct EffectsBasics: ReducerProtocol {
state.numberFact = nil
// Return an effect that fetches a number fact from the API and returns the
// value back to the reducer's `numberFactResponse` action.
return .task { [count = state.count] in
await .numberFactResponse(TaskResult { try await self.factClient.fetch(count) })
return .run { [count = state.count] send in
await send(.numberFactResponse(TaskResult { try await self.factClient.fetch(count) }))
}

case let .numberFactResponse(.success(response)):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ struct EffectsCancellation: ReducerProtocol {
state.currentFact = nil
state.isFactRequestInFlight = true

return .task { [count = state.count] in
await .factResponse(TaskResult { try await self.factClient.fetch(count) })
return .run { [count = state.count] send in
await send(.factResponse(TaskResult { try await self.factClient.fetch(count) }))
}
.cancellable(id: CancelID.factRequest)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ComposableArchitecture
import SwiftUI
@preconcurrency import SwiftUI

private let readMe = """
This application demonstrates how to make use of SwiftUI's `refreshable` API in the Composable \
Expand Down Expand Up @@ -53,10 +53,12 @@ struct Refreshable: ReducerProtocol {

case .refresh:
state.fact = nil
return .task { [count = state.count] in
await .factResponse(TaskResult { try await self.factClient.fetch(count) })
return .run { [count = state.count] send in
await send(
.factResponse(TaskResult { try await self.factClient.fetch(count) }),
animation: .default
)
}
.animation()
.cancellable(id: CancelID.factRequest)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ struct WebSocket: ReducerProtocol {
case .sendButtonTapped:
let messageToSend = state.messageToSend
state.messageToSend = ""
return .task {
return .run { send in
try await self.webSocket.send(WebSocketClient.ID(), .string(messageToSend))
return .sendResponse(didSucceed: true)
} catch: { _ in
.sendResponse(didSucceed: false)
await send(.sendResponse(didSucceed: true))
} catch: { _, send in
await send(.sendResponse(didSucceed: false))
}
.cancellable(id: WebSocketClient.ID())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ struct LoadThenNavigateList: ReducerProtocol {
for row in state.rows {
state.rows[id: row.id]?.isActivityIndicatorVisible = row.id == navigatedId
}
return .task {
return .run { send in
try await self.clock.sleep(for: .seconds(1))
return .setNavigationSelectionDelayCompleted(navigatedId)
await send(.setNavigationSelectionDelayCompleted(navigatedId))
}
.cancellable(id: CancelID.load, cancelInFlight: true)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ struct NavigateAndLoadList: ReducerProtocol {

case let .setNavigation(selection: .some(id)):
state.selection = Identified(nil, id: id)
return .task {
return .run { send in
try await self.clock.sleep(for: .seconds(1))
return .setNavigationSelectionDelayCompleted
await send(.setNavigationSelectionDelayCompleted)
}
.cancellable(id: CancelID.load, cancelInFlight: true)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ struct LoadThenNavigate: ReducerProtocol {

case .setNavigation(isActive: true):
state.isActivityIndicatorVisible = true
return .task {
return .run { send in
try await self.clock.sleep(for: .seconds(1))
return .setNavigationIsActiveDelayCompleted
await send(.setNavigationIsActiveDelayCompleted)
}
.cancellable(id: CancelID.load)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ struct NavigateAndLoad: ReducerProtocol {
switch action {
case .setNavigation(isActive: true):
state.isNavigationActive = true
return .task {
return .run { send in
try await self.clock.sleep(for: .seconds(1))
return .setNavigationIsActiveDelayCompleted
await send(.setNavigationIsActiveDelayCompleted)
}
.cancellable(id: CancelID.load)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ struct LoadThenPresent: ReducerProtocol {

case .setSheet(isPresented: true):
state.isActivityIndicatorVisible = true
return .task {
return .run { send in
try await self.clock.sleep(for: .seconds(1))
return .setSheetIsPresentedDelayCompleted
await send(.setSheetIsPresentedDelayCompleted)
}
.cancellable(id: CancelID.load)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ struct PresentAndLoad: ReducerProtocol {
switch action {
case .setSheet(isPresented: true):
state.isSheetPresented = true
return .task {
return .run { send in
try await self.clock.sleep(for: .seconds(1))
return .setSheetIsPresentedDelayCompleted
await send(.setSheetIsPresentedDelayCompleted)
}
.cancellable(id: CancelID.load)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ struct Favoriting<ID: Hashable & Sendable>: ReducerProtocol {
case .buttonTapped:
state.isFavorite.toggle()

return .task { [id = state.id, isFavorite = state.isFavorite, favorite] in
await .response(TaskResult { try await favorite(id, isFavorite) })
return .run { [id = state.id, isFavorite = state.isFavorite, favorite] send in
await send(.response(TaskResult { try await favorite(id, isFavorite) }))
}
.cancellable(id: CancelID(id: state.id), cancelInFlight: true)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import XCTest
@MainActor
final class LongLivingEffectsTests: XCTestCase {
func testReducer() async {
let (screenshots, takeScreenshot) = AsyncStream<Void>.streamWithContinuation()
let (screenshots, takeScreenshot) = AsyncStream.makeStream(of: Void.self)

let store = TestStore(initialState: LongLivingEffects.State()) {
LongLivingEffects()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import XCTest
@MainActor
final class WebSocketTests: XCTestCase {
func testWebSocketHappyPath() async {
let actions = AsyncStream<WebSocketClient.Action>.streamWithContinuation()
let messages = AsyncStream<TaskResult<WebSocketClient.Message>>.streamWithContinuation()
let actions = AsyncStream.makeStream(of: WebSocketClient.Action.self)
let messages = AsyncStream.makeStream(of: TaskResult<WebSocketClient.Message>.self)

let store = TestStore(initialState: WebSocket.State()) {
WebSocket()
Expand Down Expand Up @@ -57,8 +57,8 @@ final class WebSocketTests: XCTestCase {
}

func testWebSocketSendFailure() async {
let actions = AsyncStream<WebSocketClient.Action>.streamWithContinuation()
let messages = AsyncStream<TaskResult<WebSocketClient.Message>>.streamWithContinuation()
let actions = AsyncStream.makeStream(of: WebSocketClient.Action.self)
let messages = AsyncStream.makeStream(of: TaskResult<WebSocketClient.Message>.self)

let store = TestStore(initialState: WebSocket.State()) {
WebSocket()
Expand Down Expand Up @@ -103,7 +103,7 @@ final class WebSocketTests: XCTestCase {
}

func testWebSocketPings() async {
let actions = AsyncStream<WebSocketClient.Action>.streamWithContinuation()
let actions = AsyncStream.makeStream(of: WebSocketClient.Action.self)
let clock = TestClock()
var pingsCount = 0

Expand Down Expand Up @@ -137,7 +137,7 @@ final class WebSocketTests: XCTestCase {
}

func testWebSocketConnectError() async {
let actions = AsyncStream<WebSocketClient.Action>.streamWithContinuation()
let actions = AsyncStream.makeStream(of: WebSocketClient.Action.self)

let store = TestStore(initialState: WebSocket.State()) {
WebSocket()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import XCTest

@MainActor
final class ReusableComponentsDownloadComponentTests: XCTestCase {
let download = AsyncThrowingStream<DownloadClient.Event, Error>.streamWithContinuation()
let download = AsyncThrowingStream.makeStream(of: DownloadClient.Event.self)

func testDownloadFlow() async {
let store = TestStore(
Expand Down
4 changes: 2 additions & 2 deletions Examples/CaseStudies/UIKitCaseStudies/LoadThenNavigate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ struct LazyNavigation: ReducerProtocol {

case .setNavigation(isActive: true):
state.isActivityIndicatorHidden = false
return .task {
return .run { send in
try await self.clock.sleep(for: .seconds(1))
return .setNavigationIsActiveDelayCompleted
await send(.setNavigationIsActiveDelayCompleted)
}
.cancellable(id: CancelID.load)

Expand Down
4 changes: 2 additions & 2 deletions Examples/CaseStudies/UIKitCaseStudies/NavigateAndLoad.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ struct EagerNavigation: ReducerProtocol {
switch action {
case .setNavigation(isActive: true):
state.isNavigationActive = true
return .task {
return .run { send in
try await self.clock.sleep(for: .seconds(1))
return .setNavigationIsActiveDelayCompleted
await send(.setNavigationIsActiveDelayCompleted)
}
.cancellable(id: CancelID.load)

Expand Down
14 changes: 8 additions & 6 deletions Examples/Search/Search/SearchView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ struct Search: ReducerProtocol {
guard !state.searchQuery.isEmpty else {
return .none
}
return .task { [query = state.searchQuery] in
await .searchResponse(TaskResult { try await self.weatherClient.search(query) })
return .run { [query = state.searchQuery] send in
await send(.searchResponse(TaskResult { try await self.weatherClient.search(query) }))
}
.cancellable(id: CancelID.location)

Expand All @@ -96,10 +96,12 @@ struct Search: ReducerProtocol {
case let .searchResultTapped(location):
state.resultForecastRequestInFlight = location

return .task {
await .forecastResponse(
location.id,
TaskResult { try await self.weatherClient.forecast(location) }
return .run { send in
await send(
.forecastResponse(
location.id,
TaskResult { try await self.weatherClient.forecast(location) }
)
)
}
.cancellable(id: CancelID.weather, cancelInFlight: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct SpeechRecognition: ReducerProtocol {

guard state.isRecording
else {
return .fireAndForget {
return .run { _ in
await self.speechClient.finishTask()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import XCTest

@MainActor
final class SpeechRecognitionTests: XCTestCase {
let recognitionTask = AsyncThrowingStream<SpeechRecognitionResult, Error>.streamWithContinuation()
let recognitionTask = AsyncThrowingStream.makeStream(of: SpeechRecognitionResult.self)

func testDenyAuthorization() async {
let store = TestStore(initialState: SpeechRecognition.State()) {
Expand Down
20 changes: 11 additions & 9 deletions Examples/TicTacToe/tic-tac-toe/Sources/LoginCore/LoginCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public struct Login: ReducerProtocol, Sendable {
public init() {}
}

public enum Action: Equatable {
public enum Action: Equatable, Sendable {
case alertDismissed
case emailChanged(String)
case passwordChanged(String)
Expand Down Expand Up @@ -60,13 +60,15 @@ public struct Login: ReducerProtocol, Sendable {

case .loginButtonTapped:
state.isLoginRequestInFlight = true
return .task { [email = state.email, password = state.password] in
.loginResponse(
await TaskResult {
try await self.authenticationClient.login(
.init(email: email, password: password)
)
}
return .run { [email = state.email, password = state.password] send in
await send(
.loginResponse(
await TaskResult {
try await self.authenticationClient.login(
.init(email: email, password: password)
)
}
)
)
}

Expand All @@ -75,7 +77,7 @@ public struct Login: ReducerProtocol, Sendable {

case .twoFactorDismissed:
state.twoFactor = nil
return .cancel(id: TwoFactor.TearDownToken.self)
return .cancel(id: TwoFactor.CancelID.tearDown)
}
}
.ifLet(\.twoFactor, action: /Action.twoFactor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public struct TwoFactor: ReducerProtocol, Sendable {
}
}

public enum Action: Equatable {
public enum Action: Equatable, Sendable {
case alertDismissed
case codeChanged(String)
case submitButtonTapped
case twoFactorResponse(TaskResult<AuthenticationResponse>)
}

public enum TearDownToken {}
public enum CancelID { case tearDown }

@Dependency(\.authenticationClient) var authenticationClient

Expand All @@ -42,14 +42,16 @@ public struct TwoFactor: ReducerProtocol, Sendable {

case .submitButtonTapped:
state.isTwoFactorRequestInFlight = true
return .task { [code = state.code, token = state.token] in
.twoFactorResponse(
await TaskResult {
try await self.authenticationClient.twoFactor(.init(code: code, token: token))
}
return .run { [code = state.code, token = state.token] send in
await send(
.twoFactorResponse(
TaskResult {
try await self.authenticationClient.twoFactor(.init(code: code, token: token))
}
)
)
}
.cancellable(id: TearDownToken.self)
.cancellable(id: CancelID.tearDown)

case let .twoFactorResponse(.failure(error)):
state.alert = AlertState { TextState(error.localizedDescription) }
Expand Down
Loading