Operator Binding Reactive Swift <~ Được thiết kế như thế nào?

Bài toán! Ở bài viết trước Link mình đã viết một ví dụ nhỏ về Signal và Observable và cách chúng được thiết kế và hôm nay mình hướng dẫn các bạn viết về Operator Binding giống Reactive Swift "<~" như đã nói ở bài trước.

  • Trước hết ta cần 1 thằng BindBox<T>: nhiệm vụ của ẻm nó là gọi thằng Listener trong Binding.
class BindBox<T> {
    weak var bind: Binding<T>?
    init(_ b: Binding<T>) {
        bind = b
    }
}
  • class Binding có 2 nhiệm vụ chính:
  1. lắng nghe sự kiện value change trong Dynamic thì phát ra giá trị mới cho thằng nào đó muốn bắt.
  2. xác định đối tượng lắng nghe thuộc kiểu Dynamic<T>, (có thể lắng nghe 3 - 4 hoặc n thằng Dynamic ấy mà).
class Binding<T> {
    
    typealias Listener = (T) -> Void
    var listener: Listener
    init(_ listener: @escaping Listener) {
        self.listener = listener
    }
    func bindTo(dynamic: Dynamic<T>) {
        dynamic.bindBoxs.append(BindBox(self))
    }
}

class Dynamic<T> có 2 nhiệm vụ :

  • mỗi khi value thay đổi sẽ bảo thằng BindBox gọi thằng Binding tao thay đổi giá trị rồi mày bảo thằng closure update giá trị mới cho ẻm nó đi.
  • quản lý tập hợp các [BindBox], để mỗi khi Dynamic có giá trị mới thì phát ra tín hiệu.
class Dynamic<T> {
    var value: T {
        didSet {
            for bindBox in bindBoxs {
                bindBox.bind?.listener(value)
            }
        }
    }
    
    var bindBoxs: [BindBox<T>] = []
    
    init(_ v: T) {
        value = v
    }
}

Sử dụng

// MARK: - Example + Function
var nameStringF = ""
let bindingF = Binding() { value in
    nameStringF = value
}
var dynamicF = Dynamic("")
bindingF.bindTo(dynamic: dynamicF)
dynamicF.value = "Binding with Function"
print(nameStringF)

và bây giờ mỗi khi dynamicF.value thay đổi giá trị thì nameStringF cũng sẽ thay đổi giá trị.

Phần Binding coi như xong, tiếp đến là Operator:

precedencegroup BindingPrecedence {
    associativity: right
    higherThan: AssignmentPrecedence
}
infix operator <~ : BindingPrecedence

func <~ <T>(left: Binding<T>, right: Dynamic<T>) {
    left.bindTo(dynamic: right)
}

tự rưng chán không muốn giải thích. nhưng mà có đồ chơi ở đây Advanced Operatorsở đây nữa . Sử dụng:

// MARK: - Example + Operator Binding
var nameStringO = ""
let bindingO = Binding() { value in
    nameStringO = value
}
var dynamicO = Dynamic("")
bindingO <~ dynamicO
dynamicO.value = "Binding with Operator"
print(nameStringO)

Sử dụng Operator Binding được rồi đó chỉ là thay cái func bindTo ở trên thành <~ cũng không có gì khó khăn cả. Github

Kết Tới đây thì bạn hiểu dần Reactive là gì rồi đấy, bài tiếp theo mình sẽ hướng dẫn các bạn sử dụng function programming, kết hợp function + reactive ta có FRP rồi đó, nào Try and Try!