Calculated Data Value

Hi Team, I am very new and may not be explaining myself very well here but hope someone can help.

I have a section of my app where the user will input a figure (I.e. weekly income amount) and I want to calculate the annual amount ( * 52) then store both figures to be used in other screens oif the app.

Below is how I am passing the data through bindings:

import Foundation

struct IncomeDetails: Identifiable, Codable {
let id: UUID
var incomeDetails: String
var incomeFrequency: Period
var incomeAmount: Int
var incomeAnnual: Int

init(id: UUID = UUID(), incomeDetails: String, incomeFrequency: Period, incomeAmount: Int, incomeAnnual: Int) {
    self.id = id
    self.incomeDetails = incomeDetails
    self.incomeFrequency = incomeFrequency
    self.incomeAmount = incomeAmount
    self.incomeAnnual = incomeAnnual
}

}

extension IncomeDetails {

struct Data {
var incomeDetails: String = “”
var incomeFrequency: Period = .Weekly
var incomeAmount: Int = 0
var incomeAnnual: Int = 0
}

var idata: Data {
Data(incomeDetails: incomeDetails, incomeFrequency: incomeFrequency, incomeAmount: incomeAmount, incomeAnnual: incomeAnnual)
}

mutating func update(from idata: Data) {
    incomeDetails = idata.incomeDetails
    incomeFrequency = idata.incomeFrequency
    incomeAmount = Int(idata.incomeAmount)
    incomeAnnual = Int(idata.incomeAnnual)
}

init(idata: Data) {
    id = UUID()
    incomeDetails = idata.incomeDetails
    incomeFrequency = idata.incomeFrequency
    incomeAmount = Int(idata.incomeAmount)
    incomeAnnual = Int(idata.incomeAnnual)
}

}

extension IncomeDetails {
static let sampledata: [IncomeDetails] =
[
IncomeDetails(incomeDetails: “Wages”, incomeFrequency: .Weekly, incomeAmount: 1000, incomeAnnual: 52000),
IncomeDetails(incomeDetails: “Dividends”, incomeFrequency: .Quarterly, incomeAmount: 500, incomeAnnual: 2000)
]
}

Then this is the code which I am using to have users input data: (I have bolded the area which I have having trouble with):

import SwiftUI

struct IncomeEditView: View {
@Binding var data: IncomeDetails.Data
@State private var newIncomeDetails = “”
@State private var newIncomeAmount = “”
@State private var isPresentingEditView = false

var body: some View {
    Form {
        Section(header: Text("Details")) {
            TextField("Details", text: $data.incomeDetails)
        }
        Section(header: Text("Frequency")) {
            PeriodPicker(selection: $data.incomeFrequency)
        }
        Section(header: Text("Amount")) {
            TextField("Amount", value: $data.incomeAmount, format: .number)
                .keyboardType(.numberPad)
        }
        **Section(header: Text("Annual Amount")) {**

** TextField(“Amount”, value: ( if data.incomeFrequency == “Annually” {**
** data.incomeAmount**
** }**
** else if data.incomeFrequency == “Quarterly” {**
** data.incomeAmount * 4**
** }**
** ), format: .number)**
** .keyboardType(.numberPad)**
** .disabled(true)**
}
}
}
}

struct IncomeEditView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
IncomeEditView(data: .constant(IncomeDetails.sampledata[0].idata))
}
}
}

The errors which I am getting are:
Expected expression in list of expressions
No exact matches in call to initilizer

if anyone can help that would be awesome.

Thanks

@scott_foley

Hi Scott,

Welcome to the community.

Just a tip regarding adding code in your post.

To format the code nicely, place 3 back-ticks ``` on the line above your code and 3 back-ticks ``` on the line below your code. Like this:

```
Code goes here
```

The 3 back-ticks must be the ONLY characters on the line. The back-tick character is located on the same keyboard key as the tilde character ~ (which is located below the Esc key). You can also highlight an entire code block and click the </> button on the toolbar to wrap the block for you.

This also makes it easier for anyone assisting as they can copy the code and carry out some testing.

Maybe you could take a few moments to edit your post and delete then re-insert the blocks of code and wrap them as suggested. This will make a huge difference in allowing us to assist.

Cheers
Chris Parker

Hi @scott_foley, and welcome to the forums!

