Getting objects from to-many relationship in Swift Core Data

7.4k views Asked by At

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.

2

There are 2 answers

11
Eendje On BEST ANSWER

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]
4
Mundi On

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.