NetNewsWire/Core/Sources/Core/MainThreadOperation.swift

100 lines
3.3 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// MainThreadOperation.swift
// RSCore
//
// Created by Brent Simmons on 1/10/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
/// Code to be run by MainThreadOperationQueue.
///
/// When finished, it must call operationDelegate.operationDidComplete(self).
/// If its canceled, it should not call the delegate.
/// When its canceled, it should do its best to stop
/// doing whatever its doing. However, it should not
/// leave data in an inconsistent state.
public protocol MainThreadOperation: AnyObject {
// These three properties are set by MainThreadOperationQueue. Dont set them.
@MainActor var isCanceled: Bool { get set } // Check this at appropriate times in case the operation has been canceled.
@MainActor var id: Int? { get set }
@MainActor var operationDelegate: MainThreadOperationDelegate? { get set } // Make this weak.
/// Name may be useful for debugging. Unused otherwise.
@MainActor var name: String? { get set }
typealias MainThreadOperationCompletionBlock = (MainThreadOperation) -> Void
/// Called when the operation completes.
///
/// The completionBlock is called
/// even if the operation was canceled. The completionBlock
/// takes the operation as parameter, so you can inspect it as needed.
///
/// Implementations of MainThreadOperation are *not* responsible
/// for calling the completionBlock  MainThreadOperationQueue
/// handles that.
///
/// The completionBlock is always called on the main thread.
/// The queue will clear the completionBlock after calling it.
@MainActor var completionBlock: MainThreadOperationCompletionBlock? { get set }
/// Do the thing this operation does.
///
/// This code runs on the main thread. If you want to run
/// code off of the main thread, you can use the standard mechanisms:
/// a DispatchQueue, most likely.
///
/// When this is called, you dont need to check isCanceled:
/// its guaranteed to not be canceled. However, if you run code
/// in another thread, you should check isCanceled in that code.
@MainActor func run()
/// Cancel this operation.
///
/// Any operations dependent on this operation
/// will also be canceled automatically.
///
/// This function has a default implementation. Its super-rare
/// to need to provide your own.
@MainActor func cancel()
/// Make this operation dependent on an other operation.
///
/// This means the other operation must complete before
/// this operation gets run. If the other operation is canceled,
/// this operation will automatically be canceled.
/// Note: an operation can have multiple dependencies.
///
/// This function has a default implementation. Its super-rare
/// to need to provide your own.
@MainActor func addDependency(_ parentOperation: MainThreadOperation)
}
public extension MainThreadOperation {
@MainActor func cancel() {
operationDelegate?.cancelOperation(self)
}
@MainActor func addDependency(_ parentOperation: MainThreadOperation) {
operationDelegate?.make(self, dependOn: parentOperation)
}
@MainActor func informOperationDelegateOfCompletion() {
guard !isCanceled else {
return
}
if Thread.isMainThread {
operationDelegate?.operationDidComplete(self)
}
else {
DispatchQueue.main.async {
self.informOperationDelegateOfCompletion()
}
}
}
}