BackupEngine: - fetchAssets() runs in Task.detached — prevents 100-500ms UI freeze during PHFetchResult enumeration on large libraries - Manifest JSON decode + ManifestIndex Set construction run in Task.detached - pendingAssets filter runs in Task.detached — O(N) array filter off main thread - Progress updates throttled to 8 Hz via ProgressThrottle — eliminates the continuous SwiftUI full-tree redraws caused by per-byte upload callbacks - bytesSoFar captured by value in upload closure — avoids mutable var capture - reserveCapacity on manifestEntries array - os_signpost intervals on FetchAssets, NASConnect, ManifestLoad, BuildPendingQueue, UploadLoop, UploadFile for Instruments profiling BackupStatusService: - countSafe() marked nonisolated — runs in Task.detached off main actor, keeping touch/animation responsive during PHFetchResult batch enumeration - ManifestIndex JSON decode + Set construction run in Task.detached - Bootstrap ManifestIndex(nasListing:) construction off main actor - writeManifest JSON decode/encode/merge off main actor - os_signpost intervals on Reconcile, ManifestLoad, CountSafe Model types: - ManifestIndex, ManifestEntry, PhotoAsset, BackupFilter marked Sendable — safe to pass across task boundaries without warnings Co-Authored-By: Kutesir <kutesir@provoc.ug> Co-Authored-By: Sentry <sentry@provoc.ug>
30 lines
824 B
Swift
30 lines
824 B
Swift
import Foundation
|
|
import Photos
|
|
|
|
struct PhotoAsset: Sendable {
|
|
let localIdentifier: String
|
|
let filename: String
|
|
let creationDate: Date?
|
|
let mediaType: PHAssetMediaType
|
|
let pixelWidth: Int
|
|
let pixelHeight: Int
|
|
let duration: TimeInterval
|
|
let isScreenshot: Bool
|
|
let isRAW: Bool
|
|
}
|
|
|
|
protocol PhotoLibraryProtocol: AnyObject {
|
|
var authorizationStatus: PHAuthorizationStatus { get }
|
|
func requestAuthorization() async -> PHAuthorizationStatus
|
|
func fetchAssets(filter: BackupFilter) -> [PhotoAsset]
|
|
func exportAsset(_ asset: PhotoAsset) async throws -> URL
|
|
}
|
|
|
|
struct BackupFilter: Codable, Sendable {
|
|
var includePhotos: Bool = true
|
|
var includeVideos: Bool = true
|
|
var includeScreenshots: Bool = false
|
|
var includeRAW: Bool = false
|
|
var newFilesOnly: Bool = true
|
|
}
|