How can I send data from FirstTableView to SecondTableView using Notification Center without segue - ios

I have a problem when I try to pass an array from a UITableView to another one using NotificationCenter Design Pattern (because I don't have a segue between this 2 UIViewControllers). I don't know what I'm doing wrong but I don't receive any data in my second view controller.
My functions looks like this:
* First VC - The Sender Controller (From where I send data) *
class ProductsViewController: UIViewController{
var selectedProductsArray = [Product]()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Implement Notification Design Pattern to send data
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "productsToLoad"), object: selectedProductsArray)
print(selectedProductsArray) // Here I have some data in this array (Photo here: https://ibb.co/k8hoEy)
}
* Second ViewController - The Receiver Controller (Where I will receive the data) *
class CartViewController: UIViewController {
var productsInCartArray = [Product]()
// We retrieve data from "selectedProductsArray" and we append all the products into "productsInCartArray"
#objc func notificationRecevied(notification: Notification) {
productsInCartArray = notification.object as! [Product]
print(productsInCartArray)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Add observer to watch when something was changed in "selectedProductsArray"
NotificationCenter.default.addObserver(self, selector: #selector(notificationRecevied(notification:)), name: NSNotification.Name(rawValue: "productsToLoad"), object: nil)
print(productsInCartArray) // Output: []
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// We remove the observer from the memory
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "productsToLoad"), object: nil)
}
}
Screenshot:
Thank you for your time if you are reading this !

You need to remove this from viewWillDisappear of CartViewController
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "productsToLoad"), object: nil)
as when you post in products the cards are not shown so there is no listener inside it , beside that CartViewController should be opened at least once before you post any data from ProductsViewController
//
you can completely remove the NotificationCenter work , and do this in CartViewController
let products = ((self.tabBarController?.viewControllers![0] as! UINavigationController).topViewController as! ProductsViewController).selectedProductsArray
Note : Don't worry for ! unwrapping it won't crash

Related

NSNotification not observing or posting data

I am trying to learn how to use NSNotification for a project I am working on, and since I have never used it before, I am first trying to learn how to use it first, however; every time I try to follow a youtube tutorial or a tutorial found online my code doesn't seem to be working. Also, when trying to debug the issue, it is showing the observer part of the code isn't going inside the #obj c function. Below is my code showing how it is being used to post and observe a notification.
extension Notification.Name {
static let notifyId = Notification.Name("NotifyTest")
}
ViewController.swift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
var test: ObserverObj = ObserverObj(observerLblText: "Observing")
#IBAction func notifyObserver(_ sender: Any) {
let vc = storyboard?.instantiateViewController(identifier: "ObserverVC")
vc?.modalPresentationStyle = .fullScreen
guard let vcL = vc else {
return
}
NotificationCenter.default.post(name: .notifyId, object: nil)
self.navigationController?.pushViewController(vcL, animated: true)
}
}
NotificationTestViewController.swift
import UIKit
class NotificationTestViewController: UIViewController {
#IBOutlet weak var observerLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(observingFunc(notification:)), name: .notifyId, object: nil)
// Do any additional setup after loading the view.
}
#objc func observingFunc(notification: Notification) {
observerLbl.text = "notifying"//text.test.observerLblText
}
Can someone help me and let me know where I am going wrong as I've been trying for 2 days.
The notification is sent before the observer is added, that means viewDidLoad in the destination controller is executed after the post line in the source view controller.
Possible solutions are :
Override init(coder in the destination controller and add the observer there.
required init?(coder: NSCoder) {
super.init(coder: coder)
NotificationCenter.default.addObserver(self, selector: #selector(observingFunc), name: .notifyId, object: nil)
}
If init(coder is not called override init()
Add the observer before posting the notification (this solution is only for education purpose)
#IBAction func notifyObserver(_ sender: Any) {
guard let vcL = storyboard?.instantiateViewController(identifier: "ObserverVC") as? NotificationTestViewController else { return }
vcL.modalPresentationStyle = .fullScreen
NotificationCenter.default.addObserver(vcL, selector: #selector(NotificationTestViewController.observingFunc), name: .notifyId, object: nil)
self.navigationController?.pushViewController(vcL, animated: true)
NotificationCenter.default.post(name: .notifyId, object: nil)
}
However in practice you are strongly discouraged from sending a notification to a destination you have the reference to.
The reason is that when you send out a notification, your NotificationTestViewController has not yet called the viewdidload method
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
var test: ObserverObj = ObserverObj(observerLblText: "Observing")
#IBAction func notifyObserver(_ sender: Any) {
let vc = storyboard?.instantiateViewController(identifier: "ObserverVC")
vc?.modalPresentationStyle = .fullScreen
guard let vcL = vc else {
return
}
self.navigationController?.pushViewController(vcL, animated: true)
NotificationCenter.default.post(name: .notifyId, object: nil)
}
}

Notification center values is not getting update for first, second time

I have an pagMenuController on did select method I am passing the index. And in another vc I am getting that index values and I needs to show the view respective with incoming index value.
Code in my FirstVC:
func pageMenuController(_ pageMenuController: PageMenuController, didSelectMenuItem index: Int, direction: PageMenuNavigationDirection) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateView"), object: nil, userInfo: ["indexValue": index])
}
Code in my SecondVC:
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(ContViewController.incomingNotification(_:)), name: NSNotification.Name(rawValue: "updateView"), object: nil)
}
#objc func incomingNotification(_ notification: Notification) {
if let indexVal = notification.userInfo?["indexValue"] as? Int {
print(indexVal)
}
}
When ever my screen appears, I needs to get the index values. But when I click on my menu items first, second time index values in not showing. 3rd, and above time its coming. what I have missed.
Any solutions?
Thank you
move your observer to viewdidload
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ContViewController.incomingNotification(_:)), name: NSNotification.Name(rawValue: "updateView"), object: nil)
}

