diff --git a/World Manager for Minecraft/Models/MinecraftContentItem.swift b/World Manager for Minecraft/Models/MinecraftContentItem.swift index 2e7f1b7..02fdc25 100644 --- a/World Manager for Minecraft/Models/MinecraftContentItem.swift +++ b/World Manager for Minecraft/Models/MinecraftContentItem.swift @@ -7,7 +7,7 @@ import Foundation -enum MinecraftContentType: String, CaseIterable, Hashable, Sendable, Codable { +nonisolated enum MinecraftContentType: String, CaseIterable, Hashable, Sendable, Codable { case world = "World" case behaviorPack = "Behavior 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 embeddedInWorld case foundInCollection } -struct ContentPackReference: Identifiable, Hashable, Sendable, Codable { +nonisolated struct ContentPackReference: Identifiable, Hashable, Sendable, Codable { let id: String let name: String 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 difficulty: String? var seed: String? @@ -113,11 +113,11 @@ struct WorldMetadata: Hashable, Sendable, Codable { var networkVersion: String? } -struct PackMetadataDetails: Hashable, Sendable, Codable { +nonisolated struct PackMetadataDetails: Hashable, Sendable, Codable { var minimumEngineVersion: String? } -struct MinecraftContentItem: Identifiable, Hashable, Sendable, Codable { +nonisolated struct MinecraftContentItem: Identifiable, Hashable, Sendable, Codable { let id: URL let folderURL: URL let folderName: String diff --git a/World Manager for Minecraft/Models/SourceOrigin.swift b/World Manager for Minecraft/Models/SourceOrigin.swift index 9843b41..f942ed9 100644 --- a/World Manager for Minecraft/Models/SourceOrigin.swift +++ b/World Manager for Minecraft/Models/SourceOrigin.swift @@ -7,7 +7,7 @@ import Foundation -struct ConnectedDevice: Identifiable, Hashable, Sendable, Codable { +nonisolated struct ConnectedDevice: Identifiable, Hashable, Sendable, Codable { let udid: String var name: String var productType: String? @@ -18,19 +18,19 @@ struct ConnectedDevice: Identifiable, Hashable, Sendable, Codable { var id: String { udid } } -enum DeviceConnection: String, Hashable, Sendable, Codable { +nonisolated enum DeviceConnection: String, Hashable, Sendable, Codable { case usb case network } -enum DeviceTrustState: String, Hashable, Sendable, Codable { +nonisolated enum DeviceTrustState: String, Hashable, Sendable, Codable { case unavailable case locked case untrusted case trusted } -struct DeviceAppContainer: Identifiable, Hashable, Sendable, Codable { +nonisolated struct DeviceAppContainer: Identifiable, Hashable, Sendable, Codable { let deviceUDID: String let appID: 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 container } -enum MinecraftSourceOrigin: Hashable, Sendable, Codable { +nonisolated enum MinecraftSourceOrigin: Hashable, Sendable, Codable { case localFolder(bookmarkData: Data?) 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 connectedDevice } diff --git a/World Manager for Minecraft/Models/SourceRecord.swift b/World Manager for Minecraft/Models/SourceRecord.swift index 444e5ec..68e5846 100644 --- a/World Manager for Minecraft/Models/SourceRecord.swift +++ b/World Manager for Minecraft/Models/SourceRecord.swift @@ -9,7 +9,7 @@ import Foundation typealias SourceAccessorIdentifier = String -enum SourceAvailability: String, Hashable, Sendable, Codable { +nonisolated enum SourceAvailability: String, Hashable, Sendable, Codable { case unknown case available case disconnected @@ -17,18 +17,18 @@ enum SourceAvailability: String, Hashable, Sendable, Codable { case unavailable } -enum SourceRefreshStrategy: String, Hashable, Sendable, Codable { +nonisolated enum SourceRefreshStrategy: String, Hashable, Sendable, Codable { case eagerFullScan case staged } -struct SourceAccessDescriptor: Hashable, Sendable, Codable { +nonisolated struct SourceAccessDescriptor: Hashable, Sendable, Codable { var accessorIdentifier: SourceAccessorIdentifier var kind: MinecraftSourceKind var refreshStrategy: SourceRefreshStrategy } -struct SourceRecord: Identifiable, Hashable, Sendable, Codable { +nonisolated struct SourceRecord: Identifiable, Hashable, Sendable, Codable { let id: URL var displayName: String var rootURL: URL diff --git a/World Manager for Minecraft/Services/SourceScanExecution.swift b/World Manager for Minecraft/Services/SourceScanExecution.swift index 03d6b58..31eb5b8 100644 --- a/World Manager for Minecraft/Services/SourceScanExecution.swift +++ b/World Manager for Minecraft/Services/SourceScanExecution.swift @@ -39,7 +39,7 @@ enum SourceScanExecutor { let previousSource = source let performanceContext = SourceScanPolicy.performanceContext(for: source) - await host.updateSource(sourceID) { source in + host.updateSource(sourceID) { source in source.isScanning = true source.scanError = nil source.scanDiagnostic = nil @@ -51,11 +51,11 @@ enum SourceScanExecutor { source.sizeLoadedCount = 0 } - await host.updateSource(sourceID) { source in + host.updateSource(sourceID) { source in source.accessDescriptor = sourceAccessMethod.accessDescriptor(for: source) } let currentAvailability = await sourceAccessMethod.availability(for: source) - await host.updateSource(sourceID) { source in + host.updateSource(sourceID) { source in source.availability = currentAvailability } @@ -67,7 +67,7 @@ enum SourceScanExecutor { } } - await host.updateSource(sourceID) { source in + host.updateSource(sourceID) { source in source.availability = .available source.scanStatus = SourceScanPolicy.scanningLibraryStatus(for: source, mode: mode) } @@ -86,7 +86,9 @@ enum SourceScanExecutor { let enrichedItem = await sourceAccessMethod.enrich(item, for: source) 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, discoveredCount: discoveredCount ) { - await host.applySnapshot(snapshot, to: sourceID) + host.applySnapshot(snapshot, to: sourceID) } if itemForIndex.id == item.id, itemForIndex.metadataLoaded == false { await enrichmentQueue.enqueue(item) @@ -170,13 +172,13 @@ enum SourceScanExecutor { cachedItem, discoveredCount: discoveredCount ) { - await host.applySnapshot(snapshot, to: sourceID) + host.applySnapshot(snapshot, to: sourceID) } } } } - await host.logScanStage( + host.logScanStage( "Discovery", elapsed: Date().timeIntervalSince(discoveryStartTime), context: performanceContext, @@ -184,7 +186,7 @@ enum SourceScanExecutor { ) if let snapshot = await index.markDiscoveryFinished() { - await host.applySnapshot(snapshot, to: sourceID) + host.applySnapshot(snapshot, to: sourceID) } await enrichmentQueue.finish() let enrichmentStartTime = Date() @@ -193,7 +195,7 @@ enum SourceScanExecutor { await workerTask.value } - await host.logScanStage( + host.logScanStage( "Enrichment", elapsed: Date().timeIntervalSince(enrichmentStartTime), context: performanceContext, @@ -201,9 +203,9 @@ enum SourceScanExecutor { ) 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 previewSeedItems = await index.currentItems() @@ -213,11 +215,11 @@ enum SourceScanExecutor { ) for previewItem in previewItems { if let snapshot = await index.applyPreviewItem(previewItem) { - await host.applySnapshot(snapshot, to: sourceID) + host.applySnapshot(snapshot, to: sourceID) } } - await host.logScanStage( + host.logScanStage( "Previews", elapsed: Date().timeIntervalSince(previewStageStartTime), context: performanceContext, @@ -225,9 +227,9 @@ enum SourceScanExecutor { ) 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 { try await finishConnectedDeviceScan( @@ -271,7 +273,7 @@ enum SourceScanExecutor { await sizeWorkerTask.value } - await host.logScanStage( + host.logScanStage( "Size", elapsed: Date().timeIntervalSince(sizeStageStartTime), context: performanceContext, @@ -291,7 +293,7 @@ enum SourceScanExecutor { minimumVisibleScanDuration: minimumVisibleScanDuration ) } catch { - await host.updateSource(sourceID) { source in + host.updateSource(sourceID) { source in if SourceScanRecovery.shouldPreservePartialResults(currentSource: source, previousSource: previousSource) { source.scanStatus = source.indexedItemCount == 0 ? previousSource.scanStatus @@ -320,7 +322,7 @@ enum SourceScanExecutor { source.scanProgress = nil source.isScanning = false } - await host.persistSourceIfAvailable(withID: sourceID) + host.persistSourceIfAvailable(withID: sourceID) } } @@ -345,11 +347,11 @@ enum SourceScanExecutor { ) for sizedItem in sizedItems { if let snapshot = await index.applySizedItem(sizedItem) { - await host.applySnapshot(snapshot, to: sourceID) + host.applySnapshot(snapshot, to: sourceID) } } - await host.logScanStage( + host.logScanStage( "Size", elapsed: Date().timeIntervalSince(sizeStageStartTime), context: performanceContext, @@ -390,24 +392,24 @@ enum SourceScanExecutor { } 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 { source.snapshot = SourceScanPolicy.buildSnapshot(for: source, scanRootURL: scanContextURL) } else { source.snapshot = nil } } - await host.persistSourceIfAvailable(withID: sourceID) - await host.logScanStage( + host.persistSourceIfAvailable(withID: sourceID) + host.logScanStage( "Total", elapsed: Date().timeIntervalSince(scanStartTime), context: performanceContext, itemCount: discoveredCount ) - if let completedSource = await host.source(withID: sourceID) { + if let completedSource = host.source(withID: sourceID) { await notificationService.notifyScanCompleted( for: completedSource, duration: Date().timeIntervalSince(scanStartTime) diff --git a/World Manager for Minecraft/Services/ZipArchiveReader.swift b/World Manager for Minecraft/Services/ZipArchiveReader.swift index 496fb09..1e1e5dc 100644 --- a/World Manager for Minecraft/Services/ZipArchiveReader.swift +++ b/World Manager for Minecraft/Services/ZipArchiveReader.swift @@ -8,7 +8,7 @@ import Foundation import zlib -struct ZipArchiveEntry: Sendable, Hashable { +nonisolated struct ZipArchiveEntry: Sendable, Hashable { let path: String let compressionMethod: UInt16 let compressedSize: UInt32 @@ -17,7 +17,7 @@ struct ZipArchiveEntry: Sendable, Hashable { let isDirectory: Bool } -enum ZipArchiveReaderError: LocalizedError { +nonisolated enum ZipArchiveReaderError: LocalizedError { case invalidArchive case unsupportedCompressionMethod(UInt16) case unsupportedFeatures(String) @@ -40,7 +40,7 @@ enum ZipArchiveReaderError: LocalizedError { } } -struct ZipArchiveReader { +nonisolated struct ZipArchiveReader { private let data: Data let entries: [ZipArchiveEntry] @@ -241,7 +241,7 @@ struct ZipArchiveReader { } } -private extension Data { +private nonisolated extension Data { func readUInt16LE(at offset: Int) -> UInt16 { return self.withUnsafeBytes { bytes in let base = bytes.baseAddress!.advanced(by: offset)