-3

I would like to have the same "Save to Calendar Button" in every row of a tableView and each button opens a different Event from JSON imported through an api using decodable.

I need to assign the indexPath.row for each button so that the first button fills with the information from the first JSON object, the second button fills with the information from the second JSON object, ect... There's an example of the tableview below. I want each button to save a different event to a user's calendar with the "Title" passed from the api. (The same title that's passed to the "Title" UILabel you see below.) I would also like the "Description" to autofill with the description from the api. I have imported the json information using decodable.

enter image description here

Here is the cellForRowAt code found in the viewController:

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

        let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as! EventTableViewCell
        let item = page!.tradeshows[indexPath.row]

        cell.registrationUrl = item.link
        cell.eventTitle.text = item.title
        cell.eventDate.text = item.tradeshowDescription
        cell.addEventToCalendar(title: item.title!, description: item.tradeshowDescription, startDate: NSDate() as Date, endDate: NSDate() as Date)


        return cell
    }

I need the JSON information that is applied above (title: item.title!) and (description: item.tradeshowDescription) to autofill the information when the "Save to Calendar" button is pressed in each row. I need the indexPath to be assigned so that the information that autofills the event when saved to calendar is the correct information for each row.

Any advice is appreciated.

Custom Xib Cell Class Code found in the TableViewCell:

import UIKit
import EventKit

class EventTableViewCell: UITableViewCell {

    var registrationUrl : URL!

    @IBOutlet weak var eventDate: UILabel!
    @IBOutlet weak var eventTitle: UILabel!
    @IBOutlet weak var saveToCalendarButton: UIButton!
    @IBOutlet weak var registerButton: UIButton!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code

        selectionStyle = UITableViewCell.SelectionStyle.none
    }


    @IBAction func registerButtonPressed(_ sender: Any) {

        UIApplication.shared.open(registrationUrl)
    }


    @IBAction func saveToCalenderButtonPressed(_ sender: Any) {

       addEventToCalendar(title: "", description: "", startDate: NSDate() as Date, endDate: NSDate() as Date)

    }

    func addEventToCalendar(title: String, description: String?, startDate: Date, endDate: Date, completion: ((_ success: Bool, _ error: NSError?) -> Void)? = nil) {

        let eventStore = EKEventStore()

        eventStore.requestAccess(to: .event, completion: { (granted, error) in
            if (granted) && (error == nil) {
                let event = EKEvent(eventStore: eventStore)
                event.title = title
                event.startDate = startDate
                event.endDate = endDate
                event.notes = description
                event.calendar = eventStore.defaultCalendarForNewEvents
                do {
                    try eventStore.save(event, span: .thisEvent)
                } catch let e as NSError {
                    completion?(false, e)
                    return
                }
                completion?(true, nil)
            } else {
                completion?(false, error as NSError?)
            }
        })
    }

}
  • why [String()] instead of [String]() are you tring yo make an array of function with type of String(), – Yoel Jimenez del valle Nov 18 '19 at 18:16
  • I'm not sure how I should format that variable. I have a "Save Event" button in every row. I want when you click the "Save Event" button in the first row it saves the event with the title as the "title" string from the title in the first json object to the user's calendar. Then if you click the next "Save Event" button from the second row it saves the title from the second json object as the event title. – Allissa Hertz Nov 18 '19 at 23:24
  • The is as suggested in my comment you want an array of string that is [String]() so you yet off the error and are able to save the string in array – Yoel Jimenez del valle Nov 19 '19 at 11:56
  • add the code of `addEventTocalendar` please, and here is the event value read from in the save to calendar. – Yoel Jimenez del valle Nov 19 '19 at 15:00
  • This solution gives me the error "Cannot assign value of type '()' to type '[String]" when I use "[String]()" I added the "addEventToCalender" code. – Allissa Hertz Nov 19 '19 at 15:11
  • what's your event in cell. is button. so why don't you add target. and where are you using var ever = [String]() how does this give you error since you are declaring a variable. – Yoel Jimenez del valle Nov 19 '19 at 15:52
  • The error "Cannot assign value of type '()' to type '[String]'" occurs at the line `cell.event = addEventToCalendar(title: item.title!, description: item.tradeshowDescription, startDate: NSDate() as Date, endDate: NSDate() as Date)` The variable and the button press are both located in the swift for the cell xib file. The tableview cellForRowAt code is located in the controller that contains the tableview. I can't use a selector, because I need to assign using[indexPath.row] so that each button in each tableview cell is assigned the event information sequentially. – Allissa Hertz Nov 19 '19 at 16:11
  • Put cell class code please – Yoel Jimenez del valle Nov 19 '19 at 16:57
  • Added cell class code. I also think I realized the issue, which is that I'm trying to pass in the entire function instead of just the paramaters, but I'm not sure how to apply the parameters to the function in the cell for row at function and then also have them in the "saveToCalender" button function. That's why I was trying to store the parameters in a variable. – Allissa Hertz Nov 19 '19 at 17:27
  • first you are doing this wrong if you want to user save even to calendar when a cell is selected o the button is touchedupinside then you just not call directo to cell.addeventtoCalendar. I write a answer since is not very clear your question but I would asume somethings. – Yoel Jimenez del valle Nov 19 '19 at 19:49