NotificationCenter Selector method is not calling in Swift

I am trying to send first viewcontroller textfield data into second viewcontroller label.
In first controller, Inside send action button adding notification post method
#IBAction func sendtBtn(_ sender: Any) {
let secVc = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
self.navigationController?.pushViewController(secVc, animated: true)
NotificationCenter.default.post(name: Notification.Name( "notificationName"), object: nil, userInfo: ["text": firstTextField.text])
}
And second viewcontroller addobserver method inside view didload
NotificationCenter.default.addObserver(self, selector: #selector(self.showMsg(_:)), name: Notification.Name( "notificationName"), object: nil)
Selector function :
func showMsg(_ notification: Notification){
print("helloooo")
var vcData = notification.userInfo?["text"]
firstLabel.text = vcData as! String
}
When keeping break points for add observer it observers but it does not calling showMsg function.
Please help me in this code.
You do have the reference to the second view controller. There is no reason at all to use Notification. Don't use notifications if there is only one receiver and the objects are related.
The code doesn't work because the view is not loaded yet when the notification is sent.
Forget the notification. Instead create a property in the second view controller, assign the value in sendtBtn and show the message in viewDidLoad
#IBAction func sendtBtn(_ sender: Any) {
let secVc = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
secVc.message = firstTextField.text
self.navigationController?.pushViewController(secVc, animated: true)
}
Second view controller
var message = ""
func viewDidLoad() {
super.viewDidLoad()
firstLabel.text = message
}

tableview reloadData crashes unexpected found nil while unwrapping optional value

I'm having some trouble understanding why the reloadData() line crashes with the following error 'unexpected found nil while unwrapping optional value'(also, why is it an optional?). Basically, when user taps a button it fires an API request, second VC(recipesVC) is shown and when data is retrieved from the API, in receivedRecipes method (previous VC) I want to reloadData from recipesVC (currentVC)
As my data is correctly passed to recipesVC, I don't see why reloadData() won't work on the same VC. Could you please give me a little help with this?
thank you.
override func viewDidLoad() {
super.viewDidLoad()
let recipes = Notification.Name(rawValue: "gotRecipes")
NotificationCenter.default.addObserver(self, selector: #selector(receivedRecipes),name: recipes, object: nil)
}
#objc func receivedRecipes() {
let recipesVC = storyboard?.instantiateViewController(withIdentifier: "recipesList") as! RecipesViewController
recipesVC.recipesList = request.recipeDetails
recipesVC.recipes.reloadData()
}
#objc func receivedRecipes() {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "recipes"), object: nil, userInfo: ["data":request.recipeDetails])
}
In your currentVC
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(receivedRecipes(notification:)),name: NSNotification.Name(rawValue: "recipes"), object: nil)
}
#objc func receivedRecipes(notification: Notification) {
recipesList = notification.userInfo
recipes.reloadData()
}
Add validation to your data:
func receivedRecipes() {
guard let details = request.recipeDetails else { return }
let recipesVC = storyboard?.instantiateViewController(withIdentifier: "recipesList") as! RecipesViewController
recipesVC.recipesList = details
recipesVC.recipes.reloadData()
}
The next tip, you can update your notification and get recipes from the notification.object, but previously you should paste them into:
let parsedRecipes = Recipes()
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "gotRecipes"), object: parsedRecipes, userInfo: nil)
Also show recipesVC after it initialization with:
self.present(...) or self.navigationController?.show(...)

