11

I am creating my first simple budgeting app. Basically, I take a few user inputs like monthly income & savings goal. Then they click "start", & the app calculates stuff such as, their daily budget etc.

Here I'm running into trouble. After all the calculations, I display "how much you can spend each day" (e.g. $20 a day), which I pass forward through segues from their original inputs on the original screen.

Now, in this VC (UserInfoVC) I created a button which lets them add how much money they spent today. So when they click this "add money spent" button, I open a new VC (AddSubtractMoney) where I present a calculator where they can enter how much they spent today (i.e. $12) and click submit.

I run their input compared to their daily budget to get a New daily budget.

Now, I'm having trouble passing this updated number backwards, to display it on the previous VC on the label "dailySpendingLimitLabel". I know segues are not the best way to go about passing data backwards.

I've tried closures, but I end up getting lost in the syntax, and protocols and delegates (it's my 2nd month coding).

Is there a simple way to achieve passing this data back to the previous VC and populating the data in that previous display label?

Below is the code.

The First snippet is from the UserInfoVC where I display their originally entered data that I segued through. The Second snippet is from the AddSubtractMoney class where I placed the calculator and created an object "newestUpdate" inside a function that allows me to calculate the number they entered on the calculator minus their old daily budget. To arrive at a new budget which I want to present backwards to the UserInfoVC.

class UserInfoViewController : ViewController {

var userNamePassedOver : String?
var userDailyBudgetPassedOver : Double = 99.0
var userDailySavingsPassedOver : Double = 778.00
var userMonthlyEarningsPassedOver : Double?
var userDesiredSavingsPassedOver : Double?
var newAmountPassedBack : Double = 0.0


@IBOutlet weak var dailySavingsNumberLabel: UILabel!
@IBOutlet weak var userNameLabel: UILabel!
@IBOutlet weak var dailySpendingLimitLabel: UILabel!



override func viewDidLoad() {
    super.viewDidLoad()

    userNameLabel.text = userNamePassedOver
    dailySpendingLimitLabel.text = String(format: "%.2f", userDailyBudgetPassedOver)
    dailySavingsNumberLabel.text = String(format: "%.2f", userDailySavingsPassedOver)

}


@IBAction func addSubtractMoneyPressed(_ sender: UIButton) {

    performSegue(withIdentifier: "addOrSubtractMoney", sender: self)


}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "addOrSubtractMoney"{

        let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney

            addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
        }
    }

}

extension UserInfoViewController: AddSubtractMoneyDelegate {

func calculatedValue(value: Double) {

    dailySpendingLimitLabel.text = String(userDailyBudgetPassedOver - value)

}

}

import UIKit

protocol AddSubtractMoneyDelegate {
  func calculatedValue(value: Double)
}

class AddSubtractMoney: UIViewController {

@IBOutlet weak var outputLabel: UILabel!

var runningNumber = ""
var finalNumberPassedOver : Double?
var amountPassedBackToUserInfo : Double = 0.0
var dailyBudgetPassedThrough : Double = 0.0

var delegate: AddSubtractMoneyDelegate?

override func viewDidLoad() {
    super.viewDidLoad()

    outputLabel.text = "0"


    // Do any additional setup after loading the view.
}


@IBAction func buttonPressed(_ sender: UIButton) {
    runningNumber += "\(sender.tag)"
    outputLabel.text = runningNumber


}

@IBAction func submitNewInfo(_ sender: UIButton) {

    // FIX FIX

    AddSubtractMoneyController.addToMoneySpentArray(amountISpent: outputLabel.text!)

    sendBackUpdatedNumber()

    dismiss(animated: true, completion: nil)

}


@IBAction func allClearedPressed(_ sender: UIButton) {
    runningNumber = ""
    outputLabel.text = "0"
}

// THIS LINE PRODUCES THE CORRECT INPUT IN OUTPUT CONSOLE WHEN I PRINT- BUT I CANT FIGURE HOW TO TRANSFER IT BACK TO PREVIOUS VC

func sendBackUpdatedNumber(){

    let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)

   amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
   newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo

    print(amountPassedBackToUserInfo)

    self.delegate?.calculatedValue(value: amountPassedBackToUserInfo)


}


}

4 Answers4

30

My suggestion is to use a callback closure. It's less code and easier to handle than protocol / delegate.

In AddSubtractMoney declare a callback variable and call it in sendBackUpdatedNumber passing the Double value

class AddSubtractMoney: UIViewController {

   // ...

    var callback : ((Double)->())?

  // ...

    func sendBackUpdatedNumber(){

        let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)
        amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
        newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo
        print(amountPassedBackToUserInfo)
        callback?(amountPassedBackToUserInfo)
    }
}

In prepare(for segue assign the closure to the callback variable and add the code to be executed on return

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "addOrSubtractMoney"{
        let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
        addOrSubtractMoneyVC.callback = { result in
             print(result)
            // do something with the result
        }
        addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
    }
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • 1
    Wow, that was super easy and it worked! Thanks a lot! Closures are an advanced topic from my understanding, but you just painted a very clear picture in my mind after following and understanding your logic.. thanks again – Coding while Loading Mar 10 '18 at 07:34
  • Don't forget to use [weak self] inside the closure to avoid memory leaks. – atereshkov Aug 21 '18 at 13:31
  • @atereshkov Hi. Where in the closure should I use `weak self`? Can you provide a snippet/template of how the code should look like? Thanks. – fishhau Nov 29 '19 at 07:19
  • 2
    @fishhau just google "weak self in closuers". https://stackoverflow.com/questions/24468336/how-to-correctly-handle-weak-self-in-swift-blocks-with-arguments/33210838 In this particular case if you use result later firing self, then: addOrSubtractMoneyVC.callback = { [weak self] result in ... } – atereshkov Nov 29 '19 at 07:54
  • Please mention that which is first viewcontroller and which is second one....confusion is created..who is sending to whom. – Clean Coder Jul 13 '22 at 17:47
5

Using delegate

if segue.identifier == "addOrSubtractMoney" {

    let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney

        addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver

       addOrSubtractMoneyVC.delegate = self
    }
}

