Once-a-year Black Friday deals are coming. Read more.
Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Typed notifications using custom extensions

The Swift API contains a lot of notifications sent out by the system like NSManagedObjectContextObjectsDidChange in Core Data or the AppDelegate events like UIApplicationDidBecomeActive. Some of these notifications contain rich data in their user info dictionary. Reading the user info data using typed notifications can clean up your code, especially if you use these notifications on multiple places throughout your code.

Extension compared to custom classes are better discoverable by the implementors and therefore more likely to integrate.

Creating the extension

It’s often a good idea to write an extension on top of the Swift API to write custom solutions for readability. Extensions compared to custom classes are better discoverable by the implementors and therefore more likely to integrate.

Our extension is on top of NotificationCenter and makes use of a custom protocol called NotificationRepresentable.

extension NotificationCenter {
    /// Adds an observer using the given representable type to parse the notification to typed data.
    func addObserver<T: NotificationRepresentable>(for representableType: T.Type, object obj: Any?, queue: OperationQueue?, using block: @escaping (T) -> Swift.Void) -> NSObjectProtocol {
        return addObserver(forName: T.name, object: obj, queue: queue) { (notification) in

            // Parse the user info to the representable type.
            let notificationRepresenter = T(notification: notification)
            block(notificationRepresenter)
        }
    }
}

The protocol used in this extension contains a required initialiser which makes use of the notification and the related notification name which is used to observe for.

/// A representable for notifications containing rich data in their user info.
protocol NotificationRepresentable {
    /// The related notification name for which this representable is able to parse data.
    static var name: Notification.Name { get }

    /// Creates a new representer using the given notification data.
    ///
    /// - Parameter notification: The posted `Notification` which is used to parse into rich typed data.
    init(notification: Notification)
}

Stay updated with the best of Swift

Join over 20,005 Swift developers in SwiftLee Weekly for exclusive tips and updates. Don’t miss out – subscribe now:

You can always unsubscribe, no hard feelings.

Writing a notification representer

A good use case for creating a typed notifications is the NSManagedObjectContextObjectsDidChange notification. A representer includes the inserted, deleted, updated and refresh objects.

final class NSManagedObjectContextChanges: NotificationRepresentable {
    static let name = Notification.Name.NSManagedObjectContextObjectsDidChange

    let managedObjectContext: NSManagedObjectContext
    let insertedObjects: Set<NSManagedObject>
    let updatedObjects: Set<NSManagedObject>
    let refreshedObjects: Set<NSManagedObject>
    let deletedObjects: Set<NSManagedObject>

    init(notification: Notification) {
        managedObjectContext = notification.object as! NSManagedObjectContext

        insertedObjects = notification.userInfo?[NSInsertedObjectsKey] as? Set<NSManagedObject> ?? []
        updatedObjects = notification.userInfo?[NSUpdatedObjectsKey] as? Set<NSManagedObject> ?? []
        refreshedObjects = notification.userInfo?[NSRefreshedObjectsKey] as? Set<NSManagedObject> ?? []
        deletedObjects = notification.userInfo?[NSDeletedObjectsKey] as? Set<NSManagedObject> ?? []
    }
}

This representer will parse the user info into a class containing all changes in typed sets and makes the code on implementation level a lot more readable.

Using the extension

A new method is available on top of the NotificationCenter class which makes it really easy to use. For our example, we could monitor the amount of inserted items using a print statement.

NotificationCenter.default.addObserver(for: NSManagedObjectContextChanges.self, object: managedObjectContext, queue: nil) { (changes) in
    print("The change contained \(changes.insertedObjects.count) inserts")
}

The use case for writing these extensions is especially useful when using notifications in multiple places throughout your project. However, this is just an example as writing extension on top of other Swift APIs can also improve your code a lot. The great benefit of extensions is that they are easily discoverable on top of default Swift APIs.

 
Antoine van der Lee

Written by

Antoine van der Lee

iOS Developer since 2010, former Staff iOS Engineer at WeTransfer and currently full-time Indie Developer & Founder at SwiftLee. Writing a new blog post every week related to Swift, iOS and Xcode. Regular speaker and workshop host.

Are you ready to

Turn your side projects into independence?

Learn my proven steps to transform your passion into profit.