How to unregister an NSNotification in Swift iOS

I have two controllers
class CtrlA: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(CtrlB.self, selector: #selector(CtrlB.badge(notification:)), name: NSNotification.Name(rawValue: "badge"), object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(CtrlB.self, name: NSNotification.Name(rawValue: "badge"), object: nil)
}
}
class CtrlB: UIViewController {
static func badge (notification: NSNotification) {
// blah blah
}
}
Whats the correct way to unregister the notification listener above?
I'm not certain this is correct:
NotificationCenter.default.removeObserver(CtrlB.self, name: NSNotification.Name(rawValue: "badge"), object: nil)
I don't think I can use self either, since it was registered on CtrlB.self
So the best way to implement the notification in your project is create one class called NotificationManager inside that declare one dictionary in which you can always update the observers
class NotificationManager {
var observers = [String: AnyObject]()
}
Create addObserver method, post notification method and remove
observer method inside the same class.
func postNotification(_ name: String, userInfo: [AnyHashable: Any]? = nil) {
NotificationCenter.default.post(name: name, object: nil, userInfo: userInfo)
}
func addObserver(_ name: String, block: #escaping (Notification) -> Void) {
//if observer is already in place for this name, remove it
removeObserverForName(name)
let observer = NotificationCenter.default.addObserver(forName: name), object: nil, queue: OperationQueue.main, using: block)
self.observers[name] = observer
}
func removeObserver(_ name: name) {
guard let observer = self.observers[name] else { return }
NotificationCenter.default.removeObserver(observer)
self.observers.removeValue(forKey: name)
}
//Removes all observers
func removeAllObservers() {
for observer in self.observers.values {
NotificationCenter.default.removeObserver(observer)
}self.observers = [:]
}
So access the above method in any of your class wherever its required and it will take care of everything. This will also prevent crash in your code. If try to remove the same observer more than one time.
I am not sure why you are registering/unregistering to notifications with a class and not an instance. 'CtrlB.self' - will not give you an instance of the CtrlB class, in fact it will return a class itself.
Instead you should use something like this:
class CtrlA {
let ctrlBInstance = CtrlB()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(ctrlBInstance, selector: #selector(CtrlB.badge(notification:)), name: NSNotification.Name(rawValue: "badge"), object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(ctrlBInstance, name: NSNotification.Name(rawValue: "badge"), object: nil)
}
}
And your ClassB should look like this in this case:
class CtrlB {
func badge (notification: NSNotification) {
// blah blah
}
}
You need to get the instance of the observer,which you haven't declared...
for instance you need to set class variable secondA...
class CtrlA: UIViewController {
var secondController: CtrlB?
override func viewDidLoad()
{
super.viewDidLoad()
if let unwrappedController = storyboard.instantiateViewController(withIdentifier: "someViewController") as? CtrlB
{
secondController = unwrappedController
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let secondController = secondController
{
NotificationCenter.default.addObserver(CtrlB.self, selector: #selector(CtrlB.badge(notification:)), name: NSNotification.Name(rawValue: "badge"), object: nil)
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let secondController = secondController
{
NotificationCenter.default.removeObserver(CtrlB.self, name: NSNotification.Name(rawValue: "badge"), object: nil)
}
}
//Also don't forget to remove listening on deinit
deinit
{
if let secondController = secondController
{
NotificationCenter.default.removeObserver(secondController, name: NSNotification.Name(rawValue: "badge"), object: nil)
}
}
}
class CtrlB: UIViewController {
//Here you go with notification...
static func badge (notification: NSNotification) {
// blah blah
}
}

Resources