If we think about how apps work, we can see they're usually multiple screens displaying or passing information.
In this last remark, there are several methods or patterns we can use to achieve communication between elements in an app.
- Identify and implement the Target-Action pattern for event handling.
- Understand how Notifications work using Notification Center.
- Analyze and decide which pattern to use based on the requirements of an app.
Target-Action is a design pattern in which an object holds the information necessary to send a message to another object when an event occurs.
It's the pattern used to send messages in response to user-interface events, specifically from UIControl.
The Target-Action object has:
- Target - The object that will be notified
- Action - The method that will be used
- Event that will trigger the Target-Action
If the target is specified then the action message, it is sent to the object.
Unrecognized selector sent to instance
Wave you ever seen this error in which the app crashes?
What was the problem?
A: Xcode couldn't find the method being called 😰
Selectors are the names of methods used to execute code at runtime.
The name of the method that will be called and the object to send the message to are not known until runtime.
Selectors are not part of the default Swift runtime behavior so we include @objc
to our methods.
Creating a UIButton
let loginButton = UIButton(type: .roundedRect)
loginButton.setTitle("Login", for: .normal)
Adding the target
loginButton.addTarget(self, action: #selector(login), for: .touchUpInside)
Created a UIButton and called addTarget(_:action:for:)
with these parameters:
- target is
self
- action is
#selector(login)
- event is
.touchUpInside
When the event .touchUpInside
occurs, send a message to self
to execute login
Here's the method called
@objc func login(){
print("Button tapped")
}
Variation passing in the sender object
loginButton.addTarget(self, action: #selector(login(sender:)), for: .touchUpInside)
@objc func login(sender: UIButton){
print("Button tapped")
//Because you received the sender as a parameter, you can access its properties.
print(sender.titleLabel?.text)
}
Something to note is that the messages sent can't carry custom information. Which is why we can't send values in a UIButton's target action. They could send the sender that triggered the action as an argument, but that is as far as it goes.
Notifications can broadcast messages between relatively unrelated parts of your code. They use the Observer pattern to inform registered observers when a notification comes in, using a central dispatcher called Notification Center
.
The Notification Center deals with registering observers and delivering notifications.
Notifications can include a payload in form of their property called userInfo
, which is a dictionary.
The sender and the recipient don’t have to know each other so they are useful to send information between very distant modules.
Has 3 components
- An observer that listens for notifications
- A sender that sends notifications when something happens
- The notification center that keeps track of observers and notifications
This adds an entry to the Notification Center.
print("Added Observer")
NotificationCenter.default.addObserver(self,
selector: #selector(receivedNotification(_:)),
name: Notification.Name("receivedNotification"),
object: nil)
@objc func receivedNotification(_ notification:Notification) {
// Do something here
print("Received notification")
self.view.backgroundColor = UIColor.purple
}
Needs the name of the notification and the object that posts the notification.
print("Post Notification")
NotificationCenter.default.post(
name: Notification.Name("receivedNotification"),
object: self)
deinit {
print("Removed Observer")
NotificationCenter.default.removeObserver(self,
name: Notification.Name("receivedNotification"),
object: nil)
}
A deinitializer is called immediately before a class instance is deallocated. Swift automatically deallocates your instances when they are no longer needed, to free up resources.
Notifications syntax practice - Replit
Tips
- When creating a notification, use unique names that are also helpful.
- It's best if you declare a constant for the name.
- Use the name of the Notification for the selector method. This is less confusing.
- Do not overuse this communication pattern or you'll end up with too many dependencies
- Use Notifications when you need to communicate between 2 or more objects in the app that don't know about each other. Or when using one-to-many or many-to-many communications that are consistent.
-
Learn what the Pomodoro technique is: go to video
-
Then follow the instructions in this repo.
-
Submit your completed version to Gradescope.