TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: SwiftData and mapping two data models (changing Moonshot project to SwiftData)

Forums > SwiftUI

I have a question about SwiftData and mapping two data models. I have an app structure in mind that is very similar to the Moonshot app example, Day 39. I've translated this project to SwiftData as a trial project. The only error that I receive has to do with not conforming to the protocol 'PersistentModel' when loading one of the json files:

Type 'Dictionary<String, Activity>.Element' (aka '(key: String, value: Activity)') does not conform to protocol 'PersistentModel'

I have two json files: recommendations (similar to mission.json) and activities (similar to astronauts.json) with the corresponding SwiftData classes Recommendation and Activity. The problem might have to do with the structure of the activities dictionary: a dictionary of dictionaries.

The error occurs when retrieving the data:

@Query var activities: [String: Activity]

Which does the following in the background:

private (set) var _activities: SwiftData.Query<[String: Activity].Element, [String: Activity]> = .init()

Where [String: Activity] seems to cause the problem.

Does anyone have encountered this same problem? Or knows how to solve it?

2      

Hi! I doubt that this query is valid at all @Query var activities: [String: Activity]. You can query Activity class or Recommendation class. But @Query cannot retrieve [String: Activity] as it is not an entity. Please share your models, and json structure maybe it will be more informative to see how it can be handled.

2      

Just as an example. The process might look like this. If you get data from json even as Dictionary you can create your model as you wish to save to SwiftData.

@Model
class AstronautModel {
    let id: String
    let name: String
    let descr: String
    let key: String

    init(id: String, name: String, descr: String, key: String) {
        self.id = id
        self.name = name
        self.descr = descr
        self.key = key
    }
}

struct ContentView: View {
    @Query var astronauts: [AstronautModel]
    @Environment(\.modelContext) var modelContext

    var body: some View {
        NavigationStack {
            List {
                ForEach(astronauts) { astronaut in
                    VStack {
                        Text(astronaut.id)
                        Text(astronaut.name)
                        Text(astronaut.descr)
                        Text("[\(astronaut.key)]")
                    }
                }
            }
            .task {
                fetchData()
            }
        }
    }

    func fetchData() {
        let astronauts: [String: Astronaut] = Bundle.main.decode("astronauts.json")
        for person in astronauts {
            let newPerson = AstronautModel(
                id: person.value.id,
                name: person.value.name,
                descr: person.value.description,
                key: person.key
            )
            modelContext.insert(newPerson)
        }
    }
}

#Preview {
    ContentView()
        .modelContainer(for: AstronautModel.self, inMemory: true)
}

2      

Thank you, that is helpful. I indeed changed the format of the dictionary and then mapped the datamodels by index.

1      

Hacking with Swift is sponsored by Superwall.

SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge.

Learn More

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.