问题
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