3

I'd like to trigger a "change" event on every change of either the username or password published properties and set a new Credentials published property derived of those two and emit an event.

What would be the simplest solution to achieve this result using SwiftUI & Combine?

Some sample code with the idea I'm trying to achieve:

import SwiftUI
import Combine
import Foundation

struct Credentials {
    let username: String
    let password: String

    init(username: String = "",
         password: String = "") {
        self.userName = username
        self.password = password
    }
}

final class ViewModel: ObservableObject {
    @Published var username = ""
    @Published var password = ""

    @Published var credentials = Credentials()


    init() {
        [$username, $password]// ..... What to do here? 
// How to "subscribe" each of those properties to emit an event
// so that I get notified each time one of them changes

        credentials = Credentials(username: $username, password: $password)
    }
}

Essentially, I'm looking to something similar to this answer: Swift Combine: How to create a single publisher from a list of publishers?

But with the notification should be triggered each time any of the publishers produce a value, not all of them.

Richard Topchii
  • 7,075
  • 8
  • 48
  • 115

1 Answers1

0

Instead of using Publishers.MergeMany as in your linked question, you want to use .combineLatest(_:) on your first publisher, like so:

import SwiftUI
import Combine
import Foundation

struct Credentials {
    let username: String
    let password: String

    init(username: String = "",
         password: String = "") {
        self.userName = username
        self.password = password
    }
}

final class ViewModel: ObservableObject {
    @Published var username = ""
    @Published var password = ""

    @Published var credentials = Credentials()

    private var cancellable: Cancellable? = nil


    init() {
        cancellable = $username.combineLatest($password).sink { tuple in
            self.credentials = Credentials(username: tuple.0, password: tuple.1)
        }

        credentials = Credentials(username: username, password: password)
    }
}

(it's been a bit so this code may not run immediately, but hopefully you see where this is going).

Sam
  • 2,350
  • 1
  • 11
  • 22
  • What if I have 5 publishers that I want to track? 10 publishers? It's a form that I'd like to update as soon as any of the data changes. I use it to enable/disable buttons to save the data. – Richard Topchii Feb 14 '22 at 00:39
  • 1
    make a model struct(not a class) including all of those fields in your form. and add a @Published variable for that and change it whenever you want, then you can subscribe to it to get any of those changes. – YodagamaHeshan Feb 14 '22 at 02:33
  • @Yodagama great suggestion, that's how I ended up doing. Exactly what I was looking for, thanks! – Richard Topchii Feb 16 '22 at 06:44