Swift5 Simple Core Data NSPredicate Get - core-data

I'm simply trying to access a record in core data by a property I've named "id" that is of String type. The following keeps complaining about 'Unable to parse the format string "id == 8DF3F2C6741B47C8864D1052C36E2C4D"'. How can I solve this issue?
private func getEntity(id: String) -> NSManagedObject? {
var myEntity: NSManagedObject?
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "MyEntity")
fetchRequest.predicate = NSPredicate(format: "id = \(id)")
do {
var tempArray = try getCurrentManagedContext().fetch(fetchRequest)
myEntity = tempArray.count > 0 ? tempArray[0] : nil
} catch let error as NSError {
print("get failed ... \(error) ... \(error.userInfo)")
}
return myEntity
}

It’s a format string. Instead of
NSPredicate(format: "id = \(id)")
Write
NSPredicate(format: "id == %#", id)

Related

How to Fetch NSManagedObject from Core data given its type and property

I need to get a NSManagedObject from Core data so I can share it with cloud Kit. I fetch the result based on the entity property and type. Then I try to convert the result into NSManagedObject.
// Fetch NSManagedObject so it can be shared
if let estProfile: NSManagedObject = fetchEntity(uniqueId: self.energyProfileId!, entityType: EstEnergyProfile.self) {
print("fetched NSManagedObject for sharing with cloud kit")
}
//Fetch NSManagedObject given specific property and its type
func fetchEntity (uniqueId: String, entityType: NSManagedObject.Type) -> NSManagedObject?{
var obj: NSManagedObject? = nil
let context = appDelegate.persistentContainer.viewContext
do {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = entityType.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "uniqueId == %#", uniqueId)
let fetchedResults = try context.fetch(fetchRequest)
obj = fetchedResults.first as NSManagedObject
}
catch {
print("Error fetching entity: ", entityType)
}
return obj
}
In the above code at the line
obj = fetchedResults.first as NSManagedObject
I get the error : 'NSFetchRequestResult?' is not convertible to 'NSManagedObject
I don't think I am doing this right. Can someone help fix this code?
I would make the fetch function generic
func fetchEntity<EntityType: NSManagedObject>(_: EntityType.Type, uniqueId: String) -> EntityType? {
let context = appDelegate.persistentContainer.viewContext
do {
let fetchRequest = EntityType.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "uniqueId == %#", uniqueId)
let fetchedResults = try context.fetch(fetchRequest)
return fetchedResults.first as? EntityType
}
catch {
print("Error fetching entity: ", error)
return nil
}
}
Example
let estProfile: NSManagedObject = fetchEntity(EstEnergyProfile.self, uniqueId: self.energyProfileId!)

reload FetchedResults when changing the predicate in swiftUI

