Blog

Swift Tip: Notifications

In Episode 107 of our Swift Talk series on refactoring large view controllers, we move code from a view controller to a child view controller. The resulting child view controller deals with keyboard notifications.

In this week’s tip, we’ll show how to factor out that code as well.

In multiple places throughout the code base there are notification listeners for keyboard events. In each of the callback methods the properties are manually extracted, for example:

@objc func keyboardChanged(notification: NSNotification) {
  guard let userInfo = notification.userInfo,
        let frameValue = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
  let keyboardScreenFrame = frameValue.cgRectValue
  // ...
}

Instead of repeating this kind of code over and over, we can make life easier by writing a single struct that contains all the properties:

struct KeyboardInfo {
    var animationCurve: UIView.AnimationCurve
    var animationDuration: Double
    var isLocal: Bool
    var frameBegin: CGRect
    var frameEnd: CGRect
}

We can now write an initializer that constructs the struct from a notification:

extension KeyboardInfo {
    init?(_ notification: Notification) {
        guard notification.name == UIResponder.keyboardWillShowNotification || notification.name == UIResponder.keyboardWillChangeFrameNotification else { return nil }
        let u = notification.userInfo!

        animationCurve = UIView.AnimationCurve(rawValue: u[UIWindow.keyboardAnimationCurveUserInfoKey] as! Int)!
        animationDuration = u[UIWindow.keyboardAnimationDurationUserInfoKey] as! Double
        isLocal = u[UIWindow.keyboardIsLocalUserInfoKey] as! Bool
        frameBegin = u[UIWindow.keyboardFrameBeginUserInfoKey] as! CGRect
        frameEnd = u[UIWindow.keyboardFrameEndUserInfoKey] as! CGRect
    }
}

In our keyboardChanged(notification:) method, we can construct that struct, rather than parsing the user info dictionary manually:

@objc func keyboardChanged(notification: NSNotification) {
  guard let payload = KeyboardInfo(notification) else { return }
  let keyboardScreenFrame = payload.frameEnd
  // ...
}

Our code is a little bit more concise, and it’s also a little safer (the dictionary reading is now in a single place, rather than spread across the code base). When we want to access other properties, we can now type payload. and auto complete will suggest the different names.

In Swift Talk Episode 27 (a public episode), we take this approach a bit further, and we also add a way to create type-safe observers.

If you’d like to follow the series, the first episode is public, and you can become a subscriber to see how we progress. 👍

Stay up-to-date with our newsletter or follow us on Twitter.

Back to the Blog

recent posts