GO FURTHER, FASTER: Try the Swift Career Accelerator today! >>

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      

Go further, faster with the Swift Career Accelerator.

GO FURTHER, FASTER Unleash your full potential as a Swift developer with the all-new Swift Career Accelerator: the most comprehensive, career-transforming learning resource ever created for iOS development. Whether you’re just starting out, looking to land your first job, or aiming to become a lead developer, this program offers everything you need to level up – from mastering Swift’s latest features to conquering interview questions and building robust portfolios.

Learn more here

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.