diff --git a/fints4k/src/iosMain/kotlin/Extensions.kt b/fints4k/src/iosMain/kotlin/Extensions.kt new file mode 100644 index 00000000..9dcc30de --- /dev/null +++ b/fints4k/src/iosMain/kotlin/Extensions.kt @@ -0,0 +1,32 @@ +import com.soywiz.klock.Date +import kotlinx.cinterop.addressOf +import kotlinx.cinterop.convert +import kotlinx.cinterop.usePinned +import platform.Foundation.* + + +fun String.toNsData(): NSData { + return this.encodeToByteArray().toNSData() +} + +fun ByteArray.toNSData(): NSData = NSMutableData().apply { + if (isEmpty()) return@apply + this@toNSData.usePinned { + appendBytes(it.addressOf(0), size.convert()) + } +} + + +fun NSDate?.toDate(): Date? { + return this?.toDate() +} + +fun NSDate.toDate(): Date { + val calendar = NSCalendar.currentCalendar() + + return Date.invoke( + calendar.component(NSCalendarUnitYear, this).toInt(), + calendar.component(NSCalendarUnitMonth, this).toInt(), + calendar.component(NSCalendarUnitDay, this).toInt() + ) +} \ No newline at end of file diff --git a/fints4k/src/iosMain/kotlin/FinTsClientSwift.kt b/fints4k/src/iosMain/kotlin/FinTsClientSwift.kt new file mode 100644 index 00000000..df7c0d05 --- /dev/null +++ b/fints4k/src/iosMain/kotlin/FinTsClientSwift.kt @@ -0,0 +1,207 @@ +import net.dankito.banking.fints.FinTsClient +import net.dankito.banking.fints.callback.FinTsClientCallback +import net.dankito.banking.fints.model.AccountData +import net.dankito.banking.fints.model.AccountFeature +import net.dankito.banking.fints.model.BankData +import net.dankito.banking.fints.model.CustomerData +import net.dankito.banking.fints.response.client.AddAccountResponse +import net.dankito.banking.fints.response.client.FinTsClientResponse +import net.dankito.banking.fints.response.client.GetTransactionsResponse +import net.dankito.banking.fints.util.log.LoggerFactory +import net.dankito.banking.fints.webclient.IWebClient +import kotlin.native.concurrent.TransferMode +import kotlin.native.concurrent.Worker +import kotlin.native.concurrent.freeze + + +open class FinTsClientWorkerArgument(val bank: BankData, val customer: CustomerData, val webClient: IWebClient, val callback: FinTsClientCallback) + +class GetTransactionsWorkerArgument(val parameter: GetTransactionsParameter, val account: AccountData, + bank: BankData, customer: CustomerData, webClient: IWebClient, callback: FinTsClientCallback) + : FinTsClientWorkerArgument(bank, customer, webClient, callback) + +data class FinTsClientWorkerResult( + val bank: BankData, val customer: CustomerData, val response: T) + + +open class FinTsClientSwift( + protected val bank: BankData, + protected val customer: CustomerData, + protected val webClient: IWebClient, + protected val callback: FinTsClientCallback +) { + + init { + webClient.freeze() + callback.freeze() + } + + + protected val worker = Worker.start(name = "FinTsClient worker for $customer") + + + open fun addAccountAsync(callback: (AddAccountResponse) -> Unit) { + val future = worker.execute(TransferMode.SAFE, { createWorkerArgument() }) { arg -> + val client = FinTsClient(arg.callback, arg.webClient) + + // unbelievable, i am not allowed to use the createCopy() methods + val bank = BankData( + arg.bank.bankCode, arg.bank.countryCode, arg.bank.finTs3ServerAddress, arg.bank.bic, arg.bank.bpdVersion, + arg.bank.name, arg.bank.countMaxJobsPerMessage, arg.bank.supportedHbciVersions, arg.bank.supportedTanProcedures, + arg.bank.changeTanMediumParameters, arg.bank.pinInfo, arg.bank.supportedLanguages, arg.bank.supportedJobs + ) + val customer = CustomerData( + arg.customer.customerId, arg.customer.pin, arg.customer.userId, arg.customer.name, arg.customer.updVersion, + arg.customer.supportedTanProcedures, arg.customer.selectedTanProcedure, arg.customer.tanMedia, + arg.customer.selectedLanguage, arg.customer.customerSystemId, arg.customer.customerSystemStatus + ) + + val response = client.addAccount(bank, customer) + + FinTsClientWorkerResult(bank.freeze(), customer.freeze(), response.freeze()) + } + + future.consume { result -> + map(bank, result.bank) + map(customer, result.customer) + + callback(result.response) + } + } + + + open fun getTransactionsAsync(parameter: GetTransactionsParameter, account: AccountData, callback: (GetTransactionsResponse) -> Unit) { + val future = worker.execute(TransferMode.SAFE, { createWorkerArgument(parameter, account) }) { arg -> + val client = FinTsClient(arg.callback, arg.webClient) + + // unbelievable, i am not allowed to use the createCopy() methods + val bank = BankData( + arg.bank.bankCode, arg.bank.countryCode, arg.bank.finTs3ServerAddress, arg.bank.bic, arg.bank.bpdVersion, + arg.bank.name, arg.bank.countMaxJobsPerMessage, arg.bank.supportedHbciVersions, arg.bank.supportedTanProcedures, + arg.bank.changeTanMediumParameters, arg.bank.pinInfo, arg.bank.supportedLanguages, arg.bank.supportedJobs + ) + val customer = CustomerData( + arg.customer.customerId, arg.customer.pin, arg.customer.userId, arg.customer.name, arg.customer.updVersion, + arg.customer.supportedTanProcedures, arg.customer.selectedTanProcedure, arg.customer.tanMedia, + arg.customer.selectedLanguage, arg.customer.customerSystemId, arg.customer.customerSystemStatus + ) + + val parameter = net.dankito.banking.fints.model.GetTransactionsParameter( + arg.parameter.alsoRetrieveBalance, arg.parameter.fromDate.toDate(), arg.parameter.toDate.toDate(), + arg.parameter.maxCountEntries, arg.parameter.abortIfTanIsRequired, arg.parameter.retrievedChunkListener + ) + + val response = client.getTransactions(parameter, bank, customer, arg.account) // TODO: map account + + FinTsClientWorkerResult(bank.freeze(), customer.freeze(), response.freeze()) + } + + future.consume { result -> + map(bank, result.bank) + map(customer, result.customer) + + callback(result.response) + } + } + + + protected open fun createWorkerArgument(): FinTsClientWorkerArgument { + val bank = createImmutableCopy(this.bank) + val customer = createImmutableCopy(this.customer) + + return FinTsClientWorkerArgument(bank, customer, webClient, callback) + } + + protected open fun createWorkerArgument(parameter: GetTransactionsParameter, account: AccountData): GetTransactionsWorkerArgument { + val bank = createImmutableCopy(this.bank) + val customer = createImmutableCopy(this.customer) + val frozenAccount = createImmutableCopy(account) + + return GetTransactionsWorkerArgument(parameter.freeze(), frozenAccount, bank, customer, webClient, callback) + } + + + protected open fun createCopy(bank: BankData): BankData { + return BankData( + bank.bankCode, bank.countryCode, bank.finTs3ServerAddress, bank.bic, bank.bpdVersion, + bank.name, bank.countMaxJobsPerMessage, bank.supportedHbciVersions, bank.supportedTanProcedures, + bank.changeTanMediumParameters, bank.pinInfo, bank.supportedLanguages, bank.supportedJobs + ) + } + + protected open fun createImmutableCopy(bank: BankData): BankData { + return createCopy(bank).freeze() + } + + protected open fun createCopy(customer: CustomerData): CustomerData { + return CustomerData( + customer.customerId, customer.pin, customer.userId, customer.name, customer.updVersion, + customer.supportedTanProcedures, customer.selectedTanProcedure, customer.tanMedia, + customer.selectedLanguage, customer.customerSystemId, customer.customerSystemStatus + ) + } + + protected open fun createImmutableCopy(customer: CustomerData): CustomerData { + return createCopy(customer).freeze() + } + + protected open fun createCopy(account: AccountData): AccountData { + val copy = AccountData( + account.accountIdentifier, account.subAccountAttribute, account.bankCountryCode, + account.bankCode, account.iban, account.customerId, account.accountType, account.currency, + account.accountHolderName, account.productName, account.accountLimit, account.allowedJobNames, + account.allowedJobs, account.supportsRetrievingTransactionsOfLast90DaysWithoutTan, + account.triedToRetrieveTransactionsOfLast90DaysWithoutTan + ) + + AccountFeature.values().forEach { feature -> + copy.setSupportsFeature(feature, account.supportsFeature(feature)) + } + + return copy + } + + protected open fun createImmutableCopy(account: AccountData): AccountData { + return createCopy(account).freeze() + } + + + protected open fun map(to: BankData, from: BankData) { + to.bankCode = from.bankCode + to.countryCode = from.countryCode + to.finTs3ServerAddress = from.finTs3ServerAddress + to.bic = from.bic + to.bpdVersion = from.bpdVersion + to.name = from.name + to.countMaxJobsPerMessage = from.countMaxJobsPerMessage + to.supportedHbciVersions = from.supportedHbciVersions + to.supportedTanProcedures = from.supportedTanProcedures + to.changeTanMediumParameters = from.changeTanMediumParameters + to.pinInfo = from.pinInfo + to.supportedLanguages = from.supportedLanguages + to.supportedJobs = from.supportedJobs + } + + protected open fun map(to: CustomerData, from: CustomerData) { + to.customerId = from.customerId + to.pin = from.pin + to.userId = from.userId + to.name = from.name + to.updVersion = from.updVersion + to.supportedTanProcedures = from.supportedTanProcedures + to.selectedTanProcedure = from.selectedTanProcedure + to.tanMedia = from.tanMedia + to.selectedLanguage = from.selectedLanguage + to.customerSystemId = from.customerSystemId + to.customerSystemStatus = from.customerSystemStatus + + // TODO: create convenience method to set accounts + to.accounts.forEach { account -> + to.removeAccount(account) + } + from.accounts.forEach { account -> + to.addAccount(account) // TODO: also map account? + } + } + +} \ No newline at end of file diff --git a/fints4k/src/iosMain/kotlin/GetTransactionsParameter.kt b/fints4k/src/iosMain/kotlin/GetTransactionsParameter.kt new file mode 100644 index 00000000..db72da47 --- /dev/null +++ b/fints4k/src/iosMain/kotlin/GetTransactionsParameter.kt @@ -0,0 +1,16 @@ +import net.dankito.banking.fints.model.AccountTransaction +import platform.Foundation.NSDate + + +class GetTransactionsParameter( + val alsoRetrieveBalance: Boolean = true, + val fromDate: NSDate? = null, + val toDate: NSDate? = null, + val maxCountEntries: Int? = null, + val abortIfTanIsRequired: Boolean = false, + val retrievedChunkListener: ((List) -> Unit)? = null +) { + + constructor() : this(true, null, null, null, false, null) + +} \ No newline at end of file