Search code examples
iosswiftcore-datansset

How do you fetch NSSet from NSManagedObject in Swift?


I have a one to many relationship from Set to Card for a basic Flashcard App modelled in my Core Data.

Each Set has a set name, set description, and a relationships many card1s. Each Card1 has a front, back, and photo. In my table view, I've managed to retrieve all saved Sets from core data and display them. Now I want to fetch each Set's cards when a user clicks on the appropriate cell in my next view controller.

This is my code for the table view controller:

// MARK: Properties
var finalArray = [NSManagedObject]()

override func viewDidLoad() {
    getAllSets()
    println(finalArray.count)
}

func getAllSets() {
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext!
    let fetchRequest = NSFetchRequest(entityName:"Set")
    var error: NSError?
    let fetchedResults = managedContext.executeFetchRequest(fetchRequest,error: &error) as? [NSManagedObject]
    println("Am in the getCardSets()")
    if let results = fetchedResults {
        finalArray = results
        println(finalArray.count)
    }
    else {
        println("Could not fetch \(error), \(error!.userInfo)")
    }
}

// MARK: Displaying the data

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
     return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return finalArray.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! SetTableViewCell

    let sets = finalArray[indexPath.row]

    cell.setName.text = sets.valueForKey("setName")as? String
    cell.setDescription.text = sets.valueForKey("setDescription")as? String
    return cell
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "ShowDetail" {
        let dest = segue.destinationViewController as! Display

        // Get the cell that generated this segue.
        if let selectedCell = sender as? SetTableViewCell {
            let indexPath = tableView.indexPathForCell(selectedCell)!

            let selectedSet = finalArray[indexPath.row]
            dest.recievedSet = selectedSet
        }
    }
}

In my destination view controller, how would I go about retrieving all the cards in that the recievedSet? I've tried converting the NSSet to an array and casting it to a [Card1] array but when I attempt to display the first Card1's front String property onto the label, the app crashes, giving me the error

CoreData: error: Failed to call designated initializer on NSManagedObject class 'NSManagedObject' 
fatal error: Array index out of range

This is my code for the detailed viewController.

@IBOutlet weak var front: UILabel!

var finalArray = [Card1]()
finalArray = retrievedSet.allObjects as![Card1]
front.text = finalArray[0].front

Solution

  • Give your detail controller a property of type CardSet (I use "CardSet" because "Set" is a Swift built-in type name). You pass the selected set to this controller.

    You could have a property by which you sort, or generate an array without a particular order with allObjects.

    var cardArray = [Card1]()
    var cardSet: CardSet?
    
    viewDidLoad() {
       super.viewDidLoad()
       if let validSet = cardSet {
         cardArray = validSet.cards.allObjects as! [Card1]
       }
    }
    

    Your code is not working because finalArray is of type [CardSet], so finalArray[indexPath.row] is of type CardSet which is not transformable into type NSSet. Rather the relationship to Card1s is the NSSet you are looking for.

    Finally, I recommend to give the detail controller a NSFetchedResultsController, have an attribute to sort by and use the passed CardSet in the fetched results controller's predicate.