I'm learning Swift through a project. I'm creating an app where the user can create multiple accounts and register transactions within each account. The models and views that are relevant to my question are shown below.
When the user taps on an account, they are taken to the transactions view which shows all the transactions for that selected account. The user can then add a new transaction to the account.
What's not clear to me is how to pass the selected account to the TransactionView and the AddTransactionView when using a NavigationStack. The code I currently have throws the error: Cannot convert value of type 'Bindable<Account>' to expected argument type 'Binding<Account>'
If I change Bindable to .constant that seems to work, but I'm not sure if that's correct.
Would appreciate an explanation of why what I have doesn't work as well.
import Foundation
import SwiftData
@Model
final class Account {
var created: Date
var name: String
var transactions: [Transaction]
init(created: Date, name: String, transactions: [Transaction] = []) {
self.created = created
self.name = name
self.transactions = transactions
}
}
import Foundation
import SwiftData
@Model
final class Transaction {
var date: Date
var value: Decimal?
init(date: Date, value: Decimal? = nil) {
self.date = date
self.value = value
}
}
import SwiftData
import SwiftUI
struct AccountsView: View {
@Query private var accounts: [Account]
@State private var isAddingAccount = false
var body: some View {
NavigationStack {
List {
ForEach(accounts) { account in
NavigationLink(destination: TransactionsView(account: Bindable(account))) {
Text(account.name)
}
}
}
.navigationTitle("Accounts")
.toolbar {
ToolbarItem {
Button {
isAddingAccount = true
} label: {
Label("Add Account", systemImage: "plus")
}
}
}
}
.fullScreenCover(isPresented: $isAddingAccount) {
AddAccountView(isPresented: $isAddingAccount)
}
}
}
import SwiftUI
struct TransactionsView: View {
@Binding var account: Account
var transactions: [Transaction] { account.transactions }
@State private var isAddingTransaction = false
var body: some View {
NavigationStack {
List {
ForEach(transactions) { transaction in
NavigationLink(destination: Text(transaction.date.description)) {
Text(transaction.value!.formatted())
}
}
}
.toolbar {
ToolbarItem {
Button {
isAddingTransaction = true
} label: {
Label("Add transaction", systemImage: "plus")
}
}
}
}
.fullScreenCover(isPresented: $isAddingTransaction) {
AddTransactionView(isPresented: $isAddingTransaction, account: $account)
}
}
}
import SwiftUI
struct AddTransactionView: View {
@Binding var isPresented: Bool
@Binding var account: Account
@State private var newTransaction = Transaction(date: Date())
var body: some View {
NavigationView {
VStack {
Form {
DatePicker(selection: $newTransaction.date, displayedComponents: .date) { Text("Date") }
.datePickerStyle(.compact)
TextField(value: $newTransaction.value, format: .number) {
Text("Value")
}
}
Button("Add") {
addTransaction(transaction: newTransaction)
isPresented = false
}
}
.navigationTitle("Add transaction")
.navigationBarItems(trailing: Button("Cancel") {
isPresented = false
})
}
}
private func addTransaction(transaction: Transaction) {
withAnimation {
account.transactions.append(transaction)
}
}
}
I would suggest you read up more look at some tutorials on how to use @Bindable so you properly understand this.
You should declare properties in your view using Bindable and not Binding so it should be
@Bindable var account: Account
and when calling a view with such a property you do not need to do anything special with the value passed
NavigationLink(destination: TransactionsView(account: account))
As for your actual code you do not need to use @Bindable at all when adding transactions to an account since the Account object isn't updated really. This might be a bit confusing but when adding a transaction to an account object you are updating the relationship between them and not the account object itself.
So with that said we can remove @Bindable for the Account property in both the child views and change the declaration to
let account: Account
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With