How do I pass data to another view controller in Swift 4?

Xcode, Swift 4


How do I pass data to another view controller in Swift 4?


Below is my code and I am getting an error message.


I am getting an error on line 181. It says


Cannot assign value of type 'DataToPass' to type 'ViewController'


struct DataToPass {  
    var principal: Double = 0  
    var balance: Double = 0  
    var monthlyInterest: Double = 0  
    var paymentNumber: Int = 0  
}  

class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {  


    var valueToPass: DataToPass = DataToPass(principal: 0.00, balance: 0.00, monthlyInterest: 0.00, paymentNumber: 0)  

    // Class-level constant to hold the months per year.  
    let dblMONTHS_YEAR: Double = 12  

    // To not allow more than 1 decimal point.  
    func textField(_ textField: UITextField,shouldChangeCharactersIn range: NSRange,replacementString string: String) -> Bool  
    {  
        let countdots = (textField.text?.components(separatedBy: ".").count)! - 1  

        if countdots > 0 && string == "."  
        {  
            return false  
        }  
        return true  
    }  
    @IBAction func textFieldCost(_ sender: TextField) {  
        func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {  
            self.view.endEditing(true)  
        }  
    }  
    @IBAction func textFieldDownPayment(_ sender: TextField) {  
        func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {  
            self.view.endEditing(true)  
        }  
    }  
    @IBAction func textFieldMonths(_ sender: TextField) {  
        func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {  
            self.view.endEditing(true)  
        }  
    }  
    @IBAction func textFieldAPR(_ sender: TextField) {  
        func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {  
            self.view.endEditing(true)  
        }  
    }  

    @IBOutlet weak var txtViewLabels: UITextView!  
    @IBOutlet weak var txtViewResults: UITextView!  

    // Text Field Outlets.  
    @IBOutlet weak var txtCost: UITextField!  
    @IBOutlet weak var txtDownPayment: UITextField!  
    @IBOutlet weak var txtMonths: UITextField!  
    @IBOutlet weak var txtAPR: UITextField!  

    // Error Message.  
    @IBOutlet weak var lblMessage: UILabel!  