edit: so far i think i've narrowed the problem done to the fetchresult in listview.
when the fetchrequest(predicate) changes, it doesn't refetch.
i'm building a todo app with swiftUI and Core Data.
in the listView i have a button that'll change the predicate for the fetchrequest, but i can't get it to work.
code in the listView
struct HomeListView: View {
#Environment(\.managedObjectContext) var context
#FetchRequest(fetchRequest: ItemEntity.loadItems()) var items: FetchedResults<ItemEntity>
Text("someText")
.onTapGesture {
ItemEntity.predicateType.next() //next() is a method defined in core data entity class
}
code in the core data entity class
extension ItemEntity {
static var sortType = sort.ascending
static var predicateType = predicate.all
static func loadItems() -> NSFetchRequest<ItemEntity> {
var request: NSFetchRequest<ItemEntity> = ItemEntity.fetchRequest() as! NSFetchRequest<ItemEntity>
var predicate: NSPredicate {
switch predicateType {
case .all:
return NSPredicate(format: "type = %i", 0)
case .imur:
return NSPredicate(format: "type = %i", 0)
case .unimur:
return NSPredicate(format: "type = %i", 1)
case .imunur:
return NSPredicate(format: "type = %i", 2)
case .unimunur:
return NSPredicate(format: "type = %i", 3)
case .entry:
return NSPredicate(format: "type = %i", 4)
}
request.sortDescriptors = [sortDescriptor]
request.predicate = predicate
return request
}
enum predicate {
case all
case imur
case unimur
case imunur
case unimunur
case entry
mutating func next() {
switch self {
case .all:
self = .imur
case .imur:
self = .unimur
case .unimur:
self = .imunur
case .imunur:
self = .unimunur
case .unimunur:
self = .entry
case .entry:
self = .all
}
}
}
}
the idea is when user tap on the button on list view, it call the next() method and set predicateType property in ItemEntity class to another value, then the predicate property in loadItems() will update the fetchrequest, then the listview will reload.
i know there is something wrong with this approach, but i can't figure out how to fix it.
Thanks for helping!
You need to change some state so that the view re-renders.
Try adding a #State var, change it from the button action and use it in the view:
struct HomeListView: View {
#Environment(\.managedObjectContext) var context
#FetchRequest(fetchRequest: ItemEntity.loadItems()) var items: FetchedResults<ItemEntity>
#State private var refresh: Bool = false // add a state
var body: some View {
Text("someText")
.onTapGesture {
ItemEntity.predicateType.next()
refresh.toggle() // change the state
}
.background(toggle ? Color.clear : Color.clear) // use the state
}
}

Core data Fetch request with predicate swift 3

I know how to fetch from core data with predicates using a
let predicate = NSPredicate(format: "MyEntityAttribute == %#", "Matching Value"). I want to know if it's possible to fetch all of the values for a particular attribute without using a Matching Value. I want to get a count of the total number of values for a particular attribute.
This is what I got so far, but I am only getting back what is matching the name attribute.
let filter = "wayne"
let fetchRequest = NSFetchRequest<Likes>(entityName: "Likes")
let predicate = NSPredicate(format: "name == %#", filter)
fetchRequest.predicate = predicate
do {
let nameCount = try context.fetch(fetchRequest)
if nameCount.count >= 0 {
print("name exist")
}
} catch{
print(error.localizedDescription)
}
I took a different approach and use NSFetchRequestResult to get back the result of the single attribute.
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Likes")
fetchRequest.resultType = .dictionaryResultType
fetchRequest.propertiesToFetch = ["name"] // Single attribute I wanted to fetch
fetchRequest.returnsDistinctResults = true
do {
let result = try context.fetch(fetchRequest)
let resultDic = result as! [[String:String]]
print(resultDic.count)
print(resultDic)
} catch{
print(error.localizedDescription)
}

NSFetchedResultsController and to-many relationship not working

Ok, I was searching and trying in that case for the last 1-2 weeks and I didn't get it work. I would be able to achieve what I want without NSFRC but for performance reasons and convienience I would like to do it with the NSFRC.
So, I have a DataModel with 2 Entities - see the picture
There is one Account and one account can have many accountchanges - which is quite obvious.
So I want to be able to choose an Account and then show all AccountChanges for that specific Account.
So far I was able to get the Account and also accessing the NSSet in cellForRow Function but I am not getting the correct sections and numberOfRowsInSection - this is the main issue.
Here is some code:
func numberOfSections(in tableView: UITableView) -> Int {
print("Sections : \(self.fetchedResultsController.sections?.count)")
if (self.fetchedResultsController.sections?.count)! <= 0 {
print("There are no objects in the core data - do something else !!!")
}
return self.fetchedResultsController.sections?.count ?? 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Section Name")
print(self.fetchedResultsController.sections![section].name)
let sectionInfo = self.fetchedResultsController.sections![section]
print("Section: \(sectionInfo) - Sections Objects: \(sectionInfo.numberOfObjects)")
return sectionInfo.numberOfObjects
}
There are some print statements which are only for information!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = myTable.dequeueReusableCell(withIdentifier: "myCell")! as UITableViewCell
let accountBalanceChanges = self.fetchedResultsController.object(at: indexPath)
print("AccountBalanceChanges from cell....")
print(accountBalanceChanges)
let details = accountBalanceChanges.accountchanges! as NSSet
print("Print out the details:")
print(details)
let detailSet = details.allObjects
let detailSetItem = detailSet.count // Just for information!
let myPrint = detailSet[indexPath.row] as! AccountChanges
let myVal = myPrint.category
myCell.textLabel?.text = myVal
return myCell
}
So, I am able to get the data but always only one item and not the whole set - I guess due to the fact that the sections/ numberOfRows are wrong.
Here is my NSFRC
var fetchedResultsController: NSFetchedResultsController<Accounts> {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest: NSFetchRequest<Accounts> = Accounts.fetchRequest()
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "aName", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
let predicate = NSPredicate(format: "(ANY accountchanges.accounts = %#)", newAccount!)
fetchRequest.predicate = predicate
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.coreDataStack.context, sectionNameKeyPath: nil, cacheName: nil)
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
do {
try _fetchedResultsController!.performFetch()
} catch {
// 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.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
return _fetchedResultsController!
}
I am assuming it is the SortDescriptor or the predicate - or maybe both?
Any help or at least directions are well appreciated.
I already tried many different approaches but none was giving me the correct results.
I would do the opposite, I mean using the FRC to fetch all the changes for an account with a certain Id, and use the following predicate:
let predicate = NSPredicate(format: "accounts.aId = %#", ACCOUNTID)
or
let predicate = NSPredicate(format: "accounts = %#", account.objectID)
I would rename Accounts entity to Account and same for the relationship since it's a to-one relationship.
That's assuming you have a table view with all the accounts and when you click on one it gives you back its changes.
var fetchedResultsController: NSFetchedResultsController<AccountChanges> {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest: NSFetchRequest<AccountChanges> = AccountChanges.fetchRequest()
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "aName", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
let predicate = NSPredicate(format: "accounts.aId = %#", ACCOUNTID)
fetchRequest.predicate = predicate
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.coreDataStack.context, sectionNameKeyPath: nil, cacheName: nil)
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
do {
try _fetchedResultsController!.performFetch()
} catch {
// 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.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
return _fetchedResultsController!
}
Cheers

SWIFT - 'AnyObject' does not have a member name 'fooBar'

I am trying to fetch some data from Core Data and have run into a slight problem. I can fetch the data with no problem. The moment I try to grab a specific piece of data (i.e. data.fooBar), it throws up an error:
"'AnyObject' does not have a member name 'fooBar'
If I println(data) it will show that fooBar does exist with data stored in it.
I am not really sure why it is doing this. I have tried to search for an answer and tried a bunch of different things but none have seemed to work. Any help would be great. Thanks. :)
var results : Array<AnyObject> = []
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
//get the data for that storedItem
var appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
var context:NSManagedObjectContext = appDel.managedObjectContext!
let req = NSFetchRequest(entityName: "storedItems")
let name:String = results[indexPath.row].name
req.predicate = NSPredicate(format: "name == %#", name)
req.returnsObjectsAsFaults = false
var tapResults = context.executeFetchRequest(req, error: nil)!
for item in tapResults {
println(item) //works, shows all data correctly(including subText)
println(item.name) //works, the only one that does for some reason???
println(item.subText) //Error 'AnyObject' does not have a member name 'subText'
}
Here is the result for: println(item)
println(item) <NSManagedObject: 0x7f04be60> (entity: storedItems; id: 0x7f041de0 <x-coredata://DD4F8E68-2234-46B5-B1D8-AE2F75245C63/storedItems/p1> ; data: {
alarmSound = default;
isDefault = 0;
name = "test";
sliderHours = 0;
sliderMinutes = 0;
sliderSeconds = 0;
subText = "00:00:00";
UPDATE: Based on discussion over vacawama answer (Thank you Aaron). For correct solution please see the answer I accepted.
my itemObj class
#objc(itemObj)
class itemObj: NSManagedObject {
#NSManaged var name:String!
#NSManaged var sliderHours:NSNumber
#NSManaged var sliderMinutes:NSNumber
#NSManaged var sliderSeconds:NSNumber
#NSManaged var subText:String!
#NSManaged var alarmSound:String!
#NSManaged var isDefault:NSNumber
}
my AddItem VC:
var tResults = (context.executeFetchRequest(req, error: nil))
for item in tResults as [itemObj!] {
println(item.name)
println(item.subText)
}
executeFetchRequest returns an optional array of AnyObject. You shouldn't force-unwrap it (this can cause a crash). So optionally unwrap it and do an optional cast (as?) to make sure the type is correct:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!
let req = NSFetchRequest(entityName: "storedItems")
let name:String = results[indexPath.row].name
req.predicate = NSPredicate(format: "name == %#", name)
req.returnsObjectsAsFaults = false
let tapResults = context.executeFetchRequest(req, error: nil)
if let presentResults = tapResults {
if let castedResults = presentResults as? [MyManagedObjectSubclass] {
for item in castedResults {
println(item)
println(item.name)
println(item.subText)
}
}
}
}
I also changed all of your vars to lets since they don't need to be mutable.
Just replace MyManagedObjectSubclass with whatever your NSManagedObject subclass is.

Resources