Swift underwent drastic changes with the release of version 3 of the language. As you probably know, those changes weren't limited to the language and the standard library. Apple made many, many changes to the Foundation framework, for example.
Notification center was one of the APIs that received a facelift. The result is an API that's succinct and more intuitive. Despite these changes, there seems to be quite a bit of confusion surrounding notification names. Let me show you what the problem is and how to solve it.
What Is Notification.Name
To register a class instance as an observer for a notification, you invoke addObserver(_:selector:name:object:)
, a method of the NotificationCenter
class. Notice that you no longer need to use the NS
prefix. Many types of the Foundation framework have dropped the NS
prefix.
import UIKit
class ViewController: UIViewController {
// MARK: - Properties
@IBOutlet var textField: UITextField!
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
// Add Observer
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: Notification.Name.UITextFieldTextDidChange, object: textField)
}
// MARK: - Notification Handling
@objc private func textDidChange(_ notification: Notification) {
}
}
The third parameter of the addObserver(_:selector:name:object:)
method is what interests us most. The name
parameter is of type Notification.Name
. What is that about? Because the compiler knows what type to expect, we can omit Notification.Name
.
override func viewDidLoad() {
super.viewDidLoad()
// Add Observer
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: .UITextFieldTextDidChange, object: textField)
}
We can find out more about the Notification.Name
type by pressing Command and clicking UITextFieldTextDidChange
. This takes us to the public interface of the UITextField
class, which is defined in the UIKit framework. It appears that UITextFieldTextDidChange
is a static constant property of NSNotification.Name
, which is a synonym for Notification.Name
.
extension NSNotification.Name {
public static let UITextFieldTextDidBeginEditing: NSNotification.Name
public static let UITextFieldTextDidEndEditing: NSNotification.Name
public static let UITextFieldTextDidChange: NSNotification.Name
}
We're almost there. To understand what Notification.Name
is, we need to dive a bit deeper. Press Command and click Name
of Notification.Name
. This takes you to an extension for NSNotification
.
extension NSNotification {
public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
public init(_ rawValue: String)
public init(rawValue: String)
}
}
Name
appears to be a structure, a nested type of NSNotification
(Notification
). It conforms to four protocols, including RawRepresentable
. To create an instance of the Name
structure, we need to invoke one of two initializers, init(rawValue:)
or init(_:)
.
The documentation of Notification.Name
gives us another hint to better understand what Notification.Name
is and how to use it. The summary reads like this.
The type used for the name of a notification.
This makes sense. Right? And we already knew this by inspecting the addObserver(_:selector:name:object:)
method. But the documentation also tells us something else.
In Objective-C, NSNotification names are type aliased to the NSString class. In Swift, Notification names use the nested NSNotification.Name type.
That gives us the answer we were looking for. If you switch to Objective-C in the documentation, you can see the type definition of NSNotificationName
.
typedef NSString *NSNotificationName;
How to Use Notification.Name
What I like about Notification.Name
is that we no longer need to define string constants in one or multiple places in a project. The solution has become much easier, more elegant, and in line with Swift's API design.
This is an example I have used in a project of my own. The implementation is simple now that we understand what Notification.Name
is and how we can create an instance of the Name
structure.
import Foundation
extension Notification.Name {
static let DidResetStatistics = Notification.Name("DidResetStatistics")
}
We import the Foundation framework to have access to the Notification.Name
structure and create an extension for Notification.Name
. In this extension, we define a static constant property DidResetStatistics
. The value we assign to the static constant property is an instance of the Notification.Name
structure. The raw value of the instance is a string, "DidResetStatistics"
.
When we explored the public interface of the Name
structure, we noticed that Name
has two initializers. We could write the above example also like this.
import Foundation
extension Notification.Name {
static let DidResetStatistics = Notification.Name(rawValue: "DidResetStatistics")
}
If DidResetStatistics
needs to be publicly available, then you need to prefix it with the public
keyword. It's currently only accessible from within the module it's defined in.
Using DidResetStatistics
is identical to notification names defined by the Foundation framework.
import UIKit
class ViewController: UIViewController {
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
// Add Observer
NotificationCenter.default.addObserver(self, selector: #selector(didResetStatistics(_:)), name: Notification.Name.DidResetStatistics, object: nil)
}
// MARK: - Notification Handling
@objc private func didResetStatistics(_ notification: Notification) {
}
}
That's another mystery solved. I hope this tutorial has answered any questions you have about notification names and Swift.