1 Answers1

1

rewrite the cellForRow method.

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

    let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as! EventTableViewCell
    let item = page!.tradeshows[indexPath.row]

    cell.registrationUrl = item.link
    cell.eventTitle.text = item.title
    cell.eventDate.text = item.tradeshowDescription
    cell.saveToCalendarButton.tag = indexPath.row
    cell.saveToCalendarButton.addTarget(self, action: #selector(saveToCalenderButtonPressed(sender:)), for: .touchUpInside)


    return cell
}

then in the viewController you have to write the following function.

@IBAction func saveToCalenderButtonPressed(_ sender: Any) {

   guard let index = (sender as? UIButton).tag else {return}
   let item = eventArray[index]
   addEventToCalendar(title: item.title ?? "", description:item.tradeshowDescription, startDate: Date(), endDate: Date()){ (granted, error) in
if error == nil{
  print("success")
}else{
  print("error added to calendar")
    }
 }

and add this other function to viewController.

func addEventToCalendar(title: String, description: String?, startDate: Date, endDate: Date, completion: ((_ success: Bool, _ error: Error?) -> Void)? = nil) {
    let eventStore = EKEventStore()
    eventStore.requestAccess(to: .event, completion: { (granted, error) in
        if (granted) && (error == nil) {
            let event = EKEvent(eventStore: eventStore)
            event.title = title
            event.startDate = startDate
            event.endDate = endDate
            event.notes = description
            event.calendar = eventStore.defaultCalendarForNewEvents
            do {
                try eventStore.save(event, span: .thisEvent)
            } catch{
                completion?(false, error)
                return
            }
            completion?(true, nil)
        } else {
            completion?(false, error?)
        }
    })
}

so you viewController is responsible to add to event to calendar. and when user touch button in the cell you have to get the index of the array, with the sender tag that is equal to the indexPath.row of the cell.

Yoel Jimenez del valle
  • 1,270
  • 1
  • 9
  • 20
  • I can't get the IBAction func to connect in my viewController, because the IBOulet is in the EventTableViewCell.swift file, as shown above. If I remove it from that file then when I try to reference it in cellforRowAt it cannot find the outlet, because it is looking in "eventCell" – Allissa Hertz Nov 19 '19 at 21:06
  • I said that you must remove from cell and add it to the viewcontroller, so you would connect the target to the button no the action. so the button in the cell does not have a @IBAction you add a target for it, in the addTarget Function. – Yoel Jimenez del valle Nov 19 '19 at 23:24