How to pass data from child to parent view controller? in swift

I created an activity where when one of the text fields clicked it will pop up a child(alert dialog) with list of product but when i click one item on the list I can’t display it on the text filed once the alert dismissed.

this is the parent view

  • In iOS, using storyboard, how to setup a view controller inside a container view?
  • How to create a back button in a view controller to go to parent view controller
  • Add a child view controller's view to a subview of the parent view controller
  • iOS routing viewDidAppear to child view controllers?
  • How to transfer data between Parent and Child View Controllers
  • How to remove SFSafariViewController as a child view controller correctly?
  • import Foundation
    import UIKit
    
    class ViewAward: UIViewController{
    
    @IBOutlet var tfMCN: UITextField!
    @IBOutlet var tfAmount: UITextField!
    @IBOutlet var tfProduct: UITextField!
    @IBOutlet var tfTotal: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        let rightAddBarButtonItem:UIBarButtonItem = UIBarButtonItem(title: "Send", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(ViewAward.searchTapped))
    
        self.navigationItem.setRightBarButtonItems([rightAddBarButtonItem], animated: true)
    
        let state = String(ViewPopUpProduct.Product.ProductDescription)
        print("My view state:"+state)
    
        self.tfProduct.text = state
        tfProduct.addTarget(self, action:  #selector(ViewAward.productTapped), forControlEvents: UIControlEvents.TouchDown)
    
    }
    
    func searchTapped(sender:UIButton) {
    
        let alertController = UIAlertController(
            title: "Award",
            message:"Award successfully posted!",
            preferredStyle: UIAlertControllerStyle.Alert)
        alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default,handler: nil))
    
        self.presentViewController(alertController, animated: true, completion: nil)
    }
    
    func productTapped(textfield: UITextField){
    
        //tfProduct.endEditing(true)
        tfProduct.resignFirstResponder()
    
        let popOverVC = UIStoryboard(name:"Main",bundle:nil).instantiateViewControllerWithIdentifier("sbPopUpID") as! ViewPopUpProduct
    
        self.addChildViewController(popOverVC)
    
        popOverVC.view.frame = self.view.frame
    
        self.view.addSubview(popOverVC.view)
    
        popOverVC.didMoveToParentViewController(self)
    
    }
    }
    

    and this when the user clicked on of the items

    import UIKit
    
    class ViewPopUpProduct: UIViewController {
    
    @IBOutlet var tableView: UITableView!
    
    var productDescription = ["Product 1","Product 2","Product 3"]
    var productID = ["prdct1","prdct2","prdct3"]
    
    
    // Global Variables 
    struct Product {
        static var ProductID = String()
        static var ProductDescription = String()
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.showAnimate()
        self.view.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.4)
    
        // Do any additional setup after loading the view.
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    @IBAction func cancelPopUp(sender: AnyObject) {
        self.removeAnimate()
    }
    
    
    func showAnimate()
    {
        self.view.transform = CGAffineTransformMakeScale(1.3, 1.3)
        self.view.alpha = 0.0;
        UIView.animateWithDuration(0.25, animations: {
            self.view.alpha = 1.0
            self.view.transform = CGAffineTransformMakeScale(1.0, 1.0)
        });
    }
    
    func removeAnimate()
    {
        UIView.animateWithDuration(0.25, animations: {
            self.view.transform = CGAffineTransformMakeScale(1.3, 1.3)
            self.view.alpha = 0.0;
            }, completion:{(finished : Bool)  in
                if (finished)
                {
                    self.view.removeFromSuperview()
                }
        });
    }
    
    //Mark - Table View
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.productID.count
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = self.tableView.dequeueReusableCellWithIdentifier("cell",forIndexPath: indexPath) as! ProductViewCell
    
        cell.productLabel.text = productDescription[indexPath.row]
    
        return cell
    }
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
    
        Product.ProductID = String(productID[indexPath.row])
        Product.ProductDescription = String(productDescription[indexPath.row])
    
        self.removeAnimate()
    
    }
    
    }
    

    4 Solutions Collect From Internet About “How to pass data from child to parent view controller? in swift”

    You can use protocols/delegate

    Here is a very very straightforward explanation, no bs:
    https://www.youtube.com/watch?v=guSYMPaXLaw

    Or in your situation you can also use NSNotificationCenter

    You can do something like this:

    The “sender” view controller would do

    let nc = NSNotificationCenter.defaultCenter()
    nc.postNotificationName("printValue", object: nil, userInfo: ["value" : "Pass Me this string"])
    

    The receiver view controller then can listen to the notification.

    let nc = NSNotificationCenter.defaultCenter()
    nc.addObserver(self, selector: #selector(printValue), name: "printValue", object: nil)
    
    func printValue(notification:NSNotification) {
        let userInfo:Dictionary<String,String> = notification.userInfo as! Dictionary<String,String>
        let item = userInfo["value"]! as String
    
        print(item,self)
    }
    

    I usually use closures for this purpose. Much simpler and less verbose than delegates:

    class MainViewController: UIViewController {
    
        func showChildViewController() {
            guard let vc = storyboard?.instantiateViewControllerWithIdentifier("ChildViewController") as? ChildViewController else {
                return
            }
            vc.didSelectItem = { [weak self](item) in
                if let vc = self {
                    // Do something with the item.
                }
            }
            presentViewController(vc, animated: true, completion: nil)
        }
    
    }
    
    class ChildViewController: UIViewController {
    
        var didSelectItem: ((item: Item) -> Void)?
    
        @IBAction func buttonPressed() {
            didSelectItem?(item: <#your item goes here#>)
        }
    
    }
    

    There are few ways by which you can implement the callback functionality to pass data.

    • Delegate
    • Using Block CallBack
    • Post Notification

    But I would suggest to use delegate which is best way, Post Notification is also a way but I do not want to prefer.

    1. My first preference must be Custom Delegate which is faster and perfect. (If you can use closures as callbacks then that should be the perfect. Choosing delegate to explain using your code a bit.)

    2. Please avoid using NSNotificationCenter as much as you can because there are so many thing you have to deal with when using this and it is little slower than delegates. You may easily get into bugs with that too.

    Now here is my code.

    1. Child ViewController Setup.

       //  TrendingProductPageViewController.swift
       //  buddyiOSApp
       //
       //  Created by Tuhin Samui on 5/21/16.
       //  Copyright © 2016 Buddy. All rights reserved.
       //
    
        import UIKit
    
        protocol TrendingProductsCustomDelegate: class { //Setting up a Custom delegate for this class. I am using `class` here to make it weak.
            func sendDataBackToHomePageViewController(categoryToRefresh: String?) //This function will send the data back to origin viewcontroller.
        }
    
    
        class TrendingProductPageViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NetworkReachabilityDelegate {
    
            @IBOutlet weak var productListTableView: UITableView! //Having a tableview outlet here from storyboard itself. BTW not going to explain with tableView delegate and datasource, Sorry..:(
    
            weak var customDelegateForDataReturn: TrendingProductsCustomDelegate? //Please use weak reference for this to manage the memory issue.
    
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
                let rowNumberFromTable: Int = indexPath.row //Getting the index of the selected cell.
                let dataToSendBack = moreMenuTableData[rowNumberFromTable] as! String //This is an array which contains the data for the tableview. Getting the exact data which is selected on the table.
                customDelegateForDataReturn?.sendDataBackToHomePageViewController?(dataToSendBack) //Now sending the selected data back to parent viewController using the custom delegate which I made before.                 presentingViewController?.dismissViewControllerAnimated(true, completion: nil) //Dismissing the viewController here.
        }
    

    2. Parent ViewController code here.

    class HomePageViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, TrendingProductsCustomDelegate, UINavigationControllerDelegate{ //Adding the protocol here as `TrendingProductsCustomDelegate`
    
    @IBAction func topTrendingProductsBtnAction(sender: UIButton) { //Normal IBAction of UIButton as you are using.
            let trendingProductsPageForAnimation = storyboard!.instantiateViewControllerWithIdentifier("showTrendingProductpage") as! TrendingProductPageViewController //You can understand this right. Same as yours.
            trendingProductsPageForAnimation.customDelegateForDataReturn = self //Setting up the custom delegate for this class which I have written on the presenting class.
            trendingProductsPageForAnimation.modalPresentationStyle = UIModalPresentationStyle.FullScreen
            presentViewController(trendingProductsPageForAnimation, animated: true, completion: nil) //Same as yours.
        }
    
    
    func sendDataBackToHomePageViewController(categoryToRefresh: String?) { //Custom delegate function which was defined inside child class to get the data and do the other stuffs.
            if categoryToRefresh != nil {
                print("Got the data is \(categoryToRefresh)")
            }
        }
    
    }
    

    Hope this helped. Sorry for any mistake.