0

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!

8
  • What error? And the code your posted doesn't appear in your code. I'd guess replace salawaat with bob? Where is What's the objects in bobs? What's bobs = try context.fetch(Bob.fetchRequest())? Commented Jul 4, 2017 at 13:23
  • Hi @Larme, I just updated the code. Sorry it was a typo. Commented Jul 5, 2017 at 23:37
  • Could you show the value of bob and the EXACT error or warning message you get? Commented Jul 6, 2017 at 10:39
  • Hi, how do I show you the value of bob ? I've updated the original question to add the exact error. I couldn't paste it here as it was too long for comments lol. Commented Jul 6, 2017 at 15:34
  • bob.timeAndDaysLink that's a NSSet of Days object, not a NSSet of String object, that's why you have the cast issue. daysArray = bob.timeAndDaysLink?.allObjects.valueFor(key:"daysSelected") instead? Commented Jul 6, 2017 at 15:37

2 Answers 2

1
+50

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.

Sign up to request clarification or add additional context in comments.

22 Comments

Thank you for your reply Kuba. 1. cell.textLabel?.text = days.daysSelected! is giving compilation error so I was suggested by Xcode to use this : cell.textLabel?.text = days.value(forKey: "daysSelected") as! String . It compiles fine but crashes at this line with error Could not cast value of type '__NSSetI' (0x1033748b0) to 'NSString' (0x10255eab8)
2. cell.textLabel?.text = days.daysSelected! gives compilation error. So I changed it to cell.textLabel?.text = days.daysSelected! as! String which crashes at this line with the error Could not cast value of type '__NSSetI' (0x1023528b0) to 'NSString' (0x10153cab8) 3. daysSelected is of type Transformable and is not optional ( I need it to be not optional). I changed daysSelected to a string and get compilation error daysSelectedbyUser.daysSelected = NSSet(array: filteredWeekdays) saying cannot assign value of type 'NSSet' to type String
What is the type of yours 'days' variable? And what's the type you want it to be?
I believe it is of the same type as daysArray. From this code : daysArray = bob.timeAndDaysLink?.allObjects as! [Days] for days in daysArray { print (days.daysSelected?.description) cell.textLabel?.text = days.daysSelected?.description }
To be honest, I don't have any preference on what variable is stored as what type. I just want the functionality that the user can add a selection of days of week and time to the UITableView. The next part of the project would be to send the user internal notifications at the selected day and time using this as an example : stackoverflow.com/a/40060373/1886931
|
0

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...

1 Comment

HI Dominik, Thanks for the reply. in the Days entity, I have the daysSelected attribute which is of type Transformable.I did a find in my code and there is only one line which as @NSManaged in it which is this variable @NSManaged public var daysSelectedbyUser: NSSet. Please refer to the code in the original question under How to save days and then the code for @IBAction func saveButnTapped(_ sender: AnyObject). Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.