How to insert data to TableView in another view controller Swift 4? - ios

I have ViewControllerA and ViewControllerB. I make a network call in ViewControllerB,when it success,I want to insert a data to the TableView in ViewControllerA,so the data can appear as 1st item in the TableView.
Here is what I tried:
ViewControllerB
var myItem = [Item]() //here is the array in ViewControllerA
Alamofire.request(MyURL!, method: .post, parameters: params, encoding: URLEncoding.httpBody, headers: headers).responseJSON{
response in
switch response.result{
case .success(let result):
let json = JSON(result)
if let myJson = json.dictionary,let myItem = Item.init(dict: myJson){
self.myItem.insert(newPost, at: 0)
NotificationCenter.default.post(name: .reload, object: nil) //here I call notification center after insert to the array
self.dismiss(animated: true, completion: nil)
self.tabBarController?.selectedIndex = 0 //back to ViewControllerA
}
case .failure(let error):
print("error = \(error)")
}
}
In ViewControllerA (which contain the tableView)
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(updateTableView), name: .reload, object: nil)
}
#objc func updateTableView(_ notification: Notification){
print("here get called")
self.tableView.reloadData()
}
I create an extension for the NotificationCenter
extension Notification.Name {
static let reload = Notification.Name("reloadTableView")
}
After done all this,the item that I insert to the array of ViewControllerB didnt appear in the 1st place of TableView in ViewControllerB.
I make a print in updateTableView() function which will call when received response from NotificationCenter in ViewControllerA,it get called,but the data is not appear.
I cant use segue,cause the both ViewController are 2 of the tab in TabbarController.
So in this case,how can I insert the data from ViewControllerB to TableView of ViewControllerA?

The problem is that you create a new array by this line
var myItem = [Item]()
But the viewControllerA use the different array. Try to send a new array in notification
NotificationCenter.default.post(name: .reload, object: self.myItem)
Then in viewControllerA set the new array.
#objc func updateTableView(_ notification: Notification){
var newItems = notification.object as! [Item]
arrayOfControllerA.append(contentsOf: newItems)
self.tableView.reloadData()
}

Related

How to send data back to previous screen IOS/Swift [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 2 years ago.
I am creating a library in IOS/swift that:
takes a user to a scene --> performs a task --> return to the initial scene that called the first while passing a payload back to the user
I have figured out how to take users back to the previous scene that called it, but my issue is how to send a payload back with it using thee code snippet below:
func switchToPreviousPage(){
self.dismiss(animated: true, completion: nil)
}
How do I achieve this?
In your scenario you can use either :
Delegation Pattern
Notification/Observer
Lets discuss each one :
1. Delegation :
If you have idea about Protocol in Swift you can do it easily.
first create a protocol with the required function you want to implement :
protocol FirstControllerDelegate: AnyObject {
func sendData(data: String)
}
Suppose your firstPage is FirstViewController, it has a UILabel and we have to assign a String to it from our secondPage means SecondViewController. the Structure of your FirstViewController may be like this :
class FirstViewController: UIViewController {
#IBOutlet weak var textLabel: UILabel!
#IBAction func gotoSecondPage() {
let secondVC = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
}
}
Now your FirstViewController has to confirm to this protocol and it will implement the sendData(data: ) method :
extension FirstViewController: FirstControllerDelegate {
func sendData(data: String) {
textLabel.text = data
}
}
Now as a feature of Protocol in iOS, Protocols can work as a Type(like Int, String). So just create a variable of type FirstControllerDelegate in your SecondViewController !
class SecondViewController: UIViewController {
weak var delegate: FirstControllerDelegate!
#IBAction func switchToPreviousPage() {
delegate.sendData(data: "Hello")
self.dismiss(animated: true, completion: nil)
}
}
You can now call the sendData(data:) function with the variable you created above !
At last you have to do oneThing just assign the delegate :
secondVC.delegate = self
It should be inside the gotoSecondPage() method !
2. Notification/Observer
With this, our basic idea is to send a Notification inside our app, and it can be observed by any where inside !
So our SecondViewController will send a Notification embedded with required data that we want to pass, and FirstViewController will receive the Notification and it will extract the data from the Notification !!
Each Notification has a specific name, which will differentiate it from other Notifications. we have to create the Name :
Notification.Name(rawValue: "com.app.notificationObserver")
Now the FirstViewController will be Observe to this specific notification :
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.changeLabelText(notifcation:)), name: Notification.Name("com.app.notificationObserver"), object: nil)
}
We have to define changeLabelText(notification:) method :
private func changeLabelTExt(notification: NSNotification) {
if let dataDict = notification.userInfo as NSDictionary? {
if let message = dataDict["data"] as? String {
self.textLabel.text = message
}
}
}
Finally, SecondViewController will trigger the Notification :
#IBAction func switchToPreviousPage() {
NotificationCenter.default.post(name: Notification.Name(rawValue: "com.app.notificationObserver"), object: ["data": "hello"])
self.dismiss(animated: true, completion: nil)
}
Thats All .....

Function returning emptying String when using Notification and observers?

