Swift NSNotification Observers not working - swift

I have 2 view controllers, one with a switch that when toggled should post the following notification. In the other view controller I have Observers which should trigger the following function which just toggles a boolean. I am not able to get the observers to work and make that function call, am I doing something wrong here? I have another Notification (Doesn't trigger with user input) that is being sent in the opposite direction which works fine.
#IBAction func switchAction(_ sender: Any) {
if switchUI.isOn {
print("Collecting Data ")
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Collect"), object: self)
}
else
{
print("Not Collecting Data")
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Do Not Collect"), object: self)
}
}
func collectDataObserver () {
//Add an Observer
NotificationCenter.default.addObserver(self, selector: #selector(CentralViewController.toggleData(notification:)), name: Notification.Name(rawValue: "Collect"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(CentralViewController.toggleData(notification:)), name: Notification.Name(rawValue: "Do Not Collect"), object: nil)
}
#objc func toggleData(notification: NSNotification) {
let isCollectData = notification.name.rawValue == "Collect"
if(isCollectData){
IsCollectingData = true
}
else
{
IsCollectingData = false
}
}

You need to call collectDataObserver() in viewDidLoad() of CentralViewController, i.e.
override func viewDidLoad() {
super.viewDidLoad()
collectDataObserver()
}

Related

How to come back to previous scene and reload data?

I have two Scenes on Storyboard : One to show a list of items with plus button on the Bar to go to another Scene that has form to add a new item. I'm saving on Local Storage, in the saving function I want to come back to the previous page with the new data.
This is the saving function:
#IBAction func AddTrack(_ sender: Any) {
let item = TrackItem(context: PersistenceService.context)
item.kms = Int32(kmsField!.text!)!
item.liters = Float(litersField!.text!)!
item.date = textFieldPicker!.text!
PersistenceService.saveContext()
navigationController?.popViewController(animated: true)
self.TrackList.append(item)
self.tableView?.reloadData()
}
Knowing that I'm using this function in viewDidLoad() and viewDidAppear()
func GetData(){
let fetchRequest: NSFetchRequest<TrackItem> = NSFetchRequest<TrackItem>(entityName: "TrackItem")
do{
let TrackList = try PersistenceService.context.fetch(fetchRequest)
self.TrackList = TrackList
self.tableView?.reloadData()
}catch{
}
}
You can use NotificationCenter or Delegation (Depends on what you are trying to achieve)
Example NotificationCenter:
VC1, takes a photo:
buttonTapped / function () {
// Upload a Photo()
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.dismiss(animated: true, completion: nil)
}
}
VC2, shows all photos:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(loadList), name: NSNotification.Name(rawValue: "load"), object: nil)
}
#objc func loadList(notification: NSNotification) {
//get data, reload data, etc
}

Removing NSNotificationObserver From Different ViewController

