Prefixed Core Data entities with 'Persisted' to avoid name conflicts with UI data model. Caching mapped objects so that Core Data doesn't persist the same data multiple times.

This commit is contained in:
dankito 2020-07-23 22:43:10 +02:00
parent 043faeb604
commit c6c8f7b12e
3 changed files with 91 additions and 61 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="16119" systemVersion="19E287" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="AccountTransaction" representedClassName="AccountTransaction" syncable="YES" codeGenerationType="class">
<entity name="PersistedAccountTransaction" representedClassName="PersistedAccountTransaction" syncable="YES" codeGenerationType="class">
<attribute name="amount" attributeType="Decimal" defaultValueString="0.0"/>
<attribute name="bookingDate" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="bookingKey" attributeType="String"/>
@ -34,9 +34,9 @@
<attribute name="unparsedUsage" attributeType="String"/>
<attribute name="usageWithNoSpecialType" optional="YES" attributeType="String"/>
<attribute name="valueDate" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="BankAccount" inverseName="transactions" inverseEntity="BankAccount"/>
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistedBankAccount" inverseName="transactions" inverseEntity="PersistedBankAccount"/>
</entity>
<entity name="BankAccount" representedClassName="BankAccount" syncable="YES" codeGenerationType="class">
<entity name="PersistedBankAccount" representedClassName="PersistedBankAccount" syncable="YES" codeGenerationType="class">
<attribute name="accountHolderName" attributeType="String"/>
<attribute name="accountLimit" optional="YES" attributeType="String"/>
<attribute name="balance" attributeType="Decimal" defaultValueString="0.0"/>
@ -52,10 +52,10 @@
<attribute name="supportsRetrievingBalance" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="supportsTransferringMoney" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="type" attributeType="String"/>
<relationship name="customer" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Customer" inverseName="accounts" inverseEntity="Customer"/>
<relationship name="transactions" toMany="YES" deletionRule="Cascade" destinationEntity="AccountTransaction" inverseName="account" inverseEntity="AccountTransaction"/>
<relationship name="customer" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistedCustomer" inverseName="accounts" inverseEntity="PersistedCustomer"/>
<relationship name="transactions" toMany="YES" deletionRule="Cascade" destinationEntity="PersistedAccountTransaction" inverseName="account" inverseEntity="PersistedAccountTransaction"/>
</entity>
<entity name="Customer" representedClassName="Customer" syncable="YES" codeGenerationType="class">
<entity name="PersistedCustomer" representedClassName="PersistedCustomer" syncable="YES" codeGenerationType="class">
<attribute name="bankCode" attributeType="String"/>
<attribute name="bankName" attributeType="String"/>
<attribute name="bic" attributeType="String"/>
@ -65,11 +65,11 @@
<attribute name="iconUrl" optional="YES" attributeType="String"/>
<attribute name="password" attributeType="String"/>
<attribute name="userId" attributeType="String"/>
<relationship name="accounts" toMany="YES" deletionRule="Cascade" destinationEntity="BankAccount" inverseName="customer" inverseEntity="BankAccount"/>
<relationship name="accounts" toMany="YES" deletionRule="Cascade" destinationEntity="PersistedBankAccount" inverseName="customer" inverseEntity="PersistedBankAccount"/>
</entity>
<elements>
<element name="AccountTransaction" positionX="-36" positionY="45" width="128" height="553"/>
<element name="BankAccount" positionX="-54" positionY="63" width="128" height="28"/>
<element name="Customer" positionX="-63" positionY="-18" width="128" height="28"/>
<element name="PersistedAccountTransaction" positionX="-36" positionY="45" width="128" height="553"/>
<element name="PersistedBankAccount" positionX="-54" positionY="63" width="128" height="298"/>
<element name="PersistedCustomer" positionX="-63" positionY="-18" width="128" height="193"/>
</elements>
</model>

View File

