Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting objects from to-many relationship in Swift Core Data

I have been having some difficulties revolving around using to-many relationships in Core Data with Swift.

My data model

enter image description here

What I am trying to do is to use an instance of a Country and then display all of the Contacts that are citizens of that country. As I have been trying to do this, I have constructed a UITableViewController that will display all of the citizens of the country. However I have had major problems of getting the actual Contacts out of the relationship citizensOfCountry. Here is the code that I am using (only relevant parts).

class ShowingCitizensOfCountry: UITableViewController {

     var countryToShow: Country?
     //This is a value that is passed by a prepareForSegue method
     override func viewDidLoad() {
          //how do I get a list/set/other iterable object of Contacts?

          //***Line in question***
          let listOfPeople = countryToShow!.citizensOfCountry

          for citizen in listOfPeople {
              println(citizen)//THIS IS NOT WORKING
          }

     }

So, this is not working at all. Right now in the for loop I get a compiler error that says Type 'Contacts' does not conform to protocol 'Sequence Type'. What I don't understand about this is that it is just of type Contact... I would think that it would be some sort of collection. So, that wasn't working so I tried this.

let listOfPeople = countryToShow!.citizensOfCountry as! [Contacts]

This doesn't work either and I get the error 'NSArray' is not a subtype of 'Contacts'. Next I tried converting with a NSSet.

let listOfPeople = countryToShow!.citizensOfCountry as! NSSet<Contacts>

Unfortunately this doesn't work either and I get the warnings Cast from 'Contacts' to unrelated type 'NSSet' always fails and Cannot specialize non-generic type 'NSSet'. Next I tried something else. I tried using the valueForKey method.

let listOfPeople = countryToShow!.citizensOfCountry.valueForKey("name") as! Set<String>

This works! I could print out just the name of all the people that are a citizen of that country. However I don't understand why. Why does it work when I use valueForKey("name") but I can't get an individual Contact? Is there a better way to be doing this that I have simply missed? Any explanation about why I am not able to get a collection of Contacts from a single Country is greatly appreciated. Thanks.

like image 886
Anthony Dito Avatar asked Jun 07 '15 06:06

Anthony Dito


People also ask

What is NSManagedObject in Core Data?

In some respects, an NSManagedObject acts like a dictionary—it's a generic container object that provides efficient storage for the properties defined by its associated NSEntityDescription instance.

Should have an inverse Core Data?

Inverse relationships enable Core Data to propagate change in both directions when an instance of either the source or destination type changes. Every relationship must have an inverse. When creating relationships in the Graph editor, you add inverse relationships between entities in a single step.

What is fetched property in Core Data?

Fetched Properties in Core Data are properties that return an array value from a predicate. A fetched property predicate is a Core Data query that evaluates to an array of results.

What is transformable in Core Data?

Core Data allows us to store integers, booleans, strings, UUID, date, etc. but sometimes we want to store a specific data type like UIColor, UIImage, our own class, struct, or enum, and even arrays, but that is simply not an option in Attribute's Type.


2 Answers

Sorry because I'm really tired I couldn't read everything. But from what I've read, you want to get the citizens of a country which should be a NSSet. To get that you can simply do:

let contacts = yourArrayOfCountries.flatMap { 
    $0.citizensOfCountry.allObjects as! [Contacts] 
}

According to your comment:

let contacts = countryToShow!.citizensOfCountry.allObjects as! [Contacts]
like image 174
Eendje Avatar answered Oct 14 '22 16:10

Eendje


The reason for your errors is that the to-many relationship results in a NSSet. All of your attempts to iterate though these sets should take this into account.

Much better is to implement a NSFetchResultsController, the standard way for displaying Core Data in a table view.

  • In the FRC, fetch people
  • Have the FRC sort by whatever you need
  • Give it a predicate that filters for the countryToShow

FRC implementation:

var fetchedResultsController: NSFetchedResultsController {
    if _fetchedResultsController != nil {
        return _fetchedResultsController!
    }

    let fetchRequest = NSFetchRequest(entityName: "Contact")
    fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
    fetchRequest.predicate = NSPredicate(format:"countryOfOrigin = %@", self.countryToShow!)
    let aFetchedResultsController = NSFetchedResultsController(
       fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, 
       sectionNameKeyPath: nil, cacheName: nil)
    _fetchedResultsController = aFetchedResultsController

    var error: NSError? = nil
    if !_fetchedResultsController!.performFetch(&error) {
        NSLog("%@", error!)
    }

    return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil

I made Contacts into the more appropriate singular.

like image 36
Mundi Avatar answered Oct 14 '22 16:10

Mundi