How do you reuse nested property wrappers?

You can't nest property wrappers inside of protocols, so what do you do instead? Type aliases don't seem to work.

import SwiftUI

@propertyWrapper struct ModuleName_Wrapped<Value, Storage: ModuleName_Storage> {
  let wrappedValue = "๐ŸŽ"
}

protocol ModuleName_Storage: DynamicProperty {
  associatedtype Value
  typealias Wrapped = ModuleName_Wrapped<Value, Self>
}

extension AppStorage: ModuleName_Storage  { }
extension SceneStorage: ModuleName_Storage  { }

struct S {
  let x = AppStorage<Int>.Wrapped() // fine.
  @AppStorage.Wrapped var y = โ€ฆ // Unknown attribute 'AppStorage.Wrapped'
}

It works fine when you put typealias inside a concrete type. Sounds like a bug... :thinking:

1 Like

This compiles for me:

@propertyWrapper struct ModuleName_Wrapped<Value, Storage: ModuleName_Storage> {
    let wrappedValue = "๐ŸŽ"
}

protocol ModuleName_Storage: DynamicProperty {
    associatedtype Value
    associatedtype Wrapped = ModuleName_Wrapped<Value, Self>
}

extension AppStorage: ModuleName_Storage { }
extension SceneStorage: ModuleName_Storage { }

struct S {
    let x = AppStorage<Int>.Wrapped()
    @AppStorage<Int>.Wrapped var y: String
}
1 Like

Nice!

โ€ฆbut how can you extend the types (AppStorage.Wrapped or SceneStorage.Wrapped) then?

Depending on what your exact goals are, you can try something like this:

@propertyWrapper struct ModuleName_Wrapped<Value, Storage: ModuleName_Storage> where Value == Storage.Value {
    var storage: Storage
    var wrappedValue: Value {
        get { storage.wrappedValue }
        set { storage.wrappedValue = newValue }
    }
}

protocol ModuleName_Storage: DynamicProperty {
    associatedtype Value
    associatedtype Wrapped = ModuleName_Wrapped<Value, Self>
    var wrappedValue: Value { get set }
}

extension AppStorage: ModuleName_Storage { }

struct S {
    @AppStorage<String>.Wrapped var y: String
}

I don't have the Xcode beta installed, so I can't do much testing (other than checking if it compiles).

Play around with the generic constraints if you need to do any sort of type-conversion in the outer property wrapper.

:thinking:?

I need to extend the Wrapped types.

What I meant is: Depending on what exactly you want your extended property wrapper to do (eg. type conversion, parsing, serialization, etc.), you can try something similar to the code snippet.

I've tried a few things. I'll show my best idea shortly.

The snippet doesn't look like an extension to the nested types, to me.

1 Like

https://github.com/JessyCatterwaul/SwiftUICodableStorage

Most relevant:

/// Common functionality for `โ€ฆStorage.Codable`s.
/// - Note: Should be used directly, instead of being a property of those,
/// if property wrappers become `typealias`-able.
@propertyWrapper struct CodableStorage<Value: Codable> {
public extension AppStorage where Value: Swift.Codable {
  /// Local `Data`-storage for `Codable`s .
  /// - Warning: Not a good match for tvOS, due to severely limited local storage allowance.
  @propertyWrapper struct Codable {
    public var wrappedValue: Value {
      get { value }
      set { value = newValue }
    }

    @CodableStorage private var value: Value
  }
}
public extension SceneStorage where Value: Swift.Codable {
  /// Local `Data`-storage for `Codable`s .
  /// - Warning: Not a good match for tvOS, due to severely limited local storage allowance.
  @propertyWrapper struct Codable {
    public var wrappedValue: Value {
      get { value }
      set { value = newValue }
    }

    @CodableStorage private var value: Value
  }
}
@AppStorage.Codable("") var bool = Bool()
@SceneStorage.Codable("") var int = Int()

Agreed. Anyone know if this has been filed as such? If not I can do so...

EDIT: Filed as SR-14797

2 Likes