简体   繁体   中英

Combine: Init mutating 'self' parameter

I'm messing around with Combine code and Swift UI and came across this problem. Effectively I want to pass a Publisher into a View and have that View update every time the publisher publishes an update.

Here's a sample playground that will not compile. Instead it puts up an error - Escaping closure captures mutating 'self' parameter on the .sink(... line.

import Combine
import SwiftUI

struct MyView: View {

    let cancellable: AnyCancellable
    @State var current: Int = 0

    init<P>(publisher: P) where P: Publisher, P.Output == Int, P.Failure == Never {
        cancellable = publisher.sink { value in
            self.current = value
        }
    }

    var body: some View {
        Text("\(current)")
    }
}

let subject = PassthroughSubject<Int, Never>()
let x = MyView(publisher: subject)
subject.send(5)

Currently I've changed the code to use an ObservableObject view model with the value inside it and telling that object to send an update. But I'm interested how others have gotten around this problem as I'd like a none view model option too.

What have you guys done?

You can use onReceive to subscribe to Combine Publisher s in SwiftUI View s. That way the SwiftUI runtime will manage the subscription for you, even while your view may be recreated many times.

struct MyView: View {
    @State var current: Int = 0

    var body: some View {
        Text("\(current)")
            .onReceive(somePublisher) { self.current = $0 }
    }
}

However, using ObservableObject s directly is often a better idea, as they integrate directly into SwiftUI.

Currently I'm doing something like this:

import Combine
import SwiftUI
import PlaygroundSupport

class Model: ObservableObject {
    var current: Int = 0 {
        willSet {
            self.objectWillChange.send()
        }
    }
}

struct MyView: View {

    @EnvironmentObject var model: Model

    var body: some View {
        Text("\(model.current)")
    }
}

let model = Model()
let myView = MyView().environmentObject(model)
PlaygroundPage.current.liveView = UIHostingController(rootView: myView)

model.current = 5

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