I have an observer that I register in one class as so:
class ViewControllerA: UIViewController {
//create shared class reference
var sharedClassReference_A = SharedClass()
//initialize Notification Observer and store observer reference in sharedClass
override func viewDidLoad() {
super.viewDidLoad()
var observerHandler: Any? = nil
observerHandler = NotificationCenter.default.addObserver(self, selector: #selector(ViewControllerA.appDidTerminate(_:)), name: .UIApplicationWillTerminate, object: nil)
self.sharedClassReference_A.sharedHandler = observerHandler
}
//perform some action when a notification is received
#objc func appDidTerminate(_ notification: NSNotification) {
//perform some action
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueA_X" {
let destinationController = segue.destination as! ViewControllerX
destinationController.sharedClassReference_X = self.sharedClassReference_A
}
}
}
I store a reference to the observer in a shared class:
class SharedClass {
var sharedHandler: Any? = nil
}
I attempt to remove the observer once I reach a different view controller as so:
class ViewControllerX: UIViewController {
var sharedClassReference_X = SharedClass()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//Attempt to remove observer registered in ViewControllerA
if let observerHandler = self.sharedClassReference_X.sharedHandler {
NotificationCenter.default.removeObserver(observerHandler)
}
}
}
I know that removing the observer using this approach is failing, because the observer is getting called after ViewControllerX is deallocated.
My question is: How can I successfully initialize an observer in one class (ViewControllerA) and be able to remove it later in a different class (ViewControllerX)?
I think it's better to follow the general guidelines of setting the observers inside viewDidLoad/viewWillAppear and removing them inside deinit / viewDidDisappear respectively according to your case , as this
NotificationCenter.default.addObserver(self, selector: #selector(ViewControllerA.appDidTerminate(_:)), name: .UIApplicationWillTerminate, object: nil)
returns void
//
class ViewControllerX: UIViewController {
var aRef:ViewControllerA!
}
in prepareForSegue
destinationController.aRef = self
Then use
NotificationCenter.default.removeObserver(aRef, name: NSNotification.Name.UIApplicationWillTerminate, object: nil)

Swift: Network-Request at AppStart in AppDelegate - CompletionHandler in ViewController?

my app is based on a TabBarController with 4 ViewControllers. All 4 of them are dependent of the same data. This is why I would like to load the data at the App start in the AppDelegate.
However, how does the ViewController know, that the request is completed? For example, if there is an error (e.g. no internet connection), how do I pass this error to any of these 4 ViewController to present an alert?
Use NotificationCenter to implement (Swift 3 code):
extension Notification.Name {
static var RequestCompleted = Notification.Name(rawValue: "MyRequestIsCompleted")
static var RequestError = Notification.Name(rawValue: "MyRequestError")
}
class DataRequest {
func request() {
// if there is error:
let error = NSError(domain: "Error Sample", code: 0, userInfo: nil)
NotificationCenter.default.post(name: .RequestError, object: error)
// if the request is completed
let data = "your data is here"
NotificationCenter.default.post(name: .RequestCompleted, object: data)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(requestCompleted(_:)), name: .RequestCompleted, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(requestError(_:)), name: .RequestError, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
func requestCompleted(_ notification: Notification) {
if let obj = notification.object {
print(obj)
}
}
func requestError(_ notification: Notification) {
if let obj = notification.object {
print(obj)
}
}
}

NotificationCenter not fired with network change

I want to detect network changes. For example when switching from wifi to 4G or back. But my code doesn't detect these changes. I get only some output when the app has started.
I have this code:
import ReachabilitySwift
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let reachability = Reachability()!
NotificationCenter.default.addObserver(self, selector: #selector(self.reachabilityChanged),name: ReachabilityChangedNotification,object: reachability)
do{
try reachability.startNotifier()
}catch{
print("could not start reachability notifier")
}
}
func reachabilityChanged(note: NSNotification) {
let reachability = note.object as! Reachability
if reachability.isReachable {
if reachability.isReachableViaWiFi {
print("Reachable via WiFi")
} else {
print("Reachable via Cellular")
}
} else {
print("Network not reachable")
}
}
Did I have implemented something wrong?
The action method takes a parameter so the selector must be
#selector(reachabilityChanged(_:)) // self is not needed
And the method itself is supposed to be
func reachabilityChanged(_ notification: Notification) {
Selector is wrong
change this
NotificationCenter.default.addObserver(self, selector: #selector(self.reachabilityChanged),name: ReachabilityChangedNotification,object: reachability)
to
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(_:),name: ReachabilityChangedNotification,object: reachability)
I hope this will work.
and also
func reachabilityChanged(note: NSNotification) {
to
func reachabilityChanged(note: NSNotification) {
name is wrong. Change
name: ReachabilityChangedNotification
to
name: NSNotification.Name.reachabilityChanged
or to
name: .reachabilityChanged
It will work.

How is that possible to post notification after 1 second delay or in a dispatch asyn?

i just want to call a method after loading my view. i have set notification to one view and postnotification to other view you will get more idea from my code.. here is my code..
in a one.swift file
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "InsertNote:", name: "insert", object: nil)
}
func InsertNote(notification:NSNotification)
{
println("blablalbla")
}
in a second.swift file
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("getData"), userInfo: nil, repeats: false)
}
func getData()
{
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
i have try also in a dispatch_asynch but still it is not work.
Try this
let delayInSeconds: Double = 1
override func viewDidLoad() {
super.viewDidLoad()
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue()){
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
}
The only reason I can come up with that this doesn't work, is that your first ViewController A is already deallocated when you send the notification with the second ViewController B. So, you are sending the notification with B correctly, but A isn't around to receive it.
You can test this by adding an observer in B as well.
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("getData"), userInfo: nil, repeats: false)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "insertNote:", name: "insert", object: nil)
}
func getData() {
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
func insertNote:(note: NSNotification) {
print("Banana")
}
// Should print 'Banana' after one second.