How to define a protocol to include a property with @Published property wrapper

瘦欲@ 提交于 2019-12-06 14:35:20

问题


When using @Published property wrapper following current SwiftUI syntax, it seems very hard to define a protocol that includes a property with @Published, or I definitely need help :)

As I'm implementing dependency injection between a View and it's ViewModel, I need to define a ViewModelProtocol so to inject mock data to preview easily.

This is what I first tried,

protocol PersonViewModelProtocol {
    @Published var person: Person
}

I get "Property 'person' declared inside a protocol cannot have a wrapper".

Then I tried this,

protocol PersonViewModelProtocol {
    var $person: Published
}

Obviously didn't work because '$' is reserved.

I'm hoping a way to put a protocol between View and it's ViewModel and also leveraging the elegant @Published syntax. Thanks a lot.


回答1:


This is how I suppose it should be done:

public protocol MyProtocol {
    var _person: Published<Person> { get set }
}

class MyClass: MyProtocol, ObservableObject {
    @Published var person: Person

    public init(person: Published<Person>) {
        self._person = person
    }
}

Although the compiler seems to sort of like it (the "type" part at least), there is a mismatch in the property's access control between the class and the protocol (https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html). I tried different combinations: private, public, internal, fileprivate. But none worked. Might be a bug? Or missing functionality?




回答2:


We've encountered this as well. As of Catalina beta7, there doesn't seem to be any workaround, so our solution is to add in a conformance via an extension like so:


struct IntView : View {
    @Binding var intValue: Int

    var body: some View {
        Stepper("My Int!", value: $intValue)
    }
}

protocol IntBindingContainer {
    var intValue$: Binding<Int> { get }
}

extension IntView : IntBindingContainer {
    var intValue$: Binding<Int> { $intValue }
}

While this is a bit of extra ceremony, we can then add in functionality to all the IntBindingContainer implementations like so:

extension IntBindingContainer {
    /// Reset the contained integer to zero
    func resetToZero() {
        intValue$.wrappedValue = 0
    }
}




回答3:


You have to be explicit and describe all synthetized properties:

protocol WelcomeViewModel {
    var person: Person { get }
    var personPublished: Published<Person> { get }
    var personPublisher: Published<Person>.Publisher { get }
}

class ViewModel: ObservableObject {
    @Published var person: Person = Person()
    var personPublished: Published<Person> { _person }
    var personPublisher: Published<Person>.Publisher { $person }
}


来源:https://stackoverflow.com/questions/57517320/how-to-define-a-protocol-to-include-a-property-with-published-property-wrapper

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!