Fix service concurrency isolation warnings

This commit is contained in:
John Burwell 2026-05-29 07:24:11 -05:00
parent 2df126ebe2
commit 64f75e73df
5 changed files with 49 additions and 47 deletions

View File

@ -7,7 +7,7 @@
import Foundation import Foundation
enum MinecraftContentType: String, CaseIterable, Hashable, Sendable, Codable { nonisolated enum MinecraftContentType: String, CaseIterable, Hashable, Sendable, Codable {
case world = "World" case world = "World"
case behaviorPack = "Behavior Pack" case behaviorPack = "Behavior Pack"
case resourcePack = "Resource Pack" case resourcePack = "Resource Pack"
@ -56,13 +56,13 @@ enum MinecraftContentType: String, CaseIterable, Hashable, Sendable, Codable {
} }
} }
enum PackSource: String, Hashable, Sendable, Codable { nonisolated enum PackSource: String, Hashable, Sendable, Codable {
case referencedByWorld case referencedByWorld
case embeddedInWorld case embeddedInWorld
case foundInCollection case foundInCollection
} }
struct ContentPackReference: Identifiable, Hashable, Sendable, Codable { nonisolated struct ContentPackReference: Identifiable, Hashable, Sendable, Codable {
let id: String let id: String
let name: String let name: String
let type: MinecraftContentType let type: MinecraftContentType
@ -93,7 +93,7 @@ struct ContentPackReference: Identifiable, Hashable, Sendable, Codable {
} }
} }
struct WorldMetadata: Hashable, Sendable, Codable { nonisolated struct WorldMetadata: Hashable, Sendable, Codable {
var gameMode: String? var gameMode: String?
var difficulty: String? var difficulty: String?
var seed: String? var seed: String?
@ -113,11 +113,11 @@ struct WorldMetadata: Hashable, Sendable, Codable {
var networkVersion: String? var networkVersion: String?
} }
struct PackMetadataDetails: Hashable, Sendable, Codable { nonisolated struct PackMetadataDetails: Hashable, Sendable, Codable {
var minimumEngineVersion: String? var minimumEngineVersion: String?
} }
struct MinecraftContentItem: Identifiable, Hashable, Sendable, Codable { nonisolated struct MinecraftContentItem: Identifiable, Hashable, Sendable, Codable {
let id: URL let id: URL
let folderURL: URL let folderURL: URL
let folderName: String let folderName: String

View File

@ -7,7 +7,7 @@
import Foundation import Foundation
struct ConnectedDevice: Identifiable, Hashable, Sendable, Codable { nonisolated struct ConnectedDevice: Identifiable, Hashable, Sendable, Codable {
let udid: String let udid: String
var name: String var name: String
var productType: String? var productType: String?
@ -18,19 +18,19 @@ struct ConnectedDevice: Identifiable, Hashable, Sendable, Codable {
var id: String { udid } var id: String { udid }
} }
enum DeviceConnection: String, Hashable, Sendable, Codable { nonisolated enum DeviceConnection: String, Hashable, Sendable, Codable {
case usb case usb
case network case network
} }
enum DeviceTrustState: String, Hashable, Sendable, Codable { nonisolated enum DeviceTrustState: String, Hashable, Sendable, Codable {
case unavailable case unavailable
case locked case locked
case untrusted case untrusted
case trusted case trusted
} }
struct DeviceAppContainer: Identifiable, Hashable, Sendable, Codable { nonisolated struct DeviceAppContainer: Identifiable, Hashable, Sendable, Codable {
let deviceUDID: String let deviceUDID: String
let appID: String let appID: String
var appName: String var appName: String
@ -42,12 +42,12 @@ struct DeviceAppContainer: Identifiable, Hashable, Sendable, Codable {
} }
} }
enum DeviceContainerAccessMode: String, Hashable, Sendable, Codable { nonisolated enum DeviceContainerAccessMode: String, Hashable, Sendable, Codable {
case documents case documents
case container case container
} }
enum MinecraftSourceOrigin: Hashable, Sendable, Codable { nonisolated enum MinecraftSourceOrigin: Hashable, Sendable, Codable {
case localFolder(bookmarkData: Data?) case localFolder(bookmarkData: Data?)
case connectedDevice(device: ConnectedDevice, container: DeviceAppContainer) case connectedDevice(device: ConnectedDevice, container: DeviceAppContainer)
@ -79,7 +79,7 @@ enum MinecraftSourceOrigin: Hashable, Sendable, Codable {
} }
} }
enum MinecraftSourceKind: String, Hashable, Sendable, Codable { nonisolated enum MinecraftSourceKind: String, Hashable, Sendable, Codable {
case localFolder case localFolder
case connectedDevice case connectedDevice
} }

View File

@ -9,7 +9,7 @@ import Foundation
typealias SourceAccessorIdentifier = String typealias SourceAccessorIdentifier = String
enum SourceAvailability: String, Hashable, Sendable, Codable { nonisolated enum SourceAvailability: String, Hashable, Sendable, Codable {
case unknown case unknown
case available case available
case disconnected case disconnected
@ -17,18 +17,18 @@ enum SourceAvailability: String, Hashable, Sendable, Codable {
case unavailable case unavailable
} }
enum SourceRefreshStrategy: String, Hashable, Sendable, Codable { nonisolated enum SourceRefreshStrategy: String, Hashable, Sendable, Codable {
case eagerFullScan case eagerFullScan
case staged case staged
} }
struct SourceAccessDescriptor: Hashable, Sendable, Codable { nonisolated struct SourceAccessDescriptor: Hashable, Sendable, Codable {
var accessorIdentifier: SourceAccessorIdentifier var accessorIdentifier: SourceAccessorIdentifier
var kind: MinecraftSourceKind var kind: MinecraftSourceKind
var refreshStrategy: SourceRefreshStrategy var refreshStrategy: SourceRefreshStrategy
} }
struct SourceRecord: Identifiable, Hashable, Sendable, Codable { nonisolated struct SourceRecord: Identifiable, Hashable, Sendable, Codable {
let id: URL let id: URL
var displayName: String var displayName: String
var rootURL: URL var rootURL: URL

View File

@ -39,7 +39,7 @@ enum SourceScanExecutor {
let previousSource = source let previousSource = source
let performanceContext = SourceScanPolicy.performanceContext(for: source) let performanceContext = SourceScanPolicy.performanceContext(for: source)
await host.updateSource(sourceID) { source in host.updateSource(sourceID) { source in
source.isScanning = true source.isScanning = true
source.scanError = nil source.scanError = nil
source.scanDiagnostic = nil source.scanDiagnostic = nil
@ -51,11 +51,11 @@ enum SourceScanExecutor {
source.sizeLoadedCount = 0 source.sizeLoadedCount = 0
} }
await host.updateSource(sourceID) { source in host.updateSource(sourceID) { source in
source.accessDescriptor = sourceAccessMethod.accessDescriptor(for: source) source.accessDescriptor = sourceAccessMethod.accessDescriptor(for: source)
} }
let currentAvailability = await sourceAccessMethod.availability(for: source) let currentAvailability = await sourceAccessMethod.availability(for: source)
await host.updateSource(sourceID) { source in host.updateSource(sourceID) { source in
source.availability = currentAvailability source.availability = currentAvailability
} }
@ -67,7 +67,7 @@ enum SourceScanExecutor {
} }
} }
await host.updateSource(sourceID) { source in host.updateSource(sourceID) { source in
source.availability = .available source.availability = .available
source.scanStatus = SourceScanPolicy.scanningLibraryStatus(for: source, mode: mode) source.scanStatus = SourceScanPolicy.scanningLibraryStatus(for: source, mode: mode)
} }
@ -86,7 +86,9 @@ enum SourceScanExecutor {
let enrichedItem = await sourceAccessMethod.enrich(item, for: source) let enrichedItem = await sourceAccessMethod.enrich(item, for: source)
if let snapshot = await index.applyEnrichedItem(enrichedItem) { if let snapshot = await index.applyEnrichedItem(enrichedItem) {
await host.applySnapshot(snapshot, to: sourceID) await MainActor.run {
host.applySnapshot(snapshot, to: sourceID)
}
} }
} }
} }
@ -143,7 +145,7 @@ enum SourceScanExecutor {
itemForIndex, itemForIndex,
discoveredCount: discoveredCount discoveredCount: discoveredCount
) { ) {
await host.applySnapshot(snapshot, to: sourceID) host.applySnapshot(snapshot, to: sourceID)
} }
if itemForIndex.id == item.id, itemForIndex.metadataLoaded == false { if itemForIndex.id == item.id, itemForIndex.metadataLoaded == false {
await enrichmentQueue.enqueue(item) await enrichmentQueue.enqueue(item)
@ -170,13 +172,13 @@ enum SourceScanExecutor {
cachedItem, cachedItem,
discoveredCount: discoveredCount discoveredCount: discoveredCount
) { ) {
await host.applySnapshot(snapshot, to: sourceID) host.applySnapshot(snapshot, to: sourceID)
} }
} }
} }
} }
await host.logScanStage( host.logScanStage(
"Discovery", "Discovery",
elapsed: Date().timeIntervalSince(discoveryStartTime), elapsed: Date().timeIntervalSince(discoveryStartTime),
context: performanceContext, context: performanceContext,
@ -184,7 +186,7 @@ enum SourceScanExecutor {
) )
if let snapshot = await index.markDiscoveryFinished() { if let snapshot = await index.markDiscoveryFinished() {
await host.applySnapshot(snapshot, to: sourceID) host.applySnapshot(snapshot, to: sourceID)
} }
await enrichmentQueue.finish() await enrichmentQueue.finish()
let enrichmentStartTime = Date() let enrichmentStartTime = Date()
@ -193,7 +195,7 @@ enum SourceScanExecutor {
await workerTask.value await workerTask.value
} }
await host.logScanStage( host.logScanStage(
"Enrichment", "Enrichment",
elapsed: Date().timeIntervalSince(enrichmentStartTime), elapsed: Date().timeIntervalSince(enrichmentStartTime),
context: performanceContext, context: performanceContext,
@ -201,9 +203,9 @@ enum SourceScanExecutor {
) )
if let snapshot = await index.markMetadataFinished() { if let snapshot = await index.markMetadataFinished() {
await host.applySnapshot(snapshot, to: sourceID) host.applySnapshot(snapshot, to: sourceID)
} }
await host.persistSourceIfAvailable(withID: sourceID) host.persistSourceIfAvailable(withID: sourceID)
let previewStageStartTime = Date() let previewStageStartTime = Date()
let previewSeedItems = await index.currentItems() let previewSeedItems = await index.currentItems()
@ -213,11 +215,11 @@ enum SourceScanExecutor {
) )
for previewItem in previewItems { for previewItem in previewItems {
if let snapshot = await index.applyPreviewItem(previewItem) { if let snapshot = await index.applyPreviewItem(previewItem) {
await host.applySnapshot(snapshot, to: sourceID) host.applySnapshot(snapshot, to: sourceID)
} }
} }
await host.logScanStage( host.logScanStage(
"Previews", "Previews",
elapsed: Date().timeIntervalSince(previewStageStartTime), elapsed: Date().timeIntervalSince(previewStageStartTime),
context: performanceContext, context: performanceContext,
@ -225,9 +227,9 @@ enum SourceScanExecutor {
) )
if let snapshot = await index.markPreviewsFinished() { if let snapshot = await index.markPreviewsFinished() {
await host.applySnapshot(snapshot, to: sourceID) host.applySnapshot(snapshot, to: sourceID)
} }
await host.persistSourceIfAvailable(withID: sourceID) host.persistSourceIfAvailable(withID: sourceID)
if source.origin.kind == .connectedDevice { if source.origin.kind == .connectedDevice {
try await finishConnectedDeviceScan( try await finishConnectedDeviceScan(
@ -271,7 +273,7 @@ enum SourceScanExecutor {
await sizeWorkerTask.value await sizeWorkerTask.value
} }
await host.logScanStage( host.logScanStage(
"Size", "Size",
elapsed: Date().timeIntervalSince(sizeStageStartTime), elapsed: Date().timeIntervalSince(sizeStageStartTime),
context: performanceContext, context: performanceContext,
@ -291,7 +293,7 @@ enum SourceScanExecutor {
minimumVisibleScanDuration: minimumVisibleScanDuration minimumVisibleScanDuration: minimumVisibleScanDuration
) )
} catch { } catch {
await host.updateSource(sourceID) { source in host.updateSource(sourceID) { source in
if SourceScanRecovery.shouldPreservePartialResults(currentSource: source, previousSource: previousSource) { if SourceScanRecovery.shouldPreservePartialResults(currentSource: source, previousSource: previousSource) {
source.scanStatus = source.indexedItemCount == 0 source.scanStatus = source.indexedItemCount == 0
? previousSource.scanStatus ? previousSource.scanStatus
@ -320,7 +322,7 @@ enum SourceScanExecutor {
source.scanProgress = nil source.scanProgress = nil
source.isScanning = false source.isScanning = false
} }
await host.persistSourceIfAvailable(withID: sourceID) host.persistSourceIfAvailable(withID: sourceID)
} }
} }
@ -345,11 +347,11 @@ enum SourceScanExecutor {
) )
for sizedItem in sizedItems { for sizedItem in sizedItems {
if let snapshot = await index.applySizedItem(sizedItem) { if let snapshot = await index.applySizedItem(sizedItem) {
await host.applySnapshot(snapshot, to: sourceID) host.applySnapshot(snapshot, to: sourceID)
} }
} }
await host.logScanStage( host.logScanStage(
"Size", "Size",
elapsed: Date().timeIntervalSince(sizeStageStartTime), elapsed: Date().timeIntervalSince(sizeStageStartTime),
context: performanceContext, context: performanceContext,
@ -390,24 +392,24 @@ enum SourceScanExecutor {
} }
if let snapshot = await index.finishScan() { if let snapshot = await index.finishScan() {
await host.applySnapshot(snapshot, to: sourceID) host.applySnapshot(snapshot, to: sourceID)
} }
await host.updateSource(sourceID) { source in host.updateSource(sourceID) { source in
if source.origin.kind == .localFolder { if source.origin.kind == .localFolder {
source.snapshot = SourceScanPolicy.buildSnapshot(for: source, scanRootURL: scanContextURL) source.snapshot = SourceScanPolicy.buildSnapshot(for: source, scanRootURL: scanContextURL)
} else { } else {
source.snapshot = nil source.snapshot = nil
} }
} }
await host.persistSourceIfAvailable(withID: sourceID) host.persistSourceIfAvailable(withID: sourceID)
await host.logScanStage( host.logScanStage(
"Total", "Total",
elapsed: Date().timeIntervalSince(scanStartTime), elapsed: Date().timeIntervalSince(scanStartTime),
context: performanceContext, context: performanceContext,
itemCount: discoveredCount itemCount: discoveredCount
) )
if let completedSource = await host.source(withID: sourceID) { if let completedSource = host.source(withID: sourceID) {
await notificationService.notifyScanCompleted( await notificationService.notifyScanCompleted(
for: completedSource, for: completedSource,
duration: Date().timeIntervalSince(scanStartTime) duration: Date().timeIntervalSince(scanStartTime)

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
import zlib import zlib
struct ZipArchiveEntry: Sendable, Hashable { nonisolated struct ZipArchiveEntry: Sendable, Hashable {
let path: String let path: String
let compressionMethod: UInt16 let compressionMethod: UInt16
let compressedSize: UInt32 let compressedSize: UInt32
@ -17,7 +17,7 @@ struct ZipArchiveEntry: Sendable, Hashable {
let isDirectory: Bool let isDirectory: Bool
} }
enum ZipArchiveReaderError: LocalizedError { nonisolated enum ZipArchiveReaderError: LocalizedError {
case invalidArchive case invalidArchive
case unsupportedCompressionMethod(UInt16) case unsupportedCompressionMethod(UInt16)
case unsupportedFeatures(String) case unsupportedFeatures(String)
@ -40,7 +40,7 @@ enum ZipArchiveReaderError: LocalizedError {
} }
} }
struct ZipArchiveReader { nonisolated struct ZipArchiveReader {
private let data: Data private let data: Data
let entries: [ZipArchiveEntry] let entries: [ZipArchiveEntry]
@ -241,7 +241,7 @@ struct ZipArchiveReader {
} }
} }
private extension Data { private nonisolated extension Data {
func readUInt16LE(at offset: Int) -> UInt16 { func readUInt16LE(at offset: Int) -> UInt16 {
return self.withUnsafeBytes { bytes in return self.withUnsafeBytes { bytes in
let base = bytes.baseAddress!.advanced(by: offset) let base = bytes.baseAddress!.advanced(by: offset)