skip to Main Content

Here is my code :

    class SchedulerViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,
    scheduleCellDelegate
    {
    
        
        var scheduleArray : Array<Array<String>>?
        var scheduler : [String : Array<Array<String>>]?
        var deviceID : String = ""
        let retrievedString = KeychainWrapper.standard.string(forKey: "token")
    
        var day = ""
        var dayNum = 0
      
        @IBOutlet weak var spinner: UIActivityIndicatorView!
        @IBOutlet var buttons: [UIButton]!
        @IBOutlet weak var scheduleView: UITableView!
        
        var header : HTTPHeaders? = nil
        var ScheduleURL : Dictionary<String, String>?
        
        override func viewDidLoad() {
            super.viewDidLoad()
            scheduleView.delegate = self
            scheduleView.dataSource = self
            spinner.isHidden = true
            //scheduleView.allowsSelection = false
            scheduleView.register(UINib(nibName: "schedulerCell", bundle: nil), forCellReuseIdentifier: "schedulerCell")
            self.getData()
            
        }
        
        func getData(){

                AFFunctions.getAFRequest(ofType: ScheduleResponse.self, url: ScheduleURL!["GET"]!) { responseData, statusCode in
                    print(responseData?.data?.scheduler, statusCode)
                    self.scheduler = responseData?.data?.scheduler
                    DispatchQueue.main.async {
        
                        self.scheduleView.reloadData()
                    }
                    
                }
            }
            
           
            var buttonNum : Int?
            
            @IBAction func daySelected(_ sender: UIButton) {
                
                self.buttons.forEach { $0.tintColor = ($0 == sender) ? UIColor.orange : UIColor.systemTeal }
                
                self.dayNum = sender.tag
                switch dayNum {
                case 0 : self.day = "Sunday"
                case 1 : self.day = "Monday"
                case 2 : self.day = "Tuesday"
                case 3 : self.day = "Wednesday"
                case 4 : self.day = "Thursday"
                case 5 : self.day = "Friday"
                case 6 : self.day = "Saturday"
                default : self.day = "Sunday"
           
            }
            showDetail(day : day,dayNum : dayNum)
        }
        
        func showDetail(day : String, dayNum : Int) {

            if let dayArray = scheduler?[day]
            {  
                scheduleArray = dayArray
                self.scheduleView.reloadData()
            } 
        }
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return scheduleArray?.count ?? 0
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            
            let cell = Bundle.main.loadNibNamed("scheduleCell", owner: self, options: nil)?.first as! scheduleCell
            cell.cellDelegate = self
            cell.editBtn.tag = indexPath.row
            cell.deleteSchedule.tag = indexPath.row
            scheduleArray = scheduler![self.day]
/////////////THE BELOW STATEMENT THROWS THE ERROR ///////////////////
                if let firstLabel = self.scheduleArray?[indexPath.row][0], let secondLabel = self.scheduleArray?[indexPath.row][1] {
                DispatchQueue.main.async {
                        cell.timeLabel1.text = firstLabel
                        cell.timeLabel2.text = secondLabel
                    }
                }
            return cell
        }
        
        func didPressButton(_ tag: Int, btnType: String) {
            let deleteURL = K.delURL
            if(btnType == "delete") {
                AFFunctions.deleteAFRequest(ofType: scheduleResponse.self, url: "(deleteURL)?day=(self.day)&place=(tag)") { [self]
                    responseData, statusCode in
                    if(statusCode == 200){
                        let deleteAlert = UIAlertController(title: "Deleted", message: "Device Schedule Successfully Deleted", preferredStyle: UIAlertController.Style.alert)
    
                        deleteAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action: UIAlertAction!) in
                            
                            self.scheduler![self.day]?.remove(at: tag)
                            self.scheduleView.reloadData()
        
                         }))
                        self.present(deleteAlert, animated: true, completion: nil)
    
                    }
                }
            }
        }
     
    }

