I have an observable view model with several published properties:
final class ProtectionViewModel: ObservableObject {
@Published
var showSomething = false
@Published
var showSomethingElse = false
}
The properties are updated similarly but their updates are triggered by different events. E.g.:
final class ProtectionViewModel: ObservableObject {
// ...
private func doSomething() {
let someCondition = // ...
if someCondition {
showSomething = true
Task {
try? await Task.sleep(nanoseconds: 1_000_000_000)
await MainActor.run { showSomething = false }
}
} else {
showSomethingElse = true
Task {
try? await Task.sleep(nanoseconds: 1_000_000_000)
await MainActor.run { showSomethingElse = false }
}
}
}
}
There's an obvious code duplication there. Is there a way to extract a method that would accept a published property and change its wrapped value? Something like this:
// For the sake of example. The code doesn't compile.
private func doSomething() {
let someCondition = // ...
if someCondition {
doSomething(with: showSomething)
} else {
doSomething(with: showSomethingElse)
}
}
private func doSomething(with something: Published<Bool>) {
something = true
Task {
try? await Task.sleep(nanoseconds: 1_000_000_000)
await MainActor.run { something = false }
}
}
Any piece of advice is appreciated.
Use key paths. Since you are dealing with properties of a class, the function can take a ReferenceWritableKeyPath<ProtectionViewModel, Bool>
:
private func doSomething(with something: ReferenceWritableKeyPath< ProtectionViewModel, Bool>) {
self[keyPath: something] = true
Task {
try? await Task.sleep(nanoseconds: 1_000_000_000)
await MainActor.run { self[keyPath: something] = false }
}
}
Example usage:
doSomething(with: \.showSomething)
doSomething(with: \.showSomethingElse)
Note that ProtectionViewModel
should be MainActor
for MainActor.run { self[keyPath: something] = false }
to be thread safe.