How to use @Dependency
on Codable
types: "does not conform to protocol 'Decodable'"
#283
-
Apologies if this has been asked/addressed before but I couldn't find anything in the dicussions or issues. We've got a type struct Model: Codable {
@Dependency(\.date.now) var now
var value: Int
func foo() -> String { "\(value) \(now)" }
} fails to compile with
Do you have any guidance how to best instrument this use-case? I can think of a couple of mechanisms:
struct Model: Codable {
@Dependency(\.date.now) var now
var value: Int
private enum CodingKeys: CodingKey {
case value
}
init(from decoder: any Decoder) throws {
let container: KeyedDecodingContainer<DepTest.Model.CodingKeys> = try decoder.container(keyedBy: DepTest.Model.CodingKeys.self)
self.value = try container.decode(Int.self, forKey: DepTest.Model.CodingKeys.value)
}
func encode(to encoder: any Encoder) throws {
var container: KeyedEncodingContainer<DepTest.Model.CodingKeys> = encoder.container(keyedBy: DepTest.Model.CodingKeys.self)
try container.encode(self.value, forKey: DepTest.Model.CodingKeys.value)
}
func foo() -> String { "\(value) \(now)" }
} Even in this simple case it adds a lot of boilerplate that we didn't need before. Moreover, any changes to the set of properties now also requires syncing up the In our case
The downside is that we'd have to add another depenendency, and for a very critical part of our app. I've seen there's the
That would be feasible, although it would clutter up some call-sites. I guess it could be made not to be too terrible with default arguments but it still feels quite counter to the elegance of the Is there a better solution than these three? How are people dealing with |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 12 replies
-
Hey @finestructure, I think first and foremost we would advise against having a However, if you do want to continue doing this you could add a |
Beta Was this translation helpful? Give feedback.
Hey @finestructure, ok I think I see the problem. It isn't the unstructured task, those specifically do inherit task locals (only
Task.detached
does not).However, escaping closures lose dependencies. So by the time you get inside
transaction { … }
you have lost your dependencies, and similarly formakeFutureWithTask { … }
. In general, escaping closures are the enemy of structured programming, and so therefore also the enemy of task locals or any of the tools in structured concurrency.Luckily we do have a tool for this though. You can use
withEscapedDependencies
to escape dependencies in order to propagate them into an escaping closure: