Search code examples
iosswiftnsnotificationcenternsnotification

How to pass in a nil notification variable in Swift


I am relatively new to Swift and so my head often returns to Objective-C solutions when coding.

I am coding an audio cell for a podcast player. This has a custom UIView slider inside and an audio manager singleton. The slider communicates with its parent UITableViewCell when it is manually changed by sending a notification, the cell then communicates with the audio manager to play/pause the audio.

I am currently writing the code to make the audio react to a manual change of the slider, for this I am sending the percent drag on the slider to the Cell so it can play the appropriate audio.

// Notification sent containing information - notification sent from SlideView
NotificationCenter.default.post(name: Notification.Name("SlidebarManuallyMovedNotification"), object: nil, userInfo: ["percent": Double(self.getCurrentPercent())])

// Notification observed in UITableViewCell
NotificationCenter.default.addObserver(self, selector: #selector(self.refreshCell(notification:)), name: Notification.Name("SlidebarManuallyMovedNotification"), object: nil)

The sending and receiving of notifications is working fine:

// Some info needs to be refreshed depending on the cell state
func refreshCell(notification: NSNotification) {

// If we have user information it means we have manually changed the position so want to refresh the information
    if (notification.userInfo != nil) {

... // I then run code depending on whether we have user info or not

As you can see I receive the notification and then move to refresh the cell.

The issue now comes from trying to call the refreshCell manually from inside my UITableViewCell:

// 1
self.refreshCell(notification: nil)
// 2
self.refreshCell(notification: NSNotification())
// 3
let notify: NSNotification = NSNotification()
self.refreshCell(notification: notify)

I tried the first method as in Objective-C it was often easy to load a different view with or without information initWithCellInfo: (NSDictionary *) userInfo. In this scenario I want to do the same, call the function without any userInfo so that I can refresh my cell slightly differently.

I then tried adding an empty notification so that it would be passed in and would return nil userInfo. When I do this I receive the following warning:

[NSConcreteNotification init]: should never be used'

This points me towards this answer but I don't want to create an actual notification, just point towards a dummy on to call the function.

Other thoughts: I have thought of creating a two functions:

func refreshCellInfo(notification: NSNotification) { 

    let userInfo = "foo"
    self.refreshCellInfo(userInfo)
}

func refreshCellInfo(info: String) { 

I could then just call refreshCellInfo("") and add an if statement checking the string instead of the notification.

This just doesn't seem a very elegant way of doing it, adding a completely unnecessary function and fudging the if statement to get round the issue instead of understanding it.

If anyone can explain what is happening here and whether there is a good solution for this I would be very appreciative, I feel that this really highlights a gap in my understanding and although I can probably patch the issue I really want to learn from it to move forward as a developer.


Solution

  • You need to refactor slightly. Separate the function that receives the notification from the function that refreshes the cell and then call the second from the first.

    You could say something like:

    func receiveDragNotification(notification: NSNotification) {
         self.refreshCellInfo(notification)
    }
    
    func refreshCellInfo(_ notification: Notification?) {
         //  process update
    }
    

    Now, since the notification parameter is optional in refreshCellInfo, you can pass nil if you want to.

    But really, you should further separate the functionality, so that you have one function that is responsible for receiving the notification and updating the data model and a second that is responsible for updating the cell based on the data model (Note that you haven't provided all the details so I am using a fictitious self.position as a data model)

     func receiveDragNotification(notification: NSNotification) {
         // update data model based on notification 
             self.position = someDataFromNotification
         //  Now refresh the cell based on this new data
         self.refreshCellInfo()
    }
    
    func refreshCellInfo() {
         // use data model self.position to update cell
    }
    

    Now you can call refreshCellInfo any time you need without needing to pass a parameter.