Hey, yes we did.
On the ApiClient I added an addMiddleware
function. Middleware
has a request
and a response
function on it. Each of which is called before the request and after the response respectively.
So for instance, our AuthHeaders
middleware is defined like...
public extension Middleware {
static func authHeaders(
credentialsStore: CredentialsStore,
date: @escaping () -> Date,
device: UIDevice,
appVersion: String
) -> Middleware {
let headerStorage = HeaderStorage(
credentialsStore: credentialsStore,
date: date,
device: device,
appVersion: appVersion
)
return .init(
request: { request in
await headerStorage.process(request: &request)
},
response: { response, request in
await headerStorage.process(response: &response, request: request)
}
)
}
}
The apiclient then stores the middleware added to it and runs them each time a request happens...
Our ApiClient request looks like this...
func request(route: ServerRoute) async throws -> (Data, URLResponse) {
var urlRequestData = URLRequestData()
try router
.baseURL(baseUrlString)
.print(route, into: &urlRequestData)
guard var request = URLRequest(data: urlRequestData) else {
fatalError()
}
for m in middleware {
await m.request(&request)
}
var (data, response) = try await URLSession.shared.data(for: request)
for m in middleware {
try await m.response(&response, request)
}
return (data, response)
}
Then during start up we add the middleware to the ApiClient...
let apiClient = ApiClient.live(baseUrlString: baseURLString)
apiClient.addMiddleware(.authHeaders(
credentialsStore: credentialsStore,
date: Date.init,
device: .current,
appVersion: AppVersionInfo.live.fullVersionString()
))
apiClient.addMiddleware(.statusCodes)
apiClient.addMiddleware(.errorCodes)
And then set the dependency on the store... .dependency(\.apiClient, apiClient)