4

I would like to create a property wrapper for CurrentValueSubject. I have done this like that:

@propertyWrapper
public class CurrentValue<Value> {

    public var wrappedValue: Value {
        get { projectedValue.value }
        set { projectedValue.value = newValue }
    }

    public var projectedValue: CurrentValueSubject<Value, Never>

    public init(wrappedValue: Value) {
        self.projectedValue = CurrentValueSubject(wrappedValue)
    }

}

This works but there is a little thing I would like to change with it - use struct instead of class. The problem with using struct for this is that sometimes I could get Simultaneous accesses error. And I know why, this happens when in sink from this publisher I would try to read the value from wrapped value. So for example with code like this:

@CurrentValue
let test = 1
$test.sink { _ in
    print(self.test)
} 

And I more or less know why - because when projectedValue executes its observation, wrapped value is still in process of setting its value. In class this is ok, because it would just change the value, but with struct it actually modifies the struct itself, so Im trying to write and read from it at the same time.

My question is - is there some clever way to overcome this, while still using struct? I don't want to dispatch async.

Also I know that @Projected works similarly to this propertyWrapper, but there is a big difference - Projected executes on willSet, while CurrentValueSubject on didSet. And Projected has the same issue anyway.

I know that I can read the value inside the closure, but sometimes Im using this with various function calls, that might eventually use self.test instead.

Damian Dudycz
  • 2,622
  • 19
  • 38

1 Answers1

0

Try my implementation

@propertyWrapper
class PublishedSubject<T> {
    
    var wrappedValue: T {
        didSet {
            subject.send(wrappedValue)
        }
    }
    
    var projectedValue: some Publisher<T, Never> {
        subject
    }
    
    private lazy var subject = CurrentValueSubject<T, Never>(wrappedValue)
    
    init(wrappedValue: T) {
        self.wrappedValue = wrappedValue
    }
}
Nikaaner
  • 1,022
  • 16
  • 19