Having trouble implementing SwiftData on my working solution to day 60, Milestone: Projects 10-12.
Specifically, I'm seeing an error on ContentView.swift, "Instance method 'insert' requires that '[User]' conform to 'PersistentModel'"
Does anyone have any insight on what the issue could be? It seems I've wired up SwiftData correctly, and have all of the needed imports, but I'm having trouble debugging the issue.
Also, i've tried to search the web and these forums for example solutions, but it seems that this is the new version of this challenge. From looking at past implementations, day 61 used to be a CoreData problem.
AllMyFriends_ConsolidationVApp.swift
@main
struct AllMyFriends_ConsolidationVApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: User.self)
}
}
Friend.swift
class Friend: Codable {
var id: String
var name: String
init(id: String, name: String) {
self.id = id
self.name = name
}
}
User.swift
@Model class User: Codable, Hashable {
//struct User: Codable {
enum CodingKeys: String, CodingKey {
case id
case isActive
case name
case age
case company
case email
case address
case about
case registered
case tags
case friends
}
var id: String
var isActive: Bool
var name: String
var age: Int
var company: String
var email: String
var address: String
var about: String
var registered: Date
var tags: [String]
var friends: [Friend]
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: .id)
try container.encode(self.isActive, forKey: .isActive)
try container.encode(self.name, forKey: .name)
try container.encode(self.age, forKey: .age)
try container.encode(self.company, forKey: .company)
try container.encode(self.email, forKey: .email)
try container.encode(self.address, forKey: .address)
try container.encode(self.about, forKey: .about)
try container.encode(self.registered, forKey: .registered)
try container.encode(self.tags, forKey: .tags)
try container.encode(self.friends, forKey: .friends)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
//don't need to write CodingKeys because all of the data names match
self.id = try container.decode(String.self, forKey: .id)
self.isActive = try container.decode(Bool.self, forKey: .isActive)
self.name = try container.decode(String.self, forKey: .name)
self.age = try container.decode(Int.self, forKey: .age)
self.company = try container.decode(String.self, forKey: .company)
self.email = try container.decode(String.self, forKey: .email)
self.address = try container.decode(String.self, forKey: .address)
self.about = try container.decode(String.self, forKey: .about)
self.registered = try container.decode(Date.self, forKey: .registered)
self.tags = try container.decode([String].self, forKey: .tags)
self.friends = try container.decode([Friend].self, forKey: .friends)
}
init(id: String, isActive: Bool, name: String, age: Int, company: String, email: String, address: String, about: String, registered: Date, tags: [String], friends: [Friend]) {
self.id = id
self.isActive = isActive
self.name = name
self.age = age
self.company = company
self.email = email
self.address = address
self.about = about
self.registered = registered
self.tags = tags
self.friends = friends
}
}
ContentView.swift
struct ContentView: View {
@Environment(\.modelContext) var modelContext
@Query(sort: \User.name) var users: [User]
var body: some View {
NavigationStack {
List(users, id: \.id) { user in
NavigationLink(value: user) {
Text(user.name)
}
}
.navigationTitle("All My Friends")
.task {
if users.isEmpty {
await loadFriendData()
print(users.count)
}
}
.navigationDestination(for: User.self) { user in
DetailView(user: user)
}
}
}
func loadFriendData() async {
guard let url = URL(string: "https://www.hackingwithswift.com/samples/friendface.json") else {
print("invalid url")
return
}
do {
let (responseData, _) = try await URLSession.shared.data(from: url)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
if let decodedResponse = try decoder.decode([User]?.self, from: responseData) {
modelContext.insert(decodedResponse)
} else {
print("if let block not working")
}
} catch {
print(error.localizedDescription)
print(String(describing: error))
}
}
}
DetailView.swift
struct DetailView: View {
var user: User
var body: some View {
List {
UserAttribute(label: "Address", attribute: user.address)
UserAttribute(label: "Company", attribute: user.company)
UserAttribute(label: "Email", attribute: user.email)
UserAttribute(label: "Joined", attribute: returnDateString(user.registered))
// Spacer()
ScrollView(.horizontal) {
Text("Friends")
.font(.caption)
.foregroundColor(.accentColor)
.padding(EdgeInsets(top: 0, leading: -.infinity, bottom: 0, trailing: 0))
HStack {
ForEach(user.friends, id: \.self.id) { friend in
Text(friend.name)
.padding()
}
}
}
.padding()
// Spacer()
UserAttribute(label: "About", attribute: user.about)
}
.navigationTitle(user.name)
.toolbar {
ToolbarItem {
user.isActive ?
Text("🟢 Active")
.foregroundStyle(Color.green)
:
Text("🔴 Inactive")
.foregroundStyle(Color.red)
}
}
}
func returnDateString(_ date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
return dateFormatter.string(from: date)
}
}
struct UserAttribute: View {
var label: String
var attribute: String
var body: some View {
VStack(alignment: .leading) {
Text(label)
.font(.caption)
.padding(EdgeInsets(top: 0, leading: 0, bottom: 7, trailing: 0))
.foregroundColor(.accentColor)
Text(attribute)
}
}
}