How to use sortDescriptors in order to fetch data from the previous view?

80 Views Asked by At

The problem I have encountered is that when I fetch the data without any sort descriptor I receive the whole list of data stored in CoreData. However, I want to sort it so that the user would see the data he/she entered in the particular view. The logic is this: I have a TermDefinitionView where user enters the data and then it saves using CoreData. After the user presses "save" button, he/she is relocated to the SetView - the view with the stack of flashcards based on data user entered.

here's SetView():

import SwiftUI
import CoreData

struct SetView: View {
    
    
    @Environment(\.managedObjectContext) private var viewContext
   // @FetchRequest(entity: FlashCardData.entity(), sortDescriptors: NSSortDescriptor[key: ])
    @FetchRequest(
        entity: FlashCardData.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \FlashCardData.date, ascending: false)],
        predicate: NSPredicate(format: "date > %@", Date().addingTimeInterval(-5*60) as NSDate)
    ) var flashCardData: FetchedResults<FlashCardData>

     
    let dataController = DataController.shared
    var removal: (() -> Void)? = nil
    var onRemove: ((SwipeDirection) -> Void)? = nil
    @State private var isShown = false
    @State private var offset = CGSize.zero
    @State private var label: String = "Still Learning"  // Define a label string
    @State private var showPositiveIndicator = false
    @State private var showNegativeIndicator = false
    @State private var showMiddleIndicator = false
    @State private var showEasyIndicator = false
    @State var redirectToSet = false
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 25, style: .continuous)
                .fill(Color.white)
                .overlay(RoundedRectangle(cornerRadius: 25).stroke(getColor(), lineWidth: 2))  // Here we change the border color based on the swipe direction
                .shadow(radius: 3)

           //rest of the code...
                VStack {
                    ForEach(flashCardData, id: \.self) { flashcards in
                                 //   FlashcardView(flashcard: flashcard)
                      //  Text(flashcards.name ?? "nothing") - have to create a code where it's above
                        VStack {
                            Text(flashcards.term ?? "nothing")
                                .font(.largeTitle)
                                .bold()
                            
                            
                            if isShown {
                                Text(flashcards.definition ?? "nothing")
                                    .font(.largeTitle)
                                    .bold()
                            }
                        }
                    }
                }
                        
            // Display positive indicator
            if showPositiveIndicator {
                VStack {
                    Spacer()
                    HStack {
                        Image(systemName: "checkmark")
                            .foregroundColor(.green)
                            .font(.system(size: 40))
                        Spacer()
                    }
                }
            }
            
            // Display negative indicator
            if showNegativeIndicator {
                VStack {
                    Spacer()
                    HStack {
                        Spacer()
                        Image(systemName: "xmark")
                            .foregroundColor(.red)
                            .font(.system(size: 40))
                    }
                }
            }
            if showMiddleIndicator {
                VStack {
                    Spacer()
                    HStack {
                        Image(systemName: "light.beacon.min")
                            .foregroundColor(.green)
                            .font(.system(size: 40))
                        Spacer()
                    }
                }
            }
            if showEasyIndicator {
                VStack {
                    Spacer()
                    HStack {
                        Image(systemName: "light.beacon.max")
                            .foregroundColor(.green)
                            .font(.system(size: 40))
                        Spacer()
                    }
                }
            }
        }
        .frame(width: 300, height: 550)
        .rotationEffect(.degrees(Double(offset.width / 10)))
        // makes an effect when swiping the card and it gets back if swipped not too much
        .offset(x: offset.width, y: offset.height)
        .navigationBarBackButtonHidden(true)// changes the position of the card by x - direction
        .onTapGesture {
            isShown.toggle()
        }
        .gesture(
            DragGesture().onChanged { value in
                offset = value.translation
                showPositiveIndicator = offset.width > 50
                showNegativeIndicator = offset.width < -50
                showMiddleIndicator = offset.height > 50
                showEasyIndicator = offset.height < -50
            }
            .onEnded { value in
                if abs(offset.width) > 100 {
                    withAnimation {
                        offset.width = value.translation.width > 0 ? 1000 : -1000
                        onRemove?(value.translation.width > 0 ? .right : .left)
                    }
                } else if abs(offset.height) > 100 {
                    withAnimation {
                        offset.height = value.translation.height > 0 ? 1000 : -1000
                        onRemove?(value.translation.height > 0 ? .up : .down)
                    }
                } else {
                    offset = .zero
                    showPositiveIndicator = false
                    showNegativeIndicator = false
                    showMiddleIndicator = false
                    showEasyIndicator = false
                }
            }

        )
    }
    
    func getColor() -> Color {
        if offset.width > 0 {
          //  label = "Good Job!"  // Change the label based on the swipe direction
            return Color.green
        } else if offset.width < 0 {
          //  label = "Needs Improvement"
            return Color.red
        } else if offset.height > 0{
         //   label = "Still Learning"
            return Color.orange
        }
        else if offset.height < 0 {
            return Color.blue
        }
        else {
            return Color.gray
        }
    }
}
       /* VStack {
            ForEach(flashcards) { flashcard in
                Text(flashcard.name ?? "")
                Text(flashcard.term ?? "")
                Text(flashcard.definition ?? "")
            }
        }
        .onAppear {
            fetchFlashcards()
        }
    }
    
    private func fetchFlashcards() {
        do {
            try viewContext.performAndWait {
                try viewContext.save()
            }
        } catch {
            print("Error saving view context: \(error)")
        }
    }
}
*/
struct SetView_Previews: PreviewProvider {
    static var previews: some View {
        SetView()
    }
}

