Primary sync: replace PersistenceActor JSON file with SwiftData + CloudKit - Add SavedCardModel (@Model class) and PersistenceController (ModelContainer with .automatic CloudKit, fallback to local). BackgroundPersistenceActor (@ModelActor) handles all DB I/O off the main thread. - One-time migration imports user_collection.json into SwiftData and renames the original file to prevent re-import. - Inject modelContainer into SwiftUI environment in IYmtgApp. Image storage: Documents/UserContent/ subfolder (blueprint requirement) - ImageManager.dir now targets iCloud Documents/UserContent/ (or local equiv). - migrateImagesToUserContent() moves existing JPGs to the new subfolder on first launch; called during the SwiftData migration. Firebase: demoted to optional manual backup (metadata only, no images) - Remove all automatic CloudEngine.save/delete/batchUpdatePrices calls from CollectionViewModel mutations. - Add backupAllToFirebase() for user-triggered metadata sync. - Add isFirebaseBackupEnabled to AppConfig (default false). - Add Cloud Backup section in Library settings with iCloud vs Firebase explanation and "Backup Metadata to Firebase Now" button. Also: full modular refactor (Data/, Features/, Services/ directories) and README updated with CloudKit setup steps and revised release checklist. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
157 lines
5.2 KiB
Swift
157 lines
5.2 KiB
Swift
import SwiftData
|
|
import Foundation
|
|
|
|
// MARK: - SAVED CARD MODEL
|
|
// SwiftData @Model class for structured persistence with automatic CloudKit sync.
|
|
// Mirrors the SavedCard value-type struct so the rest of the app stays unchanged.
|
|
// Requires iOS 17+. Set minimum deployment target to iOS 17 in Xcode.
|
|
|
|
@Model
|
|
final class SavedCardModel {
|
|
@Attribute(.unique) var id: UUID
|
|
var scryfallID: String
|
|
var name: String
|
|
var setCode: String
|
|
var collectorNumber: String
|
|
var imageFileName: String
|
|
var condition: String
|
|
var foilType: String
|
|
var currentValuation: Double?
|
|
var previousValuation: Double?
|
|
var dateAdded: Date
|
|
var classification: String
|
|
var collectionName: String
|
|
var storageLocation: String
|
|
var rarity: String?
|
|
var colorIdentity: [String]?
|
|
var gradingService: String?
|
|
var grade: String?
|
|
var certNumber: String?
|
|
var isCustomValuation: Bool
|
|
var isSerialized: Bool?
|
|
var currencyCode: String?
|
|
|
|
init(
|
|
id: UUID = UUID(),
|
|
scryfallID: String,
|
|
name: String,
|
|
setCode: String,
|
|
collectorNumber: String,
|
|
imageFileName: String,
|
|
condition: String,
|
|
foilType: String,
|
|
currentValuation: Double? = nil,
|
|
previousValuation: Double? = nil,
|
|
dateAdded: Date = Date(),
|
|
classification: String = "Unknown",
|
|
collectionName: String,
|
|
storageLocation: String,
|
|
rarity: String? = nil,
|
|
colorIdentity: [String]? = nil,
|
|
gradingService: String? = nil,
|
|
grade: String? = nil,
|
|
certNumber: String? = nil,
|
|
isCustomValuation: Bool = false,
|
|
isSerialized: Bool? = false,
|
|
currencyCode: String? = nil
|
|
) {
|
|
self.id = id
|
|
self.scryfallID = scryfallID
|
|
self.name = name
|
|
self.setCode = setCode
|
|
self.collectorNumber = collectorNumber
|
|
self.imageFileName = imageFileName
|
|
self.condition = condition
|
|
self.foilType = foilType
|
|
self.currentValuation = currentValuation
|
|
self.previousValuation = previousValuation
|
|
self.dateAdded = dateAdded
|
|
self.classification = classification
|
|
self.collectionName = collectionName
|
|
self.storageLocation = storageLocation
|
|
self.rarity = rarity
|
|
self.colorIdentity = colorIdentity
|
|
self.gradingService = gradingService
|
|
self.grade = grade
|
|
self.certNumber = certNumber
|
|
self.isCustomValuation = isCustomValuation
|
|
self.isSerialized = isSerialized
|
|
self.currencyCode = currencyCode
|
|
}
|
|
|
|
convenience init(from card: SavedCard) {
|
|
self.init(
|
|
id: card.id,
|
|
scryfallID: card.scryfallID,
|
|
name: card.name,
|
|
setCode: card.setCode,
|
|
collectorNumber: card.collectorNumber,
|
|
imageFileName: card.imageFileName,
|
|
condition: card.condition,
|
|
foilType: card.foilType,
|
|
currentValuation: card.currentValuation,
|
|
previousValuation: card.previousValuation,
|
|
dateAdded: card.dateAdded,
|
|
classification: card.classification,
|
|
collectionName: card.collectionName,
|
|
storageLocation: card.storageLocation,
|
|
rarity: card.rarity,
|
|
colorIdentity: card.colorIdentity,
|
|
gradingService: card.gradingService,
|
|
grade: card.grade,
|
|
certNumber: card.certNumber,
|
|
isCustomValuation: card.isCustomValuation,
|
|
isSerialized: card.isSerialized,
|
|
currencyCode: card.currencyCode
|
|
)
|
|
}
|
|
|
|
func update(from card: SavedCard) {
|
|
name = card.name
|
|
setCode = card.setCode
|
|
collectorNumber = card.collectorNumber
|
|
condition = card.condition
|
|
foilType = card.foilType
|
|
currentValuation = card.currentValuation
|
|
previousValuation = card.previousValuation
|
|
classification = card.classification
|
|
collectionName = card.collectionName
|
|
storageLocation = card.storageLocation
|
|
rarity = card.rarity
|
|
colorIdentity = card.colorIdentity
|
|
gradingService = card.gradingService
|
|
grade = card.grade
|
|
certNumber = card.certNumber
|
|
isCustomValuation = card.isCustomValuation
|
|
isSerialized = card.isSerialized
|
|
currencyCode = card.currencyCode
|
|
}
|
|
|
|
func toSavedCard() -> SavedCard {
|
|
SavedCard(
|
|
id: id,
|
|
scryfallID: scryfallID,
|
|
name: name,
|
|
setCode: setCode,
|
|
collectorNumber: collectorNumber,
|
|
imageFileName: imageFileName,
|
|
condition: condition,
|
|
foilType: foilType,
|
|
currentValuation: currentValuation,
|
|
previousValuation: previousValuation,
|
|
dateAdded: dateAdded,
|
|
classification: classification,
|
|
collectionName: collectionName,
|
|
storageLocation: storageLocation,
|
|
rarity: rarity,
|
|
colorIdentity: colorIdentity,
|
|
gradingService: gradingService,
|
|
grade: grade,
|
|
certNumber: certNumber,
|
|
isCustomValuation: isCustomValuation,
|
|
isSerialized: isSerialized,
|
|
currencyCode: currencyCode
|
|
)
|
|
}
|
|
}
|