Комбинировать: инициировать изменяющийся параметр 'self'

Я возился с кодом Combine и Swift UI и столкнулся с этой проблемой. Фактически я хочу передать Publisher в View и получать View обновление каждый раз, когда издатель публикует обновление.

Вот примерная площадка, которая не компилируется. Вместо этого он выдает ошибку - Escaping closure captures mutating 'self' parameter в строке .sink(....

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)

В настоящее время я изменил код, чтобы использовать модель представления ObservableObject со значением внутри нее и указанием этому объекту отправить обновление. Но мне интересно, как другие решили эту проблему, так как я бы тоже хотел вариант без представления модели.

Что вы наделали, ребята?


person drekka    schedule 23.01.2020    source источник


Ответы (2)


Вы можете использовать onReceive для подписки на Combine Publishers в SwiftUI Views. Таким образом, среда выполнения SwiftUI будет управлять подпиской за вас, даже если ваше представление может быть воссоздано много раз.

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

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

Однако использование ObservableObjects напрямую часто является лучшей идеей, поскольку они интегрируются непосредственно в SwiftUI.

person Jon Shier    schedule 23.01.2020
comment
Интересно. Я займусь этим. Спасибо. - person drekka; 23.01.2020

Сейчас делаю что-то вроде этого:

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
person drekka    schedule 23.01.2020
comment
Вы можете использовать оболочку свойств @Published в Model - person krjw; 23.01.2020