    // Buttons  
    @IBAction func btnCalculate(_ sender: UIButton) {  

        var dblAPR: Double = 0                         // To hold the Annual Rate.  
        var dblTotalAmount: Double = 0                 // To hold the vehicle total cost.  
        var dblCost: Double = 0                        // To hold vehicle cost.  
        var dblDownPayment: Double = 0                 // To hold down payment.  
        var intMonths: Int = 0                         // To hold number of months for the loan.  
        var dblLoan: Double = 0                        // To hold the amount of the loan.  
        var dblMonthlyPayment: Double = 0              // To hold the monthly payment.  
        var dblTotalInterest: Double = 0               // To hold the total interest.  
        var dblMonthlyRate: Double = 0                 // To hold the monthly Rate.  

        var monthlyInterest: Double = 0  
        var balance: Double = 0  
        var principal: Double = 0  
        var paymentNumber: Int = 0  

        var data: [String] = []  

        func passedValue() {  

            func paymentStructure() {  

                data = []  

                balance = dblLoan  

                for _ in 1...intMonths {  
                    paymentNumber+=1  

                    dblCost = Double(txtCost.text!)!  
                    dblDownPayment = Double(txtDownPayment.text!)!  
                    intMonths = Int(txtMonths.text!)!  
                    dblAPR = Double(txtAPR.text!)!  

                    monthlyInterest = balance * ((dblAPR/100)/12)  
                    principal = dblMonthlyPayment - monthlyInterest  
                    balance = balance - principal  

                    let fPrincipal = NSNumber(value: principal)  
                    let fResultPrincipal = NumberFormatter.localizedString(from: fPrincipal, number: .currency)  

                    let fBalance = NSNumber(value: balance)  
                    let fResultBalance = NumberFormatter.localizedString(from: fBalance, number: .currency)  

                    let fMonthlyInterest = NSNumber(value: monthlyInterest)  
                    let fResultMonthlyInterest = NumberFormatter.localizedString(from: fMonthlyInterest, number: .currency)  

                    data += ["\(paymentNumber). P: \(fResultPrincipal), I: \(fResultMonthlyInterest), B: \(fResultBalance)"]  
                }  
                valueToPass.paymentNumber = intMonths  
                valueToPass.monthlyInterest = monthlyInterest  
                valueToPass.principal = principal  
                valueToPass.balance = balance  
            }  
        }  
else if txtCost.text != nil && txtDownPayment.text != nil && txtMonths.text != nil && txtAPR.text != nil {  
            // Clear the message.  
            lblMessage.text = ""  

            dblCost = Double(txtCost.text!)!  
            dblDownPayment = Double(txtDownPayment.text!)!  
            intMonths = Int(txtMonths.text!)!  
            dblAPR = Double(txtAPR.text!)!  

            // Get the APR rate.  
            dblAPR = dblAPR / 100  

            // Get the monthly rate.  
            dblMonthlyRate = dblAPR / dblMONTHS_YEAR  

            // Get the monthly payment.  
            dblMonthlyPayment = (dblCost - dblDownPayment) * (dblMonthlyRate / (1 - pow(1 + dblMonthlyRate, Double(-intMonths))))  

            // Get the amount of the loan.  
            dblLoan = dblCost - dblDownPayment  

            // Get the total interest.  
            dblTotalInterest = dblMonthlyPayment * Double(intMonths) - dblLoan  

            // Get the Total Amount.  
            dblTotalAmount = dblCost + dblTotalInterest  

            // Get the Vehicle Cost.  
            dblCost = dblTotalAmount - dblTotalInterest  

            // Format the results.  
            let fCost = NSNumber(value: dblCost)  
            let fResultCost = NumberFormatter.localizedString(from: fCost, number: .currency)  

            let fDownPayment = NSNumber(value: dblDownPayment)  
            let fResultDownPayment = NumberFormatter.localizedString(from: fDownPayment, number: .currency)  

            let fLoan = NSNumber(value: dblLoan)  
            let fResultLoan = NumberFormatter.localizedString(from: fLoan, number: .currency)  

            let fMonthlyPayment = NSNumber(value: dblMonthlyPayment)  
            let fResultMonthkyPayment = NumberFormatter.localizedString(from: fMonthlyPayment, number: .currency)  

            let fAPR = NSNumber(value: dblAPR)  
            let fResultAPR = NumberFormatter.localizedString(from: fAPR, number: .percent)  

            let fTotalInterest = NSNumber(value: dblTotalInterest)  
            let fResultTotalInterest = NumberFormatter.localizedString(from: fTotalInterest, number: .currency)  

            let fTotalAmount = NSNumber(value: dblTotalAmount)  
            let fResultTotalAmount = NumberFormatter.localizedString(from: fTotalAmount, number: .currency)  

            txtViewLabels.text = " APR:\n Months: \n Cost:\n Down Payment:\n Loan Amount:\n Monthly Payment:\n Total Interest:\n Total Amo  



            txtViewResults.text = " \(fResultAPR)\n \(intMonths)\n \(fResultCost)\n \(fResultDownPayment)\n \(fResultLoan)\n \(fResultMonthkyPayment)\n \(fResultTotalInterest)\n \(fResultTotalAmount)"  

            passedValue()  
        }  
    }  
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {  
        if segue.identifier == "payout structure" {  
            if let destVC = segue.destination as? PayoutStructure {  
                destVC.passedData = valueToPass  
            }  
        }  
    }  
} 

As I suggested before, you should better show both sides, passing and receiving, when describing passing data.

What is `PayoutStructure`? Is it a subclass of `UIViewController` and is the destination of the segue?

And how `passedData` is declared inside `PayoutStructure`?


Please clarify all such things.

