Skip to content

Commit

Permalink
Main Queue proxies
Browse files Browse the repository at this point in the history
  • Loading branch information
mattmassicotte committed Aug 3, 2023
1 parent b5a3b3c commit b9e40a1
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import PackageDescription

let settings: [SwiftSetting] = [
// .unsafeFlags(["-Xfrontend", "-strict-concurrency=complete"])
.unsafeFlags(["-Xfrontend", "-strict-concurrency=complete"])
]

let package = Package(
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
# MainOffender
A tiny package with utilities to help with Swift Concurrency

You can also just copy-paste the stuff you need into your project if you aren't into taking on the dependnecy. I won't be offended (ha!).
You can also just copy-paste the stuff you need into your project if you aren't into taking on the dependency. I won't be offended (ha!).

Features:
- `MainActor.runUnsafely` for `MainActor.assumeIsolated` with lower OS version requirements.
- `DispatchQueue.mainActor` for a `DispatchQueue` proxy that is `@MainActor`-compatible
- `OperationQueue.mainActor` for an `OperationQueue` proxy that is `@MainActor` compatible
- `UnsafeBlockOperation` for `BlockOperation` without `Sendable` checking
- Additions to `OperationQueue` to submit blocks directly without `Sendable` checking
- `addUnsafeObserver(forName:object:queue:using:)` for `NotificationCenter`
Expand Down
68 changes: 68 additions & 0 deletions Sources/MainOffender/MainDispatchQueue.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Foundation

public final class MainDispatchQueue {
public func async(
group: DispatchGroup? = nil,
qos: DispatchQoS = .unspecified,
flags: DispatchWorkItemFlags = [],
execute work: @MainActor @escaping @Sendable @convention(block) () -> Void
) {
DispatchQueue.main.async(group: group, qos: qos, flags: flags) {
MainActor.runUnsafely(work)
}
}

public func asyncUnsafe(
group: DispatchGroup? = nil,
qos: DispatchQoS = .unspecified,
flags: DispatchWorkItemFlags = [],
execute unsafeWork: @MainActor @escaping @convention(block) () -> Void
) {
let work = unsafeBitCast(unsafeWork, to: (@Sendable @convention(block) () -> Void).self)

DispatchQueue.main.async(group: group, qos: qos, flags: flags) {
MainActor.runUnsafely(work)
}
}

public func asyncAfter(
deadline: DispatchTime,
qos: DispatchQoS = .unspecified,
flags: DispatchWorkItemFlags = [],
execute work: @MainActor @escaping @Sendable @convention(block) () -> Void
) {
DispatchQueue.main.asyncAfter(deadline: deadline, qos: qos, flags: flags) {
MainActor.runUnsafely(work)
}
}

public func asyncAfterUnsafe(
deadline: DispatchTime,
qos: DispatchQoS = .unspecified,
flags: DispatchWorkItemFlags = [],
execute unsafeWork: @MainActor @escaping @convention(block) () -> Void
) {
let work = unsafeBitCast(unsafeWork, to: (@Sendable @convention(block) () -> Void).self)

DispatchQueue.main.asyncAfter(deadline: deadline, qos: qos, flags: flags) {
MainActor.runUnsafely(work)
}
}
}

extension DispatchQueue {
public static let mainActor = MainDispatchQueue()
}

extension DispatchGroup {
public func notify(
qos: DispatchQoS = .unspecified,
flags: DispatchWorkItemFlags = [],
queue: MainDispatchQueue,
execute work: @MainActor @escaping @Sendable @convention(block) () -> Void
) {
self.notify(qos: qos, flags: flags, queue: .main) {
MainActor.runUnsafely(work)
}
}
}
40 changes: 40 additions & 0 deletions Sources/MainOffender/MainOperationQueue.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Foundation

public final class MainOperationQueue {
public func addOperation(_ block: @MainActor @escaping @Sendable () -> Void) {
OperationQueue.main.addOperation {
MainActor.runUnsafely(block)
}
}

public func addUnsafeOperation(_ unsafeBlock: @escaping () -> Void) {
OperationQueue.main.addUnsafeOperation {
MainActor.runUnsafely(unsafeBlock)
}
}

public func addBarrierBlock(_ barrier: @MainActor @escaping @Sendable () -> Void) {
OperationQueue.main.addBarrierBlock {
MainActor.runUnsafely(barrier)
}
}

public func addUnsafeBarrierBlock(_ unsafeBlock: @escaping () -> Void) {
OperationQueue.main.addUnsafeBarrierBlock {
MainActor.runUnsafely(unsafeBlock)
}
}

public func addOperation(_ op: Operation) {
OperationQueue.main.addOperation(op)
}

public func waitUntilAllOperationsAreFinished() {
OperationQueue.main.waitUntilAllOperationsAreFinished()
}
}

extension OperationQueue {
/// An OperationQueue proxy that is compatible with MainActor isolation
public static let mainActor = MainOperationQueue()
}

0 comments on commit b9e40a1

Please sign in to comment.