An equivalent to computed properties using @Published in Swift Combine?
You don't need to do anything for computed properties that are based on @Published
properties. You can just use it like this:
class UserManager: ObservableObject { @Published var currentUser: User? var userIsLoggedIn: Bool { currentUser != nil }}
What happens in the @Published
property wrapper of currentUser
is that it will call objectWillChange.send()
of the ObservedObject
on changes. SwiftUI views don't care about which properties of @ObservedObject
s have changed, it will just recalculate the view and redraw if necessary.
Working example:
class UserManager: ObservableObject { @Published var currentUser: String? var userIsLoggedIn: Bool { currentUser != nil } func logOut() { currentUser = nil } func logIn() { currentUser = "Demo" }}
And a SwiftUI demo view:
struct ContentView: View { @ObservedObject var userManager = UserManager() var body: some View { VStack( spacing: 50) { if userManager.userIsLoggedIn { Text( "Logged in") Button(action: userManager.logOut) { Text("Log out") } } else { Text( "Logged out") Button(action: userManager.logIn) { Text("Log in") } } } }}
Create a new publisher subscribed to the property you want to track.
@Published var speed: Double = 88lazy var canTimeTravel: AnyPublisher<Bool,Never> = { $speed .map({ $0 >= 88 }) .eraseToAnyPublisher()}()
You will then be able to observe it much like your @Published
property.
private var subscriptions = Set<AnyCancellable>()override func viewDidLoad() { super.viewDidLoad() sourceOfTruthObject.$canTimeTravel.sink { [weak self] (canTimeTravel) in // Do something… }) .store(in: &subscriptions)}
Not directly related but useful nonetheless, you can track multiple properties that way with combineLatest
.
@Published var threshold: Int = 60@Published var heartData = [Int]()/** This publisher "observes" both `threshold` and `heartData` and derives a value from them. It should be updated whenever one of those values changes. */lazy var status: AnyPublisher<Status,Never> = { $threshold .combineLatest($heartData) .map({ threshold, heartData in // Computing a "status" with the two values Status.status(heartData: heartData, threshold: threshold) }) .receive(on: DispatchQueue.main) .eraseToAnyPublisher()}()