pointfreeco/swift-dependencies

Parent dependency is resolved from cache after child has changed, instead of being recalculated

kirillsh opened this issue · 1 comments

Description

In our project we're using dependencies that rely on other lower level dependencies. We're usually doing that by declaring @Dependency(\.child) property wrapper inside parent's implementation of liveValue.
But because parent's value is cached upon its first resolution -> no matter how we override the child's value, the parent will not be recalculated capturing its child's initial value.
This might produce unexpected side effects like in the example below.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

Parent.liveValue is recalculated after Child is overwritten.

Actual behavior

Parent.liveValue is resolved from cache capturing the initial Child value.

Steps to reproduce

// -------- Declaring dependencies

struct Parent: DependencyKey {
    let child: Child
    
    static var liveValue: Parent {
        @Dependency(Child.self)
        var child
        
        return Self(child: child)
    }
}

struct Child: DependencyKey {
    let id: String
    
    static let liveValue = Child(id: "Child")
}

// --------- Consuming dependencies

withDependencies {
    $0[Child.self] = Child(id: "Overriden Child")
} operation: {
    @Dependency(Parent.self)
    var parent
    
    print(parent.child.id) // "Overriden Child"
}

@Dependency(Parent.self)
var parent

print(parent.child.id) // Expected: "Child", Actual: "Overriden Child"

Dependencies version information

1.3.0

Destination operating system

iOS 17

Xcode version information

15.4

Hi @kirillsh, the behavior you have demonstrated above is to be expected. By doing this:

static var liveValue: Parent {
  @Dependency(Child.self) var child      
  return Self(child: child)
}

…you have decided to resolve the Child dependency at this moment and pass a concrete version of the child to Parent. There will not be another resolution of this dependency at a later time, and so nothing can change its value. The Child object is held inside the Parent outside of the dependency system.

I'm not entirely sure what you are trying to accomplish in your real code. Perhaps you can share something more real world. But since this isn't an issue with the library I am going to convert it to a discussion, and please feel free to continue the conversation over there.