-
So, I've got an application (macOS) which communicates to an API service for data persistence. One of the screens is for editing a person's details (first name, last name, birthday, etc.) and the service provides an "update person" endpoint rather than distinct endpoints for "update person first name" etc. In the Reducer for the feature, I've written a single function to make the API call to update the person, including a debounce modifier so that if there are several successive edits to the same person, I don't spam the service: private func updatePerson(state: State,
person: Person?) -> Effect<Action, Never> {
let user = state.user
if let person = person {
return .task {
await .updated(
TaskResult {
try await endpoints.update(person: person, for: user)
}
)
}
.debounce(id: person.id, for: 1, scheduler: mainQueue)
}
return .none
} Now, I'm working on updating the code for the 1.0 release of TCA, so the private func updatePerson(state: State, person: Person?) -> EffectTask<Action> {
let user = state.user
guard let person = person else { return .none }
return .run { send in
await send(.updated(TaskResult {
return try await endpoints.update(person: person, for: user)
}))
}
} And then, in the View there'd be a .task(id: viewStore.localWorkingCopyOfPerson) {
do {
try await Task.sleep(for: Duration.seconds(1))
await viewStore.send(.updatePersonDebounced).finish()
} catch {}
} And then, in the reducer, in all the places where I'm currently calling But then, if the user modifies the person record and then immediately navigates away from the view, wouldn't SwiftUI cancel the update task, leaving the server's version of the person unmodified? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
Hi @sbeitzel, the example code shows one way of debouncing where it is possible to offload the majority of work onto SwiftUI's private func updatePerson(state: State,
person: Person?) -> Effect<Action, Never> {
let user = state.user
if let person = person {
return .task {
+ try await mainQueue.sleep(for: .seconds(1))
return await .updated(
TaskResult {
try await endpoints.update(person: person, for: user)
}
)
}
- .debounce(id: person.id, for: 1, scheduler: mainQueue)
+ .cancellable(id: person.id)
}
return .none
} |
Beta Was this translation helpful? Give feedback.
Hi @sbeitzel, the example code shows one way of debouncing where it is possible to offload the majority of work onto SwiftUI's
task(id:)
view modifier rather than re-creating it in the reducer. If you do not want to follow that pattern you can still perform a debounce that is similar to the old style. Just perform a sleep before executing the effect and then make the effect cancellable:private func updatePerson(state: State, person: Person?) -> Effect<Action, Never> { let user = state.user if let person = person { return .task { + try await mainQueue.sleep(for: .seconds(1)) return await .updated( Task…