UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Day 61: Missing Friends…

Forums > 100 Days of SwiftUI

@SD449  

After completing Day 61 on my own and looking at Pauls answer I noticed that the a number of Friends go missing after Core Data is introduced.

Has anyone else noticed this? Is there a reason why this happens?

Examples from Pauls solution: Allie Mendoza only has Calderon Blackwell, should have 6 friends Brooks Spence only has Henry Charles, should have 8 friends Barnett Heath has no friends, should have 2 friends

I believe it must has somthing to do with the merge policy as if I remove moc.save() and go back and forth on the NavigationLinks my code shows different friends, and different quantity of friends, each time.

2      

Not sure where your problem could be, as you provide no code.

I do not have the problem. Maybe it is in your code that fills the database? My ContentView, including the load code, is as follows:

import SwiftUI

struct ContentView: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(sortDescriptors: [SortDescriptor(\.name)]) var cachedUsers: FetchedResults<CachedUser>

    @State private var users = [User]()

    var body: some View {
        NavigationView {

            List {
                ForEach(cachedUsers) { cachedUser in
                    NavigationLink {
                        CachedUserView(cachedUser: cachedUser)
                    } label: {
                        Text(cachedUser.wrappedName)
                    }

                }
            }
            .task {
                if users == [] {
                    do {
                        try await loadData()
                    } catch {
                        fatalError("\(error)")
                    }
                }
            }
            .navigationTitle("All Users")
        }
    }

    func loadData() async throws {
        let url = URL(string: "https://www.hackingwithswift.com/samples/friendface.json")!

        do {
            let (data, _) = try await URLSession.shared.data(for: URLRequest(url: url))

            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601

            users = try decoder.decode([User].self, from: data)
        } catch {
            print("Failed to load data from JSON, continuing with stored version")
            return
        }

        await MainActor.run {
            for user in users {
                let cachedUser = CachedUser(context: moc)
                cachedUser.id = user.id
                cachedUser.isActive = user.isActive
                cachedUser.name = user.name
                cachedUser.age = Int16(user.age)
                cachedUser.company = user.company
                cachedUser.email = user.email
                cachedUser.address = user.address
                cachedUser.about = user.about
                cachedUser.registered = user.registered
                cachedUser.tags = user.tags.joined(separator: ",")
                for friend in user.friends {
                    let cachedFriend = CachedFriend(context: moc)
                    cachedFriend.id = friend.id
                    cachedFriend.name = friend.name
                    cachedUser.addToHasFriend(cachedFriend)
                }

                try? moc.save()
            }
        }
    }
}

The CachedUserView:

import SwiftUI

struct CachedUserView: View {
    let cachedUser: CachedUser

    var body: some View {
        Text(cachedUser.wrappedName)
            .font(.title)

        Text("\(cachedUser.wrappedRegistered.formatted(date: .long, time: .omitted))")
        Form {
            Section(header: Text("Tags")
                .font(.headline)
                .foregroundColor(.primary)
            ) {
                let tags = cachedUser.wrappedTags.components(separatedBy: ",")
                VStack(alignment: .leading) {
                    ForEach(tags, id: \.self) { tag in
                        Text(tag)
                    }
                }
            }
            Section(header: Text("Friends")
                .font(.headline)
                .foregroundColor(.primary)
            ) {
                List {
                    ForEach(cachedUser.friendsArray) { cachedFriend in
                        Text(cachedFriend.wrappedName)
                    }
                }
            }
        }
    }
}

My DataController:

import Foundation
import CoreData

class DataController: ObservableObject {
    let container = NSPersistentContainer(name: "UsersAndFriends")

    init() {
        container.loadPersistentStores { description, error in
            if let error = error {
                print("Core Data failed to load: \(error.localizedDescription)")
                return
            }
            self.container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
        }
    }
}

My data model has one-to-many relations from CachedUser to CachedFriend and vice versa, which are declared to be each others inverse.

Hope this helps, Jeroen.

3      

@SD449  

@Jeroen537 thank you very much for your reply.

My code is very similar to yours, so I cannot see why mine doesn't want to show all friends

I did try altering my code to be more like yours, but no different. The only thing I could do was

if usersData == [] {

as my User didn't conform to Equatable and adding it didn't compile, but now I think that is because I didn't add comparrison code.

I have added my code in case you can find where I have gone wrong.

ContentView

import SwiftUI

struct ContentView: View {
//    @State private var usersData = [User]()
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(sortDescriptors: [SortDescriptor(\.name)]) var users: FetchedResults<CachedUser>

    var body: some View {
        NavigationView {
            List(users) { user in
                NavigationLink {
                    UserView(user: user)
                } label: {
                    HStack {
                        Circle()
                            .fill(user.isActive ? .green : .red)
                            .frame(width: 30)

                        Text(user.wrappedName)
                    }
                }
            }
            .navigationTitle("FriendFace")
            .task {
                await fetchUsers()
            }
        }
    }

    func fetchUsers() async {
        guard users.isEmpty else { return }

        do {
            let url = URL(string: "https://www.hackingwithswift.com/samples/friendface.json")!
            let (data, _) = try await URLSession.shared.data(from: url)

            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601
            let users = try decoder.decode([User].self, from: data)

            await MainActor.run {
                updateCache(with: users)
            }

        } catch {
            print("Download failed")
        }
    }

    func updateCache(with downloadedUsers: [User]) {
        for user in downloadedUsers {
            let cachedUser = CachedUser(context: moc)

            cachedUser.id = user.id
            cachedUser.isActive = user.isActive
            cachedUser.name = user.name
            cachedUser.age = Int16(user.age)
            cachedUser.company = user.company
            cachedUser.email = user.email
            cachedUser.address = user.address
            cachedUser.about = user.about
            cachedUser.registered = user.registered
            cachedUser.tags = user.tags.joined(separator: ",")

            for friend in user.friends {
                let cachedFriend = CachedFriend(context: moc)
                cachedFriend.id = friend.id
                cachedFriend.name = friend.name
                cachedUser.addToFriends(cachedFriend)
            }
        }

        try? moc.save()
    }
}

UserView

import SwiftUI

struct UserView: View {
    let user: CachedUser

    var body: some View {
        List {
            Section {
                Text(user.wrappedAbout)
                    .padding(.vertical)
            } header: {
                Text("About")
            }

            Section {
                Text("Address: \(user.wrappedAddress)")
                Text("Email: \(user.wrappedEmail)")
            } header: {
                Text("Contact details")
            }

            Section {
                ForEach(user.friendsArray) { friend in
                    Text(friend.wrappedName)
                }
            } header: {
                Text("Friends")
            }
        }
        .listStyle(.grouped)
        .navigationTitle(user.wrappedName)
        .navigationBarTitleDisplayMode(.inline)
    }
}  

DataController

import CoreData
import Foundation

class DataController: ObservableObject {
    let container = NSPersistentContainer(name: "FriendFaceV2")

    init() {
        container.loadPersistentStores { description, error in
            if let error = error {
                print("Core Data failed to load: \(error.localizedDescription)")
            }

            self.container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
        }
    }
}

2      

I was also facing the same issue. The issue was with the one to many relationships. While you probally set the relationship to be one-to-many for the CachedUsers you also had to set the Cached friends to have a one-to-many relationship.

The answer is also found in this post https://www.hackingwithswift.com/forums/100-days-of-swiftui/day-61-missing-data-while-working-with-core-data/13612

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.