NotificationCenter and KVO

  • KVO and NotificationCenter are two forms that use the principles of the observer standard.
  • It has The subject and the observer.
  • You need to "subscribe and publish".

NotificationCenter

The Notification Center uses the singleton pattern, and in its operation, one object does not need to know the other exists.

The notification center can notify the observer when an event occurs. However, through the notification, it is possible to send a message and find out who was the object that triggered the notification.

How it works:

Following the Observer Patter model, you need to register the observer.Use this function to register:

NotificationCenter.default.addObserver(observer: Any, selector: Selector, name: NSNotification.Name, object: Any)

We have some parameters to understand:

observer: Any = Who do you want to register

selector: Selector = creation of a function so that it can alert the observer

name: NSNotification.Name = unique identifier, which links the subject to the observer

object: Any = The object that sends notifications to the observer.

Use the function to trigger the notification:

//1.
NotificationCenter.default.post(name: NSNotification.Name, object: Any?, userInfo: [AnyHashable : Any])

//or

//2.
NotificationCenter.default.post(name: NSNotification.Name, object: Any?)

We have some parameters to understand:

NSNotification.Name = identify unique that needs to be the same as the observer

object: Any? = the object, if you want to pass the sender

userInfo: [AnyHashable: Any] = if you wanted to pass on a message, use the dictionary

Let's go to practice:

  • let's use view code with a navigation controller
  • We will have two ViewControllers

Screen Shot 2021-03-07 at 15 38 52

FirstViewController

//MARK: - Attributes
let btnSend: UIButton = {
    let btn = UIButton()
    btn.setTitle("Send", for: .normal)
    btn.backgroundColor = .gray
    btn.titleLabel?.font = UIFont.systemFont(ofSize: 22, weight: .medium)
    btn.addTarget(self, action: #selector(changeView), for: .touchUpInside)
    btn.translatesAutoresizingMaskIntoConstraints = false
    return btn
}()

let lbName: UILabel = {
    let lb = UILabel()
    lb.font = UIFont.systemFont(ofSize: 25, weight: .bold)
    lb.translatesAutoresizingMaskIntoConstraints = false
    lb.text = "Click in Button"
    lb.textAlignment = .center
    return lb
}()

The button serves to change the view, and as soon as it changes, we will trigger the notification to our observer. The label text will be changing by the message that will come with the notification.

 //MARK: - Instance Methods
    override func loadView() {
        super.loadView()

        self.view.backgroundColor = .systemBackground

        self.view.addSubview(btnSend)
        self.view.addSubview(lbName)

        self.constraints()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(sendName), name: .sendNameNotification, object: nil)

    }

Instance Methods are used to define the background color, add attributes to the view, define autoLayout and create the notificationCenter.

We need to define the name of the notificationCenter we will create an extension that will contain the name:

extension Notification.Name{
    static var sendNameNotification:Notification.Name{
        return Notification.Name(rawValue: "sendNameNotification")
    }
}

NotificationCenter prompts you to create a function. We will also create a function that will call a new view:

  @objc func sendName(_ notification: NSNotification){

        guard let name = notification.userInfo!["name"] else {return}

        self.lbName.text = "\(name)"

        let vc = notification.object

        let secondViewControler = vc as! SecondViewController

        secondViewControler.view.backgroundColor = .blue
    }

    //MARK: - Methods
    @objc func changeView(){
        let vc = SecondViewController()

        self.navigationController?.pushViewController(vc, animated: false)
    }

SecondViewController

In the SecondViewController, we will trigger the alert, which will call the observer.

import UIKit

class SecondViewController: UIViewController {


    override func loadView() {
        super.loadView()
        self.view.backgroundColor = .systemBackground
    }
    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.post(name: .sendNameNotification, object: self, userInfo: ["name" : "Hello World"])
    }
}

Em NotificationCenter.default.post, definimos o nome e passaremos o SecondViewController como um objeto para pintar a vista. Também enviaremos uma mensagem para alterar o texto que está no FirstViewController

Result

Kapture 2021-03-07 at 17 08 29

Github

KVO

KVO allows you to see if there is any change in a property, remembers didSet and willSet.

How it works:

It only works in classes.

To observe a property, you need to create a subclass of NSObject and declare the property as visible in Objective-C and as dynamic:

@objc dynamic var result = false

Let's go to practice:

Let's create a project in which the color of the UIButton will change if the user enters a number greater than 6.

The project structure will look like this:

Screen Shot 2021-03-07 at 17 17 50

Result.swift

Let's create a class called "Result" that inherits NSObject since KVO is only allowed in classes.

In this class, you will have two attributes. The note will say whether the number is greater than "6" or not, and the observable object, called result, will return true or false.

import Foundation

class Result: NSObject {

    var note = 0 {
        didSet {
            self.result = self.note > 6 ? true:false
        }
    }

    @objc dynamic var result = false
}

ViewController

Let's define our attributes:

  • TextField that accepts only numbers;
  • Button that will change color if the number is greater than 6;
  • Instance of the Result class, because observing is in the class;
  • The Observer requests a return of type NSKeyValueObservation.

//MARK: - Atributtes

    var observation: NSKeyValueObservation?

    let result = Result()

    let tfNote: UITextField = {
        let tf = UITextField()
        tf.placeholder = "Enter note here"
        tf.font = UIFont.systemFont(ofSize: 25)
        tf.borderStyle = UITextField.BorderStyle.roundedRect
        tf.keyboardType = UIKeyboardType.numberPad
        tf.returnKeyType = UIReturnKeyType.done
        tf.clearButtonMode = UITextField.ViewMode.whileEditing
        tf.translatesAutoresizingMaskIntoConstraints = false
        tf.addTarget(self, action: #selector(verifyNote), for: .editingChanged)
        return tf
    }()

    let btnResult: UIButton = {
        let btn = UIButton()
        btn.setTitle("Result", for: .normal)
        btn.backgroundColor = .gray
        btn.titleLabel?.font = UIFont.systemFont(ofSize: 22, weight: .medium)
        btn.translatesAutoresizingMaskIntoConstraints = false
        return btn

    }()
//MARK: - Instance Methods
    override func loadView() {
        super.loadView()
        self.view.backgroundColor = .systemBackground

        self.view.addSubview(tfNote)
        self.view.addSubview(btnResult)

        self.constraints()
    }



    override func viewDidLoad() {
        super.viewDidLoad()
        observe()
    }

Instance methods are using to define the background of the view, add the attributes in the view, constraints, and a function to call every time there is a change in our observer.

//MARK: - Methods

    @objc func verifyNote(_ textField: UITextField){
        let stringNote = textField.text!

        if let note = Int(stringNote){
            self.result.note = note
        }else{return}

    }

    func observe() {
        self.observation = self.result.observe(\.result, options: [.new]) { [weak self] _, change in
            guard let self = self else { return }

            if let value = change.newValue {
                if(value){
                    self.btnResult.backgroundColor = .green
                }else{
                    self.btnResult.backgroundColor = .gray
                }
            }
        }
    }

The verifyNote() function is used to validate the number. It uses the attribute note that it's in the Result class to assign the observer result.

The observe() function is our observer for each value change in the result attribute. We will receive a Boolean value and thus define the color of the button.

Result

Kapture 2021-03-07 at 17 59 20

Github