0

I'm new to SwiftUI and coding in general, so sorry if this has being covered before. I did search about it but I couldn't find it clear enough.

I'm trying to make an app for storing notes and tasks. (I'm not planning to put it on the store, I'm too newbie for that, but I do want to learn Swift and working on an actual app is more useful to me than reading about it.) I have an entity called "Base" with 8 attributes and automatic Codegen Class Definition selected. I prefer to keep it that way, no manual please.

I have three fetch requests. One gets me all data from Core Data. The other two filter one attribute called campoEstado. For now in campoEstado I only store one of two possible values, as strings: "Activas", and "Pospuestas". In the future I may add more so I can't use a boolean for this.

I get a List working with one fetch request. But I can't change that source when the app is running. I made a Picker with .pickerStyle(SegmentedPickerStyle()) that shows me: Todo, Activas, Pospuestas. When the user selects one of this tabs in the picker the list should change to:

  • Tab 1: All tasks
  • Tab 2: A filtered list containing only tasks with campoEstado = "Activas" (calls the fetch request filtroActivas)
  • Tab 3: A filtered list containing only tasks with campoEstado = "Pospuestas" (calls the fetch request filtroPospuestas)

How it should look:

enter image description here

My code in ContentView:

    struct ContentView: View {    

@Environment(\.managedObjectContext) var moc    
    @FetchRequest(entity: Base.entity(), sortDescriptors: [
        NSSortDescriptor(keyPath: \Base.campoNota, ascending: true),
        NSSortDescriptor(keyPath: \Base.campoFechaCreacion, ascending: true)
    ], predicate: NSPredicate(format: "campoEstado == %@", "Activas")
    ) var filtroActivas: FetchedResults<Base>

    @FetchRequest(entity: Base.entity(), sortDescriptors: [
        NSSortDescriptor(keyPath: \Base.campoNota, ascending: true),
        NSSortDescriptor(keyPath: \Base.campoFechaCreacion, ascending: true)
    ], predicate: NSPredicate(format: "campoEstado == %@", "Pospuestas")
    ) var filtroPospuestas: FetchedResults<Base>

     @FetchRequest(entity: Base.entity(), sortDescriptors: [
        NSSortDescriptor(keyPath: \Base.campoNota, ascending: true),
        NSSortDescriptor(keyPath: \Base.campoFechaCreacion, ascending: true)
    ]
    ) var bases: FetchedResults<Base>

var body: some View {
        NavigationView {
            List {
                Picker("Solapas", selection: $selectorIndex) {
                    //This should connect to ForEach source, currently "bases"
                }
                .pickerStyle(SegmentedPickerStyle())
                
              
                ForEach(bases, id: \.self) { lista in
                    NavigationLink(destination: VistaEditar(base: lista)) {
                        Rectangle()
                        .foregroundColor(.gray)
                        .frame(width: 7, height: 50)
                        
                        VStack(alignment: .leading) {
                            Text(lista.campoNota ?? "Unknown title")
                                .font(.headline)
                            Text(lista.campoEstado ?? "Activas")
                        }
                    }
                }

I have two problems:

  1. I don't know how to connect the selected tab in the picker with the source of ForEach inside List

  2. I made the list work for one fetch request, the one that brings me every record in Core Data, but I don't know how to change the ForEach source when the app is running. I have tried an array of names of variables for every one of the fetch requests variables names (bases, filtroActivas and filtroPospuestas) putting them in [] but that didn't work.

I know this isn't elegant, I just need it to work first and then go for efficiency and elegance. I'm sure there is some stupid thing I'm not seeing but it's been a week and I'm getting desperate.

I hope I was clear, if you need more information please ask. If anyone can help me I would very much appreciate it. Thanks in advance!

koen
  • 5,383
  • 7
  • 50
  • 89
sinojotas
  • 1
  • 1

1 Answers1

0

I worry I may get downvoted for this because it's not an entirely complete answer. But I'm out of time and I wanted to see if I could get you in the right direction.

What I believe you're looking for is an Array of Arrays. A FetchedResult will return an optional array, which you'll want to ensure isn't nil. I used compactMap in my example to generate non-nil array's of my sample optional arrays which are intended to simulate what you are getting back from your @FetchRequest properties. You should be able to load this code into a new project in Xcode to see the results. Again it's not a complete answer but will hopefully help you along in your project. I hope this helps, keep working at it, you'll get it!

struct ContentView: View {
// Sample data to mimic optional FetchedResults coming from CoreData.
// These are your @FetchRequest properties
let fetchedResultsBases: [String?] = ["Red", "Green", "Blue"]
let fetchedResultsFiltroPospuestas: [String?] = ["1", "2", "3"]
let fetchedResultsFiltroActivas: [String?] = ["☺️", "", ""]

// Options for your picker
let types = ["Bases", "Pospuestas", "Activas"]

@State private var groups = [[String]]()
@State private var selection = 0

var body: some View {
    NavigationView {
        VStack {
            Picker("Solapas", selection: $selection) {
                ForEach(0..<types.count, id: \.self) {
                    Text("\(self.types[$0])")
                }
            }.pickerStyle(SegmentedPickerStyle())

            List {
                ForEach(0..<groups.count, id: \.self) { group in
                    Text("\(self.groups[self.selection][group])")
                }
            }
        }
    }
    .onAppear(perform: {
        self.loadFetchedResults()
    })
} // This is the end of the View body

func loadFetchedResults() {
    // This is where you'll create your array of fetchedResults, then add it to the @State property.
    var fetchedResults = [[String]]()

    // Use compact map to remove any nil values.
    let bases = fetchedResultsBases.compactMap { $0 }
    let pospuestas = fetchedResultsFiltroPospuestas.compactMap { $0 }
    let filtroActivas = fetchedResultsFiltroActivas.compactMap { $0 }

    fetchedResults.append(bases)
    fetchedResults.append(pospuestas)
    fetchedResults.append(filtroActivas)

    groups = fetchedResults
}

} // This is the end of the ContentView struct
Dan O'Leary
  • 916
  • 9
  • 20
  • Thank you very much, I'll see how can I make it work with CoreData. I like SwiftUI, so I'm trying to be patient about learning it, otherwise I'll be frustrated in a week. – sinojotas Sep 03 '20 at 21:24
  • By the way, I wanted to say that I very much appreciate the effort you took on your answer. The coding is very well separated and commented. Thanks again. – sinojotas Sep 03 '20 at 21:29
  • So I tried it, and I get some errors. First I used a NavigationView instead of VStack. In ` .onAppear(perform: { self.loadFetchedResults() }) ` I get this error: "Value of type 'ContentView' has no member 'loadFetchedResults'" – sinojotas Sep 03 '20 at 22:51
  • Then, in the function func loadFetchedResults() I get an error for each line like this: "Use of unresolved identifier 'fetchedResultsBases'" – sinojotas Sep 03 '20 at 22:56
  • The first error means it's not seeing the "loadFetchedResults" function. Make sure you have that function within the ContentView struct, not outside of it. Look closely at the open and closed curly braces { } The second error means there is no constant named "fetchedResultsBases". This might be related to the first error, or it may be misspelled possibly in your code. I edited the code to include a NavigationView which may be helpful. – Dan O'Leary Sep 03 '20 at 23:51
  • You were right on the function, I moved it within ContentView and I get no error now. But I still keep getting "Cannot convert value of type '[FetchedResults.Element]' (aka 'Array') to expected argument type '[String]'" for every line inside the function. For example I got that one for line "fetchedResults.append(bases)" – sinojotas Sep 04 '20 at 11:28