Skip to content

Conversation

iampatbrown
Copy link
Collaborator

This PR aims to allow a dependency nested inside another dependency to inherit the initialValues from its ancestor.

I ran into an issue while trying to override a dependency on a model. I'm unsure if my expectations are correct though. So feel free to close this PR if the behaviour is as intended. Here is a simplified example of my use case:

struct AppBundle: DependencyKey {
  var version: () -> String
  static var liveValue = Self {
    Bundle.main.infoDictionary?["CFBundleShortVersionString"].map { "\($0)" } ?? ""
  }
}

struct Client: DependencyKey {
  @Dependency(\.bundle) private var bundle
  var version: String { self.bundle.version() }
  static var liveValue = Self()
}

class AppModel: ObservableObject {
  @Dependency(\.client) var client
}

struct ContentView: View {
  @StateObject var model = withDependencies {
    $0.bundle.version = { "0.2.0" }
  } operation: {
    AppModel()
  }

  var body: some View {
    Text(model.client.version)
  }
}

@iampatbrown iampatbrown marked this pull request as draft March 23, 2023 22:40
@iampatbrown
Copy link
Collaborator Author

iampatbrown commented Mar 23, 2023

Hmmm... I didn't rerun the tests in release mode... The fix works for the included use case in release mode though.
@stephencelis @mbrandonw is it cool for me to leave this as a draft and look into it on the weekend?
Oh it's most likely because I'm reusing EagerParentDependency and EagerChildDependency which are being cached in the other test.

@iampatbrown iampatbrown marked this pull request as ready for review March 24, 2023 09:06
@mbrandonw
Copy link
Member

Hey @iampatbrown, thanks for investigating this and opening a PR!

We looked into this issue quite a bit this morning and found some new strange behavior, even with your fix. Consider this test:

  func testDependencyDependingOnDependency_Nested() {
     @Dependency(\.nestedParent) var nestedParent: NestedParentDependency
     struct Model {
       @Dependency(\.nestedParent) var nestedParent: NestedParentDependency
       @Dependency(\.nestedChild) var nestedChild: NestedChildDependency
     }

     let model = withDependencies {
       $0.nestedChild.value = 1
     } operation: {
       Model()
     }

     XCTAssertEqual(nestedParent.value, 1_729)
     XCTAssertEqual(model.nestedParent.value, 1) // ❌ 1729 != 1
     XCTAssertEqual(model.nestedChild.value, 1)
  }

This test fails. By resolving nestedParent at the test level it changes how nestedParent resolves in the model. That seems very bad.

It seems like more needs to be figured out here. We are worried that perhaps using @Dependency from other dependencies is not safe in non-single entry point systems.

@iampatbrown
Copy link
Collaborator Author

Thanks for this @mbrandonw.

I've definitely overlooked some of the intricacies here 😅

It seems like more needs to be figured out here. We are worried that perhaps using @dependency from other dependencies is not safe in non-single entry point systems.

Yeah. I think as soon as a Dependency escapes the withDependencies context we have limited information and control over any child dependencies.

The more I think about it, the less confident I am in this change. While it solves the immediate issue I was facing... perhaps that can be solved some other way.

@stephencelis stephencelis merged commit 0f34848 into pointfreeco:main Apr 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants