Data stored fails to display in the table view, in one to many relationship of core data?
Asked Answered
H

1

0

I used core data to make a one to many relationship where the scenario of my application is to display the members for the respected team. Both teams and members are added dynamically.

The issue occurs while storing the data at the member part. The data is getting stored but it won't get displayed in the table view. where I get this error.

data: { memberDesignation = ""; memberImage = nil; memberName = bbgbb; teams = nil; }) returned nil value for section name key path 'Teams.teams'. Object will be placed in unnamed section

I have done even a silly mistake as I'm new to core data. I can't figure it out what I'm doing wrong. I will show what I have done, rectify me what I did wrong. Please manipulate my code and give me the answer, no logical or random answers 'coz I don't have enough time to try out every possibilities.Help is very much appreciated, thanks in advance.

ER Model

enter image description here

Team table view controller

import UIKit
import CoreData

class GroupTable: UITableViewController, NSFetchedResultsControllerDelegate {

let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

var teamData = [Teams]()

var fetchedResultsController : NSFetchedResultsController = NSFetchedResultsController()

let statusbarHeight: CGFloat = 20


override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.contentInset.top = statusbarHeight
    self.navigationItem.leftBarButtonItem!.image = UIImage(named: "Arrow")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)

}

@IBAction func backToLogo(sender: AnyObject) {

    let previous = self.storyboard?.instantiateViewControllerWithIdentifier("Logo")
    self.presentViewController(previous!, animated: true, completion: nil)
}

override func viewWillAppear(animated: Bool) {

    let request = NSFetchRequest(entityName: "Teams")

    do{
        teamData = try managedObjectContext.executeFetchRequest(request) as! [Teams]
    } catch let error as NSError {
        print("\(error), \(error.userInfo)")
    }

    self.tableView.reloadData()

}


override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if teamData.count > 0{

        self.tableView.backgroundView = nil
        return teamData.count

    } else {
        let emptyLabel = UILabel(frame: CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height))
        emptyLabel.text = "No Teams available at the moment, create one!"
        emptyLabel.textAlignment = NSTextAlignment.Center
        self.tableView.backgroundView = emptyLabel
        self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
        return 0

    }

}

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

    let teamDetails = teamData[indexPath.row]

    cell.textLabel?.text = teamDetails.teamName
    cell.imageView?.image = teamDetails.teamImage as? UIImage
    return cell

}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
    if segue.identifier == "memberView" {
        let destination = segue.destinationViewController as! MemberTableViewController
        let indexPath = tableView.indexPathForSelectedRow!
        let selectedObject = fetchedResultsController.objectAtIndexPath(indexPath) as! Teams
        destination.currentTeam = selectedObject            
    }
  }  
}

Member table view controller

import UIKit
import CoreData


class MemberTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {

let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

var memberData = [Members]()

var currentTeam : Teams?

var fetchedResultsController: NSFetchedResultsController!

override func viewDidLoad() {
    super.viewDidLoad()

}

override func viewWillAppear(animated: Bool) {

    let request = NSFetchRequest(entityName: "Members")
    let members = NSSortDescriptor(key: "memberName", ascending: false)
    request.sortDescriptors = [members]
    if let thisTeam = currentTeam {

    request.predicate = NSPredicate(format:"teams == %@",thisTeam)
    }

    let fetchedResults = NSFetchedResultsController(fetchRequest: request, managedObjectContext: managedObjectContext, sectionNameKeyPath: "Teams.members", cacheName: nil)
    fetchedResults.delegate = self

    do {
        try fetchedResults.performFetch()
    } catch {
        fatalError("Failed to initialize FetchedResultsController: \(error)")
    }
    self.tableView.reloadData()

}


override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows

    if memberData.count > 0{

        self.tableView.backgroundView = nil
        return memberData.count

    } else {
        let emptyLabel = UILabel(frame: CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height))
        emptyLabel.text = "No Members in the team, add one!"
        emptyLabel.textAlignment = NSTextAlignment.Center
        self.tableView.backgroundView = emptyLabel
        self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
        return 0

    }

}



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

    // Configure the cell...

    let memberDetails = memberData[indexPath.row]

    cell.memberName?.text = memberDetails.memberName
    cell.memberDesignation?.text = memberDetails.memberDesignation
    cell.memberImage?.image = memberDetails.memberImage as? UIImage

    return cell
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {


}

/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    // Return false if you do not want the specified item to be editable.
    return true
}
*/

/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == .Delete {
        // Delete the row from the data source
        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
    } else if editingStyle == .Insert {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }    
}
*/

