I have the following code fetching results from core data to populate a tableview.
private lazy var fetchedResultsController: NSFetchedResultsController = {
// Initialize Fetch Request
let fetchRequest = NSFetchRequest(entityName: "MileageLog")
// Add Sort Descriptors
let dateSort = NSSortDescriptor(key: "logDate", ascending: true)
let mileSort = NSSortDescriptor(key: "mileage", ascending: true)
fetchRequest.sortDescriptors = [dateSort, mileSort]
//// Create a new predicate that filters out any object that
//// have not been exported.
// let predicate = NSPredicate(format: "wasExported == %#", 0)
//// Set the predicate on the fetch request
// fetchRequest.predicate = predicate
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedObjectContext = delegate.managedObjectContext
// Initialize Fetched Results Controller
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}()
Without the predicate the tableview is populated with all records sorted correctly. Uncommenting the two lines to bring the predicate into force returns no results at all. My dataset has 4 records with wasExported == 1 and 3 with wasExported == 0... wasExported is a Boolean but in core data it is stored as NSNumber..... what have I done wrong?
You are using the wrong format specifier in your predicate. You want:
let predicate = NSPredicate(format: "wasExported == %d", 0)
%# is for object pointers. With %#, the 0 is interpreted as the nil pointer.
Related
No matter what I put as the value in sectionNameKeyPath: "value", the fetched objects get returned in the order in which they were saved in the database. The number of sections is correct, but whether I view the fetched objects by section fetchedResultsController.sections or fetchedResultsController.object(at: indexPath) I get the same results.
This is in the UITableViewController
lazy var fetchedResultsController: NSFetchedResultsController<Dice> = {
let fetchRequest: NSFetchRequest<Dice> = Dice.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)]
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
// Doesn't matter what I set in the "sectionNameKeyPath" value
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: "set.identifier", cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}()
Has anyone else seen this?
I have a many to many relationship in Core Data with posts and tags. Each tag has many posts and each post has many tags. I have a view controller where I want to display, for a particular tag, all of the posts associated with it.
I do this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
//2
let fetchRequest: NSFetchRequest<Tag> = Tag.fetchRequest()
//3
fetchRequest.predicate = NSPredicate(format: "Tag.name == %#", tag.name!)
// let sort = NSSortDescriptor(key: "timeStamp", ascending: true)
// fetchRequest.sortDescriptors = [sort]
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedContext, sectionNameKeyPath: nil, cacheName: nil) as? NSFetchedResultsController<Tag>
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch()
tag = fetchedResultsController.object(at: IndexPath(item: 0, section: 0))
tag = Tag(context: managedContext)
let posts = tag.posts
My posts object at the bottom is a Set of objects - unordered. I want an array of posts ordered by timestamp that all belong to this particular tag and Im not sure how to create that.
I know usually to sort items by timestamp you would introduce an NSSortDescriptor like I did in step 3 (but is commented out). But I believe this would sort my fetch request (Tag) by the Tag's timestamp. Each tag doesn't have a timestamp and what I really want to sort is the posts associated with that tag. So I don't believe this is the way to do this.
How do I go about this? I think I could just use swift but it feels like there must be a Core Data way to sort related objects by timestamp
Edit: Updated approach with Paulw11's advice:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
//2
let tagFetchRequest: NSFetchRequest<Tag> = Tag.fetchRequest()
let postFetchRequest: NSFetchRequest<Post> = Post.fetchRequest()
//3
tagFetchRequest.predicate = NSPredicate(format: "%K == %#", #keyPath(Tag.name), tag.name!)
do {
let results = try managedContext.fetch(tagFetchRequest)
tag = results.first
} catch let error as NSError {
print("Tag Fetch error: \(error) description: \(error.userInfo)")
}
guard let tag = tag else { return }
postFetchRequest.predicate = NSPredicate(format: "%# IN Post.tags", tag)
let sort = NSSortDescriptor(key: "timeStamp", ascending: true)
postFetchRequest.sortDescriptors = [sort]
fetchedResultsController = NSFetchedResultsController(fetchRequest: postFetchRequest, managedObjectContext: managedContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch()
posts = fetchedResultsController.fetchedObjects as! [Post]
firstPostDate = posts.first?.timeStamp as Date?
lastPostDate = posts.last?.timeStamp as Date?
for (i,post) in posts.enumerated() {
let date = post.timeStamp as Date?
let day = date?.days(from: Calendar.current.startOfDay(for: firstPostDate!))
indexMap[day!] = IndexPath(row: i, section: 0)
}
This gives this error message:
2018-11-29 15:36:56.261887-0800 SweatNetOffline[31250:17419187] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate : (<Tag: 0x60000216b2a0> (entity: Tag; id: 0xc36a752f5b05e34c <x-coredata://F02C5E33-89B9-4814-9D1B-8C74CAEC7DA1/Tag/p79> ; data: {
mostRecentThumbnail = nil;
mostRecentUpdate = "2018-12-07 21:47:44 +0000";
name = test;
posts = (
"0xc36a752f5af9e34e <x-coredata://F02C5E33-89B9-4814-9D1B-8C74CAEC7DA1/Post/p48>"
);
uuid = nil;
}) IN Post.tags)'
Looks like its inserting that entire object into the query string. This seems wrong but maybe I need to format the object differently?
The key was to use request.predicate = NSPredicate(format: "%# IN self.tags", tag)
as opposed to request.predicate = NSPredicate(format: "%# IN Post.tags", tag)
The context of the predicate is the Post so it no longer needs to be accessed I suppose, it is assumed. Self.tags, tags, and SELF.tags all work.
let fetchedResultsController: NSFetchedResultsController = { () -> NSFetchedResultsController<NSFetchRequestResult> in
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Message")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
fetchRequest.predicate = NSPredicate(format: "friend.name = %#", self.friend!.name!)
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
return frc
}()
At the line that says: fetchRequest.predicate = NSPredicate(format: "friend.name = %#", self.friend!.name!) I get the error: Value of type '(ChatLogController) -> () -> (ChatLogController)' has no member 'friend' So apparently it can not find friend in my class ChatLogController for some reason, or am I wrong? That's what I'm guessing since it wont find friend as a member of self.
Here is the friend variable, it is inside of ChatLogController class:
var friend: Friend?
{
didSet
{
navigationItem.title = friend?.name
messages = friend?.messages?.allObjects as? [Message]
messages = messages?.sorted(by: {$0.date!.compare($1.date! as Date) == .orderedAscending})
}
}
Anyone know why I'm getting this error and how to solve it?
Edit: I noticed that if I remove () -> NSFetchedResultsController<NSFetchRequestResult> in from the first line of the first code snippet, the error disappears, however instead I get this error: Unable to infer complex closure return type; add explicit type to disambiguate
You have declared friend as the optional property which has not initialised at the time of initialisation of fetchedResultsController which is a constant so it's giving the error. To solve this problem you can use lazy initialisation which means
delaying the creation of an object or some other expensive process
until it’s needed
make the fetchedResultsController property lazy which will delay creation of object until its required
lazy var fetchedResultsController: NSFetchedResultsController = { [unowned self] () -> NSFetchedResultsController<NSFetchRequestResult> in
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Message")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
fetchRequest.predicate = NSPredicate(format: "friend.name = %#", self.friend!.name!)
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
return frc
}()
I used below code fetch the list of Article from core data and it is giving me the expected result.
let moc = CoreDataHelper().backgroundContext
let employeesFetch = NSFetchRequest<NSFetchRequestResult>(entityName:"Article")
do {
let fetchedEmployees = try moc?.fetch(employeesFetch) as! [Article]
print(fetchedEmployees.count)
} catch {
fatalError("Failed to fetch employees: \(error)")
}
Output : 376
But when I am trying to fetch with NSFetchedResultsController. The fetchedObjects always returning nil. I used below code.
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<Article> = {
// Create Fetch Request
let moc = CoreDataHelper().backgroundContext
// let fetchRequest: NSFetchRequest<Article> = Article.fetchRequest()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName:"Article")
// Configure Fetch Request
fetchRequest.sortDescriptors = [NSSortDescriptor(key:"category", ascending: true)]
// Create Fetched Results Controller
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext:moc!, sectionNameKeyPath: nil, cacheName: nil)
// Configure Fetched Results Controller
fetchedResultsController.delegate = self
let quotes = fetchedResultsController.fetchedObjects
return fetchedResultsController as! NSFetchedResultsController<Article>
}()
The quotes is always nil. I am not able to figure out what is the problem?
try calling performFetch() on fetchedResultsController outside the closure.
hope that will work.
I want automatic updates on tableview..For that i have used NSFetchedResultsControlleras
lazy var fetchedResultsController: NSFetchedResultsController = {
// Initialize Fetch Request
let fetchRequest = NSFetchRequest(entityName: "Student")
// Add Sort Descriptors
let sortDescriptor = NSSortDescriptor(key: "grade", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
// Initialize Fetched Results Controller
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
// Configure Fetched Results Controller
fetchedResultsController.delegate = self
return fetchedResultsController
}()
But i have to filter the results after loading as of user selection as..
#IBAction func onSegmentValueChanged(sender: UISegmentedControl)
{
if sender.selectedSegmentIndex == 0{
//filter by grade
let fetchRequest = NSFetchRequest(entityName: "Student")
let sortDescriptor = NSSortDescriptor(key: "grade", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let filterfetchResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
let predicate = NSPredicate(format: "address = %#","Russia")
filterfetchResultsController.fetchRequest.predicate = predicate
self.tableView.reloadData()
}else{
let fetchRequest = NSFetchRequest(entityName: "Student")
let sortDescriptor = NSSortDescriptor(key: "grade", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let filterfetchResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
let predicate = NSPredicate(format: "grade = %d",10)
filterfetchResultsController.fetchRequest.predicate = predicate
self.tableView.reloadData()
}
}
This however reload the tableView but data is not filtered.how do i solve this problem?This is the demo project i am working on.Or should i use the normal way passing the results and reloading tableView as of normal way to solve this problem?
You must use self.fetchedResultsController, this is your datasource, not filterfetchResultsController: there´s no connection.
You must do a performFetch() to execute the changed predicates
This works for me:
#IBAction func onSegmentValueChanged(sender: UISegmentedControl)
{
if sender.selectedSegmentIndex == 1{
//filter by address
let predicate = NSPredicate(format: "address == %#","Russia")
self.fetchedResultsController.fetchRequest.predicate = predicate
}else{
let predicate = NSPredicate(format: "grade = %d",10)
self.fetchedResultsController.fetchRequest.predicate = predicate
}
do {
try self.fetchedResultsController.performFetch()
} catch {
let fetchError = error as NSError
print("\(fetchError), \(fetchError.userInfo)")
}
self.tableView.reloadData()
}