@ -19,7 +19,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
}
func saveOrUpdateAccount(customer: BUCCustomer, allCustomers: [BUCCustomer]) {
func saveOrUpdateAccount(customer: Customer, allCustomers: [Customer]) {
do {
let mapped = mapper.map(customer, context)
@ -31,22 +31,22 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
}
}
func readPersistedAccounts_() -> [BUCCustomer] {
var customers: [Customer] = []
func readPersistedAccounts_() -> [Customer] {
var customers: [PersistedCustomer] = []
do {
let request: NSFetchRequest<Customer> = Customer.fetchRequest()
let request: NSFetchRequest<PersistedCustomer> = PersistedCustomer.fetchRequest()
request.returnsObjectsAsFaults = false
try customers = context.fetch(request)
} catch {
NSLog("Could not request Customers: \(error)")
print("Could not request Customers: \(error)")
}
return customers.map( { mapper.map($0) } )
}
func deleteAccount(customer: BUCCustomer, allCustomers: [BUCCustomer]) {
func deleteAccount(customer: Customer, allCustomers: [Customer]) {
do {
let mapped = mapper.map(customer, context)
@ -54,12 +54,20 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
try context.save()
} catch {
NSLog("Could not delete Customer \(customer): \(error)")
print("Could not delete Customer \(customer): \(error)")
}
}
func saveOrUpdateAccountTransactions(bankAccount: BUCBankAccount, transactions: [BUCAccountTransaction]) {
// TODO
func saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: [AccountTransaction]) {
do {
let mapped = mapper.map(bankAccount.customer, context)
context.insert(mapped)
try context.save()
} catch {
print("Could not save transactions of account \(bankAccount): \(error)")
}
}
func saveUrlToFile(url: String, file: URL) {
@ -69,7 +77,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
func deleteAll() {
do {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Customer")
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "PersistedCustomer")
let deleteAll = NSBatchDeleteRequest(fetchRequest: request)

View File

@ -5,16 +5,26 @@ import BankingUiSwift
class Mapper {
func map(_ customer: Customer) -> BUCCustomer {
let mapped = BUCCustomer(bankCode: map(customer.bankCode), customerId: map(customer.customerId), password: map(customer.password), finTsServerAddress: map(customer.finTsServerAddress), bankName: map(customer.bankName), bic: map(customer.bic), customerName: map(customer.customerName), userId: map(customer.userId), iconUrl: customer.iconUrl, accounts: [])
/* Cache mapped object to not save them twice */
private var mappedBanks = [Customer:PersistedCustomer]()
mapped.accounts = map(mapped, customer.accounts as? Set<BankAccount>)
private var mappedAccounts = [BankAccount:PersistedBankAccount]()
private var mappedTransactions = [AccountTransaction:PersistedAccountTransaction]()
func map(_ customer: PersistedCustomer) -> Customer {
let mapped = Customer(bankCode: map(customer.bankCode), customerId: map(customer.customerId), password: map(customer.password), finTsServerAddress: map(customer.finTsServerAddress), bankName: map(customer.bankName), bic: map(customer.bic), customerName: map(customer.customerName), userId: map(customer.userId), iconUrl: customer.iconUrl, accounts: [])
mapped.accounts = map(mapped, customer.accounts as? Set<PersistedBankAccount>)
mappedBanks[mapped] = customer
return mapped
}
func map(_ customer: BUCCustomer, _ context: NSManagedObjectContext) -> Customer {
let mapped = Customer(context: context)
func map(_ customer: Customer, _ context: NSManagedObjectContext) -> PersistedCustomer {
let mapped = mappedBanks[customer] ?? PersistedCustomer(context: context)
mapped.bankCode = customer.bankCode
mapped.customerId = customer.customerId
@ -28,28 +38,32 @@ class Mapper {
mapped.accounts = NSSet(array: map(mapped, customer.accounts, context))
mappedBanks[customer] = mapped
return mapped
}
func map(_ customer: BUCCustomer, _ accounts: Set<BankAccount>?) -> [BUCBankAccount] {
func map(_ customer: Customer, _ accounts: Set<PersistedBankAccount>?) -> [BankAccount] {
return accounts?.map( { map(customer, $0) } ) ?? []
}
func map(_ customer: BUCCustomer, _ account: BankAccount) -> BUCBankAccount {
let mapped = BUCBankAccount(customer: customer, identifier: map(account.identifier), accountHolderName: map(account.accountHolderName), iban: account.iban, subAccountNumber: account.subAccountNumber, customerId: map(account.customerId), balance: map(account.balance), currency: map(account.currency), type: map(account.type), productName: account.productName, accountLimit: account.accountLimit, lastRetrievedTransactionsTimestamp: map(account.lastRetrievedTransactionsTimestamp), supportsRetrievingAccountTransactions: account.supportsRetrievingAccountTransactions, supportsRetrievingBalance: account.supportsRetrievingBalance, supportsTransferringMoney: account.supportsTransferringMoney, supportsInstantPaymentMoneyTransfer: account.supportsInstantPaymentMoneyTransfer, bookedAccountTransactions: [])
func map(_ customer: Customer, _ account: PersistedBankAccount) -> BankAccount {
let mapped = BankAccount(customer: customer, identifier: map(account.identifier), accountHolderName: map(account.accountHolderName), iban: account.iban, subAccountNumber: account.subAccountNumber, customerId: map(account.customerId), balance: map(account.balance), currency: map(account.currency), type: map(account.type), productName: account.productName, accountLimit: account.accountLimit, lastRetrievedTransactionsTimestamp: map(account.lastRetrievedTransactionsTimestamp), supportsRetrievingAccountTransactions: account.supportsRetrievingAccountTransactions, supportsRetrievingBalance: account.supportsRetrievingBalance, supportsTransferringMoney: account.supportsTransferringMoney, supportsInstantPaymentMoneyTransfer: account.supportsInstantPaymentMoneyTransfer, bookedTransactions: [], unbookedTransactions: [])
mapped.bookedTransactions = map(mapped, account.transactions as? Set<AccountTransaction>)
mapped.bookedTransactions = map(mapped, account.transactions as? Set<PersistedAccountTransaction>)
mappedAccounts[mapped] = account
return mapped
}
func map(_ customer: Customer, _ accounts: [BUCBankAccount], _ context: NSManagedObjectContext) -> [BankAccount] {
func map(_ customer: PersistedCustomer, _ accounts: [BankAccount], _ context: NSManagedObjectContext) -> [PersistedBankAccount] {
return accounts.map( { map(customer, $0, context) } )
}
func map(_ customer: Customer, _ account: BUCBankAccount, _ context: NSManagedObjectContext) -> BankAccount {
let mapped = BankAccount(context: context)
func map(_ customer: PersistedCustomer, _ account: BankAccount, _ context: NSManagedObjectContext) -> PersistedBankAccount {
let mapped = mappedAccounts[account] ?? PersistedBankAccount(context: context)
mapped.customer = customer
mapped.identifier = account.identifier
@ -70,57 +84,63 @@ class Mapper {
mapped.transactions = NSSet(array: map(mapped, account.bookedTransactions, context))
mappedAccounts[account] = mapped
return mapped
}
func map(_ type: BUCBankAccountType) -> String {
func map(_ type: BankAccountType) -> String {
return type.name
}
func map(_ type: String?) -> BUCBankAccountType {
func map(_ type: String?) -> BankAccountType {
switch type {
case BUCBankAccountType.girokonto.name:
return BUCBankAccountType.girokonto
case BUCBankAccountType.sparkonto.name:
return BUCBankAccountType.sparkonto
case BUCBankAccountType.festgeldkonto.name:
return BUCBankAccountType.festgeldkonto
case BUCBankAccountType.wertpapierdepot.name:
return BUCBankAccountType.wertpapierdepot
case BUCBankAccountType.darlehenskonto.name:
return BUCBankAccountType.darlehenskonto
case BUCBankAccountType.kreditkartenkonto.name:
return BUCBankAccountType.kreditkartenkonto
case BUCBankAccountType.fondsdepot.name:
return BUCBankAccountType.fondsdepot
case BUCBankAccountType.bausparvertrag.name:
return BUCBankAccountType.bausparvertrag
case BUCBankAccountType.versicherungsvertrag.name:
return BUCBankAccountType.versicherungsvertrag
case BUCBankAccountType.sonstige.name:
return BUCBankAccountType.sonstige
case BankAccountType.girokonto.name:
return BankAccountType.girokonto
case BankAccountType.sparkonto.name:
return BankAccountType.sparkonto
case BankAccountType.festgeldkonto.name:
return BankAccountType.festgeldkonto
case BankAccountType.wertpapierdepot.name:
return BankAccountType.wertpapierdepot
case BankAccountType.darlehenskonto.name:
return BankAccountType.darlehenskonto
case BankAccountType.kreditkartenkonto.name:
return BankAccountType.kreditkartenkonto
case BankAccountType.fondsdepot.name:
return BankAccountType.fondsdepot
case BankAccountType.bausparvertrag.name:
return BankAccountType.bausparvertrag
case BankAccountType.versicherungsvertrag.name:
return BankAccountType.versicherungsvertrag
case BankAccountType.sonstige.name:
return BankAccountType.sonstige
default:
return BUCBankAccountType.girokonto
return BankAccountType.girokonto
}
}
func map(_ account: BUCBankAccount, _ transactions: Set<AccountTransaction>?) -> [BUCAccountTransaction] {
func map(_ account: BankAccount, _ transactions: Set<PersistedAccountTransaction>?) -> [AccountTransaction] {
return transactions?.map( {map(account, $0) } ) ?? []
}
func map(_ account: BUCBankAccount, _ transaction: AccountTransaction) -> BUCAccountTransaction {
return BUCAccountTransaction(bankAccount: account, amount: map(transaction.amount), currency: map(transaction.currency), unparsedUsage: map(transaction.unparsedUsage), bookingDate: map(transaction.bookingDate), otherPartyName: transaction.otherPartyName, otherPartyBankCode: transaction.otherPartyBankCode, otherPartyAccountId: transaction.otherPartyAccountId, bookingText: transaction.bookingText, valueDate: map(transaction.valueDate), statementNumber: Int32(transaction.statementNumber), sequenceNumber: map(transaction.sequenceNumber), openingBalance: map(transaction.openingBalance), closingBalance: map(transaction.closingBalance), endToEndReference: transaction.endToEndReference, customerReference: transaction.customerReference, mandateReference: transaction.mandateReference, creditorIdentifier: transaction.creditorIdentifier, originatorsIdentificationCode: transaction.originatorsIdentificationCode, compensationAmount: transaction.compensationAmount, originalAmount: transaction.originalAmount, sepaUsage: transaction.sepaUsage, deviantOriginator: transaction.deviantOriginator, deviantRecipient: transaction.deviantRecipient, usageWithNoSpecialType: transaction.usageWithNoSpecialType, primaNotaNumber: transaction.primaNotaNumber, textKeySupplement: transaction.textKeySupplement, currencyType: transaction.currencyType, bookingKey: map(transaction.bookingKey), referenceForTheAccountOwner: map(transaction.referenceForTheAccountOwner), referenceOfTheAccountServicingInstitution: transaction.referenceOfTheAccountServicingInstitution, supplementaryDetails: transaction.supplementaryDetails, transactionReferenceNumber: map(transaction.transactionReferenceNumber), relatedReferenceNumber: transaction.relatedReferenceNumber)
func map(_ account: BankAccount, _ transaction: PersistedAccountTransaction) -> AccountTransaction {
let mapped = AccountTransaction(bankAccount: account, amount: map(transaction.amount), currency: map(transaction.currency), unparsedUsage: map(transaction.unparsedUsage), bookingDate: map(transaction.bookingDate), otherPartyName: transaction.otherPartyName, otherPartyBankCode: transaction.otherPartyBankCode, otherPartyAccountId: transaction.otherPartyAccountId, bookingText: transaction.bookingText, valueDate: map(transaction.valueDate), statementNumber: Int32(transaction.statementNumber), sequenceNumber: map(transaction.sequenceNumber), openingBalance: map(transaction.openingBalance), closingBalance: map(transaction.closingBalance), endToEndReference: transaction.endToEndReference, customerReference: transaction.customerReference, mandateReference: transaction.mandateReference, creditorIdentifier: transaction.creditorIdentifier, originatorsIdentificationCode: transaction.originatorsIdentificationCode, compensationAmount: transaction.compensationAmount, originalAmount: transaction.originalAmount, sepaUsage: transaction.sepaUsage, deviantOriginator: transaction.deviantOriginator, deviantRecipient: transaction.deviantRecipient, usageWithNoSpecialType: transaction.usageWithNoSpecialType, primaNotaNumber: transaction.primaNotaNumber, textKeySupplement: transaction.textKeySupplement, currencyType: transaction.currencyType, bookingKey: map(transaction.bookingKey), referenceForTheAccountOwner: map(transaction.referenceForTheAccountOwner), referenceOfTheAccountServicingInstitution: transaction.referenceOfTheAccountServicingInstitution, supplementaryDetails: transaction.supplementaryDetails, transactionReferenceNumber: map(transaction.transactionReferenceNumber), relatedReferenceNumber: transaction.relatedReferenceNumber)
mappedTransactions[mapped] = transaction
return mapped
}
func map(_ account: BankAccount, _ transactions: [BUCAccountTransaction], _ context: NSManagedObjectContext) -> [AccountTransaction] {
func map(_ account: PersistedBankAccount, _ transactions: [AccountTransaction], _ context: NSManagedObjectContext) -> [PersistedAccountTransaction] {
return transactions.map( {map(account, $0, context) } )
}
func map(_ account: BankAccount, _ transaction: BUCAccountTransaction, _ context: NSManagedObjectContext) -> AccountTransaction {
let mapped = AccountTransaction(context: context)
func map(_ account: PersistedBankAccount, _ transaction: AccountTransaction, _ context: NSManagedObjectContext) -> PersistedAccountTransaction {
let mapped = mappedTransactions[transaction] ?? PersistedAccountTransaction(context: context)
mapped.account = account
@ -162,6 +182,8 @@ class Mapper {
mapped.transactionReferenceNumber = transaction.transactionReferenceNumber
mapped.relatedReferenceNumber = transaction.relatedReferenceNumber
mappedTransactions[transaction] = mapped
return mapped
}