-
Hi all I'm having a little bit of a problem migrating some of my dependency containers to the new I guess in my own static let liveValue: SomeDependency = {
do {
return try SomeDependency.live()
} catch {
fatalError("Couldn't create SomeDependency")
}
}() but is there any way that Perhaps a bigger dilemma is with complex dependencies which can rely on each other. static let liveValue: APIClient = {
@Dependency(\.authClient) var authClient
return APIClient.live(authClient: authClient)
}() Another question is what happens when my live dependency is created with configuration data - i.e. a What's everyone else doing regarding these two use cases? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
Hi @rhysm94, I'd be curious how this looked in the pre- I think in both pre- and post- Instead, any notions of lifecycle and failability should be baked into the internals of the dependency rather than the creation of the dependency. For example, a location manager forever lives in the application, even if you haven't started it or are listening for location updates. Then, when a view appears you can start the manager, and when it disappears you can stop the manager. And in your example, your dependency could have a throwing struct Bootstrap: ReducerProtocol {
enum State {
case bootstrapping
case bootstrapped(App.State)
}
enum Action {
case onLaunch
case bootstrapResponse(success: Bool)
case bootstrapped(App.Action)
}
@Dependency(\.client) var client
var body: some ReducerProtocol<State, Action> {
Reduce { state, action in
switch action {
case .onLaunch:
return .run { send in
try self.client.initializer()
// Initialize any other dependencies...
await send(.bootstrapResponse(success: true))
} catch: { error, send in
await send(.bootstrapResponse(success: false))
}
case .bootstrapResponse(success: true):
state = .bootstrapped(App.State())
return .none
case .bootstrapResponse(success: false):
// Do something here... show an alert?
return .none
case .bootstrapped:
return .none
}
}
Scope(state: /State.bootstrapped, action: /Action.bootstrapped) {
App()
}
}
} And the view would use And then for your second question:
This is something that @stephencelis and I need to explore more, and I hope others share their experience too. As of right now we think using You do have to be mindful of how dependencies can be overridden in an application. For example, in the snippet you posted: static var liveValue: APIClient {
@Dependency(\.authClient) var authClient
return APIClient.live(authClient: authClient)
} …you are capturing (side note: I removed the Now this could work totally fine, but if you have the concept of overriding the auth client in a certain child feature, then this may not work as you expect. Because you are capturing the Instead of passing an APIClient(
// …a bunch of endpoints
login: { username, password in
@Dependency(\.authClient) authClient
let user = try await // network request
authClient.login(user)
},
logout: {
@Dependency(\.authClient) authClient
authClient.logout()
}
) This will guarantee that you are using the correct auth client within each endpoint of the API client. Those are just some quick thoughts. I do think there are a lot of really fun and interesting things left to explore in dependencies, and hope everyone shares what they find! |
Beta Was this translation helpful? Give feedback.
-
Wow, thank you for getting back to me with such a detailed answer so quickly! On the first point, yes - if, say, our final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
private let store: Store<AppState, AppAction>
var keyWindow: UIWindow?
override init() {
do {
let authClient = try AuthClient.live()
self.store = Store(
initialState: AppState(),
reducer: .app,
environment: AppEnvironment(
authClient: authClient,
apiClient: .live(authClient: authClient)
... // other dependencies, some of which are also throwing
)
} catch {
fatalError("Unable to initialise required dependencies")
}
super.init()
}
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) { ... }
} In moving a couple of these throwing to I really like the even more local use of I'll proceed for now with Thanks again! Really appreciate the quick response! |
Beta Was this translation helpful? Give feedback.
Hi @rhysm94, I'd be curious how this looked in the pre-
@Dependency
world. If a dependency failed to initialize did you just not create an environment, and hence not create the root store for your application?I think in both pre- and post-
@Dependency
it is best to think of dependencies as a thing that lives forever in the application. It has no lifecycle as a top-level concept and as such as no ability to fail to initialize.Instead, any notions of lifecycle and failability should be baked into the internals of the dependency rather than the creation of the dependency. For example, a location manager forever lives in the application, even if you haven't started it or are listening for loc…