In the following code I want to return a string from first controller to third controller. But it returns an empty string, when trying by notification and observers.
First View Controller
override function ViewDidLoad(){
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .token, object: nil)
}
#objc func token (notification:Notification) -> String!{
return self.token! //return token
}
extension Notification.Name {
static let token = Notification.Name("Token")
}
ThirdViewController*
override function ViewDidLoad(){
let token = NotificationCenter.default.post(name: .token, object: nil)
print(token) // () printing empty
}
If I understood you problem correctly you want to pass object from first controller to third, you can use segue for it. This is the example how you can pass it to second, the same thing to pass forward from second to third
#IBAction func goForawrd(_ sender: UIButton) {
performSegue(withIdentifier: "second", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "second" {
let vc = segue.destination as? SecondViewController
vc?.object = yourObjectYouWantToPass
}
}
or if you don't want to use segue you can use next code
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondVC = storyboard.instantiateViewController(withIdentifier: "second")
secondVC.object = yourObjectYouWantToPass
don't forget to set viewController identifier before
This
NotificationCenter.default.post(name: .token, object: nil) doesn't return anything you get the observer wrongly it posts the notification and if there is an observer it'll be forwarded to it , so this flow occurs
1-
NotificationCenter.default.post(name: .token, object: nil)
2-
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .token, object: nil
3-
#objc func token (notification:Notification) {}
if you need to send data from first to second set it when you segue/present/push , if you need to send data from second to first use a delegate
Okay so you are printing nil since that is not the value of the token just the reference for the post method.
Since it is not a bidirectional thing you cannot retrieve the value there.
If you want to achieve this by notifications these are the required steps:
Send a notification about you need a token
When notification arrived on first controller grab what you need and send another notification with the value you need, and handle it where you need.
First View Controller
override function ViewDidLoad(){
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .tokenGet, object: nil)
}
#objc func token (notification:Notification) {
NotificationCenter.default.post(name: .tokenSet, object: token)
}
extension Notification.Name {
static let tokenGet = Notification.Name("TokenGet")
static let tokenSet = Notification.Name("TokenSet")
}
ThirdViewController*
override function ViewDidLoad(){
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .tokenSet, object: nil)
NotificationCenter.default.post(name: .tokenGet, object: nil)
}
func tokenSet(notification: Notification) {
/// here you can get the value from notification
}
Note that i would NOT do in this way. Pass the token through the view controllers or create a class which is responsible for token handling and pass that around.

How to send data from RightViewController to MainView Controller in Swift 4?

Hello, I am using MMDrawerController for right side menu. I have 2 ViewController First is HomeVC with Product Listing data in UICollectionView and there's 1 filter button.
When i press that filter button I push to filter screen RightViewVC. Now what I want is, I want to pass that selected filter values to HomeVC. How can I do this?
You can do this by multiple way
1. By using Block Method
When you push to RightViewVC write below code.
let nextViewController = self.storyboard?.instantiateViewController(withIdentifier: "RightViewVC") as! RightViewVC
nextViewController.delegate = self as! customeDelegate
nextViewController.onApplyFilterTap = {(_ arrSelectedFilter: NSMutableArray) -> Void in
self.collectionView.reloadData()
}
self.show(nextViewController, sender: self)
Define this in RightViewVC controller. I created array you can change it as per your requirement.
var onApplyFilterTap: ((_ arrSelectedFilter: NSMutableArray) -> Void)? = nil
You need to call like this
self.arrFilterSelection.add(whichButtonClicked)
self.arrFilterSelection.add(locationTextView.text!)
self.arrFilterSelection.add(byPriceToTextField.text!)
self.arrFilterSelection.add(byPriceFromTextField.text!)
self.arrFilterSelection.add(timeTextview.text!)
onApplyFilterTap!(self.arrFilterSelection)
2. By using NotificationCenter
Write below in your HomeVC
NotificationCenter.default.addObserver(self, selector: #selector(refreshProductListBasedonSelectedFilterValue(_:)), name: NSNotification.Name(rawValue: "refreshProductListBasedonSelectedFilterValue"), object: nil)
#objc func refreshProductListBasedonSelectedFilterValue(_ notification: Notification) {
let info = notification.object as? NSDictionary
let arrSelectedFilteredValues = info?.value(forKey: "selectedFilter") as! NSMutableArray
self.collectionView.reloadData()
}
From RightViewVC you need to call like this.
let dict = NSMutableDictionary()
dict.setValue(self.arrFilterSelection, forKey: "selectedFilter")
NotificationCenter.default.post(name: NSNotification.Name("refreshProductListBasedonSelectedFilterValue"), object: dict)
3. You can also use delegate

Passing data between view controllers inside UITabBarController without segues

