Compare commits

...

18 Commits

Author SHA1 Message Date
dankito b5dbf92b9b Created AppIcons and configured native distributions 2024-09-20 04:16:45 +02:00
dankito d549b96e7b Ensuring BankingRepository / SqliteDriver gets created only once and catching errors 2024-09-20 02:46:37 +02:00
dankito b384f6bc00 Also configured database and image cache that for releases they get written to user's home dir (which is important for desktop app bundles 2024-09-20 02:43:23 +02:00
dankito 5d0669c5fe Fixed that on releases (which is important for desktop app bundles) logs get written to user's home dir 2024-09-20 02:41:16 +02:00
dankito 6a8b913bc4 Fixed log file timestamp 2024-09-20 02:39:41 +02:00
dankito 7d9a2695a9 Updated klf version to 1.6.2 2024-09-20 02:35:43 +02:00
dankito 7ce76d73ea Moved setting BankingRepository to App() 2024-09-20 00:35:37 +02:00
dankito 1f19da85f3 Renamed module / framework to Bankmeister 2024-09-19 23:25:31 +02:00
dankito 8707c5e3d7 Configured iOS app for AppStore distribution and uploaded version 10 to TestFlight 2024-09-19 22:43:27 +02:00
dankito ff8bf80f6d Fixed text color of balance if showColoredAmounts is false 2024-09-19 21:47:40 +02:00
dankito df093d0cd3 Updated BankingClient version to 0.6.1 2024-09-19 21:38:30 +02:00
dankito 08e3096892 Set option to fetch all transactions to true by default 2024-09-19 21:35:29 +02:00
dankito 931d41d610 By default not showing amounts colored anymore 2024-09-19 21:33:57 +02:00
dankito fc0d2642e5 Applying default hierarchy template 2024-09-19 19:10:07 +02:00
dankito 7712102af2 Suppressed EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING compiler warning 2024-09-19 19:09:44 +02:00
dankito 6564a9d33d Moved now all Sqldelight related classes and settings over to BankingPersistence - and finally it compiles on iOS! 2024-09-19 19:02:16 +02:00
dankito 0f89314ba3 Started to extract persistence library which contains all Sqldelight specific code as Sqldelight conflicts with Compose on iOS 2024-09-19 17:13:49 +02:00
dankito 4fa7adeeb1 Added implementations for iOS 2024-09-19 04:18:35 +02:00
79 changed files with 571 additions and 182 deletions

2
.gitignore vendored
View File

