import CocoaLumberjackSwift
import FileUtility
import Foundation
import PromiseKit
enum TaskManagerError: Error {
case flushedTask, noTaskQueueFound
}
typealias TaskReceiverNonce = [String: Data]
public final class TaskManager: NSObject, TaskManagerProtocol {
private let entityManager: EntityManager?
private let serverConnector: ServerConnectorProtocol
private static var taskQueue: TaskQueue?
private static let addTaskQueue = DispatchQueue(label: "ch.threema.TaskManager.addTaskQueue")
@objc required init(
backgroundEntityManager entityManager: EntityManager?,
serverConnector: ServerConnectorProtocol
) {
self.entityManager = entityManager
self.serverConnector = serverConnector
super.init()
load()
}
override convenience init() {
self.init(backgroundEntityManager: nil, serverConnector: ServerConnector.shared())
}
func add(taskDefinition: TaskDefinitionProtocol) -> CancelableTask? {
TaskManager.addTaskQueue.async {
do {
try self.add(task: taskDefinition, completionHandler: nil)
}
catch {
DDLogError("Failed add task to queue \(error)")
}
self.spool()
}
assert(taskDefinition is TaskDefinition, "We always expect a `TaskDefinition`")
// Only drop on disconnect tasks are cancelable
if let task = taskDefinition as? TaskDefinition, task.type == .dropOnDisconnect {
return CancelableDropOnDisconnectTask(taskDefinition: task, serverConnector: serverConnector)
}
else {
return nil
}
}
/// Caution: Tasks with retry the completion handler might be called multiple times!
func addWithWait(taskDefinition: TaskDefinitionProtocol) -> (WaitTask, CancelableTask?) {
// This is a bit crazy, but the best we could come up with
let completionTask: Task<Void, Error> = Task {
try await withCheckedThrowingContinuation { continuation in
TaskManager.addTaskQueue.async {
do {
try self.add(task: taskDefinition) { _, error in
if let error {
continuation.resume(throwing: error)
}
else {
continuation.resume()
}
}
}
catch {
DDLogError("Failed add task to queue \(error)")
}
self.spool()
}
}
}
let waitTask = DefaultWaitTask(completionTask: completionTask)
assert(taskDefinition is TaskDefinition, "We always expect a `TaskDefinition`")
// Only drop on disconnect tasks are cancelable
if let task = taskDefinition as? TaskDefinition, task.type == .dropOnDisconnect {
let cancelableTask = CancelableDropOnDisconnectTask(taskDefinition: task, serverConnector: serverConnector)
return (waitTask, cancelableTask)
}
else {
return (waitTask, nil)
}
}
func add(
taskDefinition: TaskDefinitionProtocol,
completionHandler: @escaping TaskCompletionHandler
) -> CancelableTask? {
TaskManager.addTaskQueue.async {
do {
try self.add(task: taskDefinition, completionHandler: completionHandler)
}
catch {
DDLogError("Failed add task to queue \(error)")
}
self.spool()
}
assert(taskDefinition is TaskDefinition, "We always expect a `TaskDefinition`")
// Only drop on disconnect tasks are cancelable
if let task = taskDefinition as? TaskDefinition, task.type == .dropOnDisconnect {
return CancelableDropOnDisconnectTask(taskDefinition: task, serverConnector: serverConnector)
}
else {
return nil
}
}
func add(taskDefinitionTuples: [(
taskDefinition: TaskDefinitionProtocol,
completionHandler: TaskCompletionHandler
)]) -> [CancelableTask?] {
TaskManager.addTaskQueue.async {
do {
for taskDefinitionTuple in taskDefinitionTuples {
try self.add(
task: taskDefinitionTuple.taskDefinition,
completionHandler: taskDefinitionTuple.completionHandler
)
}
}
catch {
DDLogError("Failed add task to queue \(error)")
}
self.spool()
}
var cancellableTasks = [CancelableTask?]()
for taskDefinitionTuple in taskDefinitionTuples {
assert(taskDefinitionTuple.taskDefinition is TaskDefinition, "We always expect a `TaskDefinition`")
// Only drop on disconnect tasks are cancelable
if let task = taskDefinitionTuple.taskDefinition as? TaskDefinition, task.type == .dropOnDisconnect {
cancellableTasks.append(CancelableDropOnDisconnectTask(
taskDefinition: task,
serverConnector: serverConnector
))
}
else {
cancellableTasks.append(nil)
}
}
assert(cancellableTasks.count == taskDefinitionTuples.count)
return cancellableTasks
}
private func add(task: TaskDefinitionProtocol, completionHandler: TaskCompletionHandler? = nil) throws {
guard let queue = TaskManager.taskQueue, let task = task as? TaskDefinition else {
throw TaskManagerError.noTaskQueueFound
}
try queue.enqueue(task: task, completionHandler: completionHandler)
}
func executeSubTask(taskDefinition: any TaskDefinitionProtocol) async throws {
try await TaskManager.taskQueue?.executeSubTask(taskDefinition: taskDefinition)
}
@objc public static func interrupt() {
TaskManager.taskQueue?.interrupt()
}
@objc public static func removeAllTasks() {
TaskManager.taskQueue?.removeAll()
}
public static func removeCurrentTask() {
TaskManager.taskQueue?.removeCurrent()
}
public static func isEmpty() -> Bool {
TaskManager.taskQueue?.list.isEmpty ?? true
}
/// Get notification name for particular reflecting/acking of a message.
///
/// - Returns: kNotificationMediatorMessageAck + hex string of reflect ID
@objc static func mediatorMessageAckObserverName(reflectID: Data) -> Notification.Name {
Notification.Name("\(kNotificationMediatorMessageAck)\(reflectID.hexString)")
}
/// Get notification name for particular sending/acking of a message.
///
/// - Returns: kNotificationChatMessageAck + hex string of message ID
@objc static func chatMessageAckObserverName(messageID: Data, toIdentity: String) -> Notification.Name {
Notification.Name("\(kNotificationChatMessageAck)\(messageID.hexString)\(toIdentity)")
}
/// Execute all pending tasks.
@objc func spool() {
TaskManager.taskQueue?.spool()
}
private func load() {
TaskManager.addTaskQueue.async {
if TaskManager.taskQueue == nil {
TaskManager.taskQueue = TaskQueue(
frameworkInjectorResolver: FrameworkInjectorResolver(backgroundEntityManager: self.entityManager)
)
if let queuePath = TaskManager.taskQueue?.queuePath(),
FileUtility.shared.fileExists(at: queuePath) {
if let data = FileUtility.shared.read(fileURL: queuePath) {
FileUtility.shared.deleteIfExists(at: queuePath)
TaskManager.taskQueue?.decode(data)
}
}
}
}
}
}
// MARK: Objective-C extension for the TaskManager
extension TaskManager {
@available(swift, obsoleted: 1.0, renamed: "add(taskDefinition:)", message: "Only use from Objective-C")
@objc func addObjc(taskDefinition: AnyObject) {
assert(taskDefinition is TaskDefinition)
guard let taskDefinition = taskDefinition as? TaskDefinition else {
DDLogError("Worng type for taskDefinition")
return
}
_ = add(taskDefinition: taskDefinition)
}
@available(
swift,
obsoleted: 1.0,
renamed: "add(taskDefinition:completionHandler:)",
message: "Only use from Objective-C"
)
@objc func addObjc(taskDefinition: AnyObject, completionHandler: @escaping (AnyObject, Error?) -> Void) {
assert(taskDefinition is TaskDefinition)
guard let taskDefinition = taskDefinition as? TaskDefinition else {
DDLogError("Worng type for taskDefinition")
return
}
_ = add(taskDefinition: taskDefinition) { task, error in
completionHandler(task as! TaskDefinition, error)
}
}
}
¤ Dauer der Verarbeitung: 0.0 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland