SwiftUI Cloudkit Relationship Save in View Causes Fetch Record to Vanish

Hello. I’ve wrestled with this for four days without success so I could use some help. The code below simply adds a new record to an Entity (‘Activity’). This code works fine and does what it is supposed to on add and delete. However, the entity now has a relationship from itself (Activity) to another Entity (Player). Each Activity has 1 player. The view is loaded on init with the currently active player and that record is stored in a state variable. Now when the viewcontext is saved, the record stored in the state variable (a Player record is invalidated). This physically is seen as the players name disappears from the screen after the context save. This ONLY happens when the relationship field is maintained - if I don’t do that, the activity record saves fine and the state is maintained. I cant figure this out and could use help.

import SwiftUI
import CoreData

struct ActivityLogView: View {
    @Environment(\.managedObjectContext) private var viewContext
    
    @State var activityDate: Date = Date()
    var activityType: ActivityType
    @State var activityDescription = ""
    var thePredicate: NSPredicate
    var playerPredicate: NSPredicate
    var passedID: String
    @State var thePlayer: Player

    var activityRequest: FetchRequest<Activity>
    private var activities: FetchedResults<Activity>{activityRequest.wrappedValue}

    @FetchRequest private var players: FetchedResults<Player>

    static let taskDateFormat: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        return formatter
    }()
    
    init(moc: NSManagedObjectContext, id: String, inputActivityType: ActivityType){
        // Get the current active player
        self.passedID = id
        if self.passedID == "" {
            self.playerPredicate = NSPredicate(value: true)
        } else {
            self.playerPredicate = NSPredicate(format: "%K == %@", "id", id
                                                as CVarArg)
        }

        let fetchRequest: NSFetchRequest<Player> = Player.fetchRequest()
        fetchRequest.sortDescriptors = []
        fetchRequest.predicate = self.playerPredicate
        self._players = FetchRequest(fetchRequest: fetchRequest)
        do {
            let firstPlayer = try moc.fetch(fetchRequest)
            if firstPlayer.isEmpty {
                self._thePlayer = State(initialValue: Player())
            } else {
                self._thePlayer = State(initialValue: firstPlayer[0])
            }
        } catch {
            fatalError("Problem fetching players in seasons...")
        }

/*
        self.playerRequest = FetchRequest(
            entity: Player.entity(),
            sortDescriptors: [],
            predicate: self.playerPredicate,
            animation: .easeIn
        )
*/

        self.activityType = inputActivityType
        self.thePredicate = NSPredicate(format: "activityType == %i", inputActivityType.rawValue)
        // TODO: Limit Fetch Rows (requires extension and new fetch request on Activity object

        self.activityRequest = FetchRequest(
            entity: Activity.entity(),
            sortDescriptors: [NSSortDescriptor(keyPath: \Activity.activityDate, ascending: false)],
            predicate: self.thePredicate,
            animation: .easeIn
        )
    }

    var body: some View {
            VStack{
                VStack{
                    HStack{
                        if !players.isEmpty {
                        Text("\(thePlayer.givenName ?? "default") \(thePlayer.familyName ?? "player")")
                            .bold()
                            .padding(.horizontal)
                        }
                        Spacer()
                    }
                    HStack{
                        Text("Date: ")
                            .padding(.leading)
                        DatePicker("", selection: $activityDate, displayedComponents: .date)
                        Spacer()
                        Text("\(activityType.fullDisplay) ")
                        Button(action: saveActivity){
                            Image(systemName: "plus.circle.fill")
                        }
                        .padding(.horizontal)
                        .disabled(activityDescription.isEmpty)
                    }
                    TextField("description", text: $activityDescription)
                        .padding(.horizontal)
                }
                .padding(.top)
                VStack{
                    if !activities.isEmpty {
                        Divider()
                        HStack{
                            Text("ACTIVITIES")
                                .font(.body)
                                .fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
                            Spacer()
                        }
                    }
                    List{
                        ForEach(activities, id: \.self) { activity in
//                            NavigationLink(destination: TeamDetailView(
//                                team: team,
//                                teamName: team.teamName ?? "",
//                                coach: team.coach ?? "",
//                                assistantCoach: team.assistantCoach ?? ""
//                            )
//                            .environment(\.managedObjectContext, self.viewContext)){
//                                Text("\(team.teamName ?? "")")
//                            }
                            VStack(alignment: .leading){
                                Text("\(activity.activityDate!, formatter: ActivityLogView.taskDateFormat)")
                                Text("\(activity.activityDescription ?? "")")
                                    .font(.callout)
                                    .italic()
                            }
                        }
                        .onDelete(perform: deleteActivity)
                    }.listStyle(PlainListStyle())
                }
                .navigationTitle("Activities")
                .navigationBarTitleDisplayMode(.inline)

            }
    }
        private func deleteActivity(offsets: IndexSet) {
            withAnimation {
                offsets.map { activities[$0] }.forEach(viewContext.delete)

                do {
                    try viewContext.save()
                } catch {
                    // Replace this implementation with code to handle the error appropriately.
                    let nsError = error as NSError
                    fatalError("Unresolved error deleting activity\(nsError), \(nsError.userInfo)")
                }
            }
        }
        
    private func saveActivity() {
            withAnimation {
                let newActivity = Activity(context: viewContext)
                newActivity.id = UUID()
                newActivity.activityDate = activityDate
                activityDate = Date()
                newActivity.activityType = activityType.rawValue
                newActivity.activityDescription = activityDescription
                activityDescription = ""
                newActivity.whichPlayer = thePlayer
                //players.first?.mutableSetValue(forKey: "activityList").add(newActivity)
                
                do {
                    try newActivity.managedObjectContext?.save()
                } catch {
                    // TODO: Replace this implementation with code to handle the error appropriately.
                    let nsError = error as NSError
                    fatalError("Unresolved error saving activity\(nsError), \(nsError.userInfo)")
                }
            }
        }
}

After the first save:
cap2

I solved this problem. The fetched object was incorrectly stored as @State instead of @StateObject.

This topic was automatically closed after 166 days. New replies are no longer allowed.