12/08/2018, 16:00

Reactive Cocoa: Function Reactive Programming!

Khởi động Auto Binding ~> Code Step 1 Tạo IBOutlet như bình thường chúng ta vẫn làm. // MARK: - IBOutlet @IBOutlet weak fileprivate var emailTextField: UITextField! @IBOutlet weak fileprivate var passwordTextField: UITextField! @IBOutlet weak fileprivate var ...

Khởi động

Auto Binding ~> Code Step 1 Tạo IBOutlet như bình thường chúng ta vẫn làm.

    // MARK: - IBOutlet
    @IBOutlet weak fileprivate var emailTextField: UITextField!
    @IBOutlet weak fileprivate var passwordTextField: UITextField!
    @IBOutlet weak fileprivate var passwordAgainTextField: UITextField!
    @IBOutlet weak fileprivate var useCreditCardSwitch: UISwitch!
    @IBOutlet weak fileprivate var creditCardTextField: UITextField!
    @IBOutlet weak fileprivate var cardStatusLabel: UILabel!
    @IBOutlet weak fileprivate var verifyCardButton: UIButton!
    @IBOutlet weak fileprivate var activityIndicator: UIActivityIndicatorView!
    @IBOutlet weak fileprivate var registerButton: UIButton!

Step 2 binding View ~> viewModel

        viewModel.email <~ emailTextField.reactive.continuousTextValues.skipNil()
        viewModel.password <~ passwordTextField.reactive.continuousTextValues.skipNil()
        viewModel.passwordAgain <~ passwordAgainTextField.reactive.continuousTextValues.skipNil()
        viewModel.useCreditCard <~ useCreditCardSwitch.reactive.isOnValues
        viewModel.creditCardNumber <~ creditCardTextField.reactive.continuousTextValues.skipNil()

Step 3 Proterties in viewModel được binding từ view(or controller)

    /// Properties
    var email = MutableProperty<String>("")
    var password = MutableProperty<String>("")
    var passwordAgain = MutableProperty<String>("")
    var useCreditCard = MutableProperty<Bool>(false)
    var creditCardNumber = MutableProperty<String>("")
    var cardStatus = MutableProperty<CardStatus>(.notVerified)

Step 4 trong viewModel có các SignalProducer phát ra tín hiệu, view có thể bắt các sự kiện ở đây để cấu hình view

    /// SignalProducer: phat tin hieu
    var correctEmailProducer: SignalProducer<Bool, NoError>!
    var correctPasswordProducer: SignalProducer<Bool, NoError>!
    var correctCreditCardProducer: SignalProducer<Bool, NoError>!
    var correctInputProducer: SignalProducer<Bool, NoError>!

Step 5 Cấu hình SignalProducer, và các giá trị mặc định.

// cau hinh tin hieu va property
extension RegisterViewModel {
    func initBindings() {
        //tao trang thai mac dinh creditCardNumber
        creditCardNumber.producer.startWithValues { _ in
            self.cardStatus.value = .notVerified
        }
        
        //kiem tra email
        correctEmailProducer =
            email.producer
                .map({ (emailText) -> Bool in
                    emailText.isValidEmailContains()
                })
        //kiem tra pass
        correctPasswordProducer =
            SignalProducer.combineLatest(password.producer, passwordAgain.producer)
                .map { (passwordText, passwordAgainText) -> Bool in
                    return (passwordText.characters.count > 5) && (passwordText == passwordAgainText)
        }
        //kiem tra card
        correctCreditCardProducer =
            SignalProducer.combineLatest(useCreditCard.producer, cardStatus.producer)
                .map({ (useCard, cardStatus) -> Bool in
                    if !useCard {
                        return true
                    }
                    else if cardStatus == .verified {
                        return true
                    }
                    return false
                })
        
        //kiem tra email password va card
        correctInputProducer =
            SignalProducer.combineLatest(correctEmailProducer, correctPasswordProducer, correctCreditCardProducer)
                .map({ (email, password, card) -> Bool in
                    return email && password && card
                })
    }
    
