0

I'm building a simple app that persists data using the SwiftData framework with the additional capability of syncing through CloudKit across devices.

All was well until I added a second model object. There's no relationship between the two models, but the simple addition of a new object causes the app to throw an error in the creation of the ModelContainer.

Does anyone have insight on how to correctly do this? Here's some code to illustrate the relevant (I think) parts...

First the main app file...

import SwiftUI
import SwiftData

@main
struct FeederApp: App {
    var sharedModelContainer: ModelContainer = {
        let feedSchema = Schema([Feed.self])
        let noteSchema = Schema([Note.self])
        let feedModelConfiguration = ModelConfiguration("default", schema: feedSchema, isStoredInMemoryOnly: false)
        let noteModelConfiguration = ModelConfiguration("NoteConfiguration", schema: noteSchema, isStoredInMemoryOnly: false)
        do {
            return try ModelContainer(for: Feed.self, Note.self, configurations: feedModelConfiguration, noteModelConfiguration)
        } catch {
            //TODO: add some error correction
            fatalError("Could not create ModelContainer: \n\(error)") //ERROR HERE
        }
    }()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .foregroundStyle(.green)
        }
        .modelContainer(sharedModelContainer)
    }
}

and here's the model objects...

import Foundation
import SwiftData

enum Source: String, Codable, CaseIterable, Identifiable, Equatable {
    case formula_standard = "Formula"
    case formula_enriched = "Formula Enriched"
    case breast = "Breast Milk"
    var id: Self { self }
}

@Model
final class Feed {
    var timestamp: Date = Date.now
    var source: Source = Source.breast
    var qty_as_int: Int = 0
    var id = UUID()
    
    init(timestamp: Date, qty_as_int: Int, source: Source) {
        self.timestamp = timestamp
        self.source = source
        self.qty_as_int = qty_as_int
    }
}

and the new model...

import Foundation
import SwiftData

enum WeightType: String, Codable, CaseIterable, Identifiable, Equatable{
    case weight = "Standard Weighing"
    case birthWeight = "Birth Weight"
    var id: Self { self }
}

@Model
final class Weight: Identifiable {
    var id = UUID()
    var weight: Double
    var date: Date = Date()
    var type: WeightType = WeightType.weight
    
    init(weight: Double,
         type: WeightType) {
        self.weight = weight
        self.type = type
    }
    
    init(weight: Double,
         type: WeightType,
         date: Date) {
        self.weight = weight
        self.type = type
        self.date = date
    }
}

And here's the error:

Feeder/FeederApp.swift:24: Fatal error: Could not create ModelContainer: SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer, _explanation: nil)

Do I need to add the new schema for the new model object manually in the CloudKit dashboard?

If I set the CloudKit database to .none then the app runs but of course doesn't sync the new model object across devices.

3
  • Did you try using only one Schema and one ModelConfiguration? Commented Nov 14, 2024 at 17:47
  • Yes, using multiple schema and config was the only way I could find to get it to work at all. Commented Nov 14, 2024 at 18:04
  • I don't exactly see a reason why you use two schemas and two configurations. Do you need to separate those models into different data stores? Check your applications "Application Support" directory: you will find your "default2.store*" and "NoteConfiguration.store*" SQLite databases. Commented Nov 30, 2024 at 16:11

1 Answer 1

1

As I have a working app with iCloud syncing (using a single "default" data store), I simply added your new Weight model type and updated my container initialization to include it.

modelContainer = try ModelContainer(for: Entry.self, Weight.self, configurations: configuration)

Launching the app crashed with the cryptic error SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer, _explanation: nil) but the app also writes a hint into the Xcode console starting with the message: error: Store failed to load. <NSPersistentStoreDescription:

CloudKit integration requires that all attributes be optional, or have a 
default value set. The following attributes are marked non-optional but
do not have a default value:
Weight: weight

This is telling us, that your definition of Weight is not CloudKit compatible. (Check Apples documentation on Define a CloudKit compatible schema.

Fixing the definition of the Weight to have a default value for the property weight fixes the app launch problem.

@Model
final class Weight: Identifiable {
    var id = UUID()
    var weight: Double = 0.0
    var date: Date = Date()
    var type: WeightType = WeightType.weight
}

For testing I've added a button to add a Weight model... which automatically syncs to CloudKit!

Sign up to request clarification or add additional context in comments.

1 Comment

Awesome thank you, can't believe I missed this. Indeed it works now I st default values for all model properties.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.