Fetching particular attributes or properties in CoreData - ios

i am using following Code to eliminate attributes while fetching from my CoreDataModel called industry. Still i am able to access those attributes which have not requested using fetchRequest.propertiesToFetch
let fetchRequest = NSFetchRequest(entityName: "Industry")
fetchRequest.propertiesToFetch = ["id","industry_name"]
After this i am using following code:
industryObject.industry_name=tuple.value(forKey: ""ind_subtype"") as? String
"ind_subtype" i have not specified in *.propertiesToFetch* and i am still able to access it
func fetchIndustryNamesWithId()->[IndustryData]{
var industryData=[IndustryData]()
//fetchRequest.predicate = NSPredicate(format: "firstName == %#", firstName)
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Industry")
fetchRequest.propertiesToFetch = ["id","industry_name"]
do {
let tuples = try managedObjectContext.fetch(fetchRequest)
for tuple in tuples{
let industryObject=IndustryData()
industryObject.id=tuple.value(forKey: "id") as? Int
industryObject.industry_name=tuple.value(forKey: "industry_name") as? String
industryObject.ind_subtype=tuple.value(forKey: "ind_subtype") as? String
industryData.append(industryObject)
}
return industryData
} catch {
let fetchError = error as NSError
print(fetchError)
}
return industryData
}

This is how it works. When you set propertiesToFetch CoreData will returns ManagedObject as a partially faulted. It means that some of the properties are populated some are not. When you access property that is not populated, core data will fulfil it for you but it probably will have performance impact because in most cases will require roundtrip to row cache.
For more info google for CoreData Faulting

Related

Output of fetched data to a variable as an Int