/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {

}
*/

/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    // Return false if you do not want the item to be re-orderable.
    return true
}
*/


// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.      
  }
}

To Add teams

import UIKit
import CoreData

class ScreenTwoPopOverViewController: UIViewController, UIImagePickerControllerDelegate,UINavigationControllerDelegate,UIPopoverControllerDelegate {

@IBOutlet weak var teamNamePO: UITextField!
@IBOutlet weak var teamImagePO: UIImageView!
@IBOutlet weak var selectPicturePO: UIButton!


var picker:UIImagePickerController?=UIImagePickerController()
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.


}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
@IBAction func submit(sender: AnyObject) {

    let entity = NSEntityDescription.entityForName("Teams", inManagedObjectContext: managedObjectContext)
    let team = Teams(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)

    team.teamName = teamNamePO.text
    team.teamImage = teamImagePO.image

    do{
        try managedObjectContext.save()
    } catch let error as NSError{
        print("\(error), \(error.userInfo)")
    }

    dismissViewControllerAnimated(true, completion: nil)

}
@IBAction func cancel(sender: AnyObject) {

    dismissViewControllerAnimated(true, completion: nil)
}

@IBAction func btnImagePickerClicked(sender: AnyObject)
{
    let alert:UIAlertController=UIAlertController(title: "Choose Image", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)

    let cameraAction = UIAlertAction(title: "Camera", style: UIAlertActionStyle.Default)
        {
            UIAlertAction in
            self.openCamera()

    }
    let gallaryAction = UIAlertAction(title: "Gallary", style: UIAlertActionStyle.Default)
        {
            UIAlertAction in
            self.openGallary()
    }
    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel)
        {
            UIAlertAction in

    }

    // Add the actions
    picker?.delegate = self
    alert.addAction(cameraAction)
    alert.addAction(gallaryAction)
    alert.addAction(cancelAction)
    // Present the controller
    self.presentViewController(alert, animated: true, completion: nil)
}

func openCamera()
{
    if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
    {
        picker!.sourceType = UIImagePickerControllerSourceType.Camera
        self .presentViewController(picker!, animated: true, completion: nil)
    }else {
        openGallary()
    }
}

func openGallary()
{
    picker!.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
    self.presentViewController(picker!, animated: true, completion: nil)
}

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
    picker .dismissViewControllerAnimated(true, completion: nil)
    teamImagePO.image=info[UIImagePickerControllerOriginalImage] as? UIImage
}

func imagePickerControllerDidCancel(picker: UIImagePickerController)
{
    print("picker cancel.")
    self.presentViewController(self, animated: true, completion: nil)
}



/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

To add members

import UIKit
import CoreData

class ScreenThreePopOverViewController: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate,UIPopoverControllerDelegate {

@IBOutlet weak var memberDesignationPO: UITextField!
@IBOutlet weak var memberNamePO: UITextField!
@IBOutlet weak var memberImagePO: UIImageView!

var picker:UIImagePickerController?=UIImagePickerController()
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

var currentTeam : Teams?


override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
@IBAction func selectPicture(sender: AnyObject) {

    let alert:UIAlertController=UIAlertController(title: "Choose Image", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)

    let cameraAction = UIAlertAction(title: "Camera", style: UIAlertActionStyle.Default)
        {
            UIAlertAction in
            self.openCamera()

    }
    let gallaryAction = UIAlertAction(title: "Gallary", style: UIAlertActionStyle.Default)
        {
            UIAlertAction in
            self.openGallary()
    }
    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel)
        {
            UIAlertAction in

    }

    // Add the actions
    picker?.delegate = self
    alert.addAction(cameraAction)
    alert.addAction(gallaryAction)
    alert.addAction(cancelAction)
    // Present the controller
    self.presentViewController(alert, animated: true, completion: nil)
}

func openCamera()
{
    if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
    {
        picker!.sourceType = UIImagePickerControllerSourceType.Camera
        self .presentViewController(picker!, animated: true, completion: nil)
    }else {
        openGallary()
    }
}

func openGallary()
{
    picker!.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
    self.presentViewController(picker!, animated: true, completion: nil)
}

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
    picker .dismissViewControllerAnimated(true, completion: nil)
    memberImagePO.image=info[UIImagePickerControllerOriginalImage] as? UIImage
}

func imagePickerControllerDidCancel(picker: UIImagePickerController)
{
    print("picker cancel.")
            dismissViewControllerAnimated(true, completion: nil)
}