I have been looking for answer to this question for a long time. I can use prepareforSegue if I am using segues. As we know, UITabBar doesn't have segues like navigation controller. In one of my view controllers, I am fetching data from firebase, and I have observer set up to listen for any changes. I want to use data fetched from firebase in this controller to access in another view controller. For example, I want to access this entryIDs in another view controller, and also listen to any changes made to this array, so I can reload data in my collectionView. To sum it up, how do I access array from one view controller to another inside UITabBarController, and also listen to any changes made to it?
var entries = [String: DiaryEntry]()
var entryIDs = [String]()
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
override func viewDidLoad() {
super.viewDidLoad()
// Register cell classes
self.collectionView!.register(DiaryCell.self, forCellWithReuseIdentifier: "homeCell")
collectionView?.backgroundColor = UIColor.white
if let userID = FIRAuth.auth()?.currentUser?.uid {
FirebaseService.service.getUserEntriesRef(uid: userID).observe(.value, with: { [weak weakSelf = self] (snapshot) in
weakSelf?.entries = [String: DiaryEntry]()
weakSelf?.entryIDs = [String]()
let enumerator = snapshot.children
while let entry = enumerator.nextObject() as? FIRDataSnapshot {
weakSelf?.entryIDs.append(entry.key)
weakSelf?.entries[entry.key] = DiaryEntry(snapshot: entry)
}
weakSelf?.entryIDs.reverse()
weakSelf?.collectionView?.reloadData()
})
}
// Do any additional setup after loading the view.
}
On sending info
let infoDict = ["info" : self.info]
NotificationCenter.default.post(name: Notification.Name(rawValue: "passInfo"), object: nil, userInfo : infoDict as! [String : AnyObject])
on receiving info
NotificationCenter.default.addObserver(self, selector: #selector(self.doSomething), name: NSNotification.Name(rawValue: "passInfo"), object: nil)
func doSomething(_ notification : Notification) {
let info = notification.userInfo!["info"] as! Int
//Do something
}

IOS Swift how can I reload a tableView from a different controller

I have 2 controllers A and Controller B . Controller A has a TableView and Controller B is a subview that when clicked opens a form and on Submit it enters data into the database. My problem is that I attempt to reload my TableView from Controller B from the user hits submit and I get the following error
fatal error: unexpectedly found nil while unwrapping an Optional value from this line
self.TableSource.reloadData()
Now the data from Controller B is successfully inserted so after I restart my app the data I submit is there . This is my code (TableSource is the TableView outlet)
Controller A
func reloadTable(latmin: Float,latmax: Float,lonmin: Float, lonmax: Float) {
let url:URL = URL(string:ConnectionString+"MY-URL")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let parameter = "parameters"
request.httpBody = parameter.data(using: String.Encoding.utf8)
session.dataTask(with:request, completionHandler: {(data, response, error) in
if error != nil {
} else {
do {
let parsed = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
if let S = parsedData["myData"] as? [AnyObject] {
for A in Data {
// gets Json Data
}
DispatchQueue.main.async {
// This is what I named my TableView
self.TableSource.reloadData()
}
}
} catch let error as NSError {
print(error)
}
}
}).resume()
}
That is my HTTP-Request that gets data from the database, now in that same Controller A I have a button that when clicked opens the SubView to Controller B and this is the code
#IBAction func Post_Action(_ sender: Any) {
let Popup = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ControllerB") as! Controller B
self.addChildViewController(Popup)
Popup.view.frame = self.view.frame
self.view.addSubview(Popup.view)
Popup.didMove(toParentViewController: self)
}
This is the code in Controller B and this is how I try to reload the TableView in Controller A
#IBAction func Submit_Form(_ sender: Any) {
// Code that submits the form no issues here
latmin = 32.18
latmax = 32.50
lonmin = -81.12
lonmax = -81.90
let Homepage = ControllerA()
Homepage.reloadTable(latmin: latmin!,latmax: latmax!,lonmin: lonmin!,lonmax: lonmax!)
}
So as stated before Controller A loads the data from the Database, Controller B has a form and when submitted it enters new data into the database . That whole process works I just now want to update the TableView in Controller A from the form is submitted in Controller B
I would suggest using protocol:
protocol SomeActionDelegate {
func didSomeAction()
}
In ViewController B
var delegate: SomeActionDelegate?
In ViewController A when segue
viewControllerB.delegate = self
You should add this
extension ViewControllerA: SomeActionDelegate {
func didSomeAction() {
self.tableView.reloadData()
}
}
And in ViewController B
func didChangeSomething() {
self.delegate?.didSomeAction()
}
It works like when ViewController B didChangeSomething() it sends message to ViewController A that it should didSomeAction()
You can do it with NSNotification
in swift 3.0
Think you have two viwe controllers called viewcontrollerA and viewControllerB
viewcontrollerA has the tableview.
you need to reload it from viewcontrolerB
implementaion of viewcontrollerA
create a function to relod your tableview in viewcontrollerA and call it in viewDidLoad
override func viewDidLoad() {
let notificationNme = NSNotification.Name("NotificationIdf")
NotificationCenter.default.addObserver(self, selector: #selector(YourControllername.reloadTableview), name: notificationNme, object: nil)
}
func relodaTableview() {
self.TableSource.reloadData()
}
implementation in viewcontrollerB (where you want to reload tableview)
post the notification in button click or anywhere you want like below
let notificationNme = NSNotification.Name("NotificationIdf")
NotificationCenter.default.post(name: notificationNme, object: nil)
hope this will help to you.

Resources