PayoutStructure is the view controller I want to pass it to. Yes, it is a subclass of view controller. I don't have anything written in PayoutStructure to receive the data. Do I need to write a variable in PayoutStructure to receive the data? If so, how would I do that in PayoutStructure?

I figured that part out. I had to define a variable in StructurePayout to receive the data which is why I was getting the error. Thank you for your help.

Do I need to write a variable in PayoutStructure to receive the data?


Of course.

As I wrote before:

You add some properties to receive the data to the other view controller, and use them in a proper method, for example `viewWillAppear()`.

And override the method `prepare(for:)` in the current view controller, in it set the next view controller's properties with the data.


You should not have marked the other thread as solved, that I could have shown you what you needed.

You should better unmark the thread and continue on the thread.

Happy to hear you have found it out.

When you confirm that the data passing issue is solved, please mark the thread as solved.

Okay. I have another problem though. I got my application to work but when I view the second view controller none of the values change. I think it is passing the values that I set in the struct DataToPass. How do I get it to pass the data that the user sets in the text fields. Below is my code. Thank you for your help.


First view controller:

struct DataToPass {
    var months: Int = 0
    var cost: Double = 0
    var downPayment: Double = 0
    var annualRate: Double = 0
}

class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {

    var valueToPass: DataToPass = DataToPass(months: 0, cost: 0.00, downPayment: 0.00, annualRate: 0)

    // Class-level constant to hold the months per year.
    let dblMONTHS_YEAR: Double = 12

    // To not allow more than 1 decimal point.
    func textField(_ textField: UITextField,shouldChangeCharactersIn range: NSRange,replacementString string: String) -> Bool
    {
        let countdots = (textField.text?.components(separatedBy: ".").count)! - 1
    
        if countdots > 0 && string == "."
        {
            return false
        }
        return true
    }
    @IBAction func textFieldCost(_ sender: TextField) {
        func touchesBegan(_ touches: Set, with event: UIEvent?) {
            self.view.endEditing(true)
        }
    }
    @IBAction func textFieldDownPayment(_ sender: TextField) {
        func touchesBegan(_ touches: Set, with event: UIEvent?) {
            self.view.endEditing(true)
        }
    }
    @IBAction func textFieldMonths(_ sender: TextField) {
        func touchesBegan(_ touches: Set, with event: UIEvent?) {
            self.view.endEditing(true)
        }
    }
    @IBAction func textFieldAPR(_ sender: TextField) {
        func touchesBegan(_ touches: Set, with event: UIEvent?) {
            self.view.endEditing(true)
        }
    }

    @IBOutlet weak var txtViewLabels: UITextView!
    @IBOutlet weak var txtViewResults: UITextView!

    // Text Field Outlets.
    @IBOutlet weak var txtCost: UITextField!
    @IBOutlet weak var txtDownPayment: UITextField!
    @IBOutlet weak var txtMonths: UITextField!
    @IBOutlet weak var txtAPR: UITextField!

    // Error Message.
    @IBOutlet weak var lblMessage: UILabel!

