Get fetch request from a different class/function

How can I get the fetch request from a different class so I can re-use it on different views and functions on other files? If you see on the code below, I have to call the fetch request twice in different classes, I want to create a class with a function called:

readData
so when I call it:
readData()
it will return the @FetchRequest . I'm using SwiftUI. This is the code I was mentioning:


import SwiftUI

struct ContentView: View {

@ObservedObject var myClass = MyClass()

@FetchRequest(
  entity: ProgrammingLanguage.entity(),
  sortDescriptors: [
  NSSortDescriptor(keyPath: \ProgrammingLanguage.id, ascending: true)
  ]
) var languages: FetchedResults

@Environment(\.managedObjectContext) var managedObjectContext

var body: some View {

  NavigationView {
  VStack {
  List(languages, id: \.self) { language in
  Text(self.language.name ?? "")
  }
  }
  }
  .onAppear{
  // Data not refreshed
  self.myClass.createData()
  }
}
}

class MyClass: ObservableObject {

@FetchRequest(
  entity: ProgrammingLanguage.entity(),
  sortDescriptors: [
  NSSortDescriptor(keyPath: \ProgrammingLanguage.id, ascending: true)
  ]
) var languages: FetchedResults

@Environment(\.managedObjectContext) var managedObjectContext

func createData() {
  for i in 1...5 {
  let language = ProgrammingLanguage(context: self.managedObjectContext)
  language.id = Int32(i) // << here !!
  language.name = "\(i) SwiftUI"
  do {
  try self.managedObjectContext.save()
  } catch {
  }
  }
}

func readData() {
  // How can I return the objects here? Not in a loop, but as a fetch request so I can use that fetch request on other views
}
}

Replies

hi,


there's a lot going on with your code and with your question, so here are a few comments.


(1) your ContentView (lines 3 - 30) has a @FetchRequest and an @Environment variable that is passed along to you by the SceneDelegate. in order to display, it uses the @FetchRequest (and its assumed managedObjectContext) to provide a list of ProgrammingLanguage objects (i.e., entities on the CoreData side). the problem, of course, is that you probably have no CoreData entities, so you rely on using the .onAppear() modifier to go out and add a few ProgrammingLanguages to work with.


you might see this recent thread.


(2) rather than just write a simple function inside ContentView that creates some data, you seem to want to invent a MyClass object that will do it for you.


OK, that design could work -- but your definition of MyClass has a serious problem: even though it has made its own @FetchRequest and @Environment declaration, these are not useful. these property wrappers make sense for Views and their child views, but they don't mean much to objects defined inside the ContentView.


so the real problem of creating objects in your MyClass.createData function is that there is no managedObjectContext available to it. we don't know where the ProgrammingLanguage objects are being created, and chances are the try self.managedObjectContext.save() statement on line 49 simply fails.


(3) if you do want MyClass to work, kill lines 34 through 41, then ask the createData() function to go find the viewContext of the persistentContainer of the AppDelegate to use, in which to create the objects and save them. the code would look like this: (you'll need to import UIKit and CoreData to make this work).


let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedObjectContext = appDelegate.persistentContainer.viewContext
for i in 1...5 {
  let language = ProgrammingLanguage(context: managedObjectContext)
  language.id = Int32(i) // << here !!
  language.name = "\(i) SwiftUI"
  do {
    try managedObjectContext.save()
  } catch let error as NSError {
    print("Error saving ProgrammingLanguages: \(error.localizedDescription), \(error.userInfo)")
  }
}


(4) if you want MyClass to do its own fetching of ProgrammingLanguage objects (the ContentView does this for you already with its @FetchRequest, so be careful in how you might use it), then you'd need to define it this way:


func readData() -> [ProgrammingLanguage] {
  let fetchRequest: NSFetchRequest<ProgrammingLanguage> = ProgrammingLanguage.fetchRequest()
  fetchRequest.sortDescriptor = NSSortDescriptor(keyPath: \ProgrammingLanguage.id, ascending: true)
  do {
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let managedObjectContext = appDelegate.persistentContainer.viewContext
    let languages = try managedObjectContext.fetch(fetchRequest)
    return languages
  } catch let error as NSError {
    print("Error fetching ProgrammingLanguages: \(error.localizedDescription), \(error.userInfo)")
  }
  return [ProgrammingLanguage]()
}


i think this all makes sense ...


hope that helps,

DMG

Thanks, the create Data works beautifuly, just as expected, with so much going around.


The readData() on the other hand gives me an error on your 2nd code, line 3. It says:


Value of type 'NSFetchRequest<ProgrammingLanguage>' has no member 'sortDescriptor'


what's happening?

hi,


oops ... it should probably be


fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \ProgrammingLanguage.id, ascending: true)]


i keep forgetting that you specificy an array of sort descriptors, not a single sort descriptor.


good luck,

DMG