Swift - Get one-to-many relationship - ios

let's imagine that we have 2 entities:
-People (name, age, ..)
-House (color)
we recorded the data several times with house.addToPeople (newPeople) for each house
we want to get all the people of the house colored blue
how do we fetch this?
I tried this code but it gets all the people
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
let peopleFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "People")
let houseFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "House")
houseFetch.fetchLimit = 1
houseFetch.predicate = NSPredicate(format: "color = %#", "blue")
...
let res = try? context.fetch(peopleFetch)
let resultData = res as! [People]
how to do this ?

Try this function. What it does is fetching all of the People and creating an array with all of the results.
func getAllItems() -> [People]? {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "People")
request.returnsObjectsAsFaults = false
do {
let result: NSArray = try context.fetch(request) as NSArray
return (result as? [People])!
} catch let error {
print("Errore recupero informazioni dal context \n \(error)")
}
return nil
}
If you want to perform your search following certain criteria such as a color, use the following code after request:
//Here i'm searching by index, if you need guidance for your case don't hesitate asking
request.predicate = NSPredicate(format: "index = %d", currentItem.index)
Edit: actually the code above is just to get all of the people, if you want to base your search on the houses do the following:
func retrieve()-> [People]{
//Fetch all of the houses with a certain name
let houseRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "House")
houseRequest.predicate = NSPredicate(format: "name = %#", "newName") //This seearch is by name
houseRequest.returnsObjectsAsFaults = false
do {
//put the fetched items in an array
let result: NSArray = try context.fetch(houseRequest) as NSArray
let houses = result as? [Houses]
//Get the people from the previous array
let people: [People]? = (houses.people!.allObjects as! [People])
return people
} catch let error {
print("Errore recupero informazioni dal context \n \((error))")
}
return nil
}

Thank you for your answer !
In this example "houses" is an array, so we have to add an index ➔ houses[0].people!.AllObjects
And thank you very much for your explanation.

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

Loading the results set for Core Data fetch request

I have a variable declared as such:
private var theText = String()
private var aId = String()
I then do a fetch request and want to load it into that variable:
let predicateA = NSPredicate(format: "active == %#", true as CVarArg);
let predicateB = NSPredicate(format: "theId == %#", aId);
let andPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.and, subpredicates: [predicateA, predicateB]);
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext;
let requestData = NSFetchRequest<NSFetchRequestResult>(entityName: "DataTable");
requestData.predicate = andPredicate;
do
{
let results = try context.fetch(requestData);
dataactivitiesid = results.first
// let resultset = results as! [NSManagedObject];
/* for data in results
{
// let testyId: Any? = (data as AnyObject).value(forKey: "testyId");
} */
}
catch
{
print("Error in fetching data");
}
Do I need to loop through the result set like in the code that is commented above or since I know it is only one row being returned can I use .first? Thanks in advance.
If you expect only one item you don't need a loop.
I recommend to optional bind the result of first to a variable which can be nil if no entry is found.
And you don't need a compound predicate, a single predicate can contain multiple conditions related to the same object.
And finally this is not Objective-C, remove the trailing semicolons.
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext;
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "DataTable")
let predicate = NSPredicate(format: "active == TRUE AND theId == %#", aId)
fetchRequest.predicate = predicate
do {
if let result = try context.fetch(fetchRequest).first {
dataactivitiesid = result
}
} catch {
print(error) // print the actual error not a meaningless literal string
}

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.

How to delete values from core data in swift 2

for example here is the portion of my code that adds values
do {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "users")
let results = try context.executeFetchRequest(request)
if results.count > 0 {
for item in results as! [NSManagedObject] {
let fName = item.valueForKey("firstName")
let lName = item.valueForKey("lastName")
print(lName!, lName!)
}
}
} catch {
print("There was error getting data")
}
Let's say I have 10 users and now I want to
1. Delete users with lastName of "DOW"
2. First 2 users and last 2 users?
3. Also, all users with the lastName starts with "D"
Thanks
Borna
You can code:
if results.count > 0 {
for item in results as! [NSManagedObject] {
let fName = item.valueForKey("firstName") as! String
let lName = item.valueForKey("lastName")
print(lName!, lName!)
if fName.contains("DOW"){
appDelegate.managedObjectContext.deleteObject(item)
}
}
}
OR just get object what you want what NSPredicate and delete it
code:
let fetchRequest = NSFetchRequest(entityName: "users")
fetchRequest.predicate = NSPredicate(format: "firstName beginswith[c] %#", "D")
fetchRequest.returnsObjectsAsFaults = false
do{
let fetchResults = try appDelegate.managedObjectContext..executeFetchRequest(fetchRequest)
if fetchResults.count>0 {
appDelegate.managedObjectContext.deleteObject(fetchResults[0])
} else {
// no data
}
}catch{
//error
}
This question sounds like homework. The problem with deleting the first 2 and last 2 items is the data is stored unordered. You may get a different set of data from fetch request unless a sort order is provided.

read array from plist and only return item if match was found

i'm trying to work my way through a plist...
Now i wanted to only fetch item 1 if it was a genre of Soft how do i achieve this ?
i'm trying to sort my way through it but it doesn't work...
var path = NSBundle.mainBundle().pathForResource("radioChannels", ofType: "plist")
self.menuItemArray = NSMutableArray(contentsOfFile: path!) as NSMutableArray!
for obj: AnyObject in menuItemArray {
if let dict = obj as? NSDictionary {
if let menuPunkt = dict["genre"] as? String {
if menuPunkt as String == ("Soft"){
println("Soft \(menuPunkt)")
}
} else {
println("failed with menuPunkt")
}
} else {
println("failed to convert to NSDictionary")
}
}
this i only tried but doest work
var descriptor: NSSortDescriptor = NSSortDescriptor(key: "Soft", ascending: true)
self.sortedResults = menuItemArray.sortedArrayUsingDescriptors([descriptor])
As mentioned in the comments, use the filter built-in. The following snippet will give you an array of items with a genre of "soft":
if let menuItemArray = menuItemArray as? [[String:AnyObject]] {
var softItems = menuItemArray.filter({
if let genre = $0["genre"] as? String {
return genre.lowercaseString == "soft"
}
return false
})
println("\(softItems)")
}
I prefer this solution over the NSPredicate solution for a couple of reasons, first, once you typecast the NSMutableArray into a swift array of the appropriate type, it's pretty much pure swift, and second, NSPredicate is a pretty heavy Objective-C bat for a really simple problem.
Also note that the typecast/check should really be done one-time when the plist is loaded rather than doing it each time you build your view contents, it's really only included in the snippet for completeness.
Also give this code a try!
if let path = NSBundle.mainBundle().pathForResource("radioChannels", ofType: "plist") {
if let myArray = NSArray(contentsOfFile: path) {
for dict in myArray {
if let genre = dict["genre"] as? String{
if genre == "genre1" {
print("genre1")
}
}
}
}
}
Why don't you use NSPredicate for filtering genre?
let predicate = NSPredicate(format: "genre = 'soft'")!
let filteredArray = self.menuItems.filteredArrayUsingPredicate(predicate)

Resources