struct FlashcardView: View {
    var flashcard: FlashCardData
    /*let term: String
    let definition: String
    let name: String
    */
    var body: some View {
        VStack {
            Text(flashcard.name ?? "")
            Text(flashcard.term ?? "")
            Text(flashcard.definition ?? "")
        }
    }
 

TermDefinitionView():

import SwiftUI
import Combine
import CoreData

// ViewModel for managing flashcards

struct TermDefinitionView: View {
    // let set: SetView
    @FetchRequest(
        entity: FlashCardData.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \FlashCardData.date, ascending: false)],
        predicate: NSPredicate(format: "date > %@", Date().addingTimeInterval(-5) as NSDate)
    ) var flashCardData: FetchedResults<FlashCardData>
    
    @ObservedObject private var viewModel = TermDefinitionViewModel()
    @State var name = "" // Separate state for the name
    @Environment(\.managedObjectContext) var managedObjectContext
    @Environment(\.dismiss) var dismiss
    let dataController = DataController.shared
    @State var showSet = false
    @Binding var saveSet: Bool
    @State var isShowingSet = false
    //@Binding var redirectToSet: Bool
    
    var body: some View {
        ZStack {
            NavigationStack {
                VStack {
                    Text("Name")
                        .padding(.trailing, 220)
                        .fontWeight(.bold)
                    TextField("Enter name", text: $name)
                        .frame(width: 250)
                        .padding()
                        .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray, lineWidth: 1))
                    List {
                        
                        ForEach(viewModel.termdefpairs.indices, id: \.self) { index in
                            TermView(term: $viewModel.termdefpairs[index].term, definition: $viewModel.termdefpairs[index].definition, tag: $viewModel.termdefpairs[index].tag)
                        
                        }
                        .onDelete { index in
                            self.viewModel.termdefpairs.remove(at: index.first!)
                        }
                    }
                    .navigationBarItems(trailing: Button(action: {
                        
                      //  redirectToSet.toggle()
                        for testForm in viewModel.termdefpairs {
                            dataController.add(term: testForm.term, name: name, definition: testForm.definition, tag: testForm.tag, date: Date(), context: managedObjectContext)
                           
                        }
                        isShowingSet = true
                        dismiss()
                    }) {
                        Text("Save")
                        NavigationLink(destination: SetView(flashCardData: _flashCardData), isActive: $isShowingSet) {
                            EmptyView()
                            }
                        
                    })
                    
                    Spacer()
                    ForEach(dataController.savedFlash) { x in
                        Text(x.term ?? "nothing")
                        Text(x.definition ?? "nothing")
                        Text(x.tag ?? "nothing")
                    }
                    Button(action: {
                        viewModel.addNew()
                    }) {
                        Image(systemName: "plus")
                            .resizable()
                            .foregroundColor(.white)
                            .font(.title2)
                            .frame(width: 30, height: 30)
                    }
                    .frame(width: 40, height: 40)
                    .background(Color.blue)
                    .clipShape(Circle())
                    .padding()
                }
                
            }
        }
    
    }
}


