I have a Networking client that contains some shared mutable state. I decided to make this class an actor to synchronize access to this state. Since it's a networking client, it needs to make calls into URLSession's async data functions.
struct MiddlewareManager {
var middleware: [Middleware] = []
func finalRequest(for request: URLRequest) -> URLRequest { ... }
}
actor Networking {
var middleware = MiddlewareManager()
var session: URLSession
func data(from request: URLRequest) async throws -> (Data, URLResponse) {
let finalRequest = middleware.finalRequest(for: request)
let (data, response) = try await session.data(for: finalRequest)
let finalResponse = middleware.finalResponse(for: response)
// ...
return (data, finalResponse)
}
}
Making a network request could be a long running operation. From my understanding long running or blocking operations should be avoided inside of actors. I'm pretty sure this is non blocking due to the await call, the task just becomes suspended and the thread continues on, but it will inherit the actor's context. As long URLSession creates a new child task then this is ok, since the request isn't actually running on the actor's task?
My alternative approach after watching the tagged WWDC 2022 videos was to add the nonisolated tag to the function call to allow the task to not inherit the actor context, and only go to the actor when needed:
nonisolated func data(from request: URLRequest) async throws -> (Data, URLResponse) {
let finalRequest = await middleware.finalRequest(for: request)
let (data, response) = try await session.data(for: finalRequest)
let finalResponse = await middleware.finalResponse(for: response)
// ...
return (data, finalResponse)
}
Is this the proper or more correct way to solve this?