Drew Antonich Drew Antonich - 6 months ago 24
Swift Question

NSNotificationCenter Notification Not Being Received When Posted in a Closure

What I am trying to accomplish is posting a notification through

NSNotificationCenter
's default center. This is being done within a closure block after making a network call using
Alamofire
. The problem I am having is that a class that should be responding to a posted notification isn't receiving such notification.

My
ViewController
simply creates a
First
object that get's things moving:

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let first = First()
}
}


My
First
class creates and instance of a
Second
class and adds itself as an observer to my
NSNotificationCenter
. This is the class that can't seem to get the notification when the notification is posted.

class First : NSObject {
let second = Second()
override init(){
super.init()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(First.gotDownloadNotification(_:)), name: "test", object: nil)
second.sendRequest()
}

// NOT REACHING THIS CODE
func gotDownloadNotification(notification: NSNotification){
print("Successfully received download notification from Second")
}

}


My
Second
class is what makes the network call through my
NetworkService
class and posts a notification in a closure once the request is successful and complete.

class Second : NSObject {

func sendRequest(){
let networkService = NetworkService()
networkService.downloadFile() { statusCode in
if let statusCode = statusCode {
print("Successfully got a status code")
// Post notification
NSNotificationCenter.defaultCenter().postNotificationName("test", object: nil)
}
}
}
}


Finally, my
NetworkService
class is what makes a network call using
Alamofire
and returns the status code from the response through a closure.

class NetworkService : NSObject {

func downloadFile(completionHandler: (Int?) -> ()){
Alamofire.download(.GET, "https://www.google.com") { temporaryURL, response in
let fileManager = NSFileManager.defaultManager()
let directoryURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let pathComponent = response.suggestedFilename

return directoryURL.URLByAppendingPathComponent(pathComponent!)
}
.response { (request, response, _, error) in
if let error = error {
print("File download failed with error: \(error.localizedDescription)")
completionHandler(nil)
} else if let response = response{
print("File downloaded successfully")
// Pass status code through completionHandler to Second
completionHandler(response.statusCode)
}
}

}
}


The output after execution is:


File downloaded successfully

Successfully got a status code


From this output I know the download was successful and
Second
got the status code from the closure and posted a notification right after.

I believe that I have tried resolving most other suggestions on Stack Overflow related to not receiving notifications such as objects not being instantiated before notification is posted or syntax of either adding an observer or posting a notification.

Does anyone have any idea why the posted notification is not being received in the
First
class?

Answer

Since there is a direct relationship between First and Second the protocol/delegate pattern is the better way to notify. Even better with this pattern and you don't have to take care of unregistering the observer. NSNotificationCenter is supposed to be used only if there is no relationship between sender and receiver.

And basically the thread doesn't matter either.

protocol SecondDelegate {
  func gotDownloadNotification()
}

class Second : NSObject {

  var delegate : SecondDelegate?

  init(delegate : SecondDelegate?) {
    self.delegate = delegate
  }

  func sendRequest(){
    let networkService = NetworkService()
    networkService.downloadFile() { statusCode in
      if let statusCode = statusCode {
        print("Successfully got a status code")
        // Post notification
       self.delegate?.gotDownloadNotification()
      }
    }
  }
}

class First : NSObject, SecondDelegate {
  let second : Second

  override init(){
    super.init()
    second = Second(delegate:self)
    second.sendRequest()
  }

  func gotDownloadNotification(){
    print("Successfully received download notification from Second")
  }
}
Comments