struct TermDefinitionView_Previews: PreviewProvider {
    static var previews: some View {
        TermDefinitionView(saveSet: .constant(true))
    }
}

struct TermView: View {
    @Binding var term: String
    @Binding var definition: String
    @Binding var tag: String
    @State private var isTagExpanded = false
    
    var body: some View {
        VStack(alignment: .leading, spacing: 20) {
            
            
                if isTagExpanded {
                    VStack(alignment: .leading, spacing: 8) {
                        Text("Tag")
                            .font(.headline)
                        TextField("Enter tag", text: $tag)
                            .padding()
                            .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray, lineWidth: 1))
                    }
                }
                
                Button(action: {
                    isTagExpanded.toggle()
                }) {
                    HStack {
                        Spacer()
                        Image(systemName: isTagExpanded ? "minus.circle.fill" : "plus.circle.fill")
                            .resizable()
                            .foregroundColor(.blue)
                            .frame(width: 25, height: 25)
                        Text(isTagExpanded ? "Hide Tag" : "Add Tag")
                            .foregroundColor(.blue)
                            .font(.headline)
                    }
                }
                .padding(.bottom, -25)
            VStack(alignment: .leading, spacing: 8) {
                Text("Term")
                    .font(.headline)
                TextField("Enter term", text: $term)
                    .padding()
                    .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray, lineWidth: 1))
            }
            
            VStack(alignment: .leading, spacing: 8) {
                Text("Definition")
                    .font(.headline)
                TextField("Enter definition", text: $definition)
                    .padding()
                    .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray, lineWidth: 1))
            }
            
            
        }
        .padding()
    }
}


struct TermAndDefinition: Identifiable {
    var id = UUID()
    var term: String
    var definition: String
    var tag: String
    
    
}

DataController:

import Foundation
import CoreData

class DataController: ObservableObject {
    static let shared = DataController()
    @Published var savedFlash: [FlashCardData] = []
    let container: NSPersistentContainer
    let fetchRequest: NSFetchRequest<FlashCardData> = FlashCardData.fetchRequest()
    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "CoreData")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                /*
                 Typical reasons for an error here include:
                 * The parent directory does not exist, cannot be created, or disallows writing.
                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                 * The device is out of space.
                 * The store could not be migrated to the current model version.
                 Check the error message to determine what the actual problem was.
                 */
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
    
    func save(context: NSManagedObjectContext) {
        do {
            try context.save()
            print("Data saved")
        } catch {
            print("We could not save the data...")
        }
    }
    func add(term: String, name: String, definition: String, tag: String, date: Date, context: NSManagedObjectContext) {
        let data = FlashCardData(context: context)
        data.name = name
        data.id = UUID()
        data.definition = definition
        data.term = term
        data.tag = tag
        data.date = date
        save(context: context)
    }
    
    func edit(data: FlashCardData ,term: String, defintion: String, tag: String, context: NSManagedObjectContext) {
        data.term = term
        data.definition = defintion
        data.tag = tag
        save(context: context)
    }
}
 

the result I get without sort descriptor:

CoreData Entity

Here's the new @FetchRequest:

@FetchRequest(
        entity: FlashCardData.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \FlashCardData.date, ascending: false)],
        predicate: NSPredicate(format: "date > %@", Date().addingTimeInterval(-5*60) as NSDate)
    ) var flashCardData: FetchedResults<FlashCardData>
0

There are 0 best solutions below