Swift: CoreData call to specific object ID - ios

I'm discovering new concepts as a fresh developer, I have been trying to understand core data, and run into an issue with a tutorial I've been walking through. I am getting an error when I call an item using the Object ID. The error is - Type 'Person.Type' has no subscript members. it may be because I am just not doing it correctly, or some other reason. I'm sure someone can shine some light on the subject
Here is a function I wrote to get a specific item out of the Data Stack,
func getObjectById(id: NSManagedObjectID) -> Person?{
return context.objectWithID(id) as? Person
}
Here is how I am calling the function
func callFirstObject(){
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let personService = PersonService(context: context)
let firstPerson = personService.getObjectById(Person[0].objectID!)
}
and from there I am just calling callFirstObject() inside a button.
Edit: I have a function to call all of my objects
func getAllObjects() -> [Person]{
return getObject(withPredicate: NSPredicate(value: true))
}
and a function to call all of my objects with a predicate
func getObject(withPredicate queryPredicate: NSPredicate) -> [Person]{
let fetchRequest = NSFetchRequest(entityName: Person.entityName)
fetchRequest.predicate = queryPredicate
do {
let response = try context.executeFetchRequest(fetchRequest)
print("\(response)")
return response as! [Person]
} catch let error as NSError {
// In case of failure
print("There was an error - \(error)")
return [Person]()
}
}
I am just trying to call a specific name in the stack.
If more information is needed, I am glad to provide.

Person has no subscript members because Person is the class name. The subscript, [0], should be called on an array of Person objects.
That could look something like this:
func callFirstObject(){
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let personService = PersonService(context: context)
// Person Array
let persons: [Person] = [person1, person2, person3 ]
let firstPerson = personService.getObjectById(persons[0].objectID!)
}
Edit: I'm kind of confused the logic though. If you have the person already, you have to in order to access the objectID, then I don't see why you need to fetch it again. Can we see some more context around these two methods?
Answering your question from the comment below:
If you want to get all of the records for the Person model you can do as follows:
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Person")
do {
let results =
try managedContext.executeFetchRequest(fetchRequest)
people = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error)")
}
This will give you all of the Person objects stored in Core Data

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

Obtaining an array of objects from fetch request

I have an object called Projects that I save to core data. I then add multiple projects to core data. I want to retrieve those objects as an array from core data. The following code gets this error:
-[NSAsynchronousFetchResult mutableCopyWithZone:]: unrecognized selector sent to instance
var uploadPhotos : NSMutableArray? = []
var activeProject : String!
override func viewDidLoad() {
super.viewDidLoad()
projectPicker.dataSource = self
projectPicker.delegate = self
isInSegmentPhoto = true
//let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Project")
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = Project.fetchRequest()
let entityDescription = NSEntityDescription.entity(forEntityName: "Project", in: sharedDelegate.managedObjectContext)
fetchRequest.entity = entityDescription
do {
let result = try self.sharedDelegate.managedObjectContext.execute(fetchRequest)
print(result)
projects = result.mutableCopy() as! [Project]
} catch {
let fetchError = error as NSError
print(fetchError)
}
}
When I run the program it prints out the list of projects, but crashes on the first line of the do-catch.
Please, please use Swift native types, NSMutableArray has no type information.
var projects = [Project]()
...
projects = try defaultManagedObjectContext().fetch(fetchRequest) as! [Project]
Since the fetch request is clearly a Project request you can safely unwrap the result.
mutableCopy() causes the error, because NSFetchRequestResult(the umbrella protocol of all possible result types) does not respond to that method.
Figured it out! I was using the wrong method. Replace execute with fetch.
self.sharedDelegate.managedObjectContext.fetch(fetchRequest)

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
}
}

How to make simple join in relationship of Core Data Swift

I have Core Data with relationship:
extension Rating {
#NSManaged var date: NSString?
#NSManaged var rating: NSNumber?
#NSManaged var ratenames: RateNames?
}
extension RateNames {
#NSManaged var name: String?
#NSManaged var rating: NSSet?
}
And I need to select all values from RateNames with all data from Rating linked to RateNames from a selected date.
For example RateNames data:
name=a; name=b;
For example RateNames data:
date=10-02-2016; rating=5; linked to a
date=10-02-2016; rating=3; linked to b
date=09-02-2016; rating=2; linked to a
date=08-02-2016; rating=3; linked to a
date=08-02-2016; rating=1; linked to b
For example I need result for 08-02-2016
Result should be:
{(name:a, date:08-02-2016, rating=3), (name:b, date:08-02-2016, rating=1)}
result for 09-02-2016 date should be the following:
{(name:a, date:09-02-2016, rating=2), (name:b, date:nil, rating=nil)}
I also do need b in results because I need to see whether it is null or not.
I am trying to do something like:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "RateNames")
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
for result in results {
let child = result. ..some var here.. as! Rating
But cannot get how to implement this
It's easier to get what you want by fetching the Rating objects, and using the relationship to get the name of the related RateName objects:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Rating")
do {
let results = try managedContext.executeFetchRequest(fetchRequest) as! [Rating]
for result in results {
let name = result.ratenames.name
let date = result.date
let rating = result.rating
print("name:\(name), date:\(date), rating:\(rating)")
}
}
This will print results similar to your expected results, though it won't include the (name:b, date:nil, rating=nil).
Update
If you want to limit the above to a given date, you can use a predicate for the fetch request:
fetchRequest.predicate = NSPredicate(format:"date == %#", chosenDate)
But since you want to include, (name:b, date:nil, rating=nil), you will need to fetch ALL the RatingName objects. The rating property of each object will include any and all Ratings to which it is related. So I think you will then need to filter them to identify any for the correct date:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "RateNames")
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
let datePredicate = NSPredicate(format:"date == %#", chosenDate)
for result in results {
let filteredRatings = result.rating.filteredSetUsingPredicate(datePredicate)
if filteredRatings.count > 0 {
let rating = filteredRatings.anyObject()
print("name:\(result.name), date:\(rating.date), rating:\(rating.rating)")
} else {
print("name:\(result.name), date:nil, rating:nil")
}
}
}
This assumes there can be only one Rating for each RatingName for a given date; if not you will have to iterate through the filteredRatings set.
you need to just setup relationship in Datamodel like i have created for following example
From your code i found that you didn't mentioned One-to-Many Relationship .
Then create again sub class of NSManageObject. xCode will create all needed variables for same.

Resources