- 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
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
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:
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.