iosswiftrx-swiftrxdatasources

How to make Dynamic sections with RxDataSource?


An overview of what I am trying to achieve I am trying to make a notifications tableview and each notification is group by its created date, so the tableview sections will be the number of created date, each section with the notifications created at this date in the section title. I have searched a lot but didn't get an absolute answer how to make with RxDataSource the array is dynamic get loaded with dates received through an API?

class T : UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return array.count
    }
}

All what I have found is to set the sections static like so

       ViewModel.AllNotificationsObservable
                .map({ [NotificationSectionViewModel(header: "Yet", items: $0.filter{$0.createAt.toDate()!.toString(format: "yyyy-MM-dd") == Date().toString(format: "yyyy-MM-dd") }),
                        NotificationSectionViewModel(header: "Yesterday", items: $0)
                ]
                })
                .bind(to: NotificationTableView.rx.items(dataSource: ViewModel.dataSource))
                .disposed(by: notificationDisposeBag)

this is my struct

struct NotificationSectionViewModel {
    var header: String
    var items: [AllNotificationModel] 
}
extension NotificationSectionViewModel: SectionModelType {
    typealias NotificationItem = AllNotificationModel
    
    init(original: NotificationSectionViewModel, items: [AllNotificationModel]) {
        self = original
        self.items = items
    }
}

and this the data model

class AllNotificationModel : Codable {
    
    let id, userID : Int
    let title, body, createAt: String
    
    enum CodingKeys: String, CodingKey {
        case id, title, body
        case userID = "user_id"
        case createAt = "create at"
    }
}

what I am trying to achieve

enter image description here

need header to be like this

“Today”: [
        {
            "id": 2421,
            "user_id": 39,
            "title": "todayNotification",
            "body": "test",
            "create at": "2021-02-26 17:33:44"
        },
        {
            "id": 2349,
            "user_id": 39,
            "title": "check",
            "body": "test",
            "create at": "2021-02-26 09:36:05"
        },
        {
            "id": 2206,
            "user_id": 39,
            "title": "New Deal",
            "body": "new Deal 2",
            "create at": "2021-02-26 13:43:16"
        } ]
“Yesterday”: [
        {
            "id": 2134,
            "user_id": 39,
            "title": "Closed Deal",
            "body": “deal deal”,
            "create at": "2021-02-25 13:21:30"
        } ]

“2021-02-24”: [
        {
            "id": 2134,
            "user_id": 39,
            "title": "Closed Deal",
            "body": “deal”,
            "create at": "2021-02-24 13:21:30"
        },
        {
            "id": 2063,
            "user_id": 39,
            "title": "New Deal",
            "body": "new Deal",
            "create at": "2021-02-24 13:21:16"
        }]

Solution

  • I figured out the answer

    override func bind(ViewModel: NotificationViewModel) {
    
            
            ViewModel.dataSource.configureCell = { [unowned self] (dataSource, tableview, indexPath, item)  in
                let cell = tableview.dequeueReusableCell(withIdentifier: self.CellIdentifier, for: indexPath) as! NotificationTableViewCell
                cell.setDataToUI(notificationData: item)
                return cell
            }
    
            ViewModel.dataSource.titleForHeaderInSection = { (dataSource, index) in
                let section = dataSource[index]
                return section.header
            }
        
            var finalSections = [NotificationSectionViewModel]()
            var sortedFinal = [NotificationSectionViewModel]()
            var result = [String : [AllNotificationModel]]()
                ViewModel.AllNotificationsObservable
                    .map({ section in
          for (i, dict) in section.enumerated() {
                            result[(section[i].createAt.toDate()?.toString(format: "yyyy-MM-dd"))!, default: []].append(dict)
                        }
                        
                        for (key, value) in result {
                            finalSections.append(NotificationSectionViewModel(header: key, items: value))
                        }
                        
                        sortedFinal = finalSections.sorted(by: >)
                        
                        for final in 0...sortedFinal.count - 1 {
                            if self.getTodayDate() == sortedFinal[final].header {
                                sortedFinal[final].header = "Today"
                            }
                            else if self.getYesterDay() == sortedFinal[final].header {
                                sortedFinal[final].header = "Yesterday"
                            }
                            else {
                                    sortedFinal[final].header = convertDateFormater(sortedFinal[final].header)
                                
                            }
                        }
                        
                        return sortedFinal
                    })
                    .bind(to: NotificationTableView.rx.items(dataSource: ViewModel.dataSource))
                    .disposed(by: notificationDisposeBag)
    }
    

    This is my cell class

    class NotificationTableViewCell: UITableViewCell {
    
        @IBOutlet weak var notificationImageIcon: UIImageView!
        @IBOutlet weak var notificationBodyMessage: UILabel!
        @IBOutlet weak var notificationTime: UILabel!
        @IBOutlet weak var seenNotificationView: UIView!
        
    
        override func awakeFromNib() {
              super.awakeFromNib()
              // Initialization code
              selectionStyle = .none
          }
    
        func setDataToUI(notificationData: AllNotificationModel) {
            DispatchQueue.main.async {
                self.seenNotificationView.isHidden = true
                self.notificationBodyMessage.text = notificationData.body
                self.notificationTime.text = self.convertDateFormater(notificationData.createAt)
            }
        }
        
        func convertDateFormater(_ date: String) -> String
            {
                let dateFormatter = DateFormatter()
                dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
                let date = dateFormatter.date(from: date)
                dateFormatter.dateFormat = "h:mm a"
                return  dateFormatter.string(from: date!)
    
            }
    
    }
    

    I used these two function to get the today and the yesterday dates

    extension UIViewController {
        func getTodayDate() -> String {
            let currentDate = Date()
            let df = DateFormatter()
            df.dateFormat = "yyyy-MM-dd"
            let dateString = df.string(from: currentDate)
            return dateString
        }
    
        func getYesterDay() -> String {
            let currentDate = Date.yesterday
            let df = DateFormatter()
            df.dateFormat = "yyyy-MM-dd"
            let dateString = df.string(from: currentDate)
            return dateString
        }
    }