You need to add delegate property in AddSubtractMoney class

var delegate: AddSubtractMoneyDelegate?

Create Protocol in AddSubtractMoney class

protocol AddSubtractMoneyDelegate {
  func calculatedValue(value: Double)
}

And respond to delegate

 func sendBackUpdatedNumber(){

    let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)

   amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
   newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo

    print(amountPassedBackToUserInfo)

    self.delegate.calculatedValue(value: amountPassedBackToUserInfo)
}

Now you need to implement this delegate method in class where delegate is set.

Here in UserInfoViewController class delegate is set so you need to implement its delegate method

extension UserInfoViewController: AddSubtractMoneyDelegate {

  func calculatedValue(value: Double) {

       //set label here
  } 

}
Jan Slominski
  • 2,968
  • 4
  • 35
  • 61
Mahendra
  • 8,448
  • 3
  • 33
  • 56
  • Thank you for the quick response! I followed all of your code, and for the extension i placed this code to set the label extension UserInfoViewController: AddSubtractMoneyDelegate { func calculatedValue(value: Double) { dailySpendingLimitLabel.text = String(userDailyBudgetPassedOver - value) } } – Coding while Loading Mar 10 '18 at 06:36
  • However, i still cannot get the view on the prior view controller to display that userDailyBudget - value i passed back – Coding while Loading Mar 10 '18 at 06:37
  • Do i need to do a "view did appear" call or anything? To replace the old number shown with these new one ? – Coding while Loading Mar 10 '18 at 06:37
  • You just missed it bro...`addOrSubtractMoneyVC.delegate = self` set it in `prepareForSegue`...:) – Mahendra Mar 10 '18 at 06:46
  • Yes!!! Thanks so much my friend.. You are a great help.. I am starting to understand the delegate and protocol flow much more! I really do appreciate the help! – Coding while Loading Mar 10 '18 at 06:52
  • I'm very glad to help you..:) – Mahendra Mar 10 '18 at 06:53
  • Hey, quick question. I notice, if i click the add money spent button , punch in amount, and calculate it. i get the right input.. however, when i go right back and click the add money spent button again, it is not subtracting from the new number anymore.. It subtracts from the original number put through. So if my daily budget is $20.. and i say i spent $5, it shows $15.. but then i go back again and say i spent another $7, it should show $8 remaining to spend, but instead it shows $13.. – Coding while Loading Mar 10 '18 at 07:01
  • so basically, i think i need to add something into the 'sendBackUpdatedNumber' function that makes sure I'm subtracting from the new subtotal everytime – Coding while Loading Mar 10 '18 at 07:05
  • Yes, You need to store updated value may be in `UserDefaults` so always fetch value form it and then subtract from that value always... – Mahendra Mar 10 '18 at 07:08
  • Ok, so i will do some studying on that. I planned to set alamo fire so that i can store database of the users. Would that work also? or User default is easier for a beginner? – Coding while Loading Mar 10 '18 at 07:13
  • UserDefault is useful to store small piece of data...but if you are planning to store more data with complex relationship then it is advisable to user DB like SQLite, Realm, Firebase, etc. – Mahendra Mar 10 '18 at 07:17
3

You could possibly also use an unwind segue to pass back the data.

Balázs Vincze
  • 3,550
  • 5
  • 29
  • 60
  • [Here](https://www.youtube.com/watch?v=akmPXZ4hDuU)'s a 5-minute video from the Google Developers YouTube channel which nicely demonstrates the ins and outs of unwind segues. – Adil Hussain Jan 10 '20 at 12:30
0

If you don't under stand flow behind delegate(protocol oriented), you can simply go through below code. it only works if both class

But it is not a good practice Learn about protocol, closure, or Notification Center broadcasting for mostly used, flexible and reusable coding methods.

UserInfoViewController

class UserInfoViewController : ViewController {
   fun receiveBackUpdatedNumber(numberString:String){

   }

   override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
      if segue.identifier == "addOrSubtractMoney"{
        let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
      addOrSubtractMoneyVC.userInfoViewController = self
        }
      }

   }
 }

AddSubtractMoney

class AddSubtractMoney: UIViewController {
  var userInfoViewController: UserInfoViewController!
  var updatedNumber = ""
  func sendBackUpdatedNumber(){
    self.userInfoViewController.receiveBackUpdatedNumber(numberString: updatedNumber)
   }
}

If you are confirtable you can go with protocols.. protocols insist a class to compulsory implement a method, which make code more reusable and independent.

In Above method we are passing instance of current viewcontroller(UserInfoViewController) to next viewcontroller(AddSubtractMoney) on performing segue, So by that we can access any properties of function in UserInfoViewController from AddSubtractMoney. So it make easy to pass data from AddSubtractMoney to -> UserInfoViewController

Britto Thomas
  • 2,092
  • 1
  • 15
  • 28
  • It is not a good practice by creating the previous VC object to pass data back. – dahiya_boy Mar 10 '18 at 06:46
  • Yes, I know! I am coming to that conclusion.. feel free to edit answer. – Britto Thomas Mar 10 '18 at 06:48
  • If some part is wrong then i will edit, but i am not gonna replace your entire answer with new one. By the way you can check other answer which is proper & even i done comment earlier then any of the answer. – dahiya_boy Mar 10 '18 at 06:49