[Swift 5.1] PropertyWrapper enclosing self in wrappedValue and projectedValue

Updated
Mar 31, 2020 10:57 AM
Created
Mar 31, 2020 10:50 AM
Tags
SwiftMemo
Keywords
Date

PropertyWrapper in Swift 5 supports to enclose instance that owner of PropertyWrapper.

When accessing wrappedValue or projectedValue, we can override get and set with subscript like following sample code.

⚠️

enclosing instance feature is not officially. But I think it would be the official feature because this has strong dependency on SwiftUI.

 @available(*, unavailable)
  public var wrappedValue: T {
    get { fatalError() }
    set { fatalError() }
  }
  
  @available(*, unavailable)
  public var projectedValue: Getter<T> {
    get { fatalError() }
    set { fatalError() }
  }

public static subscript(
      _enclosingInstance instance: Store,
      wrapped wrappedKeyPath: KeyPath<Store, T>,
      storage storageKeyPath: KeyPath<Store, Self>
    ) -> T {

public static subscript(
      _enclosingInstance instance: Store,
      projected wrappedKeyPath: KeyPath<Store, Getter<T>>,
      storage storageKeyPath: KeyPath<Store, Self>
    ) -> Getter<T> {

This code is sketch code in Verge

public enum GetterContainer<Store: StoreType> {
  
}

extension GetterContainer {
  
  @available(iOS 13, *)
  @propertyWrapper
  public struct Computed<T> {
          
    private final class Inner {
      var getter: Getter<T>?
      var lock = os_unfair_lock_s()
    }
    
    @available(*, unavailable)
    public var wrappedValue: T {
      get { fatalError() }
      set { fatalError() }
    }
    
    @available(*, unavailable)
    public var projectedValue: Getter<T> {
      get { fatalError() }
      set { fatalError() }
    }

    private let make: (GetterBuilderMethodChain<GetterBuilderTrait.Combine, Store, Store.Value>) -> Getter<T>
    
    private let inner: Inner = .init()
            
    public init(make: @escaping (GetterBuilderMethodChain<GetterBuilderTrait.Combine, Store, Store.Value>) -> Getter<T>) {
      self.make = make
    }
          
    public static subscript(
      _enclosingInstance instance: Store,
      wrapped wrappedKeyPath: KeyPath<Store, T>,
      storage storageKeyPath: KeyPath<Store, Self>
    ) -> T {
      get {
                      
        let inner = instance[keyPath: storageKeyPath].inner
        
        os_unfair_lock_lock(&inner.lock)
        defer {
          os_unfair_lock_unlock(&inner.lock)
        }
                
        guard let getter = inner.getter else {
          let new = instance[keyPath: storageKeyPath].make(instance.getterBuilder())
          inner.getter = new
          return new.value
        }
        
        return getter.value
      }
      set {
        
      }
    }
    
    public static subscript(
      _enclosingInstance instance: Store,
      projected wrappedKeyPath: KeyPath<Store, Getter<T>>,
      storage storageKeyPath: KeyPath<Store, Self>
    ) -> Getter<T> {
      get {
        
        let inner = instance[keyPath: storageKeyPath].inner
        
        os_unfair_lock_lock(&inner.lock)
        defer {
          os_unfair_lock_unlock(&inner.lock)
        }
        
        guard let getter = inner.getter else {
          let new = instance[keyPath: storageKeyPath].make(instance.getterBuilder())
          inner.getter = new
          return new
        }
        
        return getter
      }
      set {
        
      }
    }
    
  }
  
}