Files
Termind/Frameworks/TermindPersistence/SwiftDataOutboxStore.swift
T
Kaede Akino c2958a05b3 Add iOS workspaces and shared feature services
Share Vault, persistence, SSH/SFTP, forwarding, and SwiftUI components across platforms. Add mobile editors, terminal input, standalone Assistant, Metrics navigation, transfers, and a unified Live Activity with host system icons.

Validation: 35 iOS tests and 100 focused macOS tests pass; formatting and zh-Hans checks pass. The full macOS run reports 14 issues in the documented timing, Keychain, and popover suites. Remaining Assistant tool and CloudKit work is tracked in Memory/MOBILE_ROLLOUT.md.
2026-09-11 13:47:16 +08:00

85 lines
2.8 KiB
Swift

import Foundation
import SwiftData
import TermindApplication
import TermindDomain
@MainActor
public final class SwiftDataOutboxStore: OutboxStore {
private let context: ModelContext
public init(context: ModelContext) {
self.context = context
}
public func enqueue(_ jobs: [OutboxJob]) throws {
for job in jobs { context.insert(PersistentOutboxJob(job)) }
try save()
}
public func nextReady(at date: Date) throws -> OutboxJob? {
let pending = OutboxJobStatus.pending.rawValue
var descriptor = FetchDescriptor<PersistentOutboxJob>(
predicate: #Predicate { $0.status == pending && $0.nextAttemptAt <= date },
sortBy: [SortDescriptor(\.createdAt), SortDescriptor(\.handlerID)]
)
descriptor.fetchLimit = 1
return try context.fetch(descriptor).first?.value
}
public func markRunning(_ id: OutboxJob.ID) throws {
guard let job = try persistentJob(id) else { return }
job.status = OutboxJobStatus.running.rawValue
job.attemptCount += 1
job.lastError = nil
try save()
}
public func markSucceeded(_ id: OutboxJob.ID) throws {
guard let job = try persistentJob(id) else { return }
job.status = OutboxJobStatus.succeeded.rawValue
job.lastError = nil
try save()
}
public func markFailed(_ id: OutboxJob.ID, error: String, retryAt: Date?) throws {
guard let job = try persistentJob(id) else { return }
job.status = (retryAt == nil ? OutboxJobStatus.failed : .pending).rawValue
job.lastError = error
if let retryAt { job.nextAttemptAt = retryAt }
try save()
}
public func resetInterruptedJobs() throws {
let running = OutboxJobStatus.running.rawValue
let jobs = try context.fetch(
FetchDescriptor<PersistentOutboxJob>(predicate: #Predicate { $0.status == running })
)
for job in jobs { job.status = OutboxJobStatus.pending.rawValue }
if !jobs.isEmpty { try save() }
}
public func retryFailedJob(_ id: OutboxJob.ID) throws {
guard let job = try persistentJob(id),
job.status == OutboxJobStatus.failed.rawValue
else { return }
job.status = OutboxJobStatus.pending.rawValue
job.nextAttemptAt = Date()
job.lastError = nil
try save()
}
private func persistentJob(_ id: UUID) throws -> PersistentOutboxJob? {
var descriptor = FetchDescriptor<PersistentOutboxJob>(
predicate: #Predicate { $0.id == id }
)
descriptor.fetchLimit = 1
return try context.fetch(descriptor).first
}
private func save() throws {
try context.savePendingChanges()
}
}
public typealias SharedOutboxStore = SwiftDataOutboxStore