@IBAction func cancel(sender: AnyObject) {

    dismissViewControllerAnimated(true, completion: nil)

}
@IBAction func submit(sender: AnyObject) {

    let entity = NSEntityDescription.entityForName("Members", inManagedObjectContext: managedObjectContext)
    let member = Members(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)

    member.memberName = memberNamePO.text
    member.memberDesignation = memberDesignationPO.text
    member.memberImage = memberImagePO.image

if let team  = currentTeam {
        member.teams = team
    }        
    do{
        try managedObjectContext.save()
    } catch let error as NSError{
        print("\(error), \(error.userInfo)")
    }
    dismissViewControllerAnimated(true, completion: nil)


}


/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

Team entity

extension Teams {

@NSManaged var teamImage: NSObject?
@NSManaged var teamName: String?
@NSManaged var members: NSSet?

}

Members entity

extension Members {

@NSManaged var memberDesignation: String?
@NSManaged var memberImage: NSObject?
@NSManaged var memberName: String?
@NSManaged var teams: Teams?

}

I'm very much confused at, how to add the members with respect to the teams?! How could i resolve this? For a better understanding about the scenario you could refer to this question Can I add an NSManagedObject to the selected parent object just using Interface Builder and Core Data? and also you could refer to this https://github.com/pbasdf/DemoMasterDetail which is exactly how I would like my app to produce the output.

Hobbema answered 6/5, 2016 at 14:22 Comment(4)
Praveen Kumar : Post your code to add member and team to coredata. As per the data you have shown seems like you have saved the team member without linking him to any team Entity. Which means you created a member who is not joined to any team hence you are not getting team member list for team :) Post your code to add member and team to coredata will help you outEscarpment
Sandeep Bhandari : updated the description of the question, you can check it out and give me your valuable comments!Hobbema
you mean this line member.members = currentTeamHobbema
You can see my answer :)Escarpment
E
1

Hi Praveen Kumar you can do something like this :)

@IBAction func submit(sender: AnyObject) {

    let entity = NSEntityDescription.entityForName("Members", inManagedObjectContext: managedObjectContext)
    let member = Members(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)

    member.memberName = memberNamePO.text
    member.memberDesignation = memberDesignationPO.text
    member.memberImage = memberImagePO.image

     member.setValue(currentTeam, forKey: "teams") //mistake you did is you were trying to set the wrong relationship :) member has a relationship to teams and is called teams. So you can access member.teams not member.members 

    //or
    member.teams = currentTeam

    do{
        try managedObjectContext.save()
    } catch let error as NSError{
        print("\(error), \(error.userInfo)")
    }
    dismissViewControllerAnimated(true, completion: nil)


}

TIP

Relation ship names should clearly explain the link between two entities. Rather then giving simply names like members & teams make a much sensible names :)

Teams -> contains -> Members Members -> Belongs_To -> Teams

So Team entity should have one to many relation ship to Members with name Contains. So when one sees it and reads it will be Team contains members :)

Similarly Members entity should have one to one relation ship to Teams with name Belongs_To. So when one sees it and reads it will be member belongs to Team :)

Hope my answer helped you :)

Escarpment answered 6/5, 2016 at 14:58 Comment(13)
It says value of type 'Members' has no member 'teams.Hobbema
@praveen-kumar : Thats strange :) can you please post the members and teams managed object model filesEscarpment
@praveen-kumar : Your question clearly shows Members entity has a field named teams buddy which is set to nil :) Try regenerating the model class for Members and Teams entity again :) It should sort outEscarpment
okay bro I'm doing the same right now :) hope your answer will solve my issue!!Hobbema
@praveen-kumar : It should buddy :) When you generate files you should see a property named teams of type Teams inside Members class and a property named members (NSSet of members) inside Team class.Escarpment
Just did based on your input, but the issue still arises. Is there anything wrong in fetching the members. Just check it out @SandeepHobbema
@praveen-kumar : Can you please post files Members.swift and Teams.swiftEscarpment
Updated bro, check it out :)Hobbema
Let us continue this discussion in chat.Escarpment
I think there is nothing wrong in setting the relationship but there is something wrong with fetching the members for the respected team or it is not fetching the correct indexpath of the team. Could you check my code in prepareForSegue of team table view controller and viewWillAppear of member table view controller.Hobbema
while i debug the code, it says current team is nil while storing the members!Hobbema
Hey buddy found the issue, thanks for your help :) Its working now!! :)Hobbema
@praveen-kumar : Sorry buddy I was offline whole day, just saw your comment now :) am glad you solved it buddy :)Escarpment

© 2022 - 2024 — McMap. All rights reserved.