|
Not 100% sure but there is a discrepancy between you V2 and V3. In V2 your have @Attribute(.unique) var name: String and in V3 you again have it as var name: String . Make it both with attribute and check if this solve the issue.
|
|
Thank you for your reply and pointing to a mistake in the declaration of the property.
Unfortunately the error meesage is still the same
|
|
Did you try to delete app on simulator and then run it again? Or clean build folder?
The same logic works in my case. Post the full setup below:
import SwiftUI
import SwiftData
@main
struct VersioningTestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: User.self)
}
@MainActor
var container: ModelContainer {
do {
let container = try ModelContainer(
for: User.self,
migrationPlan: UserMigrationPlan.self
)
return container
} catch {
fatalError("Could not create ModelContainer with migration plan: \(error.localizedDescription)")
}
}
}
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var users: [User]
var body: some View {
NavigationStack {
List(users) { user in
Text("Username: \(user.name). Age: \(user.age). Gender: \(user.gender)")
}
.navigationTitle("Versioning")
.toolbar {
ToolbarItem {
ControlGroup {
Button("V1", systemImage: "1.square") {
User.addVersion1Data(modelContext: modelContext)
}
Button("V2", systemImage: "2.square") {
User.addVersion2Data(modelContext: modelContext)
}
Button("V3", systemImage: "3.square") {
User.addVersion3Data(modelContext: modelContext)
}
}
.controlGroupStyle(.navigation)
}
}
}
}
}
#Preview {
ContentView()
.modelContainer(User.preview)
}
import Foundation
import SwiftData
typealias User = UserSchemaV3.User
enum UserSchemaV1: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] { [User.self] }
@Model
class User {
var name: String
var age: Int
init(
name: String,
age: Int
) {
self.name = name
self.age = age
}
}
}
enum UserSchemaV2: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] { [User.self] }
@Model
class User {
@Attribute(.unique) var name: String
var age: Int
init(
name: String,
age: Int
) {
self.name = name
self.age = age
}
}
}
enum UserSchemaV3: VersionedSchema {
static var versionIdentifier = Schema.Version(3, 0, 0)
static var models: [any PersistentModel.Type] { [User.self] }
@Model
class User {
@Attribute(.unique) var name: String
var age: Int
var gender: String
init(
name: String,
age: Int,
gender: String = "N/A"
) {
self.name = name
self.age = age
self.gender = gender
}
}
}
enum UserMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[UserSchemaV1.self, UserSchemaV2.self, UserSchemaV3.self]
}
static let migrateV1toV2 = MigrationStage.custom(
fromVersion: UserSchemaV1.self,
toVersion: UserSchemaV2.self,
willMigrate: {
context in
let users = try context.fetch(FetchDescriptor<UserSchemaV1.User>())
var usedNames = Set<String>()
for user in users {
if usedNames.contains(user.name) {
context.delete(user)
}
usedNames.insert(user.name)
}
try context.save()
},
didMigrate: nil
)
static let migrateV2toV3 = MigrationStage.lightweight(
fromVersion: UserSchemaV2.self,
toVersion: UserSchemaV3.self
)
static var stages: [MigrationStage] {
[migrateV1toV2, migrateV2toV3]
}
}
// MARK: - MOCK DATA
extension User {
@MainActor
static var preview: ModelContainer {
let container = try! ModelContainer(for: User.self, configurations: ModelConfiguration(isStoredInMemoryOnly: true))
return container
}
// MOCK DATA FOR V1
static func addVersion1Data(modelContext: ModelContext) {
let users: [User] = [
User(name: "Alice", age: 25),
User(name: "Bob", age: 30),
User(name: "Charlie", age: 28),
User(name: "David", age: 35),
User(name: "Eve", age: 27),
User(name: "Frank", age: 32),
User(name: "Grace", age: 29),
User(name: "Henry", age: 31),
User(name: "Ivy", age: 26),
User(name: "Jack", age: 33)
]
for user in users {
modelContext.insert(user)
}
}
// MOCK DATA FOR V2
static func addVersion2Data(modelContext: ModelContext) {
let users: [User] = [
User(name: "Alice", age: 25),
User(name: "Bob", age: 30),
User(name: "Charlie", age: 28),
User(name: "David", age: 35),
User(name: "Eve", age: 27),
User(name: "Frank", age: 32),
User(name: "Grace", age: 29),
User(name: "Henry", age: 31),
User(name: "Ivy", age: 26),
User(name: "Jack", age: 33),
User(name: "Frank", age: 32),
User(name: "Grace", age: 29),
User(name: "Henry", age: 31),
User(name: "Ivy", age: 26),
User(name: "Jack", age: 33),
User(name: "Yuri", age: 42)
]
for user in users {
modelContext.insert(user)
}
}
// MOCK DATA FOR V3
static func addVersion3Data(modelContext: ModelContext) {
let users: [User] = [
User(name: "Alice", age: 25),
User(name: "Bob", age: 30),
User(name: "Charlie", age: 28),
User(name: "David", age: 35),
User(name: "Eve", age: 27),
User(name: "Frank", age: 32),
User(name: "Grace", age: 29),
User(name: "Henry", age: 31),
User(name: "Ivy", age: 26),
User(name: "Jack", age: 33),
User(name: "Frank", age: 32),
User(name: "Grace", age: 29),
User(name: "Henry", age: 31),
User(name: "Ivy", age: 26),
User(name: "Jack", age: 33),
User(name: "Yuri", age: 42),
User(name: "Sam", age: 33, gender: "Male"),
User(name: "Renat", age: 42, gender: "Male"),
User(name: "Natali", age: 23, gender: "Female")
]
for user in users {
modelContext.insert(user)
}
}
}
|
|
Wow! Thank you very much for your support.
To clean the build folder or deleting the app didn't help with my code.
When setting up a new app with your code it runs perfectly.
On a very rough analysis I see the main difference in the top level file, mine is much more simpler
//
// TestMigrationApp.swift
// TestMigration
//
// Created by Ralf 2 on 13.01.24.
//
import SwiftUI
import SwiftData
@main
struct TestMigrationApp: App {
let container: ModelContainer
// let myContext: ModelContext
init() {
do {
container = try ModelContainer(
for: User.self,
migrationPlan: UsersMigrationPlan.self
)
}
catch {
fatalError("Failed to initialize model container.")
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
}
Now I do have to read and understand your approach, especially the functionality of the wrapper @MainActor
It seems that I've missed some part of knowledge.
To have the chance to inform you about my progress, I'll leave this "ticket" open.
Please, give me a few days for reading, learning and testing. My real app I am working on includes relationships and I have to adapt my learnings from these test to it.
Many thanks again. Wonderful to have such a supportive community for help.
|
|
Just small note. I made a mistake there is slight addition should be made. But in any case I run the app and it worked the same way without a hitch.
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container) // <-- I forgot to use container instead of User.self
}
As for @MainActor this part is not really important in that particular case. It just makes sure the container is created on the main thread, but as far as I can see it works just fine without it.
|
|
Thanks for the reply and the correction.
If the @MainActor is not the "big" difference I have to be more smarter to discover my previous mistake ;-)
I'll rebuild my test app step by step like in a real devlopment process and will see how far or smooth it runs.
Thanks again.
|
|
|
|
@ygeras
Again many thanks for your ideas and code.
If I use your code, everything works fine.
But if I simulate the development, starting with User.Version1, than make the next step to User.Version2 the migration works wonderful.
But again, as soon as I do the next migration step to User.Version3, the app crashes.
My method to add the sample data is in both steps, Verion 1 and Version 2, as described below. The only difference is the explicit try? modelContext.save().
I am just wondering why the behaviour is different.
But I'll proceed with my real project with your ideas and hope to have more success.
Thanks,
Ralf
func addSample() {
let user1 = User(name: "Peter", age: 18)
let user2 = User(name: "Ralf", age: 59)
let user3 = User(name: "Michael", age: 57)
let user4 = User(name: "Reinhold", age: 58)
let user5 = User(name: "Peter", age: 65)
let user6 = User(name: "Peter", age: 16)
modelContext.insert(user1)
modelContext.insert(user2)
modelContext.insert(user3)
modelContext.insert(user4)
modelContext.insert(user5)
modelContext.insert(user6)
try? modelContext.save()
}
|
|
Hi Ralf! But why do you use modelContext.save() ? Did you change ModelContainer autosave to off? And what does the crash say? I mean is there any output to the console to indicate the reason?
|
|
Hi helpful @ygeras,
to answer your question: I've used the save-method because Paul always recommends to use methods explicit to have control.
But in the meanwhile I've tested my code without it and it males no difference.
Working on my project I was thinking if the lightwight method may be the problem. So I did first another kind of lightweigted migration, changing a property to an optional property. Runs correctly, even the model is more complex with a one-to-many relationship.
The next step was to add a new property like in the test app we discussed above, but unfortunately again the app crashes runtime.
The error message is:
Fatal error: Could not create ModelContainer with migration plan: The operation couldn’t be completed. (SwiftData.SwiftDataError error 1.)
or a little bit abouve in yellow/orange:
{Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={entity=Tagebuchseite, attribute=testString, reason=Validation error missing attribute values on mandatory destination attribute}}}
Update:
When I define the new property as an optional the property seems to be created but not initialized with the values given in the init() method of the class:
@Model
class Tagebuchseite {
@Attribute(.unique) var datum: Date
var datumString: String?
var tagebuchseiteId: UUID
@Relationship(deleteRule: .cascade, inverse: \Tagebucheintrag.tagebuchseite) var tagebucheintraege: [Tagebucheintrag]?
var testString: String?
init(datum: Date = .now, datumString: String = Date.now.formatted(date: .numeric, time: .omitted), tagebuchseiteId: UUID = UUID(), tagebucheintraege: [Tagebucheintrag] = [Tagebucheintrag](), testString: String = "Ein String") {
self.datum = datum
self.datumString = datumString
self.tagebuchseiteId = tagebuchseiteId
self.tagebucheintraege = tagebucheintraege
self.testString = testString
}
}
|
|
Well, without more or less full picture of the code to be migrated, it is rather difficult to say, what might be the problem.
|
|
It is no commercial project, so I have no problem to share my code here. It is just a simple form of a diary app to make notes about the weather, nature and activities. Below you will find the main parts of the app that you can get an impression of the models and migration.
Any ideas are welcome.
//
// Tagebuch_v5App.swift
// Tagebuch v5
//
// Created by Ralf 2 on 15.12.23.
//
import SwiftUI
import SwiftData
@main
struct Tagebuch_v5App: App {
var body: some Scene {
WindowGroup {
LaunchView()
}
.modelContainer(container)
}
@MainActor
var container: ModelContainer {
do {
let container = try ModelContainer(
for: Tagebuchseite.self,
migrationPlan: TagebuchseiteMigrationPlan.self
)
return container
} catch {
fatalError("Could not create ModelContainer with migration plan: \(error.localizedDescription)")
}
}
}
//
// LaunchView.swift
// StepsHundegesundheit
//
// Created by Ralf 2 on 31.10.23.
//
import SwiftUI
import SwiftData
struct LaunchView: View {
@Environment(\.modelContext) var modelContext
// @Query var tageseintraege : [Tageseintrag]
@State var tabIndex = 1
var body: some View {
TabView(selection: $tabIndex) {
ContentView()
.tabItem {
VStack {
Image(systemName: "house")
Text("Einträge")
}
}
.tag(1)
DatenSuchView()
.tabItem {
VStack {
Image(systemName: "text.magnifyingglass")
Text("Suche")
}
}
.tag(2)
}
}
}
#Preview {
LaunchView()
}
//
// Tagebuchseite.swift
// Tagebuch v5
//
// Created by Ralf 2 on 15.12.23.
//
import Foundation
import SwiftData
typealias Tagebuchseite = TagebuchseiteSchemaV4.Tagebuchseite
typealias Tagebucheintrag = TagebuchseiteSchemaV4.Tagebucheintrag
enum TagebuchseiteSchemaV1: VersionedSchema {
static var versionIdentifier = Schema.Version(1,0,0)
static var models: [any PersistentModel.Type] {[Tagebuchseite.self]}
@Model
class Tagebuchseite {
var datum: Date
var datumString: String
var tagebuchseiteId: UUID
@Relationship(deleteRule: .cascade, inverse: \Tagebucheintrag.tagebuchseite) var tagebucheintraege: [Tagebucheintrag]?
init(datum: Date = .now, datumString: String = Date.now.formatted(date: .numeric, time: .omitted), tagebuchseiteId: UUID = UUID(), tagebucheintraege: [Tagebucheintrag] = [Tagebucheintrag]()) {
self.datum = datum
self.datumString = datumString
self.tagebuchseiteId = tagebuchseiteId
self.tagebucheintraege = tagebucheintraege
}
}
@Model
class Tagebucheintrag {
var aktivitaet: String?
var fauna: String?
var flora: String?
var himmel: String?
var ort: String?
var temperatur: String?
var uhrzeit: String?
// var uhrzeitString: String
var wind: String?
var tagebuchseite: Tagebuchseite?
init(aktivitaet: String = "", fauna: String = "", flora: String = "", himmel: String = "", ort: String = "", temperatur: String = "", uhrzeit: String = "", wind: String = "") {
self.aktivitaet = aktivitaet
self.fauna = fauna
self.flora = flora
self.himmel = himmel
self.ort = ort
self.temperatur = temperatur
self.uhrzeit = uhrzeit
// self.uhrzeitString = uhrzeitString
self.wind = wind
}
}
}
enum TagebuchseiteSchemaV2: VersionedSchema {
static var versionIdentifier = Schema.Version(2,0,0)
static var models: [any PersistentModel.Type] {[Tagebuchseite.self]}
@Model
class Tagebuchseite {
@Attribute(.unique) var datum: Date
var datumString: String
var tagebuchseiteId: UUID
@Relationship(deleteRule: .cascade, inverse: \Tagebucheintrag.tagebuchseite) var tagebucheintraege: [Tagebucheintrag]?
init(datum: Date = .now, datumString: String = Date.now.formatted(date: .numeric, time: .omitted), tagebuchseiteId: UUID = UUID(), tagebucheintraege: [Tagebucheintrag] = [Tagebucheintrag]()) {
self.datum = datum
self.datumString = datumString
self.tagebuchseiteId = tagebuchseiteId
self.tagebucheintraege = tagebucheintraege
}
}
@Model
class Tagebucheintrag {
var aktivitaet: String?
var fauna: String?
var flora: String?
var himmel: String?
var ort: String?
var temperatur: String?
var uhrzeit: String?
// var uhrzeitString: String
var wind: String?
var tagebuchseite: Tagebuchseite?
init(aktivitaet: String = "", fauna: String = "", flora: String = "", himmel: String = "", ort: String = "", temperatur: String = "", uhrzeit: String = "", wind: String = "") {
self.aktivitaet = aktivitaet
self.fauna = fauna
self.flora = flora
self.himmel = himmel
self.ort = ort
self.temperatur = temperatur
self.uhrzeit = uhrzeit
// self.uhrzeitString = uhrzeitString
self.wind = wind
}
}
}
enum TagebuchseiteSchemaV3: VersionedSchema {
static var versionIdentifier = Schema.Version(3,0,0)
static var models: [any PersistentModel.Type] {[Tagebuchseite.self]}
@Model
class Tagebuchseite {
@Attribute(.unique) var datum: Date
var datumString: String?
var tagebuchseiteId: UUID
@Relationship(deleteRule: .cascade, inverse: \Tagebucheintrag.tagebuchseite) var tagebucheintraege: [Tagebucheintrag]?
// var testString: String
init(datum: Date = .now, datumString: String = Date.now.formatted(date: .numeric, time: .omitted), tagebuchseiteId: UUID = UUID(), tagebucheintraege: [Tagebucheintrag] = [Tagebucheintrag]()) {
self.datum = datum
self.datumString = datumString
self.tagebuchseiteId = tagebuchseiteId
self.tagebucheintraege = tagebucheintraege
// self.testString = testString
}
}
@Model
class Tagebucheintrag {
var aktivitaet: String?
var fauna: String?
var flora: String?
var himmel: String?
var ort: String?
var temperatur: String?
var uhrzeit: String?
// var uhrzeitString: String
var wind: String?
var tagebuchseite: Tagebuchseite?
init(aktivitaet: String = "", fauna: String = "", flora: String = "", himmel: String = "", ort: String = "", temperatur: String = "", uhrzeit: String = "", wind: String = "") {
self.aktivitaet = aktivitaet
self.fauna = fauna
self.flora = flora
self.himmel = himmel
self.ort = ort
self.temperatur = temperatur
self.uhrzeit = uhrzeit
// self.uhrzeitString = uhrzeitString
self.wind = wind
}
}
}
enum TagebuchseiteSchemaV4: VersionedSchema {
static var versionIdentifier = Schema.Version(4,0,0)
static var models: [any PersistentModel.Type] {[Tagebuchseite.self]}
@Model
class Tagebuchseite {
@Attribute(.unique) var datum: Date
var datumString: String?
var tagebuchseiteId: UUID
@Relationship(deleteRule: .cascade, inverse: \Tagebucheintrag.tagebuchseite) var tagebucheintraege: [Tagebucheintrag]?
var testString: String?
init(datum: Date = .now, datumString: String = Date.now.formatted(date: .numeric, time: .omitted), tagebuchseiteId: UUID = UUID(), tagebucheintraege: [Tagebucheintrag] = [Tagebucheintrag](), testString: String = "Ein String") {
self.datum = datum
self.datumString = datumString
self.tagebuchseiteId = tagebuchseiteId
self.tagebucheintraege = tagebucheintraege
self.testString = testString
}
}
@Model
class Tagebucheintrag {
var aktivitaet: String?
var fauna: String?
var flora: String?
var himmel: String?
var ort: String?
var temperatur: String?
var uhrzeit: String?
var wind: String?
var tagebuchseite: Tagebuchseite?
init(aktivitaet: String = "", fauna: String = "", flora: String = "", himmel: String = "", ort: String = "", temperatur: String = "", uhrzeit: String = "", wind: String = "") {
self.aktivitaet = aktivitaet
self.fauna = fauna
self.flora = flora
self.himmel = himmel
self.ort = ort
self.temperatur = temperatur
self.uhrzeit = uhrzeit
self.wind = wind
}
}
}
enum TagebuchseiteMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[TagebuchseiteSchemaV1.self, TagebuchseiteSchemaV2.self, TagebuchseiteSchemaV3.self, TagebuchseiteSchemaV4.self]
}
static let migrateV1toV2 = MigrationStage.custom(
fromVersion: TagebuchseiteSchemaV1.self,
toVersion: TagebuchseiteSchemaV2.self,
willMigrate: {
context in
let tagebuchseiten = try context.fetch(FetchDescriptor<TagebuchseiteSchemaV1.Tagebuchseite>())
var usedDatumStrings = Set<String>()
for tagebuchseite in tagebuchseiten {
if usedDatumStrings.contains(tagebuchseite.datumString) {
context.delete(tagebuchseite)
}
usedDatumStrings.insert(tagebuchseite.datumString)
}
try context.save()
},
didMigrate: nil
)
static let migrateV2toV3 = MigrationStage.lightweight(
fromVersion: TagebuchseiteSchemaV2.self,
toVersion: TagebuchseiteSchemaV3.self
)
static let migrateV3toV4 = MigrationStage.lightweight(
fromVersion: TagebuchseiteSchemaV3.self,
toVersion: TagebuchseiteSchemaV4.self
)
static var stages: [MigrationStage] {
[migrateV3toV4]
}
}
extension Tagebuchseite {
@MainActor
static var preview: ModelContainer {
let container = try! ModelContainer(for: Tagebuchseite.self, configurations: ModelConfiguration(isStoredInMemoryOnly: true))
return container
}
}
//
// ExtensionTagebucheintrag.swift
// Tagebuch v5
//
// Created by Ralf 2 on 15.12.23.
//
import Foundation
extension Tagebucheintrag {
public var wrappedAktivitaet : String {
aktivitaet ?? "Keine Aktivität"
}
public var wrappedFauna : String {
fauna ?? "Keine Angabe"
}
public var wrappedFlora : String {
flora ?? "Keine Angabe"
}
public var wrappedOrt : String {
ort ?? "Keine Angabe"
}
public var wrappedHimmel : String {
himmel ?? "Keine Angabe"
}
public var wrappedWind : String {
wind ?? "Keine Angabe"
}
public var wrappedUhrzeit : String {
uhrzeit ?? "Keine Angabe"
}
public var wrappedTemperatur : String {
temperatur ?? "Keine Temperaturangabe"
}
}
//
// ContentView.swift
// Tagebuch v5
//
// Created by Ralf 2 on 15.12.23.
//
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) var modelContext
@State private var sortOrder = SortDescriptor(\Tagebuchseite.datum, order: .reverse)
@Query var tagebuchseiten: [Tagebuchseite]
@State private var path = [Tagebuchseite]()
@State private var isAlertPresented = false
@State private var searchText = ""
var body: some View {
NavigationStack(path: $path) {
TagebuchseiteListingView(sort: sortOrder, searchString: searchText)
.navigationTitle("Tagebuch")
.searchable(text: $searchText, prompt: "Datum, numerische Angabe")
.navigationDestination(for: Tagebuchseite.self, destination: EditingTagebuchseiteView.init)
.toolbar {
EditButton()
.environment(\.locale, Locale(identifier: "de"))
Button("Daten eingeben", systemImage: "calendar.badge.plus", action: addTagebuchseite)
}
.alert("Datum vorhanden", isPresented: $isAlertPresented) {
Button("Abbrechen", role: .cancel) {
clear()
}
} message: {
Text("Das Datum ist schon vorhanden. Bitte bestehenden Eintrag verwenden")
}
.toolbar {
ToolbarItem {
ControlGroup {
Button("Init new property", systemImage: "1.square") {
initTestString()
}
}.controlGroupStyle(.navigation)
}
}
}
}
func initTestString() {
for tagebuchseite in tagebuchseiten {
if tagebuchseite.testString == nil {
tagebuchseite.testString = "Mit Button gesetzt"
}
}
}
func addTagebuchseite() {
let neuesDatum = Date()
var existiertBereits = false
for tagebuchseite in tagebuchseiten {
if Calendar.current.isDate(tagebuchseite.datum, equalTo: neuesDatum, toGranularity: .day) {
existiertBereits = true
break
}
}
if existiertBereits {
isAlertPresented = true
} else {
let neueTagebuchseite = Tagebuchseite()
modelContext.insert(neueTagebuchseite)
path = [neueTagebuchseite]
}
}
func clear() {
searchText = ""
isAlertPresented = false
}
func addSamples() {
// Definition der Beispieldaten
let heute = Date.now
let vorgestern = Date.now - (2 * 86400)
// let gestern = heute - 86400
let TBheute = Tagebuchseite(datum: heute, datumString: heute.formatted(date: .numeric, time: .omitted))
let TBgestern = Tagebuchseite(datum: heute - 86400, datumString: "14.12.2023")
let TBvorgestern = Tagebuchseite(datum: vorgestern, datumString: vorgestern.formatted(date: .numeric, time: .omitted))
modelContext.insert(TBvorgestern)
modelContext.insert(TBgestern)
modelContext.insert(TBheute)
}
}
#Preview {
ContentView()
}
|
|
Is this in your code or you just pasted for preview here like so?
static var stages: [MigrationStage] {
[migrateV3toV4]
}
it should contain all stages
static var stages: [MigrationStage] {
[migrateV1toV2, migrateV2toV3, migrateV3toV4]
}
|
|
Theoretically yes. But if I include all stages it does not work. Step by step, meaning to have only one stage, it works until V3
|
|
But for migration to work stages must include all versions, otherwise it will not know how to migrate from V1 onwards, seem like there is fundamental issue in the setup itself. I would also recommend to revise the model setup itself. Well this is my personal opinion. But what is the point intializing string with empty value, if you have them as optional? You may think to put just nil value instead. Other point you have datumString and have it initialzed upon creating of objects from Data(). But you have already property datum which is the Date type, why do you need to store datumString, you can just create computed property in model which is kind of transient data and will not be saved to persistent store. But whenever you access it you can have it computed from the datum property which is stored. I would advise to create some mock data and start with V1, save it in simulator then add V2, try to migrate and so on. So I think this will show where is the issue starts and what is the reason for that error.
|
|
Yeah. Thanks for the really good ideas.
I'll follow them during the next days . I will need some time, because coding is my sparetime activity.
Some of your remarks are still true. Sometimes the reason is just progress in learning - or making a workaround because I hadn't the knowledge at that time.
Will keep you informed.
Many thanks,
Ralf
|