I set up a UISegmented Control to toggle between All Photos and Favorites in my PhotosViewController.swift and set up a new Predicate function to fetch my Favorites.
But when I run the app, nothing happens when I toggle (I only marked a handful of photos as Favorites).
PhotosViewController.swift:
// for Silver Challenge:
@objc func photoTypeChanged(_ segControl: UISegmentedControl) {
switch segControl.selectedSegmentIndex {
case 0:
store.fetchInterestingPhotos {
(photosResult) -> Void in
self.updateDataSource()
}
default:
store.fetchAllFavorites {
(photosResult) -> Void in
self.updateDataSource()
}
}
}
func fetchAllFavorites(completion: @escaping (Result<[Photo], Error>) -> Void) {
let fetchRequest: NSFetchRequest<Photo> = Photo.fetchRequest()
let sortByDateTaken = NSSortDescriptor(key: #keyPath(Photo.dateTaken), ascending: true)
fetchRequest.predicate = NSPredicate(format: "Photo.isFavorite == true")
fetchRequest.sortDescriptors = [sortByDateTaken]
let viewContext = persistentContainer.viewContext
viewContext.perform {
do {
let allPhotos = try viewContext.fetch(fetchRequest)
completion(.success(allPhotos))
} catch {
completion(.failure(error))
}
}
}
But when I switched to Favorites, the app crashed with these log items:
Fetched favorites (console log message acknowledging the toggle to Favorites...)
2020-08-19 18:40:38.582259-0400 Photorama[53423:917810] [error] error: SQLCore dispatchRequest: exception handling request: <NSSQLFetchRequestContext: 0x600002769260> , keypath Photo.isFavorite not found in entity <NSSQLEntity Photo id=1> with userInfo of (null)
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLFetchRequestContext: 0x600002769260> , keypath Photo.isFavorite not found in entity <NSSQLEntity Photo id=1> with userInfo of (null)
2020-08-19 18:40:38.586057-0400 Photorama[53423:917810] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath Photo.isFavorite not found in entity <NSSQLEntity Photo id=1>'
If I understand NSPredicate correctly, you donât say âPhoto.isFavorite == trueâ, you just say âisFavorite == trueâ. Since the predicate is operating on a container of objects, you donât need to specify the class name - thatâs a given based on the container being searched. You just specify the attribute name.
On the plus side, those runtime errors show that the search is taking place so thatâs one thing you donât need to worry about.
Finally, I think the problem may be that youâre using â==â in the format string. Iâve seen a couple places showing format strings for NSPredicate using â=â rather than â==â. This one, for example. So maybe try âisFavorite = trueâ and see if that produces better results.
OK, I just did an experiment. I ran the program & marked a few photos as favorites, then I added your predicate to PhotoStore.fetchAllPhotos to only return favorited photos (changing the attribute name to the one I used) & re-ran the program, and it worked. Didnât matter whether I used â=â or â==â.
So I think the problem is not with your comparison, itâs somewhere else.
[Edited to add: I should have mentioned that I only got part-way through this challenge. I added code to allow favoriting a photo, but did not get around to the second part to provide a way to only display favorited photos. Thatâs why I did the experiment I mentioned.]
Did you remember to regenerate your NSManagedObject subclass files (p450) after adding the isFavorite attribute? (I think this is what GPeteN was getting at in their last post.)
In PhotoStore.processPhotoRequest, did you remember to initialize isFavorite to false when copying flickerPhoto to photo?
I figured it out. The problem is in your photoTypeChanged function. When the Favorites option is selected on the segmented control, you call store.fetchAllFavorites, but when that completes your closure calls self.updateDataSource() which in turn calls store.fetchAllPhotos, undoing the favorite filtering you just did.
You need to remove the fetchInterestingPhotos/fetchAllFavorites calls from photoTypeChanges and just have it call updateDataSource. Let updateDataSource decide which set of photos to get. Or make a copy of updateDataSource that fetches favorited photos & call that when you want to display favorites (thatâs what I did). Youâll have some duplicate code in the two functions, but you can refactor to get rid of that.
@Caps5150 finish the book you say ? What about Accessibility chapter lol.
Anyways, I think you might have over engineered this. Check out my simple solution here: