简体   繁体   中英

Is there a way to call assign() more than once in Swift Combine?

I would like to update some variables with values received when handling a newly published value. For example, given:

class ProductViewModel: ObservableObject {
    @Published var PublishedX: Int = 0
    @Published var PublishedY: Int = 0
    @Published var PublishedProduct: Int = 0
    // ...
    init() {
        productPublisher = Publishers.CombineLatest(external.XPublisher, internal.YPublisher)
            // .assignAndContinue(\.PublishedX, \.PublishedY) // something like this
            .flatMap(MyPublishers.secretMultiplication)
            .assign(to: \.PublishedProduct, on: self)
     }
 }

I would like to also assign the new values of XPublisher and YPublisher to variables (PublishedX and PublishedY respectively).

Is there a way to set these two variables and then continue handling the event?

UPDATE

If your deployment target is a 2020 system (like iOS 14+ or macOS 11+), you can use a different version of the assign operator to avoid retain cycles and to avoid storing cancellables:

init() {
    external.XPublisher.assign(to: $PublishedX)
    external.YPublisher.assign(to: $PublishedY)
    external.XPublisher
        .combineLatest(external.YPublisher) { $0 * $1 }
        .assign(to: $PublishedProduct)
}

ORIGINAL

There is no built-in variant of assign(to:on:) that returns another Publisher instead of a Cancellable .

Just use multiple assign s:

class ProductViewModel: ObservableObject {
    @Published var PublishedX: Int = 0
    @Published var PublishedY: Int = 0
    @Published var PublishedProduct: Int = 0

    init() {
        external.XPublisher
            .assign(to: \.PublishedX, on: self)
            .store(in: &tickets)
        internal.YPublisher
            .assign(to: \.PublishedY, on: self)
            .store(in: &tickets)
        external.XPublisher
            .combineLatest(internal.YPublisher) { $0 * $1 }
            .assign(to: \.PublishedProduct, on: self)
            .store(in: &tickets)
    }

    private var tickets: [AnyCancellable] = []
}

Note that these subscriptions create retain cycles. Swift will not be able to destroy an instance of ProductViewModel until the tickets array is cleared. (This is not a property of my suggestion. Your original code also needs to store its subscription somewhere, else it will be cancelled immediately.)

Also, the existence of PublishedProduct is questionable. Why not just a computed property?

var product: Int { PublishedX * PublishedY }

You may want to check out this library

From their ReadMe :

var label1: UILabel
var label2: UILabel
var text: UITextField

["hey", "there", "friend"]
    .publisher
    .assign(to: \.text, on: label1,
            and: \.text, on: label2,
            and: \.text, on: text)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM