Question: How to ensure your value type dependencies reach where they need to go #379
Replies: 3 comments
-
Hi @BrentMifsud, withDependencies {
$0.credentials = viewModel.credentials
} operation: {
MainAppView()
} That only scopes the dependencies for the duration of initializing the I don't really have any other advice to give right now because I haven't fully thought through the problem you are trying to solve, but I did want to just mention that. |
Beta Was this translation helpful? Give feedback.
-
Oh that definitely explains why my problem is happening. I guess what I am trying to solve is, I have some dependencies that need to live as long as the app is alive and maintain their state. Some of them may need to be updated along the way (such as my middleware, it would need to get the credentials added to it when the user logs in). Another example of a dependency that I need to live for most of the app is I think the issue on my end is twofold:
Im guessing that I should probably have all my global dependencies in some sort of root level class, and that class should be the one calling Another question I have is: for |
Beta Was this translation helpful? Give feedback.
-
After some more playing around, I think I can better describe the issue I'm having... My app entry point is pretty straight forward. Basically alternates from showing a splash screen initially while anything is loading, to either an auth flow, or a main app flow depending if the user is logged in or not. struct RootPage: View {
@Bindable var model: AppModel
var body: some View {
Group {
switch model.phase {
case .splash:
SplashScreen()
.transition(.asymmetric(insertion: .identity, removal: .opacity))
case let .auth(authModel):
AuthNavigation(model: authModel) {
AuthPage()
}
.transition(.move(edge: .bottom))
case let .app(tabModel):
AppTabView(model: tabModel)
.transition(.opacity)
}
}
.animation(.default, value: model.phase)
}
} The issue I am having is when switching my environment from prod to staging, my AuthModel seems to reset to the default // inside the AppModel, I've verified that the values inside of the withDependencies block are correct after swapping my environment
private func updatePhase(with account: Account?, environment: APIEnvironment) async {
await withDependencies {
// swap to the right client for the environment
$0.requestInjectionMiddleware = environment == .production ? .production : .staging
$0.client = environment == .production ? Client.production : Client.staging
// update credentials in the middleware
// RequestInjectionMiddleware is a reference type,
await $0.requestInjectionMiddleware.updateCredentials(account?.credentials)
// Reset the auth and user clients so they pick up the above client change
$0.authClient = .live
$0.userClient = .live
} operation: {
phase = account == nil ? .auth(AuthModel()) : .app(TabModel())
}
} when My authClient and userClient are hit, they still have the original values from before the environment change. I have defined them both very similar to the examples the pointfree podcast described in the past: @DependencyClient
nonisolated struct AuthClient: Sendable {
struct LoginBody: Sendable {
var email, password, deviceName: String
}
struct SignUpBody: Sendable {
var firstName, lastName, email, password, deviceID: String
}
/// Authenticates a user using their login credentials.
///
/// - Parameter loginBody: An instance of `LoginBody` containing the user's email, password, and device name.
/// - Returns: An authentication token, upon successful login.
/// - Throws: `AuthError.invalidCredentials` if the credentials are incorrect,
/// `AuthError.serverError` if there is a server-side issue,
/// or other errors related to networking or data parsing.
var login: @Sendable (LoginBody) async throws -> Credentials
/// Registers a new user account using the provided sign-up information.
///
/// - Parameter signUpBody: An instance of `SignUpBody` containing the user's first name, last name, email, password, and device ID.
/// - Returns: An authentication token upon successful registration.
/// - Throws: `AuthError.unauthorizedSignup` if the sign-up is unauthorized,
/// `AuthError.serverError` for server-side or response issues,
/// or other errors related to networking or data parsing.
var signUp: @Sendable (SignUpBody) async throws -> Credentials
}
extension AuthClient {
static var live: AuthClient {
@Dependency(\.client) var client
return Self(
login: { loginBody in
// call client.login endpoint...
},
signUp: { signUpBody in
// call client.signUpEndpoint...
}
)
}
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I've used this library briefly at a previous company, but we were generally only passing around reference types with it.
I'm currently building my own app that leverages swift-openapi-generator.
One of the features of the swift-openapi-generator is that it provides you with the means of adding middleware to do things before and after your api client sends out a network request.
I am leveraging middleware to inject our auth token/client key into the headers of every network request.
Here is an example of my middleware:
and my network client:
Basically, credentials is an optional struct that holds onto the auth token and the refresh token.
After a user logs in, and the account is stored in the device keychain, I do the following in one of my views:
My main app view is essentially just a TabView with all the root tab pages of my app.
Each of them have view models with the Dependency property wrapper used for dependencies.
One thing ive noticed, is that when my middleware is being hit, credentials is always nil.
One thing I've tried to do to debug the issue is wrap my network call in a withDependencies. For example:
I'm kind of at a loss for how to solve this. I feel like I am not using the library correctly here.
Beta Was this translation helpful? Give feedback.
All reactions