How to update view in Swift MVVM?

public protocol Resource {
init()
static func fetch(completion: @escaping (Self) -> Void)
}
public extension Resource {
static func fetch(completion: @escaping (Self) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
completion(Self())
}
}
}
public struct User: Resource {
public init() {}
}
extension Array: Resource where Element: Resource {}
//ViewController.swift
class ViewController: UITableViewController {

let viewModel = ViewModel<[User]>()

func setup() {
viewModel.tableView = tableView
viewModel.fetch()
}
}
//ViewModel.swift
class ViewModel<R: Resource> {

var state: R?

weak var tableView: UITableView?

func fetch() {
R.fetch { [weak self] data in
print("fetched", data)

self?.state = data
self?.tableView?.reloadData()
}
}
}
let vc = ViewController()
vc.setup()
//ViewController.swift
class ViewController: UITableViewController, Delegate {

let viewModel = ViewModel<[User]>()

func setup() {
viewModel.delegate = self
viewModel.fetch()
}

func didFetched() {
print("fetched", viewModel.state)

tableView.reloadData()
}
}
//ViewModel.swift
protocol Delegate: AnyObject {
func didFetched()
}
class ViewModel<R: Resource> {

weak var delegate: Delegate?

var state: R?

func fetch() {
R.fetch { [weak self] data in
self?.state = data
self?.delegate?.didFetched()
}
}
}
let vc = ViewController()
vc.setup()
//ViewController.swift
class ViewController: UITableViewController {

let viewModel = ViewModel<[User]>()

func setup() {
viewModel.bind = { [unowned self] data in
print("notified", data)

self.tableView.reloadData()
}

viewModel.fetch()
}
}
//ViewModel.swift
class ViewModel<R: Resource> {

private var state: R? {
didSet {
bind?(state)
}
}

var bind: ((R?) -> Void)?

func fetch() {
R.fetch { [weak self] data in
self?.state = data
}
}
}
let vc = ViewController()
vc.setup()
//ViewController.swift
class ViewController: UITableViewController {

let viewModel = ViewModel<[User]>()

func setup() {
viewModel.bind { [unowned self] data in
print("notified", data)

self.tableView.reloadData()
}

viewModel.fetch()
}
}
//ViewModel.swift
class ViewModel<R: Resource> {

let state = Box<R>()

func bind(_ listener: @escaping (R?) -> ()) {
state.listener = listener
}

func fetch() {
R.fetch { [weak self] data in
self?.state.value = data
}
}
}
//Box.swift
class Box<T> {
var value: T? {
didSet {
listener?(value)
}
}

var listener: ((T?) -> ())?

init(_ v: T? = nil) {
value = v
}
}
let vc = ViewController()
vc.setup()
//ViewController.swift
class ViewController: UITableViewController {

let viewModel = ViewModel()

func setup() {
viewModel.addObserver(
self,
forKeyPath: #keyPath(ViewModel.state),
options: [.new],
context: nil
)

viewModel.fetch()
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

if keyPath == #keyPath(ViewModel.state) {
let state = change?[.newKey] as? [Admin]
print("notified", state)

tableView.reloadData()
}
}
}
//ViewModel.swift
class ViewModel: NSObject {

@objc dynamic var state: [Admin]?

func fetch() {
[Admin].fetch { [weak self] data in
self?.state = data
}
}
}
final class Admin: NSObject, Resource {
required override init() {}
}
let vc = ViewController()
vc.setup()
//ViewController.swift
class ViewController: UITableViewController {

@objc let viewModel = ViewModel()

var observer: NSKeyValueObservation?

func setup() {
observer = observe(\.viewModel.state, options: [.new]) { [unowned self] (_, change) in
print("notified", change.newValue)

self.tableView.reloadData()
}

viewModel.fetch()
}
}
//ViewModel.swift
class ViewModel: NSObject {

@objc dynamic var state: [Admin]?

func fetch() {
[Admin].fetch { [weak self] data in
self?.state = data
}
}
}
final class Admin: NSObject, Resource {
required override init() {}
}
let vc = ViewController()
vc.setup()
import RxSwift//ViewController.swift
class ViewController: UITableViewController {

let viewModel = ViewModel<[User]>()

let disposeBag = DisposeBag()

func setup() {
viewModel
.state
.subscribe({ [unowned self] newValue in
print("notified", newValue)

self.tableView.reloadData()
})
.disposed(by: disposeBag)

viewModel.fetch()
}
}
//ViewModel.swift
class ViewModel<R: Resource> {

let state = PublishSubject<R>()

func fetch() {
R.fetch { [weak self] data in
self?.state.onNext(data)
}
}
}
let vc = ViewController()
vc.setup()
import Combine//ViewController.swift
class ViewController: UITableViewController {

let viewModel = ViewModel<[User]>()

var cancellables = Set<AnyCancellable>()

func setup() {
viewModel
.$state
.sink { [weak self] data in
print("notified", data)

self?.tableView.reloadData()
}
.store(in: &cancellables)

viewModel.fetch()
}
}
//ViewModel.swift
class ViewModel<R: Resource> {

@Published var state: R?

func fetch() {
R.fetch { [weak self] data in
self?.state = data
}
}
}
let vc = ViewController()
vc.setup()

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store