Hello guys I'm kinda new with implementing the MVVM pattern and I'm trying to figure out why my array is being updated so late, I mean if I'm not using DispatchQueue it wont load up, I mean I'm trying to separate the tableviewdatasource from viewcontroller and it seems to be a harm more than useful.
Here's my Code:
ViewModel:
import Foundation
import UIKit
class NotificationsViewModel: NSObject {
private(set) var notificationsData: [Notification]! {
didSet {
self.bindNotificationsViewModelToController()
}
}
var bindNotificationsViewModelToController : (() -> ()) = {}
var arrOfNotifications = [Notification]()
var selectedCounter = 0
var notificationIsRead = false
override init() {
super.init()
callFuncToGetNotData()
}
func callFuncToGetNotData() {
DispatchQueue.main.async {
var tempNotificationsArr : [Notification] = []
var notification1 = Notification(image: UIImage(named: "messageImage")!, title: "Hello1", date: Date.getCurrentDate(Date())(), team: "In: Team1", isRead: false)
var notification2 = Notification(image: UIImage(named: "messageImage")!, title: "Hello2", date: Date.getCurrentDate(Date())(), team: "In: QA Teams", isRead: true)
tempNotificationsArr.append(notification1)
tempNotificationsArr.append(notification2)
self.notificationsData = tempNotificationsArr
}
}
func setTitle(title:String, subtitle:String) -> UIView {
let titleLabel = UILabel(frame: CGRect(x: 0, y: -2, width: 0, height: 0))
titleLabel.backgroundColor = UIColor.clear
titleLabel.textColor = UIColor.white
titleLabel.font = UIFont.boldSystemFont(ofSize: 17)
titleLabel.text = title
titleLabel.sizeToFit()
let subtitleLabel = UILabel(frame: CGRect(x:0, y:18, width:0, height:0))
subtitleLabel.backgroundColor = .clear
subtitleLabel.textColor = .white
subtitleLabel.font = UIFont.systemFont(ofSize: 12)
subtitleLabel.text = subtitle
subtitleLabel.sizeToFit()
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: max(titleLabel.frame.size.width, subtitleLabel.frame.size.width), height: 30))
titleView.addSubview(titleLabel)
titleView.addSubview(subtitleLabel)
let widthDiff = subtitleLabel.frame.size.width - titleLabel.frame.size.width
if widthDiff < 0 {
let newX = widthDiff / 2
subtitleLabel.frame.origin.x = abs(newX)
} else {
let newX = widthDiff / 2
titleLabel.frame.origin.x = newX
}
return titleView
}
func selectAllRows(_ tableView: UITableView) {
for section in 0..<tableView.numberOfSections {
for row in 0..<tableView.numberOfRows(inSection: section) {
tableView.selectRow(at: IndexPath(row: row, section: section), animated: false, scrollPosition: .none)
}
}
}
func deselectAllRows(_ tableView: UITableView) {
for section in 0..<tableView.numberOfSections {
for row in 0..<tableView.numberOfRows(inSection: section) {
tableView.deselectRow(at: IndexPath(row: row, section: section), animated: false)
}
}
}
func getAlertSheet() -> UIAlertController {
let alertController = UIAlertController(title: nil, message: "(selectedCounter) Message", preferredStyle: .actionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
// ...
}
alertController.addAction(cancelAction)
let markAsUnReadAction = UIAlertAction(title: "Mark as Unread", style: .default) { (action) in
}
alertController.addAction(markAsUnReadAction)
let flag = UIAlertAction(title: "Flag", style: .default) { (action) in
//
}
alertController.addAction(flag)
let markAsRead = UIAlertAction(title: "Mark as Read", style: .default) { (action) in
//
}
alertController.addAction(markAsRead)
return alertController
}
func enableButtons(_ markBtn: UIBarButtonItem,_ selectAllBtn: UIBarButtonItem,_ deleteBtn: UIBarButtonItem) {
selectedCounter+=1
notificationIsRead = true
print(selectedCounter)
markBtn.isEnabled = true
selectAllBtn.isEnabled = true
deleteBtn.isEnabled = true
}
func disableButtons(_ markBtn: UIBarButtonItem,_ selectAllBtn: UIBarButtonItem,_ deleteBtn: UIBarButtonItem) {
selectedCounter -= 1
notificationIsRead = false
if selectedCounter == 0 {
markBtn.isEnabled = false
selectAllBtn.isEnabled = false
deleteBtn.isEnabled = false
}
}
}
ViewController:
import UIKit
class NotificationsViewController: UIViewController, ViewControllerDelegate {
@IBOutlet weak var notificationsTableView: UITableView!
@IBOutlet weak var toolBar: UIToolbar!
@IBOutlet weak var markButton: UIBarButtonItem!
@IBOutlet weak var selectAllButton: UIBarButtonItem!
@IBOutlet weak var deleteButton: UIBarButtonItem!
private var notificationsViewModel : NotificationsViewModel!
private var dataSource : NotificationTableViewDataSource<NotificationCell,Notification>!
var tableViewDelegate: TableViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
callToViewModelForUIUpdate()
configureTableView()
configureEditButton()
self.navigationItem.titleView = notificationsViewModel.setTitle(title: "Notifications", subtitle: "1 New")
}
func callToViewModelForUIUpdate(){
self.notificationsViewModel = NotificationsViewModel()
self.notificationsViewModel.bindNotificationsViewModelToController = {
self.updateDataSource()
}
}
func updateDataSource(){
self.dataSource = NotificationTableViewDataSource(cellIdentifier: "notificationCell", notifications: self.notificationsViewModel.notificationsData, configureCell: { (cell, nvm) in
cell.titleLabel.text = nvm.title
cell.imageView?.image = nvm.image
cell.dateLabel.text = nvm.date
cell.teamLabel.text = nvm.team
cell.notificationBadge.isHidden = nvm.isRead
})
self.notificationsTableView.dataSource = self.dataSource
self.notificationsTableView.reloadData()
}
func configureTableView() {
notificationsTableView.rowHeight = 80
notificationsTableView.allowsMultipleSelectionDuringEditing = true
self.tableViewDelegate = TableViewDelegate(withDelegate: self)
self.notificationsTableView.delegate = self.tableViewDelegate
self.toolBar.isHidden = true
self.markButton.isEnabled = false
self.deleteButton.isEnabled = false
}
func configureEditButton() {
self.navigationItem.rightBarButtonItem = editButtonItem
self.navigationItem.rightBarButtonItem?.tintColor = .white
}
@IBAction func markBtnPressed(_ sender: UIBarButtonItem) {
// UIAlert Pops up
self.present(notificationsViewModel.getAlertSheet(), animated: true, completion: nil)
}
@IBAction func selectAllBtnPressed(_ sender: UIBarButtonItem) {
// Selects/Diselect All The Rows In Our TableView.
if sender.title == "Select All"{
notificationsViewModel.selectAllRows(self.notificationsTableView)
markButton.isEnabled = true
deleteButton.isEnabled = true
sender.title = "Deselect All"
} else {
notificationsViewModel.deselectAllRows(self.notificationsTableView)
sender.title = "Select All"
markButton.isEnabled = false
deleteButton.isEnabled = false
}
}
@IBAction func deleteBtnPressed(_ sender: UIBarButtonItem) {
// Deletes a row
print("Delete Button Is Pressed")
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
self.notificationsTableView.setEditing(editing, animated: animated)
if editing {
//Section of when we are in Editing Mode:
toolBar.isHidden = false
self.navigationItem.rightBarButtonItem?.title = "Cancel"
toolBar.toolBarAnimation(self.toolBar)
} else {
//Section of when we are NOT in Editing Mode:
toolBar.toolBarDissapearAnimation(self.toolBar)
self.navigationItem.rightBarButtonItem?.title = "Edit"
toolBar.toolBarDissapearAnimation(self.toolBar)
}
}
func selectedCell(row: Int) {
if isEditing {
notificationsViewModel.enableButtons(self.markButton, self.selectAllButton, self.deleteButton)
}
}
func diselectedCell(row: Int) {
if isEditing {
notificationsViewModel.disableButtons(self.markButton, self.selectAllButton, self.deleteButton)
}
}
}
TableViewDataSource File:
import Foundation
import UIKit
class NotificationTableViewDataSource<CELL : UITableViewCell,T> : NSObject, UITableViewDataSource {
private var cellIdentifier : String!
private var notifications : [T]!
var configureCell : (CELL, T) -> () = {_,_ in }
init(cellIdentifier : String, notifications : [T], configureCell : @escaping (CELL, T) -> ()) {
self.cellIdentifier = cellIdentifier
self.notifications = notifications
self.configureCell = configureCell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
notifications.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! CELL
let item = self.notifications[indexPath.row]
self.configureCell(cell, item)
return cell
}
}