Combine: Init изменяет параметр «self» - PullRequest
1 голос
/ 23 января 2020

Я возился с кодом 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 со значением внутри нее и сообщать этому объекту об отправке обновления. Но мне интересно, как другие обошли эту проблему, так как я бы тоже хотел вариант без представления модели.

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

Ответы [ 2 ]

4 голосов
/ 23 января 2020

Вы можете использовать onReceive, чтобы подписаться на Объединение Publisher с в SwiftUI View с. Таким образом, среда выполнения SwiftUI будет управлять подпиской для вас, даже если ваше представление может быть многократно воссоздано.

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

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

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

0 голосов
/ 23 января 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
...