@ -21,5 +21,7 @@ xcuserdata
!*.xcodeproj/project.xcworkspace/ !*.xcodeproj/project.xcworkspace/
!*.xcworkspace/contents.xcworkspacedata !*.xcworkspace/contents.xcworkspacedata
**/xcshareddata/WorkspaceSettings.xcsettings **/xcshareddata/WorkspaceSettings.xcsettings
**/*.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
composeApp/data/ composeApp/data/
BankingPersistence/data/

View File

@ -0,0 +1,113 @@
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
alias(libs.plugins.sqldelight)
}
kotlin {
jvmToolchain(11)
jvm()
js {
moduleName = "BankingPersistence"
binaries.executable()
browser()
}
androidTarget {
}
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "BankingPersistence"
isStatic = true
}
}
applyDefaultHierarchyTemplate()
sourceSets {
commonMain.dependencies {
implementation(libs.banking.client.model)
implementation(libs.kotlinx.datetime)
implementation(libs.sqldelight.runtime)
implementation(libs.sqldelight.coroutines.extensions)
implementation(libs.sqldelight.paging.extensions)
implementation(libs.klf)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
implementation(libs.coroutines.test)
}
jvmMain.dependencies {
implementation(libs.sqldelight.sqlite.driver)
}
jvmTest.dependencies {
implementation(libs.kotlin.test.junit)
}
androidMain.dependencies {
implementation(libs.sqldelight.android.driver)
}
iosMain.dependencies {
implementation(libs.sqldelight.native.driver)
}
}
}
sqldelight {
databases {
create("BankmeisterDb") {
packageName.set("net.codinux.banking.persistence")
generateAsync = true
schemaOutputDirectory = file("src/commonMain/sqldelight/databases")
}
}
}
android {
namespace = "net.codinux.banking.persistence"
compileSdk = libs.versions.android.compileSdk.get().toInt()
// sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
// proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View File

@ -0,0 +1,7 @@
package net.codinux.banking.persistence
import android.content.Context
object AndroidContext {
lateinit var applicationContext: Context
}

View File

@ -0,0 +1,10 @@
package net.codinux.banking.persistence
import app.cash.sqldelight.async.coroutines.synchronous
import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlSchema
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
actual fun createSqlDriverDriver(dbName: String, schema: SqlSchema<QueryResult.AsyncValue<Unit>>, version: Long): SqlDriver =
AndroidSqliteDriver(schema.synchronous(), AndroidContext.applicationContext, dbName)

View File

@ -1,15 +1,15 @@
package net.codinux.banking.dataaccess package net.codinux.banking.persistence
import net.codinux.banking.client.model.AccountTransaction import net.codinux.banking.client.model.AccountTransaction
import net.codinux.banking.client.model.BankAccess import net.codinux.banking.client.model.BankAccess
import net.codinux.banking.client.model.securitiesaccount.Holding import net.codinux.banking.client.model.securitiesaccount.Holding
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity import net.codinux.banking.persistence.entities.AccountTransactionEntity
import net.codinux.banking.dataaccess.entities.BankAccountEntity import net.codinux.banking.persistence.entities.BankAccountEntity
import net.codinux.banking.dataaccess.entities.HoldingEntity import net.codinux.banking.persistence.entities.HoldingEntity
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.persistence.entities.UiSettingsEntity
import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.settings.AppSettings import net.codinux.banking.ui.model.settings.AppSettings
import net.codinux.banking.ui.settings.UiSettings
interface BankingRepository { interface BankingRepository {
@ -17,9 +17,9 @@ interface BankingRepository {
suspend fun saveAppSettings(settings: AppSettings) suspend fun saveAppSettings(settings: AppSettings)
fun getUiSettings(settings: UiSettings) fun getUiSettings(): UiSettingsEntity?
suspend fun saveUiSettings(settings: UiSettings) suspend fun saveUiSettings(settings: UiSettingsEntity)
fun getAllBanks(): List<BankAccessEntity> fun getAllBanks(): List<BankAccessEntity>

View File

@ -1,15 +1,16 @@
package net.codinux.banking.dataaccess package net.codinux.banking.persistence
import net.codinux.banking.client.model.AccountTransaction import net.codinux.banking.client.model.AccountTransaction
import net.codinux.banking.client.model.BankAccess import net.codinux.banking.client.model.BankAccess
import net.codinux.banking.client.model.securitiesaccount.Holding import net.codinux.banking.client.model.securitiesaccount.Holding
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity import net.codinux.banking.persistence.entities.AccountTransactionEntity
import net.codinux.banking.dataaccess.entities.BankAccountEntity import net.codinux.banking.persistence.entities.BankAccountEntity
import net.codinux.banking.dataaccess.entities.HoldingEntity import net.codinux.banking.persistence.entities.HoldingEntity
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.persistence.entities.UiSettingsEntity
import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.settings.AppSettings import net.codinux.banking.ui.model.settings.AppSettings
import net.codinux.banking.ui.settings.UiSettings import net.codinux.banking.ui.model.settings.TransactionsGrouping
class InMemoryBankingRepository( class InMemoryBankingRepository(
banks: Collection<BankAccess> = emptyList(), banks: Collection<BankAccess> = emptyList(),
@ -23,7 +24,7 @@ class InMemoryBankingRepository(
private val transactions = transactions.map { map(it) }.toMutableList() private val transactions = transactions.map { map(it) }.toMutableList()
private lateinit var uiSettings: UiSettings private var uiSettings: UiSettingsEntity = UiSettingsEntity(true, TransactionsGrouping.Month, true, true, true)
override fun getAppSettings(): AppSettings? = appSettings override fun getAppSettings(): AppSettings? = appSettings
@ -32,11 +33,9 @@ class InMemoryBankingRepository(
this.appSettings = settings this.appSettings = settings
} }
override fun getUiSettings(settings: UiSettings) { override fun getUiSettings() = this.uiSettings
this.uiSettings = settings
}
override suspend fun saveUiSettings(settings: UiSettings) { override suspend fun saveUiSettings(settings: UiSettingsEntity) {
this.uiSettings = settings this.uiSettings = settings
} }

View File

@ -1,25 +1,45 @@
package net.codinux.banking.dataaccess package net.codinux.banking.persistence
import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlSchema
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import net.codinux.banking.client.model.* import net.codinux.banking.client.model.AccountTransaction
import net.codinux.banking.client.model.Amount
import net.codinux.banking.client.model.BankAccess
import net.codinux.banking.client.model.BankAccount
import net.codinux.banking.client.model.BankAccountFeatures
import net.codinux.banking.client.model.BankAccountType
import net.codinux.banking.client.model.BankingGroup
import net.codinux.banking.client.model.securitiesaccount.Holding import net.codinux.banking.client.model.securitiesaccount.Holding
import net.codinux.banking.client.model.tan.* import net.codinux.banking.client.model.tan.AllowedTanFormat
import net.codinux.banking.dataaccess.entities.* import net.codinux.banking.client.model.tan.MobilePhoneTanMedium
import net.codinux.banking.client.model.tan.TanGeneratorTanMedium
import net.codinux.banking.client.model.tan.TanMedium
import net.codinux.banking.client.model.tan.TanMediumStatus
import net.codinux.banking.client.model.tan.TanMediumType
import net.codinux.banking.client.model.tan.TanMethod
import net.codinux.banking.client.model.tan.TanMethodType
import net.codinux.banking.persistence.entities.*
import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.TransactionsGrouping
import net.codinux.banking.ui.model.settings.AppAuthenticationMethod import net.codinux.banking.ui.model.settings.AppAuthenticationMethod
import net.codinux.banking.ui.model.settings.AppSettings import net.codinux.banking.ui.model.settings.AppSettings
import net.codinux.banking.ui.settings.UiSettings import net.codinux.banking.ui.model.settings.TransactionsGrouping
import net.codinux.log.logger import net.codinux.log.logger
import kotlin.enums.EnumEntries import kotlin.enums.EnumEntries
import kotlin.js.JsName import kotlin.js.JsName
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
open class SqliteBankingRepository(
sqlDriver: SqlDriver expect fun createSqlDriverDriver(dbName: String, schema: SqlSchema<QueryResult.AsyncValue<Unit>>, version: Long): SqlDriver
) : BankingRepository {
open class SqliteBankingRepository : BankingRepository {
private val schema = BankmeisterDb.Schema
private val sqlDriver = createSqlDriverDriver("Bankmeister.db", schema, 1L)
private val database = BankmeisterDb(sqlDriver) private val database = BankmeisterDb(sqlDriver)
@ -52,18 +72,20 @@ open class SqliteBankingRepository(
} }
override fun getUiSettings(settings: UiSettings) { override fun getUiSettings(): UiSettingsEntity? {
settingsQueries.getUiSettings { _, transactionsGrouping, showBalance, showBankIcons, showColoredAmounts, showTransactionsInAlternatingColors -> return settingsQueries.getUiSettings { _, transactionsGrouping, showBalance, showBankIcons, showColoredAmounts, showTransactionsInAlternatingColors ->
settings.transactionsGrouping.value = mapToEnum(transactionsGrouping, TransactionsGrouping.entries) UiSettingsEntity(
settings.showBalance.value = showBalance showBalance,
settings.showBankIcons.value = showBankIcons mapToEnum(transactionsGrouping, TransactionsGrouping.entries),
settings.showColoredAmounts.value = showColoredAmounts showTransactionsInAlternatingColors,
settings.showTransactionsInAlternatingColors.value = showTransactionsInAlternatingColors showBankIcons,
showColoredAmounts
)
}.executeAsOneOrNull() }.executeAsOneOrNull()
} }
override suspend fun saveUiSettings(settings: UiSettings) { override suspend fun saveUiSettings(settings: UiSettingsEntity) {
settingsQueries.upsertUiSettings(mapEnum(settings.transactionsGrouping.value), settings.showBalance.value, settings.showBankIcons.value, settings.showColoredAmounts.value, settings.showTransactionsInAlternatingColors.value) settingsQueries.upsertUiSettings(mapEnum(settings.transactionsGrouping), settings.showBalance, settings.showBankIcons, settings.showColoredAmounts, settings.showTransactionsInAlternatingColors)
} }

View File

@ -1,4 +1,4 @@
package net.codinux.banking.dataaccess.entities package net.codinux.banking.persistence.entities
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import net.codinux.banking.client.model.AccountTransaction import net.codinux.banking.client.model.AccountTransaction

View File

@ -1,8 +1,7 @@
package net.codinux.banking.dataaccess.entities package net.codinux.banking.persistence.entities
import net.codinux.banking.client.model.BankAccess import net.codinux.banking.client.model.BankAccess
import net.codinux.banking.client.model.BankingGroup import net.codinux.banking.client.model.BankingGroup
import net.codinux.banking.client.model.tan.TanMedium
class BankAccessEntity( class BankAccessEntity(
val id: Long, val id: Long,

View File

@ -1,4 +1,4 @@
package net.codinux.banking.dataaccess.entities package net.codinux.banking.persistence.entities
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate

View File

@ -1,4 +1,4 @@
package net.codinux.banking.dataaccess.entities package net.codinux.banking.persistence.entities
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate

View File

@ -1,4 +1,4 @@
package net.codinux.banking.dataaccess.entities package net.codinux.banking.persistence.entities
import net.codinux.banking.client.model.tan.* import net.codinux.banking.client.model.tan.*

View File

@ -1,4 +1,4 @@
package net.codinux.banking.dataaccess.entities package net.codinux.banking.persistence.entities
import net.codinux.banking.client.model.tan.AllowedTanFormat import net.codinux.banking.client.model.tan.AllowedTanFormat
import net.codinux.banking.client.model.tan.TanMethod import net.codinux.banking.client.model.tan.TanMethod

View File

@ -0,0 +1,17 @@
package net.codinux.banking.persistence.entities
import net.codinux.banking.ui.model.settings.TransactionsGrouping
class UiSettingsEntity(
val showBalance: Boolean,
val transactionsGrouping: TransactionsGrouping,
val showTransactionsInAlternatingColors: Boolean,
val showBankIcons: Boolean,
val showColoredAmounts: Boolean
)

View File

@ -3,7 +3,7 @@ package net.codinux.banking.ui.model
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import net.codinux.banking.client.model.AccountTransaction import net.codinux.banking.client.model.AccountTransaction
import net.codinux.banking.client.model.Amount import net.codinux.banking.client.model.Amount
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity import net.codinux.banking.persistence.entities.AccountTransactionEntity
data class AccountTransactionViewModel( data class AccountTransactionViewModel(
val id: Long, val id: Long,

View File

@ -1,4 +1,4 @@
package net.codinux.banking.ui.model package net.codinux.banking.ui.model.settings
enum class TransactionsGrouping { enum class TransactionsGrouping {
None, None,

View File

@ -0,0 +1,10 @@
package net.codinux.banking.persistence
import app.cash.sqldelight.async.coroutines.synchronous
import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlSchema
import app.cash.sqldelight.driver.native.NativeSqliteDriver
actual fun createSqlDriverDriver(dbName: String, schema: SqlSchema<QueryResult.AsyncValue<Unit>>, version: Long): SqlDriver =
NativeSqliteDriver(schema.synchronous(), dbName)

View File

@ -0,0 +1,9 @@
package net.codinux.banking.persistence
import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlSchema
actual fun createSqlDriverDriver(dbName: String, schema: SqlSchema<QueryResult.AsyncValue<Unit>>, version: Long): SqlDriver {
throw NotImplementedError("TODO")
}

View File

@ -0,0 +1,40 @@
package net.codinux.banking.persistence
import app.cash.sqldelight.async.coroutines.synchronous
import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlSchema
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import java.io.File
val dataDirectory: File = determineDataDirectory()
actual fun createSqlDriverDriver(dbName: String, schema: SqlSchema<QueryResult.AsyncValue<Unit>>, version: Long): SqlDriver {
val dbDir = File(dataDirectory, "db").also { it.mkdirs() }
val databaseFile = File(dbDir, dbName)
return JdbcSqliteDriver("jdbc:sqlite:${databaseFile.path}").also { driver ->
schema.synchronous().also { schema ->
if (databaseFile.exists() == false) {
schema.create(driver)
}
schema.migrate(driver, schema.version, version)
}
}
}
private fun determineDataDirectory(): File {
val currentDir = File(System.getProperty("user.dir"))
val dataDir = if (currentDir.canWrite()) { // if the current directory is writable, use that one (the default for development)
File(currentDir, "data")
} else { // otherwise use .bankmeister dir in user's home dir (the default for releases)
val userHome = System.getProperty("user.home")
File(userHome, ".bankmeister")
}
return dataDir.also { it.mkdirs() }
}

View File

@ -1,23 +1,18 @@
package net.codinux.banking.dataaccess package net.codinux.banking.dataaccess
import app.cash.sqldelight.async.coroutines.synchronous
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import net.codinux.banking.client.model.* import net.codinux.banking.client.model.*
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity import net.codinux.banking.persistence.SqliteBankingRepository
import net.codinux.banking.persistence.entities.AccountTransactionEntity
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
class SqliteBankingRepositoryTest { class SqliteBankingRepositoryTest {
private val sqlDriver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).apply { private val underTest = object : SqliteBankingRepository() {
BankmeisterDb.Schema.synchronous().create(this) public override suspend fun persistTransaction(bankId: Long, accountId: Long, transaction: AccountTransaction): AccountTransactionEntity =
}
private val underTest = object : SqliteBankingRepository(sqlDriver) {
override public suspend fun persistTransaction(bankId: Long, accountId: Long, transaction: AccountTransaction): AccountTransactionEntity =
super.persistTransaction(bankId, accountId, transaction) super.persistTransaction(bankId, accountId, transaction)
} }

View File

@ -10,18 +10,23 @@ plugins {
alias(libs.plugins.compose.compiler) alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlinxSerialization) alias(libs.plugins.kotlinxSerialization)
alias(libs.plugins.sqldelight)
} }
kotlin { kotlin {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
// suppresses compiler warning: [EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING] 'expect'/'actual' classes (including interfaces, objects, annotations, enums, and 'actual' typealiases) are in Beta.
freeCompilerArgs.add("-Xexpect-actual-classes")
}
js { js {
moduleName = "composeApp" moduleName = "Bankmeister"
browser { browser {
val projectDirPath = project.projectDir.path val projectDirPath = project.projectDir.path
commonWebpackConfig { commonWebpackConfig {
outputFileName = "composeApp.js" outputFileName = "Bankmeister.js"
devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
static = (static ?: mutableListOf()).apply { static = (static ?: mutableListOf()).apply {
// Serve sources to debug inside browser // Serve sources to debug inside browser
@ -49,15 +54,27 @@ kotlin {
iosSimulatorArm64() iosSimulatorArm64()
).forEach { iosTarget -> ).forEach { iosTarget ->
iosTarget.binaries.framework { iosTarget.binaries.framework {
baseName = "ComposeApp" baseName = "BankmeisterFramework"
isStatic = true isStatic = false
}
// don't know why but this has to be added here, adding it in BankingPersistence.build.gradle.kt does not work
iosTarget.binaries.forEach { binary ->
if (binary is org.jetbrains.kotlin.gradle.plugin.mpp.Framework) {
binary.linkerOpts.add("-lsqlite3") // without this we get a lot of "Undefined symbol _co_touchlab_sqliter..." errors in Xcode
}
} }
} }
applyDefaultHierarchyTemplate()
sourceSets { sourceSets {
val desktopMain by getting val desktopMain by getting
commonMain.dependencies { commonMain.dependencies {
implementation(project(":BankingPersistence"))
implementation(libs.banking.client.model) implementation(libs.banking.client.model)
implementation(libs.fints4k.banking.client) implementation(libs.fints4k.banking.client)
@ -65,10 +82,6 @@ kotlin {
implementation(libs.klf) implementation(libs.klf)
implementation(libs.kotlinx.serializable) implementation(libs.kotlinx.serializable)
implementation(libs.sqldelight.runtime)
implementation(libs.sqldelight.coroutines.extensions)
implementation(libs.sqldelight.paging.extensions)
// UI // UI
implementation(compose.runtime) implementation(compose.runtime)
implementation(compose.foundation) implementation(compose.foundation)
@ -93,13 +106,11 @@ kotlin {
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)
implementation(libs.androidx.biometric) implementation(libs.androidx.biometric)
implementation(libs.sqldelight.android.driver)
implementation(libs.favre.bcrypt) implementation(libs.favre.bcrypt)
} }
nativeMain.dependencies { iosMain.dependencies {
implementation(libs.sqldelight.native.driver)
} }
jvmTest.dependencies { jvmTest.dependencies {
@ -110,23 +121,10 @@ kotlin {
implementation(compose.desktop.currentOs) implementation(compose.desktop.currentOs)
implementation(libs.kotlinx.coroutines.swing) implementation(libs.kotlinx.coroutines.swing)
implementation(libs.sqldelight.sqlite.driver)
implementation(libs.favre.bcrypt) implementation(libs.favre.bcrypt)
implementation(libs.logback) implementation(libs.logback)
} implementation(libs.janino)
}
}
sqldelight {
databases {
create("BankmeisterDb") {
packageName.set("net.codinux.banking.dataaccess")
generateAsync = true
schemaOutputDirectory = file("src/commonMain/sqldelight/databases")
} }
} }
} }
@ -192,12 +190,28 @@ compose.desktop {
mainClass = "net.codinux.banking.ui.MainKt" mainClass = "net.codinux.banking.ui.MainKt"
nativeDistributions { nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb, TargetFormat.Rpm)
packageName = "net.codinux.banking.ui"
modules("java.sql", "java.naming") // java.naming is required by logback
packageName = "Bankmeister"
packageVersion = "1.0.0" packageVersion = "1.0.0"
description = "Datenschutzfreundliche Multi-Banking App für die meisten deutschen Banken" description = "Datenschutzfreundliche Multi-Banking App für die meisten deutschen Banken"
copyright = "© 2024 codinux GmbH & Co.KG. All rights reserved." copyright = "© 2024 codinux GmbH & Co.KG. All rights reserved."
vendor = "codinux GmbH & Co.KG" vendor = "codinux GmbH & Co.KG"
macOS {
bundleID = "net.codinux.banking.ui"
appCategory = "public.app-category.finance"
iconFile = project.file("../docs/res/AppIcons/distributions/AppIcon.icns")
}
windows {
iconFile = project.file("../docs/res/AppIcons/distributions/AppIcon.ico")
}
linux {
iconFile = project.file("../docs/res/AppIcons/distributions/AppIcon.png")
}
} }
buildTypes.release.proguard { buildTypes.release.proguard {

View File

@ -5,9 +5,8 @@ import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import app.cash.sqldelight.async.coroutines.synchronous import net.codinux.banking.persistence.AndroidContext
import app.cash.sqldelight.driver.android.AndroidSqliteDriver import net.codinux.banking.persistence.SqliteBankingRepository
import net.codinux.banking.dataaccess.BankmeisterDb
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.service.AuthenticationService import net.codinux.banking.ui.service.AuthenticationService
import net.codinux.banking.ui.service.BiometricAuthenticationService import net.codinux.banking.ui.service.BiometricAuthenticationService
@ -17,11 +16,11 @@ class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidContext.applicationContext = this.applicationContext
ImageService.context = this.applicationContext ImageService.context = this.applicationContext
AuthenticationService.biometricAuthenticationService = BiometricAuthenticationService(this) AuthenticationService.biometricAuthenticationService = BiometricAuthenticationService(this)
DI.setRepository(AndroidSqliteDriver(BankmeisterDb.Schema.synchronous(), this, "Bankmeister.db"))
setContent { setContent {
App() App()
} }

View File

@ -7,11 +7,14 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.codinux.banking.persistence.BankingRepository
import net.codinux.banking.persistence.SqliteBankingRepository
import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.Colors
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.model.settings.AppAuthenticationMethod import net.codinux.banking.ui.model.settings.AppAuthenticationMethod
import net.codinux.banking.ui.screens.LoginScreen import net.codinux.banking.ui.screens.LoginScreen
import net.codinux.banking.ui.screens.MainScreen import net.codinux.banking.ui.screens.MainScreen
import net.codinux.log.Log
import net.codinux.log.LoggerFactory import net.codinux.log.LoggerFactory
import org.jetbrains.compose.ui.tooling.preview.Preview import org.jetbrains.compose.ui.tooling.preview.Preview
@ -21,9 +24,10 @@ private val typography = Typography(
@Composable @Composable
@Preview @Preview
fun App() { fun App(repository: BankingRepository? = null) {
LoggerFactory.defaultLoggerName = "net.codinux.banking.ui.Bankmeister" LoggerFactory.defaultLoggerName = "net.codinux.banking.ui.Bankmeister"
val colors = MaterialTheme.colors.copy(primary = Colors.Primary, primaryVariant = Colors.PrimaryDark, onPrimary = Color.White, val colors = MaterialTheme.colors.copy(primary = Colors.Primary, primaryVariant = Colors.PrimaryDark, onPrimary = Color.White,
secondary = Colors.Accent, secondaryVariant = Colors.Accent, onSecondary = Color.White) secondary = Colors.Accent, secondaryVariant = Colors.Accent, onSecondary = Color.White)
@ -35,6 +39,14 @@ fun App() {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
try {
if (isInitialized == false) {
DI.setRepository(repository ?: SqliteBankingRepository())
}
} catch (e: Throwable) {
Log.error(e) { "Could not set repository" }
}
MaterialTheme(colors = colors, typography = typography) { MaterialTheme(colors = colors, typography = typography) {
if (isLoggedIn == false) { if (isLoggedIn == false) {

View File

@ -13,7 +13,7 @@ import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.config.Internationalization import net.codinux.banking.ui.config.Internationalization
import net.codinux.banking.ui.forms.RoundedCornersCard import net.codinux.banking.ui.forms.RoundedCornersCard
import net.codinux.banking.ui.forms.Select import net.codinux.banking.ui.forms.Select
import net.codinux.banking.ui.model.TransactionsGrouping import net.codinux.banking.ui.model.settings.TransactionsGrouping
private val uiState = DI.uiState private val uiState = DI.uiState

View File

@ -10,8 +10,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import net.codinux.banking.dataaccess.entities.BankAccountEntity import net.codinux.banking.persistence.entities.BankAccountEntity
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI
private val uiState = DI.uiState private val uiState = DI.uiState

View File

@ -19,8 +19,8 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import net.codinux.banking.dataaccess.entities.BankAccountEntity import net.codinux.banking.persistence.entities.BankAccountEntity
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.Colors
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI
@ -101,7 +101,7 @@ fun NavigationMenuItem(
if (balance != null) { if (balance != null) {
Text( Text(
formatUtil.formatAmount(balance, calculator.getTransactionsCurrency(emptyList())), formatUtil.formatAmount(balance, calculator.getTransactionsCurrency(emptyList())),
color = formatUtil.getColorForAmount(balance, showColoredAmounts), color = if (showColoredAmounts) formatUtil.getColorForAmount(balance, showColoredAmounts) else textColor,
modifier = Modifier.padding(start = 4.dp) modifier = Modifier.padding(start = 4.dp)
) )
} }

View File

@ -13,7 +13,7 @@ import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.config.Internationalization import net.codinux.banking.ui.config.Internationalization
import net.codinux.banking.ui.forms.BooleanOption import net.codinux.banking.ui.forms.BooleanOption
import net.codinux.banking.ui.forms.Select import net.codinux.banking.ui.forms.Select
import net.codinux.banking.ui.model.TransactionsGrouping import net.codinux.banking.ui.model.settings.TransactionsGrouping
@Composable @Composable
fun UiSettings(modifier: Modifier, textColor: Color = Color.Unspecified) { fun UiSettings(modifier: Modifier, textColor: Color = Color.Unspecified) {

View File

@ -14,13 +14,13 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import net.codinux.banking.client.model.Amount import net.codinux.banking.client.model.Amount
import net.codinux.banking.client.model.securitiesaccount.Holding import net.codinux.banking.client.model.securitiesaccount.Holding
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.Colors
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.config.Style import net.codinux.banking.ui.config.Style
import net.codinux.banking.ui.forms.RoundedCornersCard import net.codinux.banking.ui.forms.RoundedCornersCard
import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.TransactionsGrouping import net.codinux.banking.ui.model.settings.TransactionsGrouping
import net.codinux.banking.ui.service.TransactionsGroupingService import net.codinux.banking.ui.service.TransactionsGroupingService
private val calculator = DI.calculator private val calculator = DI.calculator

View File

@ -12,7 +12,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.Colors
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.model.TransactionsGrouping import net.codinux.banking.ui.model.settings.TransactionsGrouping
import net.codinux.banking.ui.settings.UiSettings import net.codinux.banking.ui.settings.UiSettings
import net.codinux.banking.ui.state.UiState import net.codinux.banking.ui.state.UiState
import org.jetbrains.compose.ui.tooling.preview.Preview import org.jetbrains.compose.ui.tooling.preview.Preview

View File

@ -1,9 +1,7 @@
package net.codinux.banking.ui.config package net.codinux.banking.ui.config
import app.cash.sqldelight.db.SqlDriver import net.codinux.banking.persistence.BankingRepository
import net.codinux.banking.dataaccess.BankingRepository import net.codinux.banking.persistence.InMemoryBankingRepository
import net.codinux.banking.dataaccess.InMemoryBankingRepository
import net.codinux.banking.dataaccess.SqliteBankingRepository
import net.codinux.banking.ui.Platform import net.codinux.banking.ui.Platform
import net.codinux.banking.ui.getPlatform import net.codinux.banking.ui.getPlatform
import net.codinux.banking.ui.service.* import net.codinux.banking.ui.service.*
@ -39,8 +37,6 @@ object DI {
val bankingService by lazy { BankingService(uiState, uiSettings, bankingRepository, bankFinder) } val bankingService by lazy { BankingService(uiState, uiSettings, bankingRepository, bankFinder) }
fun setRepository(sqlDriver: SqlDriver) = setRepository(SqliteBankingRepository(sqlDriver))
fun setRepository(repository: BankingRepository) { fun setRepository(repository: BankingRepository) {
this.bankingRepository = repository this.bankingRepository = repository

View File

@ -2,7 +2,7 @@ package net.codinux.banking.ui.config
import net.codinux.banking.client.model.BankAccountType import net.codinux.banking.client.model.BankAccountType
import net.codinux.banking.client.model.tan.ActionRequiringTan import net.codinux.banking.client.model.tan.ActionRequiringTan
import net.codinux.banking.ui.model.TransactionsGrouping import net.codinux.banking.ui.model.settings.TransactionsGrouping
import net.codinux.banking.ui.model.settings.AppAuthenticationMethod import net.codinux.banking.ui.model.settings.AppAuthenticationMethod
object Internationalization { object Internationalization {

View File

@ -23,6 +23,7 @@ import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.forms.* import net.codinux.banking.ui.forms.*
import net.codinux.banking.ui.forms.OutlinedTextField import net.codinux.banking.ui.forms.OutlinedTextField
import net.codinux.banking.ui.model.BankInfo import net.codinux.banking.ui.model.BankInfo
import net.codinux.log.Log
private val bankingService = DI.bankingService private val bankingService = DI.bankingService
@ -37,7 +38,7 @@ fun AddAccountDialog(
var selectedBank by remember { mutableStateOf<BankInfo?>(null) } var selectedBank by remember { mutableStateOf<BankInfo?>(null) }
var loginName by remember { mutableStateOf("") } var loginName by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") } var password by remember { mutableStateOf("") }
var retrieveAllTransactions by remember { mutableStateOf(false) } var retrieveAllTransactions by remember { mutableStateOf(true) }
val isRequiredDataEntered by remember(selectedBank, loginName, password) { val isRequiredDataEntered by remember(selectedBank, loginName, password) {
derivedStateOf { selectedBank != null && loginName.length > 3 && password.length > 3 } derivedStateOf { selectedBank != null && loginName.length > 3 && password.length > 3 }
@ -69,7 +70,12 @@ fun AddAccountDialog(
isAddingAccount = true isAddingAccount = true
addAccountJob = coroutineScope.launch(Dispatchers.IOorDefault) { addAccountJob = coroutineScope.launch(Dispatchers.IOorDefault) {
val successful = DI.bankingService.addAccount(bank, loginName, password, retrieveAllTransactions) val successful = try {
DI.bankingService.addAccount(bank, loginName, password, retrieveAllTransactions)
} catch (e: Throwable) {
Log.error(e) { "Could not add account for $bank" }
false
}
addAccountJob = null addAccountJob = null

View File

@ -1,8 +1,8 @@
package net.codinux.banking.ui.model package net.codinux.banking.ui.model
import androidx.compose.runtime.* import androidx.compose.runtime.*
import net.codinux.banking.dataaccess.entities.BankAccountEntity import net.codinux.banking.persistence.entities.BankAccountEntity
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
class AccountTransactionsFilter { class AccountTransactionsFilter {

View File

@ -1,7 +1,7 @@
package net.codinux.banking.ui.model package net.codinux.banking.ui.model
import net.codinux.banking.dataaccess.entities.BankAccountEntity import net.codinux.banking.persistence.entities.BankAccountEntity
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
data class BankAccountFilter( data class BankAccountFilter(
val bank: BankAccessEntity, val bank: BankAccessEntity,

View File

@ -1,7 +1,7 @@
package net.codinux.banking.ui.model package net.codinux.banking.ui.model
import net.codinux.banking.client.model.Amount import net.codinux.banking.client.model.Amount
import net.codinux.banking.dataaccess.entities.BankAccountEntity import net.codinux.banking.persistence.entities.BankAccountEntity
data class ShowTransferMoneyDialogData( data class ShowTransferMoneyDialogData(
val senderAccount: BankAccountEntity? = null, val senderAccount: BankAccountEntity? = null,

View File

@ -7,7 +7,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import net.codinux.banking.client.model.isNegative import net.codinux.banking.client.model.isNegative
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity import net.codinux.banking.persistence.entities.AccountTransactionEntity
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.extensions.verticalScroll import net.codinux.banking.ui.extensions.verticalScroll
import net.codinux.banking.ui.forms.LabelledValue import net.codinux.banking.ui.forms.LabelledValue

View File

@ -6,7 +6,7 @@ import androidx.compose.material.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import net.codinux.banking.dataaccess.entities.BankAccountEntity import net.codinux.banking.persistence.entities.BankAccountEntity
import net.codinux.banking.ui.config.Internationalization import net.codinux.banking.ui.config.Internationalization
import net.codinux.banking.ui.extensions.verticalScroll import net.codinux.banking.ui.extensions.verticalScroll
import net.codinux.banking.ui.forms.* import net.codinux.banking.ui.forms.*

View File

@ -5,7 +5,7 @@ import androidx.compose.material.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.extensions.verticalScroll import net.codinux.banking.ui.extensions.verticalScroll
import net.codinux.banking.ui.forms.* import net.codinux.banking.ui.forms.*

View File

@ -13,7 +13,7 @@ import androidx.compose.ui.text.style.TextAlign
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity import net.codinux.banking.persistence.entities.AccountTransactionEntity
import net.codinux.banking.ui.IOorDefault import net.codinux.banking.ui.IOorDefault
import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.Colors
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI

View File

@ -1,8 +1,8 @@
package net.codinux.banking.ui.service package net.codinux.banking.ui.service
import net.codinux.banking.dataaccess.entities.BankAccountEntity import net.codinux.banking.persistence.entities.BankAccountEntity
import net.codinux.banking.dataaccess.entities.HoldingEntity import net.codinux.banking.persistence.entities.HoldingEntity
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.AccountTransactionsFilter import net.codinux.banking.ui.model.AccountTransactionsFilter
import net.codinux.banking.ui.model.BankAccountFilter import net.codinux.banking.ui.model.BankAccountFilter
@ -46,7 +46,7 @@ class AccountTransactionsFilterService {
private fun matchesSearchTerm(transaction: AccountTransactionViewModel, searchTerm: String): Boolean = private fun matchesSearchTerm(transaction: AccountTransactionViewModel, searchTerm: String): Boolean =
transaction.reference?.contains(searchTerm, true) == true transaction.reference?.contains(searchTerm, true) == true
|| (transaction.otherPartyName != null && transaction.otherPartyName.contains(searchTerm, true)) || transaction.otherPartyName?.contains(searchTerm, true) == true
fun filterHoldings(holdings: List<HoldingEntity>, filter: AccountTransactionsFilter): List<HoldingEntity> { fun filterHoldings(holdings: List<HoldingEntity>, filter: AccountTransactionsFilter): List<HoldingEntity> {

View File

@ -16,8 +16,12 @@ import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
import net.codinux.banking.client.model.response.* import net.codinux.banking.client.model.response.*
import net.codinux.banking.client.model.securitiesaccount.Holding import net.codinux.banking.client.model.securitiesaccount.Holding
import net.codinux.banking.client.service.BankingModelService import net.codinux.banking.client.service.BankingModelService
import net.codinux.banking.dataaccess.BankingRepository import net.codinux.banking.persistence.BankingRepository
import net.codinux.banking.dataaccess.entities.* import net.codinux.banking.persistence.entities.AccountTransactionEntity
import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.persistence.entities.BankAccountEntity
import net.codinux.banking.persistence.entities.HoldingEntity
import net.codinux.banking.persistence.entities.UiSettingsEntity
import net.codinux.banking.ui.IOorDefault import net.codinux.banking.ui.IOorDefault
import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.BankInfo import net.codinux.banking.ui.model.BankInfo
@ -57,7 +61,13 @@ class BankingService(
} }
uiState.appSettings.value = appSettings uiState.appSettings.value = appSettings
bankingRepository.getUiSettings(uiSettings) bankingRepository.getUiSettings()?.let {
uiSettings.showBalance.value = it.showBalance
uiSettings.transactionsGrouping.value = it.transactionsGrouping
uiSettings.showTransactionsInAlternatingColors.value = it.showTransactionsInAlternatingColors
uiSettings.showBankIcons.value = it.showBankIcons
uiSettings.showColoredAmounts.value = it.showColoredAmounts
}
updateOnChanges(uiSettings) updateOnChanges(uiSettings)
@ -76,7 +86,9 @@ class BankingService(
suspend fun saveAppSettings(settings: AppSettings) = bankingRepository.saveAppSettings(settings) suspend fun saveAppSettings(settings: AppSettings) = bankingRepository.saveAppSettings(settings)
suspend fun saveUiSettings(settings: UiSettings) = bankingRepository.saveUiSettings(settings) suspend fun saveUiSettings(settings: UiSettings) = bankingRepository.saveUiSettings(UiSettingsEntity(
settings.showBalance.value, settings.transactionsGrouping.value, settings.showTransactionsInAlternatingColors.value, settings.showBankIcons.value, settings.showColoredAmounts.value
))
fun getAllBanks() = bankingRepository.getAllBanks() fun getAllBanks() = bankingRepository.getAllBanks()

View File

@ -1,7 +1,7 @@
package net.codinux.banking.ui.service package net.codinux.banking.ui.service
import net.codinux.banking.client.model.* import net.codinux.banking.client.model.*
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.AccountTransactionsFilter import net.codinux.banking.ui.model.AccountTransactionsFilter

View File

@ -5,7 +5,7 @@ import kotlinx.datetime.*
import net.codinux.banking.client.model.Amount import net.codinux.banking.client.model.Amount
import net.codinux.banking.client.model.isNegative import net.codinux.banking.client.model.isNegative
import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.Colors
import net.codinux.banking.ui.model.TransactionsGrouping import net.codinux.banking.ui.model.settings.TransactionsGrouping
class FormatUtil { class FormatUtil {

View File

@ -5,7 +5,7 @@ import kotlinx.datetime.LocalDate
import kotlinx.datetime.Month import kotlinx.datetime.Month
import net.codinux.banking.client.model.extensions.minusDays import net.codinux.banking.client.model.extensions.minusDays
import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.TransactionsGrouping import net.codinux.banking.ui.model.settings.TransactionsGrouping
class TransactionsGroupingService { class TransactionsGroupingService {

View File

@ -2,7 +2,7 @@ package net.codinux.banking.ui.settings
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import net.codinux.banking.ui.model.TransactionsGrouping import net.codinux.banking.ui.model.settings.TransactionsGrouping
class UiSettings : ViewModel() { class UiSettings : ViewModel() {
@ -14,6 +14,6 @@ class UiSettings : ViewModel() {
val showBankIcons = MutableStateFlow(true) val showBankIcons = MutableStateFlow(true)
val showColoredAmounts = MutableStateFlow(true) val showColoredAmounts = MutableStateFlow(false)
} }

View File

@ -7,9 +7,9 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import net.codinux.banking.client.model.tan.EnterTanResult import net.codinux.banking.client.model.tan.EnterTanResult
import net.codinux.banking.client.model.tan.TanChallenge import net.codinux.banking.client.model.tan.TanChallenge
import net.codinux.banking.dataaccess.entities.HoldingEntity import net.codinux.banking.persistence.entities.HoldingEntity
import net.codinux.banking.dataaccess.entities.BankAccessEntity import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.dataaccess.entities.BankAccountEntity import net.codinux.banking.persistence.entities.BankAccountEntity
import net.codinux.banking.ui.model.* import net.codinux.banking.ui.model.*
import net.codinux.banking.ui.model.error.ApplicationError import net.codinux.banking.ui.model.error.ApplicationError
import net.codinux.banking.ui.model.error.BankingClientError import net.codinux.banking.ui.model.error.BankingClientError

View File

@ -6,19 +6,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.* import androidx.compose.ui.window.*
import app.cash.sqldelight.async.coroutines.synchronous
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import bankmeister.composeapp.generated.resources.AppIcon_svg import bankmeister.composeapp.generated.resources.AppIcon_svg
import bankmeister.composeapp.generated.resources.Res import bankmeister.composeapp.generated.resources.Res
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import net.codinux.banking.client.model.AccountTransaction import net.codinux.banking.client.model.AccountTransaction
import net.codinux.banking.client.model.Amount import net.codinux.banking.client.model.Amount
import net.codinux.banking.dataaccess.BankmeisterDb import net.codinux.banking.persistence.InMemoryBankingRepository
import net.codinux.banking.dataaccess.InMemoryBankingRepository
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI
import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.painterResource
import java.io.File
fun main() = application { fun main() = application {
Window( Window(
@ -27,34 +22,20 @@ fun main() = application {
icon = painterResource(Res.drawable.AppIcon_svg), icon = painterResource(Res.drawable.AppIcon_svg),
state = WindowState(position = WindowPosition(Alignment.Center), size = DpSize(1000.dp, 800.dp)), state = WindowState(position = WindowPosition(Alignment.Center), size = DpSize(1000.dp, 800.dp)),
) { ) {
DI.setRepository(createSqlDriverDriver())
App() App()
} }
} }
private fun createSqlDriverDriver(): SqlDriver {
File("data/db").mkdirs()
return JdbcSqliteDriver("jdbc:sqlite:data/db/Bankmeister.db").also { driver ->
BankmeisterDb.Schema.synchronous().also { schema ->
if (File("data/db/Bankmeister.db").exists() == false) {
schema.create(driver)
}
schema.migrate(driver, schema.version, 1)
}
}
}
@Preview @Preview
@Composable @Composable
fun AppPreview() { fun AppPreview() {
DI.setRepository(InMemoryBankingRepository( DI.setRepository(
InMemoryBankingRepository(
emptyList(), emptyList(),
listOf(AccountTransaction(Amount("12.34"), "EUR", "Lohn", LocalDate(2024, 7, 5), LocalDate(2024, 6, 15), "Dein Boss")) listOf(AccountTransaction(Amount("12.34"), "EUR", "Lohn", LocalDate(2024, 7, 5), LocalDate(2024, 6, 15), "Dein Boss"))
)) )
)
App() App()
} }

View File

@ -2,6 +2,7 @@ package net.codinux.banking.ui.service
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.toComposeImageBitmap import androidx.compose.ui.graphics.toComposeImageBitmap
import net.codinux.banking.persistence.dataDirectory
import net.codinux.log.Log import net.codinux.log.Log
import org.jetbrains.skia.Image import org.jetbrains.skia.Image
import java.io.File import java.io.File
@ -9,7 +10,7 @@ import java.net.URL
import java.security.MessageDigest import java.security.MessageDigest
private val cacheDir = File("data/imageCache").also { it.mkdirs() } private val cacheDir = File(dataDirectory, "imageCache").also { it.mkdirs() }
private val messageDigest = MessageDigest.getInstance("SHA-256") private val messageDigest = MessageDigest.getInstance("SHA-256")

View File

@ -1,5 +1,19 @@
<configuration> <configuration>
<property name="logDir" value="${user.home}/.bankmeister" /> <!-- For Release, write to the user's home directory -->
<if condition='property("ENV").equalsIgnoreCase("debug")'>
<then>
<property name="logDir" value="${user.dir}/data" /> <!-- For Debug/Development, write to the current directory -->
</then>
</if>
<if condition='property("ENV") == "debug"'>
<then>
<property name="logDir" value="${user.dir}/data" /> <!-- For Debug/Development, write to the current directory -->
</then>
</if>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
@ -17,11 +31,11 @@
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/> <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<appender name="file" class="ch.qos.logback.core.FileAppender"> <appender name="file" class="ch.qos.logback.core.FileAppender">
<file>data/logs/Bankmeister-${bySecond}.log</file> <file>${logDir}/logs/Bankmeister-${bySecond}.log</file>
<!-- encoders are assigned the type <!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder> <encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder> </encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter">

View File

@ -1,13 +1,9 @@
package net.codinux.banking.ui package net.codinux.banking.ui
import androidx.compose.ui.window.ComposeUIViewController import androidx.compose.ui.window.ComposeUIViewController
import app.cash.sqldelight.async.coroutines.synchronous import net.codinux.banking.persistence.SqliteBankingRepository
import app.cash.sqldelight.driver.native.NativeSqliteDriver
import net.codinux.banking.dataaccess.BankmeisterDb
import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.DI
fun MainViewController() = ComposeUIViewController { fun MainViewController() = ComposeUIViewController {
DI.setRepository(NativeSqliteDriver(Database.Schema.synchronous(), "Bankmeister.db"))
App() App()
} }

View File

@ -1,6 +1,36 @@
package net.codinux.banking.ui package net.codinux.banking.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.useContents
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import platform.CoreGraphics.CGRect
import platform.UIKit.UIDevice import platform.UIKit.UIDevice
import platform.UIKit.UIScreen
actual val Dispatchers.IOorDefault: CoroutineDispatcher
get() = Dispatchers.IO
@OptIn(ExperimentalForeignApi::class)
@Composable
actual fun rememberScreenSizeInfo(): ScreenSizeInfo {
val density = LocalDensity.current
val screenBounds: CGRect = UIScreen.mainScreen.bounds.useContents { this }
val screenWidth = screenBounds.size.width
val screenHeight = screenBounds.size.height
return remember(density, screenWidth, screenHeight) {
ScreenSizeInfo(
heightDp = with(density) { screenHeight.dp },
widthDp = with(density) { screenWidth.dp }
)
}
}
class IOSPlatform: Platform { class IOSPlatform: Platform {
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion

View File

@ -0,0 +1,37 @@
package net.codinux.banking.ui.service
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.toComposeImageBitmap
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.readBytes
import org.jetbrains.skia.Image
import kotlinx.coroutines.suspendCancellableCoroutine
import platform.Foundation.*
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
actual fun createImageBitmap(imageBytes: ByteArray): ImageBitmap =
Image.makeFromEncoded(imageBytes).toComposeImageBitmap()
@OptIn(ExperimentalForeignApi::class)
actual suspend fun fetchBytesFromUrl(url: String): ByteArray {
val nsUrl = NSURL(string = url) ?: throw IllegalArgumentException("Invalid URL")
return suspendCancellableCoroutine { continuation ->
val request = NSURLRequest.requestWithURL(nsUrl)
val session = NSURLSession.sharedSession
val task = session.dataTaskWithRequest(request) { data, response, error ->
when {
error != null -> continuation.resumeWithException(Exception(error.localizedDescription))
data != null -> continuation.resume(data.bytes!!.readBytes(data.length.toInt()))
else -> continuation.resumeWithException(Exception("Unknown error"))
}
}
continuation.invokeOnCancellation {
task.cancel() // Cancel the task if the coroutine is cancelled
}
task.resume()
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,21 @@
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<rect width="60" height="60" fill="white"/>
<rect width="60" height="60" fill="white"/>
<circle cx="29.9999" cy="30" r="25.9091" fill="black" fill-opacity="0.09"/>
<circle cx="29.9999" cy="30" r="25.6591" stroke="#2869BF" stroke-opacity="0.04" stroke-width="0.5"/>
<circle cx="30.0001" cy="30" r="16.3636" fill="black" fill-opacity="0.09"/>
<circle cx="30.0001" cy="30" r="16.1136" stroke="#2869BF" stroke-opacity="0.04" stroke-width="0.5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.25 0H29.75V29.75H0V30.25H29.75V60H30.25V30.25H60V29.75H30.25V0Z" fill="#6277A1" fill-opacity="0.1"/>
<path d="M0 0L60 0L60 60L0 60L0 0Z" fill="#003832"/>
<path d="M16.2969 18.9254C16.2969 17.9348 17.022 17.0935 18.0018 16.9473L29.5243 15.2286C29.7173 15.1998 29.9135 15.1994 30.1067 15.2275L41.9916 16.953C42.9749 17.0957 43.7043 17.9387 43.7043 18.9322V20.963C43.7043 22.0675 42.8089 22.963 41.7043 22.963L18.2969 22.963C17.1923 22.963 16.2969 22.0675 16.2969 20.963V18.9254Z" fill="#77A83F"/>
<rect x="16.2961" y="25.5555" width="7.40741" height="19.2593" rx="2" fill="#079326"/>
<rect x="26.2961" y="25.5555" width="7.40741" height="19.2593" rx="2" fill="#68A93D"/>
<rect x="36.2961" y="25.5555" width="7.40741" height="19.2593" rx="2" fill="#CCFFB4"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="60" height="60" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,7 @@
<svg width="162" height="162" viewBox="0 0 162 162" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="81" cy="81" r="81" fill="#003832"/>
<path d="M44 48.1682C44 47.1776 44.7252 46.3363 45.7049 46.1901L80.2089 41.0434C80.4019 41.0146 80.5981 41.0142 80.7913 41.0423L116.287 46.1958C117.271 46.3385 118 47.1815 118 48.175V60C118 61.1046 117.105 62 116 62H46C44.8954 62 44 61.1046 44 60V48.1682Z" fill="#77A83F"/>
<rect x="44" y="69" width="20" height="52" rx="2" fill="#079326"/>
<rect x="71" y="69" width="20" height="52" rx="2" fill="#68A93D"/>
<rect x="98" y="69" width="20" height="52" rx="2" fill="#CCFFB4"/>
</svg>

After

Width:  |  Height:  |  Size: 630 B

View File

@ -0,0 +1,19 @@
# converts a 1024x1024 png - here named 'AppIcon.png' - to all size relevant to create
# an .icns file in AppIcon.iconset subfolder and then creates AppIcon.icns file
mkdir AppIcon.iconset
sips -z 16 16 AppIcon.png --out AppIcon.iconset/icon_16x16.png
sips -z 32 32 AppIcon.png --out AppIcon.iconset/icon_16x16@2.png
sips -z 32 32 AppIcon.png --out AppIcon.iconset/icon_32x32.png
sips -z 64 64 AppIcon.png --out AppIcon.iconset/icon_32x32@2.png
sips -z 128 128 AppIcon.png --out AppIcon.iconset/icon_128x128.png
sips -z 256 256 AppIcon.png --out AppIcon.iconset/icon_128x128@2.png
sips -z 256 256 AppIcon.png --out AppIcon.iconset/icon_256x256.png
sips -z 512 512 AppIcon.png --out AppIcon.iconset/icon_256x256@2.png
sips -z 512 512 AppIcon.png --out AppIcon.iconset/icon_512x512.png
cp AppIcon.png AppIcon.iconset/icon_512x512@2.png
iconutil -c icns AppIcon.iconset
# rm -R AppIcon.iconset

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -2,15 +2,17 @@
kotlin = "2.0.10" kotlin = "2.0.10"
kotlinx-coroutines = "1.8.1" kotlinx-coroutines = "1.8.1"
banking-client = "0.6.1-SNAPSHOT" banking-client = "0.6.1"
kcsv = "2.2.0" kcsv = "2.2.0"
kotlinx-serializable = "1.7.1" kotlinx-serializable = "1.7.1"
kotlinx-datetime = "0.5.0"
favre-bcrypt = "0.10.2" favre-bcrypt = "0.10.2"
klf = "1.6.1" klf = "1.6.2"
logback = "1.5.7" logback = "1.5.7"
janino = "3.1.12"
sqlDelight = "2.0.2" sqlDelight = "2.0.2"
@ -38,11 +40,13 @@ fints4k-banking-client = { group = "net.codinux.banking.client", name = "fints4k
kcsv = { group = "net.codinux.csv", name = "kcsv", version.ref = "kcsv" } kcsv = { group = "net.codinux.csv", name = "kcsv", version.ref = "kcsv" }
coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" } coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
kotlinx-serializable = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serializable" } kotlinx-serializable = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serializable" }
kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinx-datetime" }
favre-bcrypt = { group = "at.favre.lib", name = "bcrypt", version.ref = "favre-bcrypt" } favre-bcrypt = { group = "at.favre.lib", name = "bcrypt", version.ref = "favre-bcrypt" }
klf = { group = "net.codinux.log", name = "klf", version.ref = "klf" } klf = { group = "net.codinux.log", name = "klf", version.ref = "klf" }
logback = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" } logback = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
janino = { group = "org.codehaus.janino", name = "janino", version.ref = "janino" }
sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqlDelight" } sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqlDelight" }
sqldelight-coroutines-extensions = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqlDelight" } sqldelight-coroutines-extensions = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqlDelight" }

View File

@ -1,3 +1,3 @@
TEAM_ID= TEAM_ID=
BUNDLE_ID=net.codinux.banking.ui.Bankmeister BUNDLE_ID=net.codinux.banking.bankmeister
APP_NAME=Bankmeister APP_NAME=Bankmeister

View File

@ -318,20 +318,24 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = "${TEAM_ID}"; DEVELOPMENT_TEAM = 7WVYN7QA7Z;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
); );
INFOPLIST_FILE = iosApp/Info.plist; INFOPLIST_FILE = iosApp/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
IPHONEOS_DEPLOYMENT_TARGET = 15.3; IPHONEOS_DEPLOYMENT_TARGET = 15.3;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = net.codinux.banking.bankmeister;
PRODUCT_NAME = "${APP_NAME}"; PRODUCT_NAME = "${APP_NAME}";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -345,20 +349,24 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = "${TEAM_ID}"; DEVELOPMENT_TEAM = 7WVYN7QA7Z;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
); );
INFOPLIST_FILE = iosApp/Info.plist; INFOPLIST_FILE = iosApp/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
IPHONEOS_DEPLOYMENT_TARGET = 15.3; IPHONEOS_DEPLOYMENT_TARGET = 15.3;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = net.codinux.banking.bankmeister;
PRODUCT_NAME = "${APP_NAME}"; PRODUCT_NAME = "${APP_NAME}";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -390,4 +398,4 @@
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };
rootObject = 7555FF73242A565900829871 /* Project object */; rootObject = 7555FF73242A565900829871 /* Project object */;
} }

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -1,6 +1,6 @@
import UIKit import UIKit
import SwiftUI import SwiftUI
import ComposeApp import BankmeisterFramework
struct ComposeView: UIViewControllerRepresentable { struct ComposeView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController { func makeUIViewController(context: Context) -> UIViewController {

View File

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
@ -15,13 +17,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>UIApplicationSupportsMultipleScenes</key>
@ -36,15 +36,14 @@
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array> </array>
</dict> </dict>
</plist> </plist>

View File

@ -33,4 +33,7 @@ dependencyResolutionManagement {
} }
// had to extract Sql'delight' dependencies to an extra project as they conflict with Compose dependencies on iOS
include(":BankingPersistence")
include(":composeApp") include(":composeApp")