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"?> <?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=""> <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="amount" attributeType="Decimal" defaultValueString="0.0"/>
<attribute name="bookingDate" attributeType="Date" usesScalarValueType="NO"/> <attribute name="bookingDate" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="bookingKey" attributeType="String"/> <attribute name="bookingKey" attributeType="String"/>
@ -34,9 +34,9 @@
<attribute name="unparsedUsage" attributeType="String"/> <attribute name="unparsedUsage" attributeType="String"/>
<attribute name="usageWithNoSpecialType" optional="YES" attributeType="String"/> <attribute name="usageWithNoSpecialType" optional="YES" attributeType="String"/>
<attribute name="valueDate" attributeType="Date" usesScalarValueType="NO"/> <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>
<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="accountHolderName" attributeType="String"/>
<attribute name="accountLimit" optional="YES" attributeType="String"/> <attribute name="accountLimit" optional="YES" attributeType="String"/>
<attribute name="balance" attributeType="Decimal" defaultValueString="0.0"/> <attribute name="balance" attributeType="Decimal" defaultValueString="0.0"/>
@ -52,10 +52,10 @@
<attribute name="supportsRetrievingBalance" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="supportsRetrievingBalance" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="supportsTransferringMoney" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="supportsTransferringMoney" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="type" attributeType="String"/> <attribute name="type" attributeType="String"/>
<relationship name="customer" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Customer" inverseName="accounts" inverseEntity="Customer"/> <relationship name="customer" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistedCustomer" inverseName="accounts" inverseEntity="PersistedCustomer"/>
<relationship name="transactions" toMany="YES" deletionRule="Cascade" destinationEntity="AccountTransaction" inverseName="account" inverseEntity="AccountTransaction"/> <relationship name="transactions" toMany="YES" deletionRule="Cascade" destinationEntity="PersistedAccountTransaction" inverseName="account" inverseEntity="PersistedAccountTransaction"/>
</entity> </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="bankCode" attributeType="String"/>
<attribute name="bankName" attributeType="String"/> <attribute name="bankName" attributeType="String"/>
<attribute name="bic" attributeType="String"/> <attribute name="bic" attributeType="String"/>
@ -65,11 +65,11 @@
<attribute name="iconUrl" optional="YES" attributeType="String"/> <attribute name="iconUrl" optional="YES" attributeType="String"/>
<attribute name="password" attributeType="String"/> <attribute name="password" attributeType="String"/>
<attribute name="userId" 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> </entity>
<elements> <elements>
<element name="AccountTransaction" positionX="-36" positionY="45" width="128" height="553"/> <element name="PersistedAccountTransaction" positionX="-36" positionY="45" width="128" height="553"/>
<element name="BankAccount" positionX="-54" positionY="63" width="128" height="28"/> <element name="PersistedBankAccount" positionX="-54" positionY="63" width="128" height="298"/>
<element name="Customer" positionX="-63" positionY="-18" width="128" height="28"/> <element name="PersistedCustomer" positionX="-63" positionY="-18" width="128" height="193"/>
</elements> </elements>
</model> </model>

View File

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

View File

