Как сделать динамические разделы с помощью RxDataSource?

Обзор того, чего я пытаюсь достичь Я пытаюсь создать табличное представление уведомлений, и каждое уведомление группируется по дате создания, поэтому в разделах табличного представления будет число созданной даты, каждый раздел с уведомлениями, созданными на эту дату в заголовок раздела. Я много искал, но не получил однозначного ответа, как с помощью RxDataSource сделать массив динамически загружаемым с датами, полученными через API?

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

Все, что я нашел, это сделать разделы статичными, вот так

       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)

это моя структура

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

и это модель данных

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"
    }
}

чего я пытаюсь достичь

введите описание изображения здесь

нужно, чтобы заголовок был таким

“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"
        }]

person Adam    schedule 25.02.2021    source источник


Ответы (2)


В примере RxDataSources мы имеем:

Observable.just(sections)
  .bind(to: tableView.rx.items(dataSource: dataSource))
  .disposed(by: disposeBag)

Все, что вам нужно сделать, это заменить Observable.just (разделы) на Observable, привязанный к вашим данным. Предположим, что notifications - это Observable<[Notifications]>. Затем вы делаете что-то вроде этого:

notifications.map { sections(from: $0) }
  .bind(to: tableView.rx.items(dataSource: dataSource))
  .disposed(by: disposeBag)

sections(from: $0) - это преобразование массива [Notification] в массив sections, определите его где-нибудь. Структура вашего раздела должна соответствовать протоколу SectionModelType.

struct SectionOfNotification {
  var header: String    
  var items: [Item]
}

extension SectionOfNotification : SectionModelType {
  typealias Item = Notification

   init(original: SectionOfNotification, items: [Item]) {
    self = original
    self.items = items
  }
}

Мой пример:

public lazy var appSections: Driver<[AppSection]> = {
    Driver.combineLatest(chatAppCollectionData, functionAppCollectionData) { ($0, $1) }
        .map { (chatAppCollectionData, functionAppCollectionData) -> [AppSection] in
            let appSection1 = AppSection(header: NSLocalizedString("DASHBOARD_RECENT_CHATS", comment: ""),
                                         items: chatAppCollectionData)

            let appSection2 = AppSection(header: NSLocalizedString("DASHBOARD_OTHERS", comment: ""),
                                         items: functionAppCollectionData)

            return [
                appSection1,
                appSection2
            ]
        }
}()

Это раздел:

import RxDataSources

struct AppSection {
    var header: String
    var items: [Item]
}

extension AppSection: SectionModelType {
    typealias Item = EONApp

    init(original: AppSection, items: [Item]) {
        self = original
        self.items = items
    }
}
person Denis Kutlubaev    schedule 25.02.2021
comment
Я не понимаю вашего ответа, как получить разделы из [AllNotificationModel], посмотрите, это моя структура. `struct NotificationSectionViewModel {var header: String var items: [AllNotificationModel]} расширение NotificationSectionViewModel: SectionModelType {typealias NotificationItem = AllNotificationModel init (исходный: NotificationSectionViewModel, items: [AllNotificationModel]) {self = original self.items = items} - person Adam; 25.02.2021
comment
@Adam, добавил пример из реального проекта. Вам просто нужно инициализировать объекты SectionModelType из объектов вашей модели. - person Denis Kutlubaev; 26.02.2021
comment
Вы только что поместили два раздела со своими данными. Я спрашиваю, что динамический раздел зависит от даты уведомлений, скажем, все уведомления в 26.02.2021, а затем все уведомления в 20.02.2021, и так далее, это не только сегодня и вчера это динамично, если у меня есть уведомления сегодня, я помещу их все в раздел, называемый сегодня, если нет, я создам другой раздел с другими полученными датами. - person Adam; 27.02.2021
comment
Я добавил то, что имею в виду, после проверки изображения, пожалуйста, @Denis - person Adam; 27.02.2021

Я понял ответ

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)
}

Это мой класс клеток

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!)

        }

}

Я использовал эти две функции, чтобы получить сегодняшнюю и вчерашнюю даты.

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
    }
}
person Adam    schedule 20.04.2021