1

How can I init a struct that contains an array of another struct?

I want to create an Invoice entry with all properties and items having an array of InvoiceItem.

Also, items can be null when invoice is still a draft.

Reading the JSON response from the server with invoice data and items works fine.

On the save action of the viewcontroller I have:

var invoice: Invoice?
var client: Client?
var invoiceItems : [InvoiceItem] = [InvoiceItem]()

@IBAction func saveInvoice(_ sender: Any) {

...

invoice = Invoice(invoice_id: invoice?.invoice_id, client_id: client_id, tax: 0.00, date_issue: dateIssue, due_date: dueDate,
amount_paid: 0.00,
date_transaction: "0000-00-00",
voided: 0,
note: "",
invoice_number: "",
status: "",
items: invoiceItems
)

override func viewDidLoad() {

...
 invoiceItems = (invoice?.items)!
...
}

I get this error: Cannot convert value of type '[InvoiceItem]' to expected argument type 'InvoiceItem', on line: items: invoiceItems

struct InvoiceItem: Codable {
    var invoice_detail_id: Int!
    let description: String!
    let unit_price: Decimal!
    let quantity: Decimal!

    init(invoice_detail_id: Int! = nil, description: String, unit_price: Decimal, quantity: Decimal) {
        self.invoice_detail_id = invoice_detail_id
        self.description = description
        self.unit_price = unit_price
        self.quantity = quantity
    }
}

struct Invoice: Codable {
    var invoice_id: Int!
    let client_id: Int!
    let tax: Decimal!
    let date_issue: String!
    let due_date: String!
    let amount_paid: Decimal!
    let date_transaction: String!
    let voided: Int!
    let note: String!
    let invoice_number: String!
    let status: String!
    let items: [InvoiceItem]!

    init(invoice_id: Int! = nil, client_id: Int! = nil,tax: Decimal, date_issue: String, due_date: String, amount_paid: Decimal, date_transaction: String, voided: Int, note: String, invoice_number: String, status: String, items: InvoiceItem) {
        self.invoice_id = invoice_id
        self.client_id = client_id
        self.tax = tax
        self.date_issue = date_issue
        self.due_date = due_date
        self.amount_paid = amount_paid
        self.date_transaction = date_transaction
        self.voided = voided
        self.note = note
        self.invoice_number = invoice_number
        self.status = status
        self.items = [items]
    }


}
3
  • Unrelated but never, never, never declare properties as implicit unwrapped optional in a class / struct which are initialized in an init method. If a property is initialized with a non-optional value declare it as non-optional (no exclamation mark) otherwise as regular optional (?). Commented Feb 28, 2018 at 16:38
  • In new in Swift. I tried w ? but got erros all over the code Commented Feb 28, 2018 at 16:42
  • What errors? Remove all exclamation marks except invoice_id, client_id and invoice_detail_id. For those 3 properties change ! to ? Commented Feb 28, 2018 at 16:46

2 Answers 2

2

Your invoice init has items: InvoiceItem as a parameter. The type of items is an InvoiceItem, not an array of InvoiceItem.

Change the type to an array of InvoiceItem.

EDIT:

In your Invoice init you have:

self.items = [items]

You were trying to pass in an array of InvoiceItem into a parameter defined as items: InvoiceItem.

That obviously won't work, so I told you to change the parameter type to an array of InvoiceItem.

Now that you have, you are wrapping your array of InvoiceItem in array. So you have an array of an array of InvoiceItem.

Change that line of code to:

self.items = items
Sign up to request clarification or add additional context in comments.

2 Comments

i tried it, but still erros: init... items: [InvoiceItem]) { ... self.items = items OR self.items = [items] There was a delay on xcode. Looks ok now.
@RenanAguiar See edit, there was another problem that came up because of the first change I told you to do.
0

Let's name the method with explicit names, and add one, because you are mixing array and unique object.

I'd suggest:

init(invoice_id: ... status: String, anItem: InvoiceItem)
{
   ...
   self.items = [anItem]
}

init(invoice_id: ... status: String, multipleItems: [InvoiceItem])
{
   ...
   self.items = multipleItems
}

In your code, since it's var invoiceItems : [InvoiceItem] = [InvoiceItem](), I'd use the second one because it's an array of InvoiceItem.

invoice = Invoice(invoice_id: invoice?.invoice_id,
                  client_id: client_id,
                  tax: 0.00,
                  date_issue: dateIssue,
                  due_date: dueDate,
                  amount_paid: 0.00,
                  date_transaction: "0000-00-00",
                  voided: 0,
                  note: "",
                  invoice_number: "",
                  status: "",
                  multipleItems: invoiceItems
                  )

I don't know if you still want to use the first one, but just in case you can use keep. Do avoid duplicate code still between these two methods, the first one could call the second one with init(... multipleItems:[anItem]).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.