The issue raised in this thread was only one of a couple I faced when writing my property wrapper. I'm trying to summarize my findings below. It's mainly for me to remember, but maybe somebody will find it interesting.
This is the initial property wrapper (unnecessary code removed), that I expected to work properly for every use case. I'm using Scheduler
in this example (I'm using it in my property wrapper) but it could by any protocol, like Hashable
.
@propertyWrapper
class WrapperOfDreams<Value> {
var wrappedValue: Value
init<S: Scheduler>(wrappedValue: Value, loop: S = RunLoop.main) {
self.wrappedValue = wrappedValue
// receive published values on loop
}
}
Use cases that I expected to work with comment why they don't work (unless they do).
class Test_WrapperfDreams {
// NOK: default parameter for generic types not allowed
@WrapperOfDreams var v1 : Double
// NOK: default parameter for generic types not allowed
@WrapperOfDreams var v2 : Double = 1.0
// NOK: default parameter for generic types not allowed
@WrapperOfDreams var v3 : Double?
// NOK: default parameter for generic types not allowed
@WrapperOfDreams var v4 : Double? = nil
// Cases below assume that default parameter for generic type IS allowed
// NOK: deffered initialization of multi-argument property wrappers not possible
// see link below
@WrapperOfDreams(loop: RunLoop.main) var v5 : Double
// NOK: implicit nil doesn't work for wrappedValue (should compile like v8)
// see SR-14411 link below
@WrapperOfDreams(loop: RunLoop.main) var v6 : Double?
// OK
@WrapperOfDreams(loop: RunLoop.main) var v7 : Double = 1.0
// OK
@WrapperOfDreams(loop: RunLoop.main) var v8 : Double? = nil
init() {
v1 = 1.0
v5 = 1.0
}
}
Deffered initialization of multi-argument property wrappers not possible
Extended version of property wrapper (created before I was made aware of existence of AnyScheduler solution).
@propertyWrapper
class Wrapper<Value> {
var wrappedValue: Value
// workaround for "default value for generic types not possible" problem
init<V> (wrappedValue: Value = nil) where Value == V? {
self.wrappedValue = wrappedValue
// don't use receive(on:)
}
// workaround for "SR-14411" problem
init<V, S>(wrappedValue: Value = nil, loop: S) where Value == V?, S: Scheduler {
self.wrappedValue = wrappedValue
// receive published values on loop
}
// workaround for "default value for generic types not possible" problem
init (wrappedValue: Value) {
self.wrappedValue = wrappedValue
// don't use receive(on:)
}
init<S>(wrappedValue: Value, loop: S) where S: Scheduler {
self.wrappedValue = wrappedValue
// receive published values on loop
}
}
Use cases for this wrapper
class Test {
@Wrapper var v1 : Double
@Wrapper var v2 : Double?
@Wrapper var v3 : Double = 1.0
@Wrapper var v4 : Double? = nil
//NOK - deffered initialization for multiparameter property wrappers not possible
// @Wrapper(loop: RunLoop.main) var v5 : Double
@Wrapper(loop: RunLoop.main) var v6 : Double?
@Wrapper(loop: RunLoop.main) var v7 : Double = 1.0
@Wrapper(loop: RunLoop.main) var v8 : Double? = nil
init() {
v1 = 1.0
// v5 = 1.0
}
}