In this example, print gewicht gives an output of optional({10)} i need the output (10) as
an Int assigned to a variable . So the output has to be let mijnGewicht = 10
How can i do that. Iam new to swift, so excuse me for the question.
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let managedObjectContext = appDelegate!.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Dogs")
fetchRequest.predicate = NSPredicate(format: "name = %#", "Toni")
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.relationshipKeyPathsForPrefetching = ["gewicht"]
do {
let fetchedResults = try managedObjectContext.fetch(fetchRequest)
for i in fetchedResults {
dogs.append(i as! NSManagedObject)
for i in dogs {
let gewicht = i.value(forKeyPath: "gewicht.kg")
print(gewicht)
}
Dealing with unspecified NSManagedObject and value(forKeypath: is outdated.
Take advantage of the generic abilities of Core Data. The benefit is no type cast and no Any.
First declare dogs as
var dogs = [Dogs]()
By the way it's highly recommended to name entities in singular form. Semantically you have an array of Dog instances.
Create the fetch request for the specific NSManagedObject subclass Dogs
As gewicht is a to-many relationship you have to use a loop to get all values
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let managedObjectContext = appDelegate!.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<Dogs>(entityName: "Dogs")
fetchRequest.predicate = NSPredicate(format: "name = %#", "Toni")
fetchRequest.returnsObjectsAsFaults = false
do {
let result = try managedObjectContext.fetch(fetchRequest)
dogs.append(contentsOf: result)
for dog in dogs {
for gewicht in dog.gewicht {
let kg = gewicht.kg
print(kg)
}
}
} catch { print(error) }
If the relationship and/or the attribute is declared optional (which is still unclear) you have to unwrap the optional.
And consider that the integer value is Int16, Int32 or Int64 (unfortunately this information is missing, too). There is no Int type in Core Data.
And it's up to you how to distinguish the many values.
Try changing your code like this, it checks if the value you are assigning is not nil and then prints it
for i in dogs {
if let mijnGewicht = i.value(forKeyPath: "gewicht.kg"){
print(mijnGewicht)
}
}
You can try something like this
let gewicht = i.value(forKeyPath: "gewicht.kg")
if let gewichtInt = gewicht as? Int {
print(gewichtInt)
}
for i in dogs {
if let gewicht = i.value(forKeyPath: "gewicht.kg") {
print(gewicht) // this will give you the safe value as Int, if the value is nill it will not come in this if condition
}
let x = i.value(forKeyPath: "gewicht.kg") ?? 0
print (x) //this will give you wrapped safe value of gewicht.kg if exists else it will give you 0
}
here in above example i have shown you two ways to safe cast a value from optional, you can also use guard or guard let on the basis of your requirement

Getting specific attributes from coredata swift

I have a request for an entity in core data:
override func viewWillAppear(animated: Bool) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Constants")
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
Within the entity Constants there are 4 attributes: moneyPerSecondSave,moneySave,tapMultiplierSaveandtapValueSave.
My question is, is there a way that after I have got the results can I make four variables which each hold each of the values of the attributes.
I have already tried using valueForKey:
moneyConstants.money = results.valueForKey("moneySave")
However when I try this it pulls up an error: `Value of type '[AnyObject]' has no member 'valueForKey'. Any help would be appreciated.
'results' is an array which holds 'Any' type. You can cast it to an array with the type you expect and get the rest of the data like this:
if let constants = results as? [Constants] {
let moneyPerSecond = constants[0].moneyPerSecondSave
print("moneyPerSecond: \(moneyPerSecond)")
}
You can also use valueForKey of course, like this:
if let match = results[0] as? Constants {
let money = match.value(forKey: "moneySave") as! Float
print("money: \(money)")
}
You can use propertiesToFetch & resultType attribute of NSFetchRequest to retrieve only specific property values of Constants.
You can take inspiration from https://stackoverflow.com/a/6267875/3339346

Getting objects from NSSet and populating TableView

I have this piece of code as shown below
func getItemsOnList(){
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
//fetchRequest to get the List
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "List")
let predicate = NSPredicate(format: "title == %#", listName)
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = predicate
if let fetchResults = try? context.fetch(fetchRequest){
if fetchResults.count > 0 {
for listEntity in fetchResults {
let list = listEntity as! List
print(list.title as Any)
itemsOnListSet = list.contains!
What this does is it gets the Items from the specified List using the .contains relationship between the two entities, and saves all the items in to an NSSet.
What i want to do is to populate a TableView with the objects that are in the NSSet.
Is there a function related to NSSet which allows me to get the items from the set? Or should i save the items in an Array instead of an NSSet.
P.S. the .contains relationship is of type NSSet
#NSManaged public var contains: NSSet?
why don't you convert the Set to Array using,
if let _ = list.contains {
let itemsOnListArray = list.contains!.allObjects
}
else
if let unwrappedList = list.contains {
let itemsOnListArray = unwrappedList.allObjects
}
Now use your itemsOnListArray as your tableView's data source :)
EDIT:
Your code
let item = itemsOnListArray[indexPath.row]
cell.textLabel?.text = item as? String
Assumes itemsOnListArray is a array of strings!!! Which is absolutely impossible because list.contains! is a set of NSManagedObjects or if you created mapped subclasses of ManagedObjects than it will contain a set of your managed objects like items.
What you should be doing is (because you have not provided the description of item am assuming item has a name property in it)
let item = itemsOnListArray[indexPath.row] as! Item
cell.textLabel?.text = item.name

Querying Core data with predicates

So i am building this app using CoreData.
The two entities I have are Lists and Items. They have a to many relationship i.e. A List can have multiple items.
For example: List1 has Items: item1, item2
I have written the code for storing the Items in the specific list but i am having a difficult time on figuring out how to fetch and proccess the Items from a specific List.
What I have done so far is as follows
func getItemsOnList(){
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
//fetchRequest to get the List
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "List")
let predicate = NSPredicate(format: "title == %#", listName)
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = predicate
if let fetchResults = try? context.fetch(fetchRequest){
if fetchResults.count > 0 {
for listEntity in fetchResults {
let list = listEntity as! List
print(list.title as Any)
itemsOnList = list.contains!
print(itemsOnList)
print("The list with name:\(list.title)has \(itemsOnList.count) items")
}
}
}
}
This function returns an NSSet which is suppose to contain all the Items in that particular List.
My Data model is :
My questions are:
A. Is the way I coded the getItemsOnList() function correct? Or is there something I am doing wrong.
B. Given that the code is correct and the NSSet I get is correct with all the Items, how would I get each Item in that NSSet in order for me to put it on a TableView.
func getItemsWithFilter(filterQuery:NSPredicate,sortBy:String?,order:Bool) -> Array<Items> {
var fetchedResults:Array<Items> = Array<Items>()
let fetchRequest = NSFetchRequest(entityName: "Items")
fetchRequest.predicate = filterQuery
if sortBy != nil{
let sortDescriptor = NSSortDescriptor(key:sortBy! ,
ascending:order )
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = sortDescriptors
}
//Execute Fetch request you can go with your approach to
do {
fetchedResults = try self.mainContextInstance.executeFetchRequest(fetchRequest) as! [Items]
} catch let fetchError as NSError {
print("retrieveById error: \(fetchError.localizedDescription)")
fetchedResults = Array<Items>()
}catch {
fetchedResults = Array<Items>()
}
return fetchedResults
}
for calling this method you can pass the List item in predicate to as query saying fetch Items in which List.id == XXX
let predicate = NSPredicate(format: "ANY list.name in %#", name)
let myResult = self.getItemsWithFilter(predicate,sortBy:nil,order:false)
Answers:
A) Yes. You are using the graph of objects from a fetch. That is the main functionality of Core Data.
B) To fill a table view you cannot use a set. You need some kind of sorted list of elements. That is, an array. Use -orderedArrayUsingDescriptors: to get the sorted array.

Cryptic Core Data NSInvalidArgumentException Reason: 'Invalid relationship'

I'm trying to update a relationship in Core Data but am experiencing this issue. I have 2 entities: Trip and GPSLocation. A Trip can have many GPSLocation but a GPSLocation can only be in one Trip, hence an one-to-many relationship from Trip to GPSLocation. I set up my entities in Xcode's model editor like so:
Entity: GPSLocation relationship: trip destination: Trip inverse: gps Type: To one
Entity: Trip relationship: gps destination: GPSLocations inverse: trip Type: To many
Assuming the variable trip is an instance of my Trip entity. In my update routine, I have:
let request = NSBatchUpdateRequest(entityName: "GPSLocations")
request.predicate = somePredicate
request.propertiesToUpdate = ["trip": trip]
request.resultType = .UpdatedObjectIDsResultType
do {
let responses = try managedContext.executeRequest(request) as! NSBatchUpdateResult
print responses.result!
} catch let error as NSError {
print(error)
}
This code is giving me an NSInvalidArgumentException Reason: 'Invalid relationship' and I'm not sure why. Any help would be very appreciated! Thank you.
EDIT: My predicate is a pretty simple one: NSPredicate(format: "SELF = %#", "1"). I confirmed that record with pk 1 exists via a database visualizer.
EDIT 2:
So I did some further testing and noticed something interesting about the 2 routines that I wrote for creating and updating records in entities. Unlike with the update routine, I don't get this invalid relationship problem when I use the create routine. Here is my code for both routines:
// MARK - this routine works fine
func createRecord(entityName: String, fromKeyValue: [String:AnyObject]) -> AnyObject {
let entityDesc = NSEntityDescription.entityForName(entityName, inManagedObjectContext: managedContext)
do {
let entityType = NSClassFromString("myapp." + entityName) as! NSManagedObject.Type
let entity = entityType.init(entity: entityDesc!, insertIntoManagedObjectContext: managedContext)
for key in fromKeyValue.keys {
entity.setValue(fromKeyValue[key], forKey: key)
}
try managedContext.save()
return entity
} catch let error as NSError {
return error
}
}
// MARK - this one gives me problem..
func updateRecords(entityName: String, predicate: NSPredicate, keyValue: [String:AnyObject]) -> AnyObject {
let request = NSBatchUpdateRequest(entityName: entityName)
request.predicate = predicate
request.propertiesToUpdate = keyValue
request.resultType = .UpdatedObjectIDsResultType
do {
let responses = try managedContext.executeRequest(request) as! NSBatchUpdateResult
return responses.result!
} catch let error as NSError {
return error
}
}

Resources