Has
Has

Reputation: 885

Fetching and displaying data from core data

Aim :

To be able to display the days selected and the time picked by the user in the same row of the table view. The time should appear at the top and the days selected should appear at the bottom, both in the same row, just like an alarm clock.

Work :

This is the relationship I've got setup : enter image description here

and this is how I save the days that are selected from a UITable and the time from a UIDatepicker when the save button is tapped :

@IBAction func saveButnTapped(_ sender: AnyObject)
    {
        let context =  (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext //creates an object of a property in AppDelegate.swift so we can access it

        let bob = Bob(context: context)

        //save the time from UIDatePicker to core data
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "HH:mm"
        bob.timeToPing = dateFormatter.string(from: timePicked.date)

        // save the days selected to core data
        for weekday in filteredWeekdays
        {
            var day = Days(context: context) //create new Days object
            day.daysSelected = weekday as NSObject? //append selected weekday
            bob.addToTimeAndDaysLink(day) //for every loop add day object to bob object
        }

        //Save the data to core data
        (UIApplication.shared.delegate as! AppDelegate).saveContext()

        //after saving data, show the first view controller
        navigationController!.popViewController(animated: true)
    }

Now that the data is once saved, I get the data :

func getData()
{
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

    do
    {
        bobs = try context.fetch(Bob.fetchRequest())
    }
    catch
    {
        print("Fetching failed")
    }

}

Attempt to get the days selected :

I tried to follow this, the below comments and a formerly deleted answer to this question to do this :

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cell = UITableViewCell()

    let bob = bobs[indexPath.row]
    cell.textLabel?.text = bob.timeToPing?.description

    // retrieve the days that are selected
    var daysArray: [Days] = []
    daysArray = bob.timeAndDaysLink?.allObjects as! [Days]

    for days in daysArray
    {
        print (days.daysSelected?.description)
        cell.textLabel?.text = days.daysSelected! as! String

    }

    return cell
}

EDIT : print(daysArray) gives this :

[<Days: 0x6080000a5880> (entity: Days; id: 0xd000000000040000 <x-coredata://30B28771-0569-41D3-8BFB-D2E07A261BF4/Days/p1> ; data: <fault>)]

print(daysArray[0]) gives this :

<Days: 0x6080000a5880> (entity: Days; id: 0xd000000000040000 <x-coredata://30B28771-0569-41D3-8BFB-D2E07A261BF4/Days/p1> ; data: <fault>)

How to save days

let weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
var filteredWeekdays: [String] = []
 @NSManaged public var daysSelectedbyUser: NSSet

And then

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        selectedWeekdays()

    }

    override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath)
    {
        selectedWeekdays()
    }

    func selectedWeekdays()
    {
        if let selectedRows = tableView.indexPathsForSelectedRows
        {
            let rows = selectedRows.filter {$0.section == 0}.map{ $0.row}
            filteredWeekdays = rows.map{ weekdays[$0] }
            print(filteredWeekdays)
        }
    }

Many thanks!

Upvotes: 0

Views: 1415

Answers (2)

Jakub
Jakub

Reputation: 13860

OK based on your latest comment that the crash occur on this line:

cell.textLabel?.text = days.value(forKey: "daySelected") as! String

It's clearly pointing to the typo you've made in key name. You have: daySelected and should be (based on your core data model) daysSelected, but nevertheless it's not very good approach to use values for your core data entity and also force type like that. To make it better I suggest replacing this line with:

cell.textLabel?.text = days.daysSelected!

This should be already a String since this is a String in CoreData. In case it's optional (should be an optional), you shouldn't force it. I will assume that whenever data will be not there you will just display empty cell, so even better it will be:

cell.textLabel?.text = days.daysSelected ?? ""

This will produce empty string to text, whenever (for some reason) data will be not there.

EDIT

So for additional piece of code you put in your question:

In your CoreData field daysSelected is type of String?, right? Then you assign timeAndDateLink to NSSet<String>, right? But expected value here should be NSSet<Days>.

So let's edit your input code a bit ( i will put comment on every line):

let bob = Bob(context: context) /* create new Bob object */
for weekday in filteredWeekdays {
    var day = Days(context: context) /* create new Days object */
    day.daysSelected = weekday /* append selected weekday */
    bob.addToTimeAndDaysLink(day) /* for every loop add day object to bob object */
}   

I hope everything is clear in above example. You may have a problem with a compiler in that case, because if you choose generate class for entities you will endup with two func with the same name but different parameter (in Swift this should be two different functions, but Xcode sometimes pointing to the wrong one). If you hit that problem try:

let bob = Bob(context: context) /* create new Bob object */
var output: NSMutableSet<Days> = NSMutableSet()
for weekday in filteredWeekdays {
    var day = Days(context: context) /* create new Days object */
    day.daysSelected = weekday /* append selected weekday */
    output.add(day)
}
bob.addToTimeAndDaysLink(output) /* this will assign output set to bob object */

You should also rename your Days entity to Day to avoid future confusion that we have right now, days as array will only be in relation from other entities to this not entity itself.

Upvotes: 1

Dominik Bucher
Dominik Bucher

Reputation: 2169

I don't know why no one uses FetchedResultsController, which is made for fetching NSManagedObjects into tableView, but it doesn't matter I guess...

Problem in this question is that you didn't post here your NSManagedObject class for the variable, so I cannot see which type you set there (Should be Transformable in CoreData model and [String] in NSManagedObject class...)

Ignoring all force unwraps and force casting and that mess (which you should pretty damn well fix as first, then it won't crash at least but just don't display any data...)

Days selected by user is NSSet, which it sure shouldn't be.

Please provide you NSManagedObjectClass in here so I can edit this answer and solve your problem...

Upvotes: 0

Related Questions