    // Buttons
    @IBAction func btnCalculate(_ sender: UIButton) {
    
        var dblAPR: Double = 0                         // To hold the Annual Rate.
        var dblTotalAmount: Double = 0                 // To hold the vehicle total cost.
        var dblCost: Double = 0                        // To hold vehicle cost.
        var dblDownPayment: Double = 0                 // To hold down payment.
        var intMonths: Int = 0                         // To hold number of months for the loan.
        var dblLoan: Double = 0                        // To hold the amount of the loan.
        var dblMonthlyPayment: Double = 0              // To hold the monthly payment.
        var dblTotalInterest: Double = 0               // To hold the total interest.
        var dblMonthlyRate: Double = 0                 // To hold the monthly Rate.
    
        var monthlyInterest: Double = 0
        var balance: Double = 0
        var principal: Double = 0
        var paymentNumber: Int = 0
    
        var data: [String] = []
    
        func passedValue() {
        
            dblCost = Double(txtCost.text!)!
            dblDownPayment = Double(txtDownPayment.text!)!
            intMonths = Int(txtMonths.text!)!
            dblAPR = Double(txtAPR.text!)!
        
            valueToPass.months = intMonths
            valueToPass.cost = dblCost
            valueToPass.downPayment = dblDownPayment
            valueToPass.annualRate = dblAPR
            }
else if txtCost.text != nil && txtDownPayment.text != nil && txtMonths.text != nil && txtAPR.text != nil {
            // Clear the message.
            lblMessage.text = ""
         
            dblCost = Double(txtCost.text!)!
            dblDownPayment = Double(txtDownPayment.text!)!
            intMonths = Int(txtMonths.text!)!
            dblAPR = Double(txtAPR.text!)!
         
            // Get the APR rate.
            dblAPR = dblAPR / 100
         
            // Get the monthly rate.
            dblMonthlyRate = dblAPR / dblMONTHS_YEAR
         
            // Get the monthly payment.
            dblMonthlyPayment = (dblCost - dblDownPayment) * (dblMonthlyRate / (1 - pow(1 + dblMonthlyRate, Double(-intMonths))))
         
            // Get the amount of the loan.
            dblLoan = dblCost - dblDownPayment
         
            // Get the total interest.
            dblTotalInterest = dblMonthlyPayment * Double(intMonths) - dblLoan
         
            // Get the Total Amount.
            dblTotalAmount = dblCost + dblTotalInterest
         
            // Get the Vehicle Cost.
            dblCost = dblTotalAmount - dblTotalInterest
         
            // Format the results.
            let fCost = NSNumber(value: dblCost)
            let fResultCost = NumberFormatter.localizedString(from: fCost, number: .currency)
         
            let fDownPayment = NSNumber(value: dblDownPayment)
            let fResultDownPayment = NumberFormatter.localizedString(from: fDownPayment, number: .currency)
         
            let fLoan = NSNumber(value: dblLoan)
            let fResultLoan = NumberFormatter.localizedString(from: fLoan, number: .currency)
         
            let fMonthlyPayment = NSNumber(value: dblMonthlyPayment)
            let fResultMonthkyPayment = NumberFormatter.localizedString(from: fMonthlyPayment, number: .currency)
         
            let fAPR = NSNumber(value: dblAPR)
            let fResultAPR = NumberFormatter.localizedString(from: fAPR, number: .percent)
         
            let fTotalInterest = NSNumber(value: dblTotalInterest)
            let fResultTotalInterest = NumberFormatter.localizedString(from: fTotalInterest, number: .currency)
         
            let fTotalAmount = NSNumber(value: dblTotalAmount)
            let fResultTotalAmount = NumberFormatter.localizedString(from: fTotalAmount, number: .currency)
         
            txtViewLabels.text = " APR:\n Months: \n Cost:\n Down Payment:\n Loan Amount:\n Monthly Payment:\n Total Interest:\n Total Amount:\n"
         
            txtViewResults.text = " \(fResultAPR)\n \(intMonths)\n \(fResultCost)\n \(fResultDownPayment)\n \(fResultLoan)\n \(fResultMonthkyPayment)\n \(fResultTotalInterest)\n \(fResultTotalAmount)"
         
            passedValue()
        }
    }
@IBAction func btnPayoutStructure(_ sender: UIButton) {
      
        if txtViewLabels.text.isEmpty && txtViewResults.text.isEmpty {
            return
        }
        else {
            performSegue(withIdentifier: "payout structure", sender: self)
        }
    }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "payout structure" {
            if let destVC = segue.destination as? PayoutStructure {
                destVC.passedData = valueToPass
            }
        }
    }
}


Second view controller:

class PayoutStructure: UIViewController, UITableViewDataSource, UITableViewDelegate {
   
    var passedData = DataToPass()
    var data: [String] = []
   
    let MONTHS_YEAR: Double = 12
   
    var loanAmount: Double = 0
    var balance: Double = 0
    var principal: Double = 0
    var monthlyInterest: Double = 0
    var monthlyPayment: Double = 0
   
