Skip to content

Commit 54f80ba

Browse files
committed
Access MainActor Synchronously Safely
1 parent 522d7b6 commit 54f80ba

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
title: 'Access MainActor Synchronously Safely'
3+
author: 'Clive Liu'
4+
layout: post
5+
tags: [Swift, Concurrency]
6+
---
7+
8+
> **Use with caution:** Avoid accessing `MainActor` synchronously unless absolutely necessary.
9+
{: .prompt-warning }
10+
11+
In rare cases—particularly when integrating with legacy code—you may need to synchronously access `MainActor`-isolated code. While this approach should be avoided when possible, Swift does provide a safe mechanism for doing so when absolutely necessary.
12+
13+
Here's how you can accomplish that:
14+
15+
```swift
16+
extension MainActor {
17+
18+
/// Executes a closure synchronously on the `MainActor`.
19+
///
20+
/// This method allows synchronous access to `MainActor`-isolated code.
21+
///
22+
/// - Parameter operation: A closure that runs on the `MainActor`.
23+
/// - Returns: The value returned by `operation`.
24+
/// - Throws: Rethrows any error thrown by `operation`.
25+
///
26+
/// - Warning: Synchronous access to actors should be avoided when possible.
27+
/// This is intended for exceptional cases, such as bridging with legacy APIs.
28+
public static func sync<R: Sendable>(operation: @MainActor () throws -> R) rethrows -> R {
29+
if Thread.isMainThread {
30+
// This is valid as per SE-0424:
31+
// https://github.com/swiftlang/swift-evolution/blob/main/proposals/0424-custom-isolation-checking-for-serialexecutor.md
32+
//
33+
// Being able to assert isolation for non-task code this way is important enough
34+
// that the Swift runtime actually already has a special case for it:
35+
// even if the current thread is not running a task, isolation checking will succeed
36+
// if the target actor is the MainActor and the current thread is the main thread.
37+
try MainActor.assumeIsolated(operation)
38+
} else {
39+
// As per https://developer.apple.com/documentation/swift/mainactor
40+
// MainActor is a singleton actor whose executor is equivalent to the main dispatch queue.
41+
// The compiler would also yell at you if you use non-main queue.
42+
// Therefore, this code is valid.
43+
//
44+
// - Note: You cannot do `DispatchQueue.main.sync` if you're already on main thread,
45+
// because that will cause a deadlock. Hence, the `isMainThread` check.
46+
try DispatchQueue.main.sync(execute: operation)
47+
}
48+
}
49+
}
50+
```
51+

0 commit comments

Comments
 (0)