Generics in the environment

Hi all, I am using TCA and would like to add a dependency which wraps around UserDefaults in a type safe manner. Normally I might have its implementation look like this:

public struct PropertyPersistence {
    public func fetchProperty<T: Codable>(with key: String) -> T? {
        fatalError("implementation here")
    }
    public func set<T: Codable>(property: T, with key: String) {
        fatalError("implementation here")
    }
}

I would also normally wrap this in a protocol so that I can easily control it when testing. When translating this to the TCA environment, however, I run into an issue with generics. If I create this as a struct with closures as properties, I lose the ability to have generic implementations. I could add them to the type itself, but then my environment would need to have an entry for each specific type:

public struct PropertyPersistence<T: Codable> {
    public var fetchProperty: (String) -> T?
    public var setProperty: (T, String) -> Void
}

extension PropertyPersistence {
     static var live: Self(fetchProperty: { ... }, setProperty: { ... })
}

struct Environment {
    var propertyPersistenceString: PropertyPersistence<String>
    var propertyPersistenceInt: PropertyPersistence<Int>
    ....
}

I could see one benefit here being having a very clear dependency graph....but it still seems a bit overly restrictive and redundant.

Does anyone have any ideas around how to avoid keeping multiple variants of a generic dependency in the environment?

Have you considered just using a protocol for this dependency? You can have dependencies in your environment that are protocols as long as they don’t have associated type requirements.

Maybe one day Swift will get support for generic closure parameters (and parameter labels too hopefully!).

1 Like

That is definitely an option!

Though I used to reach for a protocol for all of my dependencies, I now find that a bit unsavory since it really, realistically, will only ever have 2 conformances. But perhaps without support for generic closures that simply isn't possible today.