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.
Codemagic makes Apple M2 machines available, even on the free tier!Codemagic is the first CI/CD to make Apple M2 machines available to everyone (including the free tier!). This is a free upgrade from M1 machines with no price change. Get started today.
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)
}
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.