skip to Main Content

I am working with MVVM design architecture, I have gone through multiple articles about data binding. We can achieve data binding through Protocol, Closure, and Third Party(like RxSwift).
So, if I am wrong please correct me otherwise let me know "how many ways to bind the data in iOS(Swift) MVVM design architecture?"

2

Answers


  1. The easiest one is by using Observable class binding known as Boxing.
    create an Observable class:

    class Observable<T> {
    
    var value: T? {
        didSet {
            DispatchQueue.main.async {
                self.listener?(self.value)
            }
        }
    }
    
    init( _ value: T?) {
        self.value = value
    }
    
    private var listener: ((T?) -> Void)?
    
    func bind(_ listener: @escaping (T?) -> Void) {
        listener(value)
        self.listener = listener
    }
    }
    

    in your viewModel define it in this way:
    (this is a sample for showing loader in your view controller)

    var isLoadingData: Observable<Bool> = Observable(false)
    

    Because the Observable class is a generic type, you can pass other types to it.

    in your view model you can set the value:

    isLoadingData.value = true
    

    then in your view controller use something like this:

       viewModel.isLoadingData.bind { [weak self] loading in
            guard let loading = loading, let self = self else {
                return
            }
            DispatchQueue.main.async {
                if loading {
                    //Show a loader
                } else {
                    //Hide a loader
                }
            }
        }
    

    As soon as a value is assigned to the isLoadingData object, it will trigger your view controller. We have to use [Weak self] to avoid strong references.

    Login or Signup to reply.
  2. Due to I need multiple observer and I don’t want the closure still exist when the ViewController is gone.
    I make some changes from Sajjad Sarkoobi code.

    typealias ListenerClosure = @convention(block) (_ T: AnyObject?) -> ()
    
    class Observable<T: AnyObject> {
    
        var value: T? {
        
            didSet {
            
                self.listeners.forEach({ unsafeBitCast($0, to: ListenerClosure.self)(value) })
            
            }
        
        }
    
        private var listeners: [AnyObject] = []
    
        init(_ value: T?) {
        
            self.value = value
        
        }
    
        func bind(_ listener: @escaping ListenerClosure) {
        
            listener(value)
        
            let anyObject = unsafeBitCast(listener, to: AnyObject.self)
        
            self.listeners.append(anyObject)
        
        }
    
        func unbind(_ listener: @escaping ListenerClosure) {
    
            let anyObject = unsafeBitCast(listener, to: AnyObject.self)
                
            self.listeners.removeAll(where: { $0 === anyObject })
        
        }
    
    }
    

    in your viewModel define it in this way

    var currentUUID: Observable<NSUUID> = Observable(nil)
    

    Set the value in ViewModel

    self.currentUUID.value = NSUUID()
    

    Use bind and unbind in your ViewController. I want the closure removed from Observable listeners array, so I unbind it when viewController deinit.

    The capture list [weak self] is required if use self
    reference in closure or this viewController will not deinit.

    class ViewController: NSViewController {
    
        var viewModel: ViewModel?
    
        private var listener: ListenerClosure!
    
        override func viewDidLoad() {
            super.viewDidLoad()
        
            self.listener = { [weak self] uuid in
                
                //self?.doSomething()
    
                print("observed ViewModel currentUUID (uuid)")
            
            }
        
            self.viewModel?.currentUUID.bind(self.listener)
        }
    
        deinit {
        
            print("ViewController deinit")
        
            self.viewModel?.currentUUID.unbind(self.listener)
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search