0

I am new to coding in Swift. My goal is to build a variable containing the sum of a number of custom class variables. To speak in clear terms:

I have a custom class called "Entry" which has the Double variable "sum", e.g. 50.00.

I am using a ViewController to let the user create a new entry with text field inputs, inter alia a sum of the new entry. This new entry element is then appended to an array of type Entry when the relevant button is pressed.

@IBAction func addEntry(_ sender: UIButton){
    // take user input
    let name = nameTextField.text ?? ""
    let sum = Double(sumTextField.text!) ?? 0.0
    let category = selectedCategory
    // save user input in new entry
    let newEntry = Entry(name: name, sum: sum, category: category)
    entries.append(newEntry)
    saveEntries()
    os_log("Saved new entry successfully.", log: OSLog.default, type: .debug)

In another ViewController, I want to access the array "entries" and build the sum of all sum variables of all Entry elements, e.g. sum of Entry 1 + sum of Entry 2 + sum of Entry 3

My current attempt in coding is as follows:

var entriesDatabase = [Entry]()
//extract sum of entries and build sumOfEntries
    var sumArray = [Double]()
    for entry in entriesDatabase {
        sumArray.append(entry.sum)
    }
    sumOfEntries = sumArray.reduce(0, +)

The entries array from the first View Controller is saved by using the NSKeyedArchiver and loaded by NSKeyedUnarchiver.unarchiveObject(withFile:) in the second View Controller before calling the function above (which, I am aware, has been deprecated, but it works for my current purposes).

I used the print function to isolate the issue and as far as I can see, the sumOfEntries always remains 0.0, no matter how many Entry elements I create (which itself seems to work, though). Has anyone a clue what I am doing wrong?

EDIT: The issue to me really seems to be that the calculation does not work rather than the passing of the data from one view to another. Somehow, the arrays always remain empty. The passage of the data works via saving it persistently on the drive and then loading it with the NSKeyedArchiver functions. For clarity see the following code:

/MARK: calculate and display balance
func calculate(){
    //load data from user defaults
    recurringCalculationValue = UserDefaults.standard.value(forKey: "recurringExpenses") ?? 0.0
    monthlyIncomeCalculationValue = UserDefaults.standard.value(forKey: "monthlyIncome") ?? 0.0
    
    //extract sum of entries and build sumOfEntries
    var sumArray = [Double]()
    for entry in entriesDatabase {
        sumArray.append(entry.sum)
    }
    sumOfEntries = sumArray.reduce(0, +)
    
    //cast the user defaults into Double
    let mICV = monthlyIncomeCalculationValue as! Double
    let rCV = recurringCalculationValue as! Double
    
    //convert the Strings to Double! and calculate balance
    balance = Double(mICV) - Double(rCV) - sumOfEntries
    
    //display balance in sumLabel
    sumLabel.text = "\(balance)"
    
    //debugging
    print("balance = \(balance)")
    print("sumOfEntries = \(sumOfEntries)")
    print("monthlyIncomeCalculationValue = \(monthlyIncomeCalculationValue)")
    print("recurringCalculationValue = \(recurringCalculationValue)")
    print("UserDefault monthlyIncome = \(String(describing: UserDefaults.standard.value(forKey: "monthlyIncome")))")
    print("UserDefault recurringExpenses = \(String(describing: UserDefaults.standard.value(forKey: "recurringExpenses")))")
}

//this function is called when the ViewController is opened
@IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
    //Load saved entries into entries array if entries were saved
    let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
    let url = NSURL(fileURLWithPath: path)
    if let pathComponent = url.appendingPathComponent("entries") {
        let filePath = pathComponent.path
        let fileManager = FileManager.default
        if fileManager.fileExists(atPath: filePath) {
            print("File available.")
            entriesDatabase = loadEntries()!
            print("Database loaded.")
        } else {
            print("File not available.")
        }
    } else {
        print("File path not available.")
    }
    
    calculate()
}

private func loadEntries() -> [Entry]?  {
       return NSKeyedUnarchiver.unarchiveObject(withFile: Entry.ArchiveURL.path) as? [Entry]
   }

I hope, that makes my problem clearer and thanks again!

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Sarovas
  • 1
  • 2
  • You should not use UserDefaults to pass values between view controllers, instead see https://stackoverflow.com/questions/5210535/passing-data-between-view-controllers – Joakim Danielson Jul 21 '20 at 21:15
  • First off, thanks for your reply. Maybe my description was not clear enough. I do not use UserDefaults for passing the data, but the NSKeyedArchiver with permanent Safe files. Also, the thread you refer to does - at least to my understanding - not solve the problem I am having. I successfully pass the data, but Mu calculation does not work. The arrays remain empty. Perhaps, however, I just do not understand what you are saying. Would you be so kind to elaborate? :) – Sarovas Jul 21 '20 at 21:23
  • @JoakimDanielson Please see updated code in question. – Sarovas Jul 22 '20 at 06:20
  • I see some things with your code that can be improved but if your claim that `entriesDatabase` contains objects then your code calculates the sum correctly. – Joakim Danielson Jul 22 '20 at 07:04
  • @JoakimDanielson Hm, okay. Well I think I'll just have to stay put and figure out what could go wrong when passing the data to entriesDatabase. Thanks for the help! – Sarovas Jul 22 '20 at 07:52

1 Answers1

0

The way I see it, in var entriesDatabase = [Entry]() you create a new array for objects of type Entry, which is (of course) initially empty. Therefore, the sum of the values will be 0.

What you want is to cache the values, e.g. in your saveEntries()-Function. You may want to take a look at UserDefaults which stores information in a map-like matter.

alistairv
  • 159
  • 9
  • Exactly, I do That because I did not know how to directly access the entries array in the other ViewController. But what I also do is load the persistently saved entries array into the entriesDatabase array before executing the calculation. Therefore, it should not be empty by then. I‘m a bit worried the entries array may be too big to be stored within UserDefaults. That’s why I am using the NSKeyedArchiver and save the data persistently on the device. – Sarovas Jul 21 '20 at 21:09
  • @Sarovas I am only answering considering what you have posted as code. In the second code chunk, I only see two brand-new arrays, `entryDatabase` and `sumArray`, being created. `for entry in entriesDatabase` does not enter loop body because `entriesDatabase` is empty. – alistairv Jul 21 '20 at 21:12
  • Okay, I See. I will add more code to make my system clearer tomorrow. Thanks for your help! – Sarovas Jul 21 '20 at 21:16