    var monthlyRate: Double = 0
    var totalInterest: Double = 0
    var totalAmount: Double = 0
   
    var paymentNumber: Int = 0
   
    func paymentStructure() {
       
      //  data = []
       
        balance = loanAmount
       
        for _ in 1...passedData.months {
            paymentNumber+=1
           
            monthlyRate = (passedData.annualRate / 100) / MONTHS_YEAR
            monthlyPayment = (passedData.cost - passedData.downPayment) * (monthlyRate / (1 - pow(1 + monthlyRate, Double(-passedData.months))))
            loanAmount = passedData.cost - passedData.downPayment
            totalInterest = monthlyPayment * Double(passedData.months) - loanAmount
            totalAmount = passedData.cost + totalInterest
            passedData.cost = totalAmount - totalInterest
           
      /*      dblCost = Double(txtCost.text!)!
            dblDownPayment = Double(txtDownPayment.text!)!
            intMonths = Int(txtMonths.text!)!
            dblAPR = Double(txtAPR.text!)!
            
            monthlyInterest = balance * ((dblAPR/100)/12)
            principal = dblMonthlyPayment - monthlyInterest
            balance = balance - principal

        */

            let fPrincipal = NSNumber(value: principal)
            let fResultPrincipal = NumberFormatter.localizedString(from: fPrincipal, number: .currency)
           
            let fBalance = NSNumber(value: balance)
            let fResultBalance = NumberFormatter.localizedString(from: fBalance, number: .currency)
           
            let fMonthlyInterest = NSNumber(value: monthlyInterest)
            let fResultMonthlyInterest = NumberFormatter.localizedString(from: fMonthlyInterest, number: .currency)
           
            data += ["\(paymentNumber). P: \(fResultPrincipal), I: \(fResultMonthlyInterest), B: \(fResultBalance)"]
        }
       
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }
   
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = data[indexPath.row]
        return cell
    }
   
    @IBOutlet weak var table: UITableView!
   
    override func viewDidLoad() {
        super.viewDidLoad()
        paymentStructure()
       
       
        // Do any additional setup after loading the view.
    }

You should better start a new thread for this question, to avoid ever lasting threads.


Have you checked the segue identifier ? And checked you do execute the prepare ?


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "payout structure" {
            if let destVC = segue.destination as? PayoutStructure {
                destVC.passedData = valueToPass
                print("valueToPass", valueToPass) // To check you pass here    
            }
        }
    }


Another comment: that is not a good idea to name a ViewController as PayoutStructure: it is not obvious it is a viewVontroller.

You should name it like PayoutViewController

I have another problem though.


It's not another problem. Your data passing issue is not solved yet. As I wrote, you should stay on this thread until your data passing issue is solved. Please concentrate on one thread until the main issue described is solved.


In your first view controller, you are not setting any value to `valueToPass`.

I recommend you not to declare it as a property.


Remove your line 10. of the first view controller:

    var valueToPass: DataToPass = DataToPass(months: 0, cost: 0.00, downPayment: 0.00, annualRate: 0) 


And update your `prepare(for:sender:)` as follows:

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "payout structure" {
            if let destVC = segue.destination as? PayoutStructure {
                var valueToPass = DataToPass()
                let dblCost = Double(txtCost.text ?? "") ?? 0.0
                let dblDownPayment = Double(txtDownPayment.text ?? "") ?? 0.0
                let intMonths = Int(txtMonths.text ?? "") ?? 0
                let dblAPR = Double(txtAPR.text ?? "") ?? 0.0
                
                valueToPass.months = intMonths
                valueToPass.cost = dblCost
                valueToPass.downPayment = dblDownPayment
                valueToPass.annualRate = dblAPR
                destVC.passedData = valueToPass
            }
        }
    }


You may need to fix many other parts, but those are other things and you may need to start other threads...

(In fact many, many parts of your code are broken.)

How do I pass data to another view controller in Swift 4?
 
 
Q