I was able to rewrite some of your code from the question, but it would be truly helpful if you can format your code using three backticks ``` for next time.

Here is the model code that I got from your code. Note that I have written the Period as an enum since it was not included alongside your code, but you may use your own version of the Period to model the income frequency if you wanted.

import Foundation

enum Period: String, Codable {
  case weekly, fortnightly, monthly, quarterly, annual
}

struct IncomeDetails: Identifiable, Codable {
  let id: UUID
  var incomeDetails: String
  var incomeFrequency: Period
  var incomeAmount: Int
  var incomeAnnual: Int
  
  init(id: UUID = UUID(), incomeDetails: String, incomeFrequency: Period, incomeAmount: Int, incomeAnnual: Int) {
    self.id = id
    self.incomeDetails = incomeDetails
    self.incomeFrequency = incomeFrequency
    self.incomeAmount = incomeAmount
    self.incomeAnnual = incomeAnnual
  }
}

extension IncomeDetails {
  
  struct Data {
    var incomeDetails: String = ""
    var incomeFrequency: Period = .weekly
    var incomeAmount: Int = 0
    var incomeAnnual: Int = 0
  }
  
  var idata: Data {
    Data(incomeDetails: incomeDetails, incomeFrequency: incomeFrequency, incomeAmount: incomeAmount, incomeAnnual: incomeAnnual)
  }
  
  mutating func update(from idata: Data) {
    incomeDetails = idata.incomeDetails
    incomeFrequency = idata.incomeFrequency
    incomeAmount = Int(idata.incomeAmount)
    incomeAnnual = Int(idata.incomeAnnual)
  }
  
  init(idata: Data) {
    id = UUID()
    incomeDetails = idata.incomeDetails
    incomeFrequency = idata.incomeFrequency
    incomeAmount = Int(idata.incomeAmount)
    incomeAnnual = Int(idata.incomeAnnual)
  }
}

extension IncomeDetails {
  static let sampledata: [IncomeDetails] =
  [
    IncomeDetails(incomeDetails: "Wages", incomeFrequency: .weekly, incomeAmount: 1000, incomeAnnual: 52000),
    IncomeDetails(incomeDetails: "Dividends", incomeFrequency: .quarterly, incomeAmount: 500, incomeAnnual: 2000)
  ]
}

I can see what you were trying to do with IncomeEditView. The annual amount should be computed based on the income amount and frequency.

I have adjusted your code but did not include the PeriodPicker section as it was not included in your code, but you should be able to use it in your copy.

import SwiftUI

struct IncomeEditView: View {
  @Binding var data: IncomeDetails.Data
  @State private var newIncomeDetails = ""
  @State private var isPresentingEditView = false
  
  @State private var newIncomeAmountString: String = ""
  @State private var newIncomeAmount: Int = 0
  
  private var computedIncomeAmount: Binding<Int> {
    Binding(
      get: {
        switch data.incomeFrequency {
        case .weekly:
          return data.incomeAmount * 52
        case .fortnightly:
          return data.incomeAmount * 26
        case .monthly:
          return data.incomeAmount * 12
        case .quarterly:
          return data.incomeAmount * 4
        case .annual:
          return data.incomeAmount
        }
      },
      set: { _ in }
    )
  }

  var body: some View {
    Form {
      Section(header: Text("Details")) {
        TextField("Details", text: $data.incomeDetails)
      }
      Section(header: Text("Frequency")) {
        // Uncomment this in your code
        // I did not include this since I don't have access to PeriodPicker view from your question
        // PeriodPicker(selection: $data.incomeFrequency)
      }
      Section(header: Text("Amount")) {
        TextField("Amount", value: $data.incomeAmount, format: .number)
          .keyboardType(.numberPad)
      }
      Section(header: Text("Annual Amount")) {
        TextField("Amount", value: computedIncomeAmount, format: .number)
          .keyboardType(.numberPad)
          .disabled(true)
      }
    }
  }
}

struct IncomeEditView_Previews: PreviewProvider {
  static var previews: some View {
    NavigationView {
      IncomeEditView(data: .constant(IncomeDetails.sampledata[0].idata))
    }
  }
}

I used a bindable computed property named computedIncomeAmount that calculates the annual amount based on the inputs for the income amount and frequency.

Your test data has a weekly income frequency, and an initial amount of 1000, which is why the annual amount becomes 52,000.

See the UI:
note this did not include the PeriodPicker section as I don't have the code from your copy for PeriodPicker

Thanks Chris, sorry I didn’t about those rules so will make sure I fix next time. Thank you

Thank you Joash that has worked.

Just so you know the enum is in my period picker file.

I am now trying to work out how to get this calculated value to update my app data which I think I need to put the calculation in my IncomeDetails file, which I have done like below but now I am getting an error on my var: data: Data line saying “Extra argument ‘incomeAnnual’ in call” and not sure what I need to do there. Are you able to help again?

Thanks

import Foundation

struct IncomeDetails: Identifiable {
    let id: UUID
    var incomeDetails: String
    var incomeFrequency: Period
    var incomeAmount: Int
    var incomeAnnual: Int
    
    init(id: UUID = UUID(), incomeDetails: String, incomeFrequency: Period, incomeAmount: Int, incomeAnnual: Int) {
        self.id = id
        self.incomeDetails = incomeDetails
        self.incomeFrequency = incomeFrequency
        self.incomeAmount = incomeAmount
        self.incomeAnnual = incomeAnnual
    }
}

extension IncomeDetails {

struct Data {
    var incomeDetails: String = ""
    var incomeFrequency: Period = .Weekly
    var incomeAmount: Int = 0
    var incomeAnnual: Int {
                    switch incomeFrequency {
                    case .Daily:
                        return incomeAmount * 365
                    case .Weekly:
                        return incomeAmount * 52
                    case .Fortnightly:
                        return incomeAmount * 26
                    case .Monthly:
                        return incomeAmount * 12
                    case .Quarterly:
                        return incomeAmount * 4
                    case .Annually:
                        return incomeAmount * 1
                    }
    }
}

var data: Data {
    Data(incomeDetails: incomeDetails, incomeFrequency: incomeFrequency, incomeAmount: incomeAmount, incomeAnnual: incomeAnnual)
}
    
    mutating func update(from data: Data) {
        incomeDetails = data.incomeDetails
        incomeFrequency = data.incomeFrequency
        incomeAmount = Int(data.incomeAmount)
        incomeAnnual = Int(data.incomeAnnual)
    }
    
    init(data: Data) {
        id = UUID()
        incomeDetails = data.incomeDetails
        incomeFrequency = data.incomeFrequency
        incomeAmount = Int(data.incomeAmount)
        incomeAnnual = Int(data.incomeAnnual)
    }
}

extension IncomeDetails {
    static let sampledata: [IncomeDetails] =
    [
        IncomeDetails(incomeDetails: "Wages", incomeFrequency: .Weekly, incomeAmount: 1000, incomeAnnual: 52000),
        IncomeDetails(incomeDetails: "Dividends", incomeFrequency: .Quarterly, incomeAmount: 500, incomeAnnual: 2000)
    ]
}
1 Like

Your Data struct has incomeAnnual as a computed property but you are also including it in the initializer, here (line reformatted for ease of viewing):

Data(incomeDetails: incomeDetails,
     incomeFrequency: incomeFrequency,
     incomeAmount: incomeAmount,
     incomeAnnual: incomeAnnual)

You don’t initialize computed properties, so remove the parameter for incomeAnnual.

Also, don’t name your data structure Data. There is already a Foundation type called Data and the duplicate names can cause issues you may not even notice until you start getting weird problems in your code that can be hard to track down. Use a more descriptive name for your own structures.

1 Like

Thank you for this while removing it from my initializer has resolved that issue in my edit screen (see code below) I can not use the incomeAnnual as the error now shows “Cannot assign property: ‘incomeAnnual’ is a get only property”. I need to be able to store this property to be used elsewhere in the app. Any thoughts on how I can achieve this for a calculated property?

Thanks

import SwiftUI

struct IncomeEditView: View {
    @Binding var data: IncomeDetails.Data
    

    var body: some View {
        Form {
            Section(header: Text("Details")) {
                TextField("Details", text: $data.incomeDetails)
            }
            Section(header: Text("Frequency")) {
                PeriodPicker(selection: $data.incomeFrequency)
            }
            Section(header: Text("Amount")) {
                TextField("Amount", value: $data.incomeAmount, format: .number)
                    .keyboardType(.numberPad)
            }
            Section(header: Text("Annual Amount")) {
                TextField("Amount", value:  $data.incomeAnnual, format: .number)
                    .keyboardType(.numberPad)
                    .disabled(true)
            }
        }
    }
}

struct IncomeEditView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            IncomeEditView(data: .constant(IncomeDetails.sampledata[0].data))
    }
}
}

1 Like