I am doing the changes in the array locally as data is fetched only once in ViewDidLoad() with getData function. It shows a schedule for each day of the week (7 buttons, one for each day, are linked to an Outlet Collection), with 2 buttons embedded in the custom cell nib – an edit button and a delete button. I have implemented the logic for deleting, button tags are equal to IndexPath.row which works perfectly and I am able to delete the values I want but when I can’t seem to get the table reload working. Even after deleting the row data, the table doesn’t update itself. I am calling reloadData after successful deletion. What am I doing wrong?

3

Answers


  1. Chosen as BEST ANSWER

    Solved it! Updated Code :

    class SchedulerViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,
    scheduleCellDelegate
    {
    
        
        var scheduleArray : Array<Array<String>>?
        var scheduler : [String : Array<Array<String>>]?
        var deviceID : String = ""
        let retrievedString = KeychainWrapper.standard.string(forKey: "token")
    
        var day = ""
        var dayNum = 0
      
        @IBOutlet weak var spinner: UIActivityIndicatorView!
        @IBOutlet var buttons: [UIButton]!
        @IBOutlet weak var scheduleView: UITableView!
        
        var header : HTTPHeaders? = nil
        var ScheduleURL : Dictionary<String, String>?
        
        override func viewDidLoad() {
            super.viewDidLoad()
            scheduleView.delegate = self
            scheduleView.dataSource = self
            spinner.isHidden = true
            //scheduleView.allowsSelection = false
            scheduleView.register(UINib(nibName: "schedulerCell", bundle: nil), forCellReuseIdentifier: "schedulerCell")
            self.getData()
            
            
        }
        
        func getData(){
           
          
            self.header =
            [
                "Content-Type" : "application/json",
                "Authorization": retrievedString!
            ]
            //scheduleView.register(scheduleCell.self, forCellReuseIdentifier: "scheduleCell")
           
            print(ScheduleURL!["GET"])
            AFFunctions.getAFRequest(ofType: ScheduleResponse.self, url: ScheduleURL!["GET"]!) { responseData, statusCode in
                print(responseData?.data?.scheduler, statusCode)
                self.scheduler = responseData?.data?.scheduler
                self.scheduleView.reloadData()
    
            }
        }
        
       
        var buttonNum : Int?
        
        @IBAction func daySelected(_ sender: UIButton) {
            
            self.buttons.forEach { $0.tintColor = ($0 == sender) ? UIColor.orange : UIColor.systemTeal }
            
            self.dayNum = sender.tag
            print(sender.tag)
            switch dayNum {
            case 0 : self.day = "Sunday"
            case 1 : self.day = "Monday"
            case 2 : self.day = "Tuesday"
            case 3 : self.day = "Wednesday"
            case 4 : self.day = "Thursday"
            case 5 : self.day = "Friday"
            case 6 : self.day = "Saturday"
            default : self.day = "Sunday"
            }
            print(day, dayNum)
            showDetail(day : day)
        }
        
        
        
        
        func showDetail(day : String) {
            
            print(day)
            print(scheduler?[day])
            if let dayArray = scheduler?[day]
            {   print(dayArray)
                scheduleArray = dayArray
                self.scheduleView.reloadData()
            }
            
        }
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            print("111 CHECK")
            return scheduleArray?.count ?? 0
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
            let cell = Bundle.main.loadNibNamed("scheduleCell", owner: self, options: nil)?.first as! scheduleCell
            cell.cellDelegate = self
            cell.editBtn.tag = indexPath.row
            cell.deleteSchedule.tag = indexPath.row
            if let item = scheduleArray?[indexPath.row],
               item.count > 1 {
                DispatchQueue.main.async {
                    cell.timeLabel1.text = item[0]
                    cell.timeLabel2.text = item[1]
                }
                   }
    
            return cell
        }
        
    
      
        
        func didPressButton(_ tag: Int, btnType: String) {
            let deleteURL = K.delURL
            print("134", self.day)
            print("135", self.scheduleArray?[tag])
            print("136", scheduler?[self.day])
            print("TAG : ", tag)
            print("BTN TYPE: ", btnType)
            if(btnType == "delete") {
                AFFunctions.deleteAFRequest(ofType: scheduleResponse.self, url: "(deleteURL)?day=(self.day)&place=(tag)") { [self]
                    responseData, statusCode in
                    print("(deleteURL)?day=(self.day)&place=(tag)")
                    print(responseData, statusCode)
                    if(statusCode == 200){
                        self.scheduler![self.day]!.remove(at: tag)
                        DispatchQueue.main.async {
                        let deleteAlert = UIAlertController(title: "Deleted", message: "Device Schedule Successfully Deleted", preferredStyle: UIAlertController.Style.alert)
    
                        deleteAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action: UIAlertAction!) in
                            self.showDetail(day: self.day)
    //                        self.scheduleView.reloadData()
                            
                         }))
                        self.present(deleteAlert, animated: true, completion: nil)
                        
                        }
                        
                    }
                }
            }
        }
     
    }
    

  2. there are 2 issues

    • when you update something on UI after a request call, you have to push the UI update process back to the main thread (tableview.reloadData() should get triggered on the main thread)
    • self.scheduleArray?[indexPath.row][0] and self.scheduleArray?[indexPath.row][1] is the issue index out of range. Because you assume it always contains 2 items without safe check.

    I’ve refactored the code a bit as below

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "scheduleCell") else {
            return Bundle.main.loadNibNamed("scheduleCell", owner: self, options: nil)?.first as! scheduleCell
        }
        cell.cellDelegate = self
        cell.editBtn.tag = indexPath.row
        cell.deleteSchedule.tag = indexPath.row
        scheduleArray = scheduler?[self.day]
       
        guard let item = scheduleArray?[indexPath.row], item.count == 2 else {
            return cell
        }
        if let firstLabel = item.first, let secondLabel = item.last {
            cell.timeLabel1.text = firstLabel
            cell.timeLabel2.text = secondLabel
        }
        return cell
    }
    
    func didPressButton(_ tag: Int, btnType: String) {
        let deleteURL = K.delURL
        if(btnType == "delete") {
            AFFunctions.deleteAFRequest(ofType: scheduleResponse.self, url: "(deleteURL)?day=(self.day)&place=(tag)") { [self]
                responseData, statusCode in
                if(statusCode == 200){
                    // This has to be executed on the main thread to get tableView updated
                    DispatchQueue.main.async {
                        let deleteAlert = UIAlertController(title: "Deleted", message: "Device Schedule Successfully Deleted", preferredStyle: UIAlertController.Style.alert)
                        
                        deleteAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action: UIAlertAction!) in
                            self.scheduler![self.day]?.remove(at: tag)
                            self.scheduleView.reloadData()
                            
                        }))
                        self.present(deleteAlert, animated: true, completion: nil)
                    }
                }
            }
        }
    }
    
    Login or Signup to reply.
  3. The issue here is you are holding multiple sources of truth and fail at synchronysing them. You have:

    var scheduleArray : Array<Array<String>>?
    var scheduler : [String : Array<Array<String>>]?
    

    Where both seem to hold the same information in different form. I can´t see why you are doing this from the example code you posted.

    You get an error at:

    self.scheduleArray?[indexPath.row][0]
    

    because when you delete your item you are removing it from scheduler and reload your tableview. The tableview on the other hand get´s the information how many rows it should render from scheduleArray:

    return scheduleArray?.count ?? 0
    

    and these differ at that time because you didn´t assign scheduler to scheduleArray.

    So 2 possible solutions here:

    • assign scheduleArray before you reload your tableview

      self.scheduler![self.day]?.remove(at: tag)
      scheduleArray = scheduler![self.day]
      

    and remove the assignment in the cellForItemAt function

    • stop using scheduleArray and scheduler. Use only a single collection to hold the information.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search