@ -5,16 +5,26 @@ import BankingUiSwift
class Mapper { class Mapper {
func map(_ customer: Customer) -> BUCCustomer { /* Cache mapped object to not save them twice */
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: []) 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 return mapped
} }
func map(_ customer: BUCCustomer, _ context: NSManagedObjectContext) -> Customer { func map(_ customer: Customer, _ context: NSManagedObjectContext) -> PersistedCustomer {
let mapped = Customer(context: context) let mapped = mappedBanks[customer] ?? PersistedCustomer(context: context)
mapped.bankCode = customer.bankCode mapped.bankCode = customer.bankCode
mapped.customerId = customer.customerId mapped.customerId = customer.customerId
@ -28,28 +38,32 @@ class Mapper {
mapped.accounts = NSSet(array: map(mapped, customer.accounts, context)) mapped.accounts = NSSet(array: map(mapped, customer.accounts, context))
mappedBanks[customer] = mapped
return 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) } ) ?? [] return accounts?.map( { map(customer, $0) } ) ?? []
} }
func map(_ customer: BUCCustomer, _ account: BankAccount) -> BUCBankAccount { func map(_ customer: Customer, _ account: PersistedBankAccount) -> BankAccount {
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: []) 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 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) } ) return accounts.map( { map(customer, $0, context) } )
} }
func map(_ customer: Customer, _ account: BUCBankAccount, _ context: NSManagedObjectContext) -> BankAccount { func map(_ customer: PersistedCustomer, _ account: BankAccount, _ context: NSManagedObjectContext) -> PersistedBankAccount {
let mapped = BankAccount(context: context) let mapped = mappedAccounts[account] ?? PersistedBankAccount(context: context)
mapped.customer = customer mapped.customer = customer
mapped.identifier = account.identifier mapped.identifier = account.identifier
@ -70,57 +84,63 @@ class Mapper {
mapped.transactions = NSSet(array: map(mapped, account.bookedTransactions, context)) mapped.transactions = NSSet(array: map(mapped, account.bookedTransactions, context))
mappedAccounts[account] = mapped
return mapped return mapped
} }
func map(_ type: BUCBankAccountType) -> String { func map(_ type: BankAccountType) -> String {
return type.name return type.name
} }
func map(_ type: String?) -> BUCBankAccountType { func map(_ type: String?) -> BankAccountType {
switch type { switch type {
case BUCBankAccountType.girokonto.name: case BankAccountType.girokonto.name:
return BUCBankAccountType.girokonto return BankAccountType.girokonto
case BUCBankAccountType.sparkonto.name: case BankAccountType.sparkonto.name:
return BUCBankAccountType.sparkonto return BankAccountType.sparkonto
case BUCBankAccountType.festgeldkonto.name: case BankAccountType.festgeldkonto.name:
return BUCBankAccountType.festgeldkonto return BankAccountType.festgeldkonto
case BUCBankAccountType.wertpapierdepot.name: case BankAccountType.wertpapierdepot.name:
return BUCBankAccountType.wertpapierdepot return BankAccountType.wertpapierdepot
case BUCBankAccountType.darlehenskonto.name: case BankAccountType.darlehenskonto.name:
return BUCBankAccountType.darlehenskonto return BankAccountType.darlehenskonto
case BUCBankAccountType.kreditkartenkonto.name: case BankAccountType.kreditkartenkonto.name:
return BUCBankAccountType.kreditkartenkonto return BankAccountType.kreditkartenkonto
case BUCBankAccountType.fondsdepot.name: case BankAccountType.fondsdepot.name:
return BUCBankAccountType.fondsdepot return BankAccountType.fondsdepot
case BUCBankAccountType.bausparvertrag.name: case BankAccountType.bausparvertrag.name:
return BUCBankAccountType.bausparvertrag return BankAccountType.bausparvertrag
case BUCBankAccountType.versicherungsvertrag.name: case BankAccountType.versicherungsvertrag.name:
return BUCBankAccountType.versicherungsvertrag return BankAccountType.versicherungsvertrag
case BUCBankAccountType.sonstige.name: case BankAccountType.sonstige.name:
return BUCBankAccountType.sonstige return BankAccountType.sonstige
default: 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) } ) ?? [] return transactions?.map( {map(account, $0) } ) ?? []
} }
func map(_ account: BUCBankAccount, _ transaction: AccountTransaction) -> BUCAccountTransaction { func map(_ account: BankAccount, _ transaction: PersistedAccountTransaction) -> AccountTransaction {
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) 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) } ) return transactions.map( {map(account, $0, context) } )
} }
func map(_ account: BankAccount, _ transaction: BUCAccountTransaction, _ context: NSManagedObjectContext) -> AccountTransaction { func map(_ account: PersistedBankAccount, _ transaction: AccountTransaction, _ context: NSManagedObjectContext) -> PersistedAccountTransaction {
let mapped = AccountTransaction(context: context) let mapped = mappedTransactions[transaction] ?? PersistedAccountTransaction(context: context)
mapped.account = account mapped.account = account
@ -162,6 +182,8 @@ class Mapper {
mapped.transactionReferenceNumber = transaction.transactionReferenceNumber mapped.transactionReferenceNumber = transaction.transactionReferenceNumber
mapped.relatedReferenceNumber = transaction.relatedReferenceNumber mapped.relatedReferenceNumber = transaction.relatedReferenceNumber
mappedTransactions[transaction] = mapped
return mapped return mapped
} }