Working with Swiftdata in the format of one model, everything is fine, there is a Transaction model, there is an Account model, they are not directly connected, but have a logical connection in the form of Accountid – ID
@Model final class Transaction: Decodable, Identifiable {
@Attribute(.unique) var id: UInt32
var accountFromID: UInt32
var accountToID: UInt32
var amountFrom: Decimal
var amountTo: Decimal
init(...) {...}
private enum CodingKeys: CodingKey { ... }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UInt32.self, forKey: .id)
accountFromID = try container.decode(UInt32.self, forKey: .accountFromID)
accountToID = try container.decode(UInt32.self, forKey: .accountToID)
amountFrom = try container.decode(Decimal.self, forKey: .amountFrom)
amountTo = try container.decode(Decimal.self, forKey: .amountTo)
}
}
@Model final class Account: Decodable, Identifiable {
@Attribute(.unique) var id: UInt32
var remainder: Decimal
init( ... ) { ... }
private enum CodingKeys: String, CodingKey { ... }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UInt32.self, forKey: .id)
remainder = try container.decode(Decimal.self, forKey: .remainder)
}
}
What I want to do:
@Model class Transaction: Decodable, Identifiable {
@Attribute(.unique) var id: UInt32
var accountFrom: Account
var accountTo: Account
var amountFrom: Decimal
var amountTo: Decimal
...
}
But I absolutely do not understand how I can turn two different JSON into a voluminous model
Transaction JSON server response:
[
{
"id": 1,
"accountFromID": 1,
"accountToID": 2,
"amountFrom": 1.3,
"amountTo": 1.3
},
...
]
Account JSON server response:
[
{
"id": 1,
"remainder": 100
},
...
]
I need to do something like such an answer from the server?
[
{
"id": 1,
"accountFrom": {
"id": 1,
"remainder": 100
},
"accountTo": {
"id": 2,
"remainder": 90
},
"amountFrom": 1.3,
"amountTo": 1.3
},
...
]
In this case, there will be a lot of duplicated accounts, which can shoot in the leg
Or general response with all info?
{
"transactions": [
{
"id": 1,
"accountFromID": 1,
"accountTo": 2,
"amountFrom": 1.3,
"amountTo": 1.3
},
...
],
"accounts": [
{
"id": 1,
"remainder": 100
},
...
]
}
This option seems to be the most optimal, in it we separately decode different parts of JSON and can connect models by identifiers, but maybe there is a better way?
This is how I would approach it:
Add the server side id keys as separate attributes in Transaction and Account. Something like
var externalId: Int
This can then be used to connect transactions and accounts but it might also be relevant to store the original id for troubleshooting etc.
Then implement Account
and Transaction
as you want them
@Model class Transaction: Decodable, Identifiable {
var accountFrom: Account
var accountTo: Account
var amountFrom: Decimal
var amountTo: Decimal
var externalId: Int
...
}
Then I would first decode and import the accounts json and then when decoding and importing the transactions I would either have an account cache, [Int: Account]
, where the key is the external id or create a specific fetch with a predicate using the external id depending on the amount of Account objects.
When importing the transactions you can then decode the id properties to Int values and use the cache or fetch function from inside init(from:)
let accountFromID = try container.decode(Int.self, forKey: .accountFromID)
if let account = accountCache[accountFromID] {
accountFrom = account
} else {
//error handling. Throw error?
}
Your ids should match the “unique” Attribute. If you do that swift data with update/create as needed. Manage the accounts and transactions as relationships where the two different models meet.
@loremipsum That is, where the Transactions and Accounts are used, to do [int: account] and compare the dictionary instead of using the binding in Swiftdata? Now it is realized, only in those places where we need to compare twice Accounts, for everyone else Accountgrounds and Currencies. I thought that if these data will immediately turn out at Query, then this will greatly accelerate the interface, since it is not necessary to get data from the hash of the table 6 times on one transaction (which seems to happen for O (1), but in fact this happens with slowing down interface)
You don’t need a dictionary or a Binding. You need a Relationship.