I am trying to access UIDevice.current.userInterfaceIdiom in Strict Concurrency Checks without needing to move all of my code to Async/Await.
Is it possible to use DispatchQeue.main to access @MainActor isolated variables?
var device : String {
DispatchQueue.main.asyncAndWait {
#if os(iOS)
// Main actor-isolated class property 'current' can not be referenced from a non-isolated context; this is an error in Swift 6
switch UIDevice.current.userInterfaceIdiom {
case .phone:
return "iPhone"
case .pad:
return "iPad"
default:
return "Unknown"
}
#else
return "Unknown"
#endif
}
}
Hi there @aliali, I think I recognize you from the server meetup just now Thanks for tuning in!
Are you actually using Xcode 16 and Swift 6 mode...?
I just checked this:
import Dispatch
@MainActor
var m: Int = 123
@available(macOS 15.0, *)
func test() {
DispatchQueue.main.async {
m += 1
}
DispatchQueue.main.asyncAndWait {
m += 1
}
#if os(iOS)
switch UIDevice.current.userInterfaceIdiom {
}
#endif
}
in a package in swift 6 mode in Xcode 16 and this seems to build and work fine.
What's your Xcode and SDK swift versions? If you do just DispatchQueue.main.async does it work? That'd mean that your SDK does not have DispatchQueue.main.asyncAndWait annotated so I'd suggest updating SDK to the latest.
In my experience the checks are (still) quite shallow (although I am not running the latest Xcode which might have better checks):
Cheating 😂
struct MyDispatchQueue {
private init() {}
static var main: MyDispatchQueue { MyDispatchQueue() }
func `async`<T>(execute: @MainActor () -> T) -> T {
DispatchQueue.main.sync { @MainActor in
execute()
}
}
}
func test() {
typealias DispatchQueue = MyDispatchQueue
var device : String {
DispatchQueue.main.async {
#if os(iOS)
// Main actor-isolated class property 'current' can not be referenced from a non-isolated context; this is an error in Swift 6
switch UIDevice.current.userInterfaceIdiom {
case .phone:
return "iPhone"
case .pad:
return "iPad"
default:
return "Unknown"
}
#else
return "Unknown"
#endif
}
}
}
BTW, what is the difference between DispatchQueue's asyncAndWait and sync?
I am using asyncAndWait because I want to assign the result to a variable and that seems to be the best method to do so without creating a mutable var that is then update via DispatchQueue.main.async.
And here are the docs
This function submits work to the specified queue for execution. Unlike dispatch_async(_:_:), this function does not return until after the block finishes. Calling this function and targeting the current queue results in deadlock.
Unlike sync(execute:), this function respects all attributes of the queue when it executes the block. For example, it respects the quality-of-service level and autorelease frequency of the target queue.
If the runtime has already brought up a thread to service asynchronous work items, the system uses that same thread to execute any synchronous blocks you submitted using this function. If the runtime hasn’t brought up a thread to service asynchronous work items, the sytem executes these synchronous blocks on the current thread as an optimization. However, these optimizations apply only when queue targets a global concurrent queue. If it targets any other queue, the system executes the work on that queue’s thread. For example, if queue targets the main queue, the block always runs on the main thread.
Unlike with dispatch_async(_:_:), no retain is performed on the target queue. Because calls to this function are synchronous, it “borrows” the reference of the caller. Moreover, no Block_copy is performed on the block.
Yes I considered it. Turning the calling functions into @MainActor.
This function called by both async/await functions and completions.
I would eventually need to make the above change to use DispatchQueue.main somewhere to get the result of the functions isolated to MainActor. Limiting that to just the variable that needs it seems like the correct way to go.