Main actor-isolated property error in non isolated context

Hi all, I have this user agent in our Networking package when we make network requests. This is used in different places in entire codebase.

private var userAgent: String {
    let currentModel = UIDevice.current.model
    let osVersion = UIDevice.current.systemVersion
}

I am migrating the app to Swift 6 and started to get this warning

The whole networking module is conforming to Sendable protocol but this warning I have no idea how to fix. Is there a way I can fix this with minimum changes. Any help is highly appreciated.

2 Likes

Hello! Great question.

What this is telling you is UIDevice.current is only safe to access from the MainActor. Meaning it is main-thread-only.

This looks like a static value. One potential option would be to instead create this string on the main thread at some high-level entry point to your application. And then, pass it down into wherever you create the networking system that needs it.

1 Like

This is something we could do, but passing user agent info in each network request is not a good design and extra overhead just to satisfy this warning.

Though I have though about using UserDefaults to rescue here like below,

let currentModel = UserDefaults.standard.string(forKey: "UIDevice.current.model")

let osVersion = UserDefaults.standard.string(forKey: "UIDevice.current.systemVersion")

and setting model and osVersion in the AppDelegate. This is hacky solution but works.

I wasn't suggesting passing it into each request! I was suggesting creating the string on the MainActor, and then passing it into your system. From there you should be able to maintain the same API you have today (computed property -> stored property).

2 Likes

Further, I think it's worthwhile filing a bug about this. I don't immediately see why these UIDevice properties need MainActor isolation. This could be an oversight!

1 Like

Sure I totally agree, will report as a bug for sure. Thanks for the help.

1 Like

Network requests usually async, why not mark property as MainActor isolated and await for it during request? :thinking:

1 Like

We could do it but as I have mentioned, the issue is networking module and await for each request is an extra overhead which is not good in terms of performance.

I don’t get that, you anyway accessing it during request?

Right now we accessing it directly like mentioned above. By the way I have reported as a potential bug here, waiting for the response.

I don't think awaiting small variable will decrease your performance somehow. This is correct behaviour in the end—there are some stuff in UIDevice (it's UI in the end) that should run on main thread, accessing it from other threads is basically a side effect.
And regarding the ticket—UIDevice being @MainActor is correct, think what @mattie ment is that this exact properties model and systemVersion shouldn't.

2 Likes

Since UIDevice info clearly is not going to change during app launch, I think the best option pass it on init or await it once and cache, then use without any additional actions in requests. I don’t think there is performance impact if you just await as @jaleel suggested, but I think in general it is better to reduce number of awaits if possible, including such cases as well.

2 Likes

Totally agree with taking on init.