    //khi button verify duoc press se goi ham nay
    func verifyCardNumber() {
        cardStatus.value = .verifying
    ServiceHelper.sharedInstance.validateCreditCardNumber(creditCardNumber.value)
            .startWithValues { valid in
                self.cardStatus.value = valid ? .verified : .denied
        }
    }
    
    //khi button register duoc press se goi ham nay
    func createMainAppViewModel() -> MainAppViewModel {
        let mainAppViewModel = MainAppViewModel()
        mainAppViewModel.email = email.value
        mainAppViewModel.creditCardNumber = useCreditCard.value ? creditCardNumber.value : nil
        return mainAppViewModel
    }
}

Step 6 ở view đơn giản mình sẽ lắng nghe từ viewModel,cấu hình view khi có sự kiện(vd correct pass thì enable button)

        // lang nghe su kien emailTextField change all value
        emailTextField.reactive.continuousTextValues.skipNil().observeValues { email in
            print(email)
        }
        
        // lang nghe tin hieu correctEmailProducer
        viewModel.correctEmailProducer.startWithValues { (correct) in
            self.emailTextField.textColor = correct ? UIColor.gray : UIColor.red
        }
        
        // lang nghe tin hieu correctInputProducer
        //registerButton.reactive.isEnabled <~ viewModel.correctInputProducer
        //or
        viewModel.correctInputProducer.startWithValues({ correct in
            self.registerButton.isEnabled = correct
        })
        
        // lang nghe tin hieu correctPasswordProducer
        viewModel.correctPasswordProducer.startWithValues { (correct) -> () in
            let backgroundColor = correct ? UIColor.gray : UIColor.red
            self.passwordTextField.textColor = backgroundColor
            self.passwordAgainTextField.textColor = backgroundColor
        }
        
        //lang nghe property useCreditCard --> producer
        viewModel.useCreditCard.producer.startWithValues { (useCard) -> () in
            self.tableView.beginUpdates()
            self.tableView.endUpdates()
        }
        
        //lang nghe property cardStatus --> producer
        viewModel.cardStatus.producer.startWithValues { (status) -> () in
            switch status {
            case .notVerified:
                self.cardStatusLabel.text = "Card has not been verified yet"
                self.activityIndicator.isHidden = true
                self.verifyCardButton.isHidden = false
                
            case .verifying:
                self.cardStatusLabel.text = "Card is currently being verified"
                self.activityIndicator.isHidden = false
                self.verifyCardButton.isHidden = true
                
            case .verified:
                self.cardStatusLabel.text = "Card is verified"
                self.activityIndicator.isHidden = true
                self.verifyCardButton.isHidden = true
                
            case .denied:
                self.cardStatusLabel.text = "Card is denied"
                self.activityIndicator.isHidden = true
                self.verifyCardButton.isHidden = false
            }
        }

Quan trọng vẫn là kết quả nó sẽ làm những gì!

  • Nhiều lắm, mình cũng không biết hết nhưng trước hết nó hết sức là clean code.
  • Thuận lợi phát triển theo mô hình MVVM và MVVM-C(binding data)
  • nó đã clean code rồi thì -> dễ maintain

Tham khảo tại đây ReactiveCocoa Không liên quan mấy nhưng đây là link Demo RxCocoa nói chung cũng thuộc FRP

Kết Thực ra trong project demo mình đã comment rất rõ ràng và dễ đọc, mình mới tìm hiểu về Reactive Cocoa thôi cũng chưa nắm được hết mọi thứ trong Reactive Cocoa, tìm được 1 project Demo về Reactive Cocoa khó quá nên mình quyết định viết 1 bài Demo để các bạn tham khảo, kiến thức về FPR đã được rất nhiều các anh/chị đi trước viết và đây là sức mạnh nho nhỏ của FRP mà mình muốn giới thiệu,cám ơn các bạn đã đọc bài viết của mình.

0