2. Refresh the interface data by listening to the property observer of the ViewModel, and invoke closure 3. Finally, the View/ViewController completes all UI interactions

Train of thought

The function of closures to control the interaction process

typealias Nothing = ()->()
var reloadTableViewClosure: Nothing?
var showAlertClosure: Nothing?Copy the code

Use the property viewer to trigger the invocation of the closure to make the UI respond

var cellViewModels: [CellViewModel] = [CellViewModel]() {
	didSet {
		reloadTableViewClosure?()
	}
}
var alertMessage: String? {
	didSet {
		showAlertClosure?()
	}
}Copy the code

The implementation process

ViewController

import UIKit
import SDWebImage
class ViewController: UIViewController {

    @IBOutlet weak var tableview: UITableView!
    lazy var viewmodel: ViewModel = {
        return ViewModel()
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        createui()
        createvm()
    }
    func createui() {
        self.view.backgroundColor = .black
        tableview.register(UINib(nibName: "ViewCell", bundle: nil), forCellReuseIdentifier: "viewcell")
    }
    func createvm() {
        
        viewmodel.reloadTableViewClosure = { [weak self] inDispatchQueue.main.async { self? .tableview.reloadData() } } viewmodel.showAlertClosure = { [weak self]in
            DispatchQueue.main.async {
                if letmessage = self? .viewmodel.alertMessage { self? .showAlert( message ) } } } viewmodel.initData() } func showAlert( _ message: String ) {let alert = UIAlertController(title: "Alert", message: message, preferredStyle: .alert)
        alert.addAction( UIAlertAction(title: "Ok", style: .cancel, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
}

//MARK: TableViewDelegate
extension ViewController: UITableViewDelegate,UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewmodel.numberCells
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "viewcell".for: indexPath) as? ViewCell else {
            fatalError("Cell not exists in storyboard")
        }
        cell.config = viewmodel.dataViewModel(indexPath)

        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        viewmodel.promptMessage(indexPath)
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return160}}Copy the code

ViewModel

import UIKit
struct CellViewModel {
    let titleText: String
    let descText: String
    let imageUrl: String
    letdateText: String } typealias Nothing = (()->()) class ViewModel: {var reloadTableViewClosure: Nothing? NSObject {var reloadTableViewClosure: Nothing? var showAlertClosure: Nothing? // var cellViewModels: [CellViewModel] = [CellViewModel]() { didSet { reloadTableViewClosure? () } } var alertMessage: String? { didSet { showAlertClosure? () } } // var numberCells: Int {return cellViewModels.count
    }
    
    func initData(api: APIService = APIService()) {
        api.fetchPopularPhoto { [weak self] (success, photos, error) in
            if leterror = error { self? .alertMessage = error.rawValue }else{ self? .processFetchedPhoto(photos: photos) } } } func dataViewModel(_ index: IndexPath) -> CellViewModel {return cellViewModels[index.row]
    }

    func promptMessage(_ index: IndexPath) {
        if index.row%2 == 0 {
            alertMessage = "Click \ (index) row)"
        }
    }

    func processFetchedPhoto(photos: [Photo]) {
        var num = [CellViewModel]()
        for photo inphotos { num.append(createCellViewModel(photo: Photo))} // Refresh tableView cellViewModels = num} Instead, it uses the ViewModel to take over data and interact with the View layer. Photo ) -> CellViewModel { //Wrap a description var descTextContainer: [String] = [String]()if let camera = photo.camera {
            descTextContainer.append(camera)
        }
        if let description = photo.description {
            descTextContainer.append( description )
        }
        let desc = descTextContainer.joined(separator: "-")
        
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        
        returnCellViewModel( titleText: photo.name, descText: desc, imageUrl: photo.image_url, dateText: dateFormatter.string(from: Photo. Created_at))}}Copy the code

GitHubDemo: github.com/marst123/ta…