I need to implement a not too complicated use case in SwiftUI: Login with username and password, authToken and refreshToken are come back from API, store the refreshToken in KeyChain and the authToken in AppState (which is an ObservableObject (environmentObject), code below). If any of my requests come back with 401 HTTP response code (so the accessToken is expired), I need to refresh the accessToken and retry my original request with the new token. But the problem is I call the RefreshTokenRequest from my ViewModel, and I cannot pass token "back" to the appState through the View. So the question is, how can I access my environmentObject from my ViewModel? Is it possible? Here's my code simplified at playground file:
class AppState: ObservableObject {
@Published var isLoggedIn: Bool = false
@Published var token: String = ""
}
struct ProtectedView: View {
@EnvironmentObject var appState: AppState
var body: some View {
VStack {
RollerListView(token: self.appState.token)
}
}
}
struct Roller: Codable, Hashable {
var scooter_name: String;
}
class RollerListVM: ObservableObject {
@Published var rollers = [Roller]()
func getRollers(token: String) {
print("FETCHING ROLLERS...")
/// Here's my API request to get the array of rollers. If the token is expired, I need to refresh it with a new request and I need to pass the new token to the appState.token variable, because I need to use the new token in all of my request instead of expired one.
/// IF THE TOKEN IS REFRESHED, HOW CAN I PASS IT TO THE AppState.token ???
DispatchQueue.main.async {
self.rollers = [Roller(scooter_name: "scooter 1"), Roller(scooter_name: "scooter 2")]
}
}
init(token: String) {
self.getRollers(token: token)
}
}
struct RollerListView: View {
@ObservedObject private var rollerListVM: RollerListVM;
@EnvironmentObject var appState: AppState
var body: some View {
List(rollerListVM.rollers, id: \.self) { roller in
HStack {
Text(roller.scooter_name)
}
}
}
init(token: String) {
self.rollerListVM = RollerListVM(token: token)
}
}
I tried to pass a closure function to the ViewModel and call it if after token refresh (with the new token as a parameter), but it's not working, because the self parameter is mutating inside the closure.
How should I do that?
RollerListVMetc with the token string, rather pass theAppStateobject and have the view model obtain the token from that as needed. If the token is expired, call arefreshTokenmethod on theAppStateobject so that it updates itstokenproperty