Post

Replies

Boosts

Views

Activity

isStoredInMemoryOnly has no effect for macOS + iCloud?
Wondering if anyone else is running into this. It seems ModelConfiguration(isStoredInMemoryOnly: true) for previews (as outlined by Paul Hudson / Hacking with Swift) works correctly for iOS + iCloud syncing macOS WITHOUT iCloud syncing But as soon as I turn on iCloud syncing capability for my macOS target, its as if the isStoredInMemoryOnly has no effect on the macOS target. Here's my code... I made a PreviewHelper to encapsulate the preview logic... enum PreviewHelper { static let previewModelContainer: ModelContainer = { do { let config = ModelConfiguration(isStoredInMemoryOnly: true) let container = try ModelContainer(for: Task.self, configurations: config) return container } catch { fatalError("Failed to create model container for previewing: \(error.localizedDescription)") } }() } And then use it like so... #Preview { let container = PreviewHelper.previewModelContainer for task in MockData.tasks { container.mainContext.insert(task) } return HorizonView() .modelContainer(container) } On the macOS target & destination, using a macOS device in the Preview canvas, with iCloud syncing turned on that code inserts the MockData.tasks into my iCloud container every time the preview refreshes, so the data just keeps getting duplicated. With iCloud syncing turned off it behaves as expected/correctly (just inserting the MockData as needed for Previews). In an iOS target, using the same helper and mock data, the helper behaves as expected/correctly (with or without iCloud syncing enabled). Assuming this might be a bug/oversight with SwiftData and macOS? Or am I missing a needed configuration/capability on the macOS side? Anybody else seeing this?
0
1
401
Jan ’24
SwiftData Custom Migration with no change in schema?
I'll preface by saying I'm a new Swift developer, having a go at my first app. Just a simple memory tracker. I'm building it using SwiftData + iCloud Syncing. I had set up my model like this: @Model final class Memory { var content: String = "" var dateCreated: Date = Date.now var dateUpdated: Date = Date.now var tags: [Tag]? = [Tag]() @Attribute(.externalStorage) var images: [Data] = [Data]() init( content: String = "", dateCreated: Date = .now, dateUpdated: Date = .now, tags: [Tag] = [Tag](), images: [Data] = [Data]() ) { self.content = content self.dateCreated = dateCreated self.dateUpdated = dateUpdated self.tags = tags self.images = images } } But I discovered that led to a massive performance issue as soon as someone added a few images to a Memory. Maybe SwiftData isn't correctly putting an ARRAY of Data into external storage? My memory usage would just balloon with each photo added. All the examples I've seen just use a singular Data type for external storage, so not sure. Anyway, I played around with different options and figured out that a new MemoryPhoto struct was probably best, so I put the old model in a V1 schema and my NEW V2 model looks like this... enum DataSchemaV2: VersionedSchema { static var versionIdentifier = Schema.Version(2, 0, 0) static var models: [any PersistentModel.Type] { [Memory.self, Tag.self, MemoryPhoto.self] } @Model final class Memory { var content: String = "" var dateCreated: Date = Date.now var dateUpdated: Date = Date.now var tags: [Tag]? = [Tag]() @Relationship(deleteRule: .cascade) var photos: [MemoryPhoto]? = [MemoryPhoto]() @Attribute(.externalStorage) var images: [Data] = [Data]() init( content: String = "", dateCreated: Date = .now, dateUpdated: Date = .now, tags: [Tag] = [Tag](), images: [Data] = [Data](), photos: [MemoryPhoto] = [MemoryPhoto]() ) { self.content = content self.dateCreated = dateCreated self.dateUpdated = dateUpdated self.tags = tags self.images = images self.photos = photos } } @Model final class MemoryPhoto { @Attribute(.externalStorage) var originalData: Data? @Relationship(inverse: \Memory.photos) var memory: Memory? init(originalData: Data? = nil, memory: Memory? = nil) { self.originalData = originalData self.memory = memory } } Here's my migration, currently, which does work, because as best I can tell this is a lightweight migration... enum DataMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [DataSchemaV1.self, DataSchemaV2.self] } static var stages: [MigrationStage] { [migrateV1toV2] } static let migrateV1toV2 = MigrationStage.lightweight(fromVersion: DataSchemaV1.self, toVersion: DataSchemaV2.self) } But what I'm trying to figure out now is to migrate the former memory.images of type [Data] to the new memory.photos of type [MemoryPhoto], and been struggling. Any type of custom migration I do fails, sometimes inconsistently. I can try to get the exact errors if helpful but at this point not even a simple fetch to existing memories and updating their content as a part of the migration works. Is there a way to write a hypothetical V2 to V3 migration that just takes the images and puts them in the photos "slot"? For instance, what I do have working is this function that basically runs a "migration" or sorts when a given memory appears and it has the former images property. .... .onAppear { convertImagesToPhotos() } } private func convertImagesToPhotos() { guard !memory.images.isEmpty && memory.unwrappedPhotos.isEmpty else { return } let convertedPhotos = memory.images.map { imageData in MemoryPhoto(originalData: imageData) } memory.photos?.append(contentsOf: convertedPhotos) memory.images.removeAll() } Any help or pointers appreciated for this newbie swift developer. If helpful, here's the main App struct too... @main struct YesterdaysApp: App { @Environment(\.scenePhase) var scenePhase @AppStorage("writingRemindersEnabled") var writingRemindersEnabled: Bool = false let container: ModelContainer init() { do { container = try ModelContainer( for: Memory.self, migrationPlan: DataMigrationPlan.self ) } catch { fatalError("Failed to initialize model container.") } } var body: some Scene { WindowGroup { OuterMemoryListView() .yesterdaysPremium() } .modelContainer(container) } }
0
0
512
Dec ’23