From 7f229565571958cfb26ea241223e5b6cb4b71145 Mon Sep 17 00:00:00 2001 From: Terry Yiu <963907+tyiu@users.noreply.github.com> Date: Sat, 31 Aug 2024 10:24:07 +0300 Subject: [PATCH] Convert SatsPrice to a Skip multiplatform app --- .gitignore | 104 +++ Android/app/build.gradle.kts | 64 ++ Android/app/proguard-rules.pro | 4 + Android/app/src/main/AndroidManifest.xml | 30 + .../app/src/main/ic_launcher-playstore.png | Bin 0 -> 45564 bytes .../app/src/main/kotlin/sats/price/Main.kt | 125 +++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 2104 bytes .../mipmap-hdpi/ic_launcher_foreground.webp | Bin 0 -> 3222 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 3826 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 1330 bytes .../mipmap-mdpi/ic_launcher_foreground.webp | Bin 0 -> 2192 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 2306 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 3088 bytes .../mipmap-xhdpi/ic_launcher_foreground.webp | Bin 0 -> 4510 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 5314 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 4828 bytes .../mipmap-xxhdpi/ic_launcher_foreground.webp | Bin 0 -> 6992 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 8806 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 6960 bytes .../ic_launcher_foreground.webp | Bin 0 -> 9948 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 12286 bytes .../res/values/ic_launcher_background.xml | 4 + Android/fastlane/Appfile | 11 + Android/fastlane/Fastfile | 49 ++ .../android/en-US/full_description.txt | 1 + .../android/en-US/short_description.txt | 1 + .../fastlane/metadata/android/en-US/title.txt | 1 + Android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 2 + Android/settings.gradle.kts | 46 ++ .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 0 .../bitcoin-calculator-1024-no-alpha 1.png | Bin .../bitcoin-calculator-1024-no-alpha.png | Bin .../bitcoin-calculator-128-no-alpha.png | Bin .../bitcoin-calculator-16-no-alpha.png | Bin .../bitcoin-calculator-256-no-alpha 1.png | Bin .../bitcoin-calculator-256-no-alpha.png | Bin .../bitcoin-calculator-32-no-alpha 1.png | Bin .../bitcoin-calculator-32-no-alpha.png | Bin .../bitcoin-calculator-512-no-alpha 1.png | Bin .../bitcoin-calculator-512-no-alpha.png | Bin .../bitcoin-calculator-64-no-alpha.png | Bin .../Assets.xcassets/Contents.json | 0 .../Entitlements.plist | 3 +- .../.xccurrentversion => Darwin/Info.plist | 4 +- Darwin/SatsPrice.xcconfig | 56 ++ Darwin/SatsPrice.xcodeproj/project.pbxproj | 294 +++++++ .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 Darwin/Sources/SatsPriceAppMain.swift | 10 + Darwin/fastlane/AppStore.xcconfig | 5 + Darwin/fastlane/Appfile | 8 + Darwin/fastlane/Deliverfile | 28 + Darwin/fastlane/Fastfile | 32 + .../fastlane/metadata/en-US/description.txt | 1 + Darwin/fastlane/metadata/en-US/keywords.txt | 1 + .../fastlane/metadata/en-US/privacy_url.txt | 1 + .../fastlane/metadata/en-US/release_notes.txt | 1 + .../fastlane/metadata/en-US/software_url.txt | 1 + Darwin/fastlane/metadata/en-US/subtitle.txt | 1 + .../fastlane/metadata/en-US/support_url.txt | 1 + Darwin/fastlane/metadata/en-US/title.txt | 1 + .../metadata/en-US/version_whats_new.txt | 1 + Darwin/fastlane/metadata/rating.json | 17 + LICENSE => LICENSE.GPL | 0 Package.swift | 24 + README.md | 45 +- SatsPrice.xcodeproj/project.pbxproj | 718 ------------------ .../xcshareddata/swiftpm/Package.resolved | 32 - .../WorkspaceSettings.xcsettings | 14 - .../xcdebugger/Breakpoints_v2.xcbkptlist | 6 - .../xcschemes/xcschememanagement.plist | 35 - SatsPrice/Network/PriceFetcher.swift | 13 - SatsPrice/SatsPrice.entitlements | 10 - .../SatsPrice.xcdatamodel/contents | 9 - SatsPrice/SatsPriceApp.swift | 17 - SatsPrice/SatsViewModel.swift | 67 -- SatsPriceTests/SatsPriceTests.swift | 35 - SatsPriceUITests/SatsPriceUITests.swift | 41 - .../SatsPriceUITestsLaunchTests.swift | 32 - Skip.env | 20 + .../SatsPrice}/ContentView.swift | 30 +- .../Network/CoinGeckoPriceFetcher.swift | 16 +- .../Network/CoinbasePriceFetcher.swift | 12 +- .../SatsPrice}/Network/FakePriceFetcher.swift | 8 +- .../Network/ManualPriceFetcher.swift | 8 +- Sources/SatsPrice/Network/PriceFetcher.swift | 15 + .../Network/PriceFetcherDelegator.swift | 6 +- .../SatsPrice}/Network/PriceSource.swift | 3 + .../SatsPrice/Resources/Localizable.xcstrings | 27 + .../Resources/Module.xcassets}/Contents.json | 0 Sources/SatsPrice/SatsPrice.swift | 6 + Sources/SatsPrice/SatsPriceApp.swift | 43 ++ Sources/SatsPrice/SatsViewModel.swift | 173 +++++ Sources/SatsPrice/Skip/skip.yml | 3 + .../SatsPriceTests}/SatsViewModelTests.swift | 50 +- Tests/SatsPriceTests/Skip/skip.yml | 3 + Tests/SatsPriceTests/XCSkipTests.swift | 32 + 101 files changed, 1379 insertions(+), 1095 deletions(-) create mode 100644 .gitignore create mode 100644 Android/app/build.gradle.kts create mode 100644 Android/app/proguard-rules.pro create mode 100644 Android/app/src/main/AndroidManifest.xml create mode 100644 Android/app/src/main/ic_launcher-playstore.png create mode 100644 Android/app/src/main/kotlin/sats/price/Main.kt create mode 100644 Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 Android/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 Android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp create mode 100644 Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 Android/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 Android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp create mode 100644 Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 Android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 Android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp create mode 100644 Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp create mode 100644 Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp create mode 100644 Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 Android/app/src/main/res/values/ic_launcher_background.xml create mode 100644 Android/fastlane/Appfile create mode 100644 Android/fastlane/Fastfile create mode 100644 Android/fastlane/metadata/android/en-US/full_description.txt create mode 100644 Android/fastlane/metadata/android/en-US/short_description.txt create mode 100644 Android/fastlane/metadata/android/en-US/title.txt create mode 100644 Android/gradle.properties create mode 100644 Android/gradle/wrapper/gradle-wrapper.properties create mode 100644 Android/settings.gradle.kts rename {SatsPrice => Darwin}/Assets.xcassets/AccentColor.colorset/Contents.json (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-1024-no-alpha 1.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-1024-no-alpha.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-128-no-alpha.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-16-no-alpha.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-256-no-alpha 1.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-256-no-alpha.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-32-no-alpha 1.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-32-no-alpha.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-512-no-alpha 1.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-512-no-alpha.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-64-no-alpha.png (100%) rename {SatsPrice => Darwin}/Assets.xcassets/Contents.json (100%) rename SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings => Darwin/Entitlements.plist (92%) rename SatsPrice/SatsPrice.xcdatamodeld/.xccurrentversion => Darwin/Info.plist (71%) create mode 100644 Darwin/SatsPrice.xcconfig create mode 100644 Darwin/SatsPrice.xcodeproj/project.pbxproj rename {SatsPrice.xcodeproj => Darwin/SatsPrice.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (100%) rename {SatsPrice.xcodeproj => Darwin/SatsPrice.xcodeproj}/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) create mode 100644 Darwin/Sources/SatsPriceAppMain.swift create mode 100644 Darwin/fastlane/AppStore.xcconfig create mode 100644 Darwin/fastlane/Appfile create mode 100644 Darwin/fastlane/Deliverfile create mode 100644 Darwin/fastlane/Fastfile create mode 100644 Darwin/fastlane/metadata/en-US/description.txt create mode 100644 Darwin/fastlane/metadata/en-US/keywords.txt create mode 100644 Darwin/fastlane/metadata/en-US/privacy_url.txt create mode 100644 Darwin/fastlane/metadata/en-US/release_notes.txt create mode 100644 Darwin/fastlane/metadata/en-US/software_url.txt create mode 100644 Darwin/fastlane/metadata/en-US/subtitle.txt create mode 100644 Darwin/fastlane/metadata/en-US/support_url.txt create mode 100644 Darwin/fastlane/metadata/en-US/title.txt create mode 100644 Darwin/fastlane/metadata/en-US/version_whats_new.txt create mode 100644 Darwin/fastlane/metadata/rating.json rename LICENSE => LICENSE.GPL (100%) create mode 100644 Package.swift delete mode 100644 SatsPrice.xcodeproj/project.pbxproj delete mode 100644 SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 SatsPrice.xcodeproj/project.xcworkspace/xcuserdata/tyiu.xcuserdatad/WorkspaceSettings.xcsettings delete mode 100644 SatsPrice.xcodeproj/xcuserdata/tyiu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist delete mode 100644 SatsPrice.xcodeproj/xcuserdata/tyiu.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 SatsPrice/Network/PriceFetcher.swift delete mode 100644 SatsPrice/SatsPrice.entitlements delete mode 100644 SatsPrice/SatsPrice.xcdatamodeld/SatsPrice.xcdatamodel/contents delete mode 100644 SatsPrice/SatsPriceApp.swift delete mode 100644 SatsPrice/SatsViewModel.swift delete mode 100644 SatsPriceTests/SatsPriceTests.swift delete mode 100644 SatsPriceUITests/SatsPriceUITests.swift delete mode 100644 SatsPriceUITests/SatsPriceUITestsLaunchTests.swift create mode 100644 Skip.env rename {SatsPrice => Sources/SatsPrice}/ContentView.swift (83%) rename {SatsPrice => Sources/SatsPrice}/Network/CoinGeckoPriceFetcher.swift (71%) rename {SatsPrice => Sources/SatsPrice}/Network/CoinbasePriceFetcher.swift (74%) rename {SatsPrice => Sources/SatsPrice}/Network/FakePriceFetcher.swift (50%) rename {SatsPrice => Sources/SatsPrice}/Network/ManualPriceFetcher.swift (52%) create mode 100644 Sources/SatsPrice/Network/PriceFetcher.swift rename {SatsPrice => Sources/SatsPrice}/Network/PriceFetcherDelegator.swift (79%) rename {SatsPrice => Sources/SatsPrice}/Network/PriceSource.swift (78%) create mode 100644 Sources/SatsPrice/Resources/Localizable.xcstrings rename {SatsPrice/Preview Content/Preview Assets.xcassets => Sources/SatsPrice/Resources/Module.xcassets}/Contents.json (100%) create mode 100644 Sources/SatsPrice/SatsPrice.swift create mode 100644 Sources/SatsPrice/SatsPriceApp.swift create mode 100644 Sources/SatsPrice/SatsViewModel.swift create mode 100644 Sources/SatsPrice/Skip/skip.yml rename {SatsPriceTests => Tests/SatsPriceTests}/SatsViewModelTests.swift (52%) create mode 100644 Tests/SatsPriceTests/Skip/skip.yml create mode 100644 Tests/SatsPriceTests/XCSkipTests.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a42c540 --- /dev/null +++ b/.gitignore @@ -0,0 +1,104 @@ +## User settings +xcuserdata/ + +xcodebuild*.log + +.gradle/ +.idea/ +.*.swp +.*.swo +.DS_Store +Android/app/keystore.jks +Android/app/keystore.properties + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +.android/ +.kotlin/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +Packages/ +Package.pins +Package.resolved +#*.xcodeproj + +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +.swiftpm +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +**/fastlane/apikey.json +**/fastlane/report.xml +**/fastlane/README.md +**/fastlane/Preview.html +**/fastlane/screenshots/**/*.png +**/fastlane/metadata/android/*/images/**/*.png +**/fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ + +# gradle properties +local.properties + diff --git a/Android/app/build.gradle.kts b/Android/app/build.gradle.kts new file mode 100644 index 0000000..18afca3 --- /dev/null +++ b/Android/app/build.gradle.kts @@ -0,0 +1,64 @@ +import java.util.Properties + +plugins { + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) + alias(libs.plugins.android.application) + id("skip-build-plugin") +} + +skip { +} + +android { + namespace = group as String + compileSdk = libs.versions.android.sdk.compile.get().toInt() + compileOptions { + sourceCompatibility = JavaVersion.toVersion(libs.versions.jvm.get()) + targetCompatibility = JavaVersion.toVersion(libs.versions.jvm.get()) + } + kotlinOptions { + jvmTarget = libs.versions.jvm.get().toString() + } + packaging { + jniLibs.keepDebugSymbols.add("**/*.so") + } + + defaultConfig { + minSdk = libs.versions.android.sdk.min.get().toInt() + targetSdk = libs.versions.android.sdk.compile.get().toInt() + // skip.tools.skip-build-plugin will automatically use Skip.env properties for: + // applicationId = PRODUCT_BUNDLE_IDENTIFIER + // versionCode = CURRENT_PROJECT_VERSION + // versionName = MARKETING_VERSION + } + + buildFeatures { + buildConfig = true + } + + // default signing configuration tries to load from keystore.properties + signingConfigs { + val keystorePropertiesFile = file("keystore.properties") + if (keystorePropertiesFile.isFile) { + create("release") { + val keystoreProperties = Properties() + keystoreProperties.load(keystorePropertiesFile.inputStream()) + keyAlias = keystoreProperties.getProperty("keyAlias") + keyPassword = keystoreProperties.getProperty("keyPassword") + storeFile = file(keystoreProperties.getProperty("storeFile")) + storePassword = keystoreProperties.getProperty("storePassword") + } + } + } + + buildTypes { + release { + signingConfig = signingConfigs.findByName("release") + isMinifyEnabled = true + isShrinkResources = true + isDebuggable = false // can be set to true for debugging release build, but needs to be false when uploading to store + proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") + } + } +} diff --git a/Android/app/proguard-rules.pro b/Android/app/proguard-rules.pro new file mode 100644 index 0000000..378b355 --- /dev/null +++ b/Android/app/proguard-rules.pro @@ -0,0 +1,4 @@ +-keeppackagenames ** +-keep class skip.** { *; } +-keep class com.sun.jna.Pointer { *; } +-keep class sats.price.** { *; } diff --git a/Android/app/src/main/AndroidManifest.xml b/Android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c6c7049 --- /dev/null +++ b/Android/app/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Android/app/src/main/ic_launcher-playstore.png b/Android/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..36bd74eb05335b96799936dbe0d158d2b6b4d2f3 GIT binary patch literal 45564 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 ahVz@2$=Z~|X^IvI_)(Nqlc|(43;Hja=xzxB literal 0 HcmV?d00001 diff --git a/Android/app/src/main/kotlin/sats/price/Main.kt b/Android/app/src/main/kotlin/sats/price/Main.kt new file mode 100644 index 0000000..4f40e99 --- /dev/null +++ b/Android/app/src/main/kotlin/sats/price/Main.kt @@ -0,0 +1,125 @@ +package sats.price + +import skip.lib.* +import skip.model.* +import skip.foundation.* +import skip.ui.* + +import android.Manifest +import android.app.Application +import androidx.activity.enableEdgeToEdge +import androidx.activity.compose.setContent +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.runtime.saveable.rememberSaveableStateHolder +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.core.app.ActivityCompat + +internal val logger: SkipLogger = SkipLogger(subsystem = "sats.price", category = "SatsPrice") + +/// AndroidAppMain is the `android.app.Application` entry point, and must match `application android:name` in the AndroidMainfest.xml file. +open class AndroidAppMain: Application { + constructor() { + } + + override fun onCreate() { + super.onCreate() + logger.info("starting app") + ProcessInfo.launch(applicationContext) + } + + companion object { + } +} + +/// AndroidAppMain is initial `androidx.appcompat.app.AppCompatActivity`, and must match `activity android:name` in the AndroidMainfest.xml file. +open class MainActivity: AppCompatActivity { + constructor() { + } + + override fun onCreate(savedInstanceState: android.os.Bundle?) { + super.onCreate(savedInstanceState) + logger.info("starting activity") + UIApplication.launch(this) + enableEdgeToEdge() + + setContent { + val saveableStateHolder = rememberSaveableStateHolder() + saveableStateHolder.SaveableStateProvider(true) { + PresentationRootView(ComposeContext()) + } + } + + // Example of requesting permissions on startup. + // These must match the permissions in the AndroidManifest.xml file. + //let permissions = listOf( + // Manifest.permission.ACCESS_COARSE_LOCATION, + // Manifest.permission.ACCESS_FINE_LOCATION + // Manifest.permission.CAMERA, + // Manifest.permission.WRITE_EXTERNAL_STORAGE, + //) + //let requestTag = 1 + //ActivityCompat.requestPermissions(self, permissions.toTypedArray(), requestTag) + } + + override fun onSaveInstanceState(bundle: android.os.Bundle): Unit = super.onSaveInstanceState(bundle) + + override fun onRestoreInstanceState(bundle: android.os.Bundle) { + // Usually you restore your state in onCreate(). It is possible to restore it in onRestoreInstanceState() as well, but not very common. (onRestoreInstanceState() is called after onStart(), whereas onCreate() is called before onStart(). + logger.info("onRestoreInstanceState") + super.onRestoreInstanceState(bundle) + } + + override fun onRestart() { + logger.info("onRestart") + super.onRestart() + } + + override fun onStart() { + logger.info("onStart") + super.onStart() + } + + override fun onResume() { + logger.info("onResume") + super.onResume() + } + + override fun onPause() { + logger.info("onPause") + super.onPause() + } + + override fun onStop() { + logger.info("onStop") + super.onStop() + } + + override fun onDestroy() { + logger.info("onDestroy") + super.onDestroy() + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: kotlin.Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + logger.info("onRequestPermissionsResult: ${requestCode}") + } + + companion object { + } +} + +@Composable +internal fun PresentationRootView(context: ComposeContext) { + val colorScheme = if (isSystemInDarkTheme()) ColorScheme.dark else ColorScheme.light + PresentationRoot(defaultColorScheme = colorScheme, context = context) { ctx -> + val contentContext = ctx.content() + Box(modifier = ctx.modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + RootView().Compose(context = contentContext) + } + } +} diff --git a/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Android/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..96c1588a0485ba45af88ab29ed6ee8b8642904ee GIT binary patch literal 2104 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 literal 0 HcmV?d00001 diff --git a/Android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Android/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..ee4b53a8107af685086df11e9aba21f3178454f5 GIT binary patch literal 1330 zcmV-21u_Q9?4x|kh5J!3fb;{&f&hX`r}Y11cu?x{RiPz99(4(n%^q{_&6M4 zTnH*69ND&QMzL)>DUYJ6o9Pv2V2sLY`b~fw2>?yo*^TXgt#8|oZJVWS+qP}nsBEYE zJZA|h(tE8A$ZgV(?Aw^;+b|LzRrTHDqrUpyIicXuCL zLK_r83DAU=;qJPG0$9OPS4{pd4%xOXL(&+i$+oZjy|3LR?Ey>z)aF0ajAbpZ;md ze0Qh6=Too)0G_P?m6)&Gk<)6$*`}4T+U4m2$>W0j$07<~$;c$Uw{Hx#st(7-((t5- zjGJnuxjwC~2=843faUlW}&;&az~ft_M+|8*hJc|-Dx4d z@)(L3Ces@I&S0h5p;N7D!}q(%ty28V%>P)bv(!UwsnnY*mEEXXSTg>1#2=Bg32_1U^{0oz@@ccVzK+YCp(!)0(;D^RjX$c7KWZc#dtBFQxgUoi zD)pZH4^Nt&D=ptG{0|X@V=;EtZn3M!N^3~QjOevSepxg2o746ieJ?BhF3M968pBU( zncnr#)5@&9Cja9B=+6AqDNp_EDz|-SB9}GBV-baulU?NYkdztOZ;konGxwTfkL%^% z;tH?vnB!WjhoarLUGF6iWFh<)P8?n+-d^c0w^HFNE^?8tv3a@Hcr+Qa7{8Z?0S4x`ra{$Oo zDahiz8rbeAmD>nSnUJh`lQ*;XyFlX5Oml5fB$GnLq6Q!@0?6igsac#G(Q8c~)o*ED z9rN*B8yL2!<_H*iO%hC~(oFLlNr*j^#2);#3ixfq6_~MwK-p!w z4k#OOWR-~CK;4v>(nLp|tdS^Xv3r1Ic>};fjf3Ws7NbyL)n0Q6ld-69nyv0=1q=`b zo|A+|Ng58q3aJW0civ#t#`4X0*NH?hNEqd0aFU3A6dt9Cs>I@G0o`owt|WvhxK#*T z2LrdQgFwWv7)ut47)leBKv|o^*&_7<>ZZh&tam2ME*O`PAqoNwrFza5r%>Sgl>F6I(tIn^zK7kF*w}XUB%Y3dZ3XbM zhz{uxLL5;n(FmdAN05akx`PEuo@qk^$key3g7d$eWk=-Jqr%6)eO^w2Noun-T5GU$%tPv1vb`#W%vm^-&x=7-Hx7zk1Nu{se?P24FAp1O=+E>VIP68ZfjBlKv4Bp;zw0&k6#LI3~& literal 0 HcmV?d00001 diff --git a/Android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/Android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..137fde281377e4cb0c898f89909677a681b420e1 GIT binary patch literal 2192 zcmV;B2ygdNNk&G92mk)0dTT+#op)d%I zP{#{Yp{_%Jbx+3V=^~X`C$e#kFG;kt3VtK&+6{gK6cn$}5xPM}NhheI@2SrqttA^( z(CG+0*F{E^_Mj`XN+s&bpN}t-mTYaCAxU32vf8$7+tz-3MYFQ5!nSSO%icyhGbBoq zY}!`O(zf?L=USibgL|KCziZpJZ3Ai`00@+K|A*!z&kW$YfNtPKs21J5Y4-()G*+Ow zJKO{e9gtxI02Tlk1E3v%^tu56bpV`8=)+MkTB2mAJBB*y1UU zoP8<;F97TSP|i94ascca%FF)@H66P3XAByuCbe1j3z14En5T~)p8^nK8UV!rTm>nd zqu{)AZ&r!s?=si5%3k+h<+e}y4A$YiUgsNjog)Ao0uW```ESK3A2M*L2CvughVT4k zYV?kiyd6Hv7I`gQW+QVv8_#D|o_wX&%yV^Snjm88jyAgdTK&$~c$t>3%v%!3O@uTlvUM6- z0GKj$J7uWp#83Ty9%8z#77lXu1+JVHwss66VuIEl_MJoh25WIoIqURN$y%(Fr6=D* zPD7ii`*oUvKk_zWqlFxE^pOgIJ(ja8U<1JQrQoES|55V7XWbEEnX)Zo_DscQF%n5_4O3d{gt zC0SJ;{-V#aMN&MMtngDl&tvf_O>yHmL~Op$;FYP|Mb?f&&(ciP@p2jMh@1a(6;_y7pyoXIXoSJWZ3!w+L#N!r zEz5JY?i7>DlZw8Z3_?E2DynBI8~|jwNi3c z3ww3KsBzgVh{m_T_(;VlwG$0H91l z?_V2tzrt&_zoRRzojwVrUVW{T%pL{LTHnI5C$zd&s_oS>G7R)~r z-T@i9o7I|m=A~u}8HbLZ^|OLE>U>3;-qK11*}4u~On3u;9S=F1dh|Eompth{d($X2 ze&@|80SXtDZ~ct(M%}Nh?<#PRtXVJ=fG_~dcJ+}O?eB5U-<~?}7S3(l5);bXkiiQ zrcxHpt>q}^#p8^nL_7Ou9ooh?-Gl{@1+*kY2PH`?b1>01m8BrW3R*G%%ptNCYkcklrBdm3 z3&yjE9EK)mgjA72IU(32un7@O_^7bpYD>WzC01h%v5i~@4x>b|m}uPl9x{IEM1xe> z|6JC|kY(3632iUj>@svn@!)&5`O1E1`j2sTB7A=5Ko-}E5dF0z0dp|X^L3;k#A=RWjgUD^3*bid05gPG&QW#@ZW2W5mg}V@ zA%ts69wo97>%8s&rBdl?6~?jXU`;EOl7bMKIA@S3L}V*A+;y*LrljwVC2|{h465NZ zW%T`^SqPW1h|W;6%uJV*jP&P5h-kgp_6hM$i7LorM0D`?YB^xN6C@7&O-!QyCQKho z#zUAC?_R)@?^%ze&hhAb{CP2A&kbW}usR7X08Fq2fP4UMY{#aP2cuB4><@qjwhRTJ S27qY*)(7LA0CWr;B2xf}$_zaK literal 0 HcmV?d00001 diff --git a/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b7d113e2e4543bfa9afb4859c35cf09ae5a8b12e GIT binary patch literal 2306 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 literal 0 HcmV?d00001 diff --git a/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..af7f38cbbaf032e49374f3eb921f476151e1b932 GIT binary patch literal 3088 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 literal 0 HcmV?d00001 diff --git a/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..9d71a4ba63db4bcdb88f83c49a51469ace2bf7b9 GIT binary patch literal 4510 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 literal 0 HcmV?d00001 diff --git a/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..8f3dfd49214b3fddf6cbdbf9bb3cf8909a184858 GIT binary patch literal 5314 zcmV;z6g}%wNk&Gx6aWBMMM6+kP&iDk6aWA(U%(d-6^DYhZKNdpS?^v35itS$Y`LgX zJp!wr$(CZQHhOC!Jm1F5lJnm;b=2Z6}p&+h^t%v$E4=Zk2SO;#8<)1MDFeu(GCU~L<~AWKir$)?(VL!yHmH$DV1Yed)r3e|NTFH z0wBQ%C`Ye*x?H-RCCt=b#>8%>B9pdpRsZj1Dh}L6QY2>?j`0g}Woa5B%8MS`jMZ%t;1{c*tTukTK!kxwvCd(F~cM9 z{)dQwAcqq)L5Cmi)@~e5uuOnD3No3)Yn%5gAb3KIP(+6q6Dwl(oR_6{|6@XEk-)W{ zAQOLdLJ%7=k?bb-$sdwW>PZ(FAVZJec1cqAqEog!{nT&$8xh@1U@fYrCVFHVxlXc4 zKLHsymq1IL^S_a-9e4SP>XloVq0ESwOd?N7i-_k+ti+_#wk>grY}F&EQooDec~t!Q zteX)P*-uL29@~y2p6P1i$hPB-uOCZP)=bvli_mi(9w4E~ zbHTTd7&S%PGvU1xgTrav4Uxn?La;QWD#iI`2Qi?vhqOoWZptAKg;8y*(H%4orLZZxJbkRR{`hyhF>jO?C~Ik14J0O5f0KwE%t zddit(yFN_M9W zynIL~jj=qhemnz18@MhAV$bw;C=Q5$FiWFg3s<^!a$d>{GiaZF_%u< z0w|HIbe$X#zm~A8z9V;n!&~N?Hr?eedi7(vjJjQ9!8W02dco*^{cVb49%j$-q#@tV(SjQE6 zbC?wA_zV}l2QNq?P2G)SRVy9}XIchnLi=~2@o}3UEBxGXC91*s>xA$LxRmk=*fj;zc){2J@Nvt_McSDX7XbbL6*%^ zau)?2t`#pBuTyg`!~p*@FO;G3`gf!8h>&{WPC!N9eE7YAR+-@I*zs5$?~@>ur=qCd zYWDSFMH1sh0gOLWBka0f>0_M}sS|KJ=GFiYD1~`~!yCdQM4I_tQo&Zt|B}1~URGm} z@v1|Z=*?5gX36GttC3>OZ;P3ZK1DFy@uz-o98GuZ0S`-mV}@6Nx^CinYNgbmoxQpl zz{u_sdtb2RTjrbA7OwgAdf`EX)9g9s_TB?;kvEUWljj%G+c4SC&3Hfcrak@;!ITbqs-b+ zW12mU$L%&GrZ-`-zLWKT>`i<837PW5Lst)WI(l4v_g$a2j_$Z)hGS3N-rs5A)#|XO z-YmL$(7Ydx_HwLw;nYOJ)qoUV(HU1?W?>LzV}e4d_Fs(Z+ZNnrH|IlzQ1t4DR-L51 z+~ZN3=Cnv2s#lx}DcAD0nCa*XWQ0p+$`G&;t+7VVm*kE+COh?@`DYk~4y&P%BA5wI zUtwWUIZS=m3li4n!zM%|-sN+ye2b88V zzuIZ-#paF(jdA{}B4GH7CzvbDXR~FuNvW5aa5^igo9Ph=*ju-zaSau8>7I>Urh=`Y zGReCZ)-uD2;;1O?R2a39@5p@zp{wBv1G6&97C%C^=#iLP4|eq=k_k`W#Q#jjI(LOl zEYQ=pbND_r^Gpyd(qSwOyUUf_Lj@|53kxrv51~B~ihmMe$!7HFl*#=_#b42m!@{FN zq-p48YE=vboO`H0PT#mAQZpWVG3${hT#>jRj65gIs3W;MWYGO_-vKZLF?W+SD;6TG zthDkpEHyqySG8stf3efuHuM-F?1QR#)*JQk$B4_nDF4JX{L;+p#T^*$J6szRt^_7L z>;d=Vrok_*7GKSCa$=x_cqf8^v{{pRuazB*(t(51b9KcGC);#Nt-2K(< z{I8~7|IQiBPWw(xH0ZUHNl!gs*4pzG1}3xRFvSzVT{oTyMDb4p^aMNrl6s>Im7!|I z`C}jBOjDHA87F_ ztN%e=k_?(;(gZsvtst^2o2}p?icq;Q2z#U}s*JF@HXsCI=wwRe3a+7HQi|Zi`y{Aj zm6wR=O)ZiSwRB{QA8mR&<9F-C50dIm2MXaJFlX(Jjlv+#pk}caWiO0oC;&X_Anxw> z7xYKQS@~WIGrr;K|8ynuQRSwMFIJ^tCq!vzJXCq(rX=iypkMLCO>t{GHHfof&Q+%Q~)}plO8%J;26*Y zpanpKfW`oA00shN19o})eOc+$qi7NDmhz6w=bTW8WGVn?Q_iv!&FdW@i+PtM#G^m>fb4)FOUEVGnt37))ilAJI(48qB8+nbI z(u$y(HA-9@0Bug0Q;MUJ-i_jP^90t)ecjdcU|HFsFo+URG**J<+R(I=3kX@%JDm{r zGyo_GJDQTzB*oE4fg(kT0Kkz3$wJKS9ny|5ylP59gJYhyTRW+_l|flGi?KAIBdun4!K^Om)|%=-hao#~n`%PS49?Bz+|bqtAYxL1 zbwRV%)-q3>s?;?gXHyjC&;j<*=S&Dnl4KLahDxJRX=nnMdJV5ryreiGxS+3##~nbW zP;tIpP@*IeNMEvFFxYIyg7%f(UzKyu14;fb8K{8nO&%@cxk%!|OBDoF78*Bgw%Ec+ zD+taXHhf+*Yt1JV5VXaP|lRRv>3hrXHpvGP;(c8H4({w#{a zk=gf;_Z-B(bP;hPeC-+ca*5xVAUNYfT_Rx%M9rD;wLsEIfnIuZKLlcLfiNnzF z9#F3Yd5tc@_+Fl?`=$Z!Qgtripk0t|8dY7F_iPM7IVG8L58%pgn$U`%n$;w(4xn`0 zG3TCQF*LdlR$qN}=N4cI^9084iYaqJe~-6dTwRbLpZx}>!4raN+BIq^=LmVhEXkI7 z=sLHpP0^y>A!Ul>r2{}4a;9DmjUNQWy%kte9UPI&mBPqRHu)K<7={GVNb@+`m_l9u zXA@%7Az0W2^LW>dG1Wum#u)&l@JJ5o-Qbqg;kq!h&Fk$tzykfH{NcrF58Hyayr#)5E5a!SD#!dQCyP~nRi8n5K~)w^J* z)>TN*ne7>iF*G(R4oxPFOgXcs!Fn`6V92GJC1sY}%cfarXn=wk$jrk=c@Ol5F@d`f zARBD1Bf-Wr>YCo?{fk21*ZaIr0~ITX1);N*zx`Pve+8-?vz$T}V=3T`tN+iG)hLdO zje|VvNP}x7wPVeLC#bO5TADO~8gJ4oz6Tn7;j*A)8!Z9W@CrCI*8s&Xhr9 zlD7>xOObRgIr;HwAp-yGKhX*?jx~f#F$6pzV$%o!vS`Mb5bkZTf(CbhU?ymD0^v9h z$%B5c?tHxVh**nwV>|Y9mar<5DO-taAQ7T|Mfs8#UD(qg3TXB(8=`haH7nu)t`3|C z)-!6IZA2Ff|}}+bqnjmn~ieC>qfo=hOl7K~Cm@;hY2I zfEECa11Q%zU@}1PI3M(Ke(V44frbd%)@Vuz)}uTx0K%=nj84{|TqQv82T~3U^k)5s zwn>F(OkT$l&~ z14I7>_tpNphhkfyfEM!aDLkJ-ldqy*0J_#FY4{|Y-8W3 zjvU(FYGf8Z+57YHt8cdKho9Ol9;MJ)khX6E zHl6wp^?q*n#zV5yCQkMt&YUpt$4&xYSHF2#+-S5E2s<(ai{tOHaiWrifIq~iQX(t( z4-wxPzw|DfkFWECWGX~T@(%8RZq*EITlDVZ@>DG=bHYok@0k6(?Gr^lOxe=Q)n`8N z{RbTEAw7kAfVe}WI^b}LsQ&Iv)%=}?Vx#N|h8@ld*^Pvg11I%+|6(yOs`GjfYo$@j zI=FT1_DsR*_zOk$a$)?!xgo>FLn1#~!O^_baaM@@=pjNB!!B#}eDMO&$M5sc^$%91 z(Vf&JTctZy?en5WJnyh>uy>Q!?otR1g%A&^wzJu+?<;wB^+SXZPUb0-*F5RyM!a6M z&*QgC)`Vz*JiwC&)UWfyMx4$Ra>tDWJ{~9YQX-hyE^p`NbaH9)0)ZJyR~J|oN6DiZ^Wq)k1HnJZe34j27fW?zX`Las+g{E=GNIe z)@?9-jfwO347?uQ=-tC!c`ZEXWH0uKiyh}M67J7!yh^~aBI#JLf- zueTF;{)o#aTw~macKu5`0bk$iZKS3AS?}K+fQXm?TgNw6 zfe9Q<)u&V@46HmMN!FFGVV*GhpcL)@S4;U7+}#~E0BIW8-CaA$o#^g2BYjBj?(Ux5 z;haAX|HJu_?5yAteF1kBH?jbC(qt^N0OCkx2cF3{-bA+KE@KIY<_?Qs3v}Wn@{RvA zjprFlY{25!D)2>|NFwVZ)U<8Dm_Of+lT6w+6aza~IJT`Fd)D{cwryK6hG3^kWsSh8 zlDTB1{QI!9o&NWS%f@XRNz`QTZMWMG(6#peyybXvY}>YN+qP}nwtZ~dW0w=#wte>h zoMNv%scfrrI@?aJ!o~$S)l3)cD&0TMx0`fR#z||NrBm6dY<8-qjLNf8&7@0~os?Gf z?_v$h*iM<1*7li;m^%ncwvlbyw$e!JULZkYBoKGUC2m#J-}le-Yq?02ZQGEf-D4Xc zUr1-!wq0%8w!4s-{deR>k|H^)hVcfwFQ6*{plREA_a0ol>w6!rZQHh9Y}>YN+qP}n zw(aC6)>`MxJ(;~Hb!P0OGdkP0)v4v}Vx!*Y6Qak{#ZH$muqx*RcqXOo)$Q1}s@ui? z!1DvKlRvPXr#2ls*X*2hua8q@UL{8WAVpHDy1QC%hX=Ujkdu;nY(LP94-nZi+}$1W z%W!vQs=BAUOO|chZQI=Yn!o~pGy$qN&pmAb$%Ooy3?P5~ci=XXBDwaMsk`T%AWfqd zfgrF5Y|+eWH%|^n}b!QAgn|z^_08qnwnNW`{?k zEJ=*AQ<}Ve!&$u2hR`&GJzzt4ZTCnZ@ktZ8#n>Iz#`D942+cS zOdHo`jZ5SDtsdwELA5|R5oyOl$2>VO0upfVT=5F1Y!90FdLR?O2RlbU3xR@&Js?`&#QCKHOTBi@}= zZOT9gRy^nkYFf1h(FHoSm#BlTLM#C#6?1#p+}W^v=0F^7k;3b!&B`&0u-0lBYErdw z10GBn*CjTgMF18loe8D3p%}xqH)qXxeb;$y$F02;tUH@&k&_9P3b_LS6f?5)s7V7TR*0PB@0r1y?ci=0!by#)HqvLi#+#{ZNGq;S+&EDsT=& z@fx<8hb+>>OCw}=rXj$Z5e9u0VKM^g@3oe{Hv)wJonVFJKvT{*fDKD*LI9O^wglsb zcI8DQ*kY3H?)pUJML>qXkkju{R_$XZEXUP7nMeUp$wW`B^@GiRefISFSAg8T{W19c z-Y~QQG(@#v-Drc(A^-_Tb+R%vRlUr(O9~Ytrd~K7ny<+`voIZ2_jdpg3_Qm58~ooZ zm+!wLCkT-B&u`kH9MF)3VSp9$Y)F6yt}<1<%6$xp6at2^-E=4JcFHuHc^7xm-Y#(R zBTC>|@;9A*)R(jT227rCTz}5Q*T)DsD2vcAhRu&}+m;0yGTgdJCslybsbJo`)c8TQ zKS`Zs=WP1D0Fj?LNHr90TcR- zWT^IY)65!{#kri#pJt8>`#|nopO!yAidNUj5O!bs`tbP${=X==bSL zS@r(~Pf=wTQy7-T$8?mS^!qCai1ezvm3`l(^-mQ@8mz4M9$m$@PlE5NJJ1YRNZG%k0x<$AUkbI5j zF%08;((cDA;}mHQA=e?SLwokr+}IP8QpvP_rT^F4I``TBnNMC)>7z5NlW1PistisB zxRaiKm%`M8rA)JrhF`Z41PjSx<4jX7lA(O=aBFWemCapxlFGRCYr~q}TdQ+%KU8%! z2soeFC}CX05b-oz(XBmmdp=ksXlIV9?e--~YQA%B;>w+?or>JQ0;!8Pe$cMR|v!4WY zfTm2WK*OyN@Of*MTHy87y?Pow)7FWin4detQU^RccLEJQVI*@p!${=fdpuuEFEk2JFEVn}|cbJ~f!{+8^%CuWCY7RPTX;N#ff^&26D1-;rM zpXY@|)>^dS*{{a8m}+h!s-wu;CX!=!r+#gyxU>6fFP&NMjVq#=%P*^+2hG7jbpCl6 z&yPR}FT2)%Qy)xRA=Rk)-^_nS9k;H8lm+m@$hS@aWLo4HAevuSU#c`iR)?|tDgygM zH)7t}sT`HboY2s5ZC|>wK$aSFM+ntz8kxGtNqqRb`saGsiAAP zrZ3;yJ^M*WGC+na0h(!7U+gyb-$AlwA7|S~r_8pCCW?wH8?|ogAS6x-S4S930?5a* ze(k~fXKLr{J0=Qg;nGX7sQ;(=ID($tJ`82QdQdiV!m{3-RaR+a+9xQIC3z-c)!So_ zJ#cbt%(_mN<`FGWNcNmPUD`9J=kLd#2j@7m3?CKko_!bdx?R}*nKwa=7aDY^FC{i% z06x!ymxfLIkn5qT;(F2o&8F#A8^?~EoqGOevurmVLx>la@pA0PT^be2?;Jjz71m#B zxpRpjRUJkZKr8rdFICQM03mxbd%V0qhGF?m!Hu4Z=G7lfk!F#d{}j+uel$|P6As+~ zg1%dq`fTI>`>jc)NAL;9mfVB@%4H76wLNBO1)fjyKg$@_pwPw7U&y!DxgFUCjk}R^ ztx^D^`gNCZzY{tC{f{9T^g_K@4y*fb05m&gBNyGxLKh#VPWC_Iw~SN<7V zZM4#!?`1wa@s^xsR)&&z_Dv0VVTA5Y?+G9q$-bOrq8$DI@zlsi<%N^P`L$nI{void zYr`+2^e+}r9LQ4g7^YGTXjL-dpq+zXAS&bd5ac*;XI?5& zZvcQj$K>{WOBdZlcCFnU0H8u{uV~e8lUaXvc`9t&F*%`~K`0;ztXOB67FnfK1a-5f zXrKq`%*)#U(yDg3%;YJnQ?4Ge1aspeV(9*|xkz=+^~Ug_a}mPbo0r4fWtoYn(0$p! z0uY2bYtl(oBGj7iNpZudN{Lu<6AILx*QY%Wre2eeY<$J;3;+|ET&|n5l!#$REn`?l zw#rvbz|q=M3gitHIrITknu8}L)738LXo5n~P9QUwgDJJDL%Y?Z0PaHEQ8UG~oO)JF zGOmW}PkFF6OzPLn!hFZT_j|{0-vxZOGCneY(ki?aL8^)9kBEkd{#RNH)HRXK1n(;aO}7_a8!6j0ggfMzi#Q+)D}AJ(>8Xn+OKe^;6h7?AJ>nu73{KN@ z7b#4U5-I__i5H5Fw{N%bdR3?lGn~>Rl!k)`KI<%ZUe+v_w9ga(T{@}AjVrv8Mon_a zj$+X8m0KJ6Pyl%I`fyH8kS8_wgu6^u)XRkw4!Jy2wKp27H~3f60bxUrTSE`IHq?8H zAS+U;p<@I1{gvvChMM*!2{0`a9&WPqS2*d~P)VdhXDM(`y(I-eC()tKqFW;6n}rWM zurLcGSEp-3Re76mT|7d^CfCi-u({ws+rhWI5~(Kkk{N)Zdr3jly{0IElnpd0Bl4?K z>M?mE!NJyfbO$5D_({V9J4k-`=8{S4^;R~Sg&{*RodB9twIvoJBP%s|tKD1nPX77u zdgNyOVe|}(by`pGib$P}BsVX!01<&*F0$SFoA5trRqLEN>fTIRb$IxzuZ~{PM?Nq@ z;Fzm?xBe#l?@IGBr^Se195gm3snabGAPsXy;3ez2>w56Qt#fTOr%ld(`tyb{c~C24 zbNd1oB`>%UH{SB`u2r62iUv6PR+{&A3FJmDg6W2#DC+1{ci=dCiC932qEuRN{*U?R zl@vuWl)s0MJDIGzrUkNmZ%uif`<`dkMYh{#pDwK4u%TN zo(8;lVX0VcMUJO17)Zc=h+Ej?jEAFS(M2>=cW|_C#%-aC>@x<`TZ%v{(p_!`?jL8K z!JzaGgF!H!W;`xfGe4(&qyX05Tj(kk_D#*2WRdMehdPLEYz3qVf4iw(5qq|#?RQV0E%6Y=#NkQ9MyE<>ONc}pI7Jr;) zkv>(KH_^v%;i=k^3ZR=9G)VW+U5vMeSJkwMUPpZj(o z(mjNEO{N)-|Ct~|_Ys4p>9sErrob2rv}=kD*aB)hhCX5|0kE@#(r7bsI049GC&UaK zrQq@Oqe{qHy@6dL!52xm4ALZ$`y1x@#uB0DD{%aV}_ty&N42;aQ`b5};kAiWT= zRfD)AdM&B6sw5!XN};3~8j(ca?8ys;qK(T)=^*lS@{0vhsNT{mXw{MJChnlM0ruNq zyalWXrjm~_eTP?5ujzf!(eY~}#c0BZ-8SdNk|#fB)Y~x{KiQrbMhwXW&i+#5&`AZS z4dixXAa7@K?VJ9*`SBLaU4S_=X%KHW25@_&pL*RlG!FjTo|?9AtQ)EWt?LvV!)j)} zKGzNVL9psJCjPy04&4E*L(76qAOju*Oba-GN`TTl^kG>L0s&KiE}-A%f1&*eRgM%7 ziBL|uXk>>YD~R*d@##V%9VIyp+({Q3wj2f1Fg`A#`%kbLKYDoN@Ngy_z|unqsQXgA zI1&mM9%=}v#yOyQVM-w}Vr5;nM*t2A6_sHRpkPlK8wmw~2n`=l2FBZyw;voNrr{xn zf$Dq*Aox5u0vigtaA*K)VHUds)ixELVF$Cay7#D#2EYL%fv^BR1v~c!14BcXod|L< zG#m;VgZv!L4~P|z{H7q8%1Xbufp9KXYw`VYAh^gdG_xzH#7`!+91bhxF?VQAzW@eb z1c0btiiJ3?$W0yoThZ(Y)gDshb%1I^vo8Po+~nuPFQrX4t-p*tr`Z8~KmnMq0}(eQ z+>i=Ie@sRaG7*~zkr$7N7>ov^-wkO?Aaebe>rzvRiVeV)T>+CjJpPDyA_m0VkU-G? zEn8<9xXSBnB@Cg_8R=zHf5-=5c3w~sT_3*Z5w?Mgsr4H%Cbh`Qs) ztv4$Hxn|$kpovbyt}%)Y&NQ@ zQE1z?ZTqlo+jdvgY1_7KyZdjb{l4G#w`=cp-e0(PY}=@D9dnwIb2w$}WNh2X(6((m z-G4yrG{ULv+_7zrY~ygGVQjNcX0~nHw(CSQI~ks}C)Fz>>tArMwI_S6T0M_#?m>(l z^JwZ{Y}>YN+qUhIQSlqIMsad{P9Mki9%b3Kosn%@BfDoYk2Q-mt{&~!c5*PMvu)ck zNxENt?^jk^W$UZ9yJphOY(<;xf3PF(pRjG)_HH(wsn3wGZQJoS*YoNNntbt$X1}_& zZM$>LbykPkZX*Q{00MAq+y0+zGuf=R(QKR1wwG+%Mzg(>1xb>tI9Yhkyfc#Ey*Gru zUCqK={O9677yr5V&&7W({&Vr)O57zthyaBGv=(5j080hfF2FGX&I@o!fHMLd5n!DF zvjylcKqCQ?1qd>~2#_K`PXX2n@I-)L0+1%ae*wOIhXC^hXf8mMIYod_0a^*LQ-DvH zmH@8=SS&!53CtuXD?lFsZg~J;OYVeK2eME7QtZrcTWQKHvT zfnHV%sY>0;s8H8~Qq48nRJ{0F{NyG-^ag+ce{W8Jnr06H%HEs@;G5paCHqbN)n45U zOIb`PV>{hiiw&>#SrGsN{1jl3010Ld0fGe>Aizh!FTGY+VO7#jgNzna>)t3vT}5kg0XnBnLbMSR+8Vc|w44TLxChNs|7qqka{Ov>r#rx*C>gwbi82 zTDL}O-5RQOn}3t_PVMz-Tt?5UMWjnJ;T;7P=YSVIS#yK{N6PG$Sr-xB7*}q&J z_N&!U9F>25Lu7rIKYA@afEBR#ApBf{)TT1#W;NYt*WIv$>_XCZgNSMmAizEWBF&I{ zw+wvITh*>in$V)sgHf!3YOfqK-}z@(&?*b<(9X5psUmfUfu3LbL z0z~V`NBa(V;u=b$GFp^+8WpLtG9X55%ftZKl3RZHHGBP7djiV~zS#{= z01mFOsWt8PYN-!h4$5So{sr)G9trSIfHwlnTt~h5tO9I=iT6}}Ox~%NDE-6403q}f zc`j_IqC2L+%{FZuRZ4bLF8R1z@?+(ajVmNQs?g#=ty)%lm0W!<%iN~L1B_MK(koA^ zTDC3*<>Dm%!5JmM6#>%pfdB(N@O&24y*-bLR974`7LYzc;ioAK2aVs>u`=?S*=tZz zb;dUWf*?WiH}#Ek&0ISK1Pry%L4ea2+7AdE7;bR36X?Q6UoIrc{Gj@1Pg~6NGI#LI zI`>s{I!m+{iB^`k0fHr~iYo&!S4Q%%)7s1D6E+sy#{MOxlwS^<*`r>}rXT0F;R z6A7j!Ks`NpZhmv{-m1^qolzpM{AWNQd7+Z_gyiFLUZ&OTUcJOKUs)pu0^@BA;PF8|*khK#9@1O%K*6$$azb%7^X6PA>rxO#RgIK@BgZwvLyX(y?+5 zE?wioWib?A^y}atsPbI zQktlM#Z^(#+sdfF;pJ69bK2YFxv*h;`Ju*9AOHLkMACP4J}75VRLt}eu!2Zz#hC{L z2ovBo{Gdf4dAHs@LB!N9^<}RHw_1T8cB8qydgF)GkHO>zgETHy(H-Xw1rz#lmcAB($SS_$T2NicF zd1+K+aRH=nsM^h$#X<@&PhBn8Qb_1fuc3g%+8=Xc^jMEm_MQB1M4#m_8)_$LDTelkn<-5tzfehLNTH^7B?nXHY& zN&&?MtVvGY>z$;U+pFh`-NJ_Cyo5q+fkGFSab^D)qpIv5<7)XVqtB=Esg|XKm!ByAhq$#^iz5Dvf_aqqt8XpVqN48;I(zY{ z6VxF`E5JW8;TO6dmQnl^b`Ij@f7w0LPySNt`B4K~ZQQx{4tmuTYAFqafS%IJGx>D? z-PTcn&sdY3mRcuQ;(o1qH#!tmTCjK~uDml&em!hrA5^&b zmU5?{m4VWKjyb`Ur^^?f(JQV3Oo1FyzbH3_l`%I1vWOoTSA%<6MeaaubDzi`_fKv( zWXUYA-Chla_IuURLu2nhkdk!!{BxO2EV7tby*`~!w+K*2fd8cGpkGa)?QX4uW62e^ zrbtxVAAz*Ag%y{~MtV#(t`IG=1@g>&W>KhiX%fu2`{$lJ!Iad$HvGYT_jiK2<*DWl zRWDDmC@Oqv6@*YHu0ZWbV`cyG!>$2-*_BiBgl{c)dD-84$*ZJ2VOKc07F#Wp+Y_@+ z`~~^_o96`ee;b6Ws{oT=)2~ONy>7L!AkX;?lHGbPy(Xg zJ%UqzM=wV-o7eV)Y*W8=W0bNeG~aCX#8A!GNhfF~a223XfR9kd{49&Il}ZI^ORl{) zk;yjau$K(e>a1TuwY&4QAnI9X!hWB8!O80HiL9djh|n3X0*ryhH{EP!u_(7i1ZEe2 z*qOgp%`_8T(&?a*Md_QD48a^N`WcTCHfOydK)3)WA&hoFJt27aj|%vrH!iH< z?N%nd*W?F-qNo4c6`1&rXKqnC%qPa_3*|ey!c~AW0=$KU>7TkaN~y<&UNAjy{qYLF zRMr{S!hWsiSFbWZTd$D;HF!T1@Nhqn{wD^e0TG5W$) zfKG=GdFMZQTJ=C`L)C7dPn$R$T68k`p(!p)+1tfbjzShl2C#o>nra{|~cTGTJRK z_v{xNKA}Zrw$WnSz&krT#&GiVhN}QUH|_y>=Dz6GC}C08qfw-(M!QYRR6FJ_b?DhdQujJ4l<{y< zZ$)a2WqH~P7T#JOcxUNE8DloLC&Ub3y89BCV{pV(?jL%)_ z?3^It0$>9X-3`lcl4j&HNr~>}@gHe!z&wHk*43W53DzgH?>Te<0oUd$5@Gy+T&yG$ zCQ{F2{1%}7XY5& z^aDjquNtq7cBA<{pz6VXPeze0hUJ=Vw5f7wTF%*T_8hdPKMEx(~D-6#1vZqf`v|glMJaSZWxda6-lKr zl5#R+7+yKeOCuT;;1yEO9q+tg@95+h(w}eduD3B?`>5h-0A(fLGQ^0Si zj4cz^4}w5Io6=A%ZtI%0JP79n$>`PVS6;9H!vxqQzzqRD3GnxAJOGcW3Gn+%{^}hB z82vV&9Fo4bd0+#yC=DNVRR|6Ihnnmxj2{GXh!|EmKB@*$PFMnnt|TFzDy1SVoA<;e7}tjEVLQt~p>q<=Ij5}I^z4+(x>#}zoq2pk&F zsx+h~CkzPh1*shu?1PWYFuD_p0CF)C)ItOn)Fw2kV}p=^N9Q#w4XN_ECiO%Rjfhcn zM`o}55h3LUH49DZx4_s?15F*G5%j28O6^I*9)JL$ zu|h*qOg934tTLfJi^53Cs*Gzt2DfeKx;H;;a7#qRS@fl>p_J9LX4Nf>h(rK8CSUP( zkMn>)1mz2@Q_UJoIkSJ461b8cY$B1+j*~9Ztb>ORkTnsFNuzhoy42p3)u=SC6xf_u z>pNqA0)+;EA0QxsJ3y$wlg5x2$B9@lU=0KS{1f2@%xtEHyu8em&j(0<@96=81lA;m z&NEgsb3e!m0sydqh~kBfX=rt_8-}WNNo2u0K~R$uOZbaowmjzRMI_{(muR$Cs~}VVKaph4(@h{!iXB`=PWsSxJUr4ls;a zzxjKoNoSba3Rg4lNy?iUrgr9Oo;=N&8-}iPHmxX=bm?y25z?h|g#68nblYP#7&~h_ zDlusah4q70Q~T|+cB1@+8UV2A50g&!t?<7A0O0Xsx_Pv0+|-e@ z@La7Ftyl9Imx2jpWj3S{g-hW%1y4BXT)f33ibHw@>=u zWYS2g8Py9dE2;pG_1Dr!|KTlt*oz*-aN?_LPwef(?Xh&&OYcyqGeovLrv&$V52&^dTR?YYXiMfDKS8Q%=A^ zugb}rDsJ$rhS|c1;YM5=SQ8POh?2NgKG%xjT38~0RXkaZZH9*p1P1bDO4(d1lPkGW z;U55k1x~(`G&6<~+l^R7)J^?l4UL{WcwiwZQ{OYTVa_n#$lmuX7=S}RsN*v5fp4AnnvM|{WkozveRlV>eAE}k8A_&=4#{PE^7+&mk0ZGhVjX6^XWnIMUk@F0f{ z@ItqqtC?WdaP+L@f&l;y{Z?+@oRPidJ9Hob5z(ngOhrTj-vAIGGEA7Y#BFV|qar>P zApwA>UYHr<6h9lTsamdWVFsUB_N=TDnPLmThnO&H*`{WVpgS2d468ezc#hyUQ}cy= z;DHcCbfOum&0P&k!yV!}5n8jB1u;c6YdM7OAz}V&n`K)CN(vZ(uZkX1r<=6YD z8bn!Ph=7kZS6l^TPnA)XBb&i`CCAKK)+RIv5;~4!KFnHQs??R$W&G;^^?I`54mNjwBF|(F_(~WvUg>vwIvOAxD}0A%NS#n{uor&DEblLM0J1G(gs+VYSwahO_;SL1`$0dOEoq9 zN)RB_v}P@>2(r2er9KRW(jLMbH%k9!Edz%ZkeDgWNTXln45yr4HS51_#Ktxo? zm1zy7J3}bzT}~uD%q*cDBi}R;JaB*zMdW-FioO#YT=X5t`9_2&!5o;i%-u{{7XUv5 z0RVS^Urb}ji|yoDFkl1t2R7as0Bj&MXDv?~z(&AMVQT<_gqHr#T28WKl3B~SXis%y zXZwx`0AO97NPD8=BPFwz&uSLTTK-g6FX&xY(c1>fx?IG|nl$?o*NF-qI9E8cnZ3|2 zK1{x5nn@!wX|H<~y;m_nXr#S2!_aZk&V1}V%oIn=T8{bT?>(mJvzBjuX>Yz_f3ubX zWI12o2?8wDO#-Cc9$c;SGy@P|x^CJ<0j|Kgy}5Jr`H`z-a+4JGZ_= ifPDgtSg5O6n2Y~h{O9677yr5V&&7W({#ywRTmb-8-cN=A literal 0 HcmV?d00001 diff --git a/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..ed5be465292e3917c8808e58506d23687eb9e795 GIT binary patch literal 8806 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 literal 0 HcmV?d00001 diff --git a/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..5b9168a14dd72c5be3a756a170a9a9abd5bb44a2 GIT binary patch literal 6960 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 C!zeZY literal 0 HcmV?d00001 diff --git a/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..d1df236fc908bb7959fba08dd9bac8428b3276ef GIT binary patch literal 9948 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 literal 0 HcmV?d00001 diff --git a/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..864236db190474388a9b6581e701a794a83fe292 GIT binary patch literal 12286 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 + + #FFFFFF + \ No newline at end of file diff --git a/Android/fastlane/Appfile b/Android/fastlane/Appfile new file mode 100644 index 0000000..bbcb0fb --- /dev/null +++ b/Android/fastlane/Appfile @@ -0,0 +1,11 @@ +# This file contains the app distribution configuration +# for the Android half of the Skip app. +# You can find the documentation at https://docs.fastlane.tools + +# Load the shared Skip.env properties with the app info +require('dotenv') +Dotenv.load '../../Skip.env' +package_name(ENV['PRODUCT_BUNDLE_IDENTIFIER']) + +# Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one +json_key_file("fastlane/apikey.json") diff --git a/Android/fastlane/Fastfile b/Android/fastlane/Fastfile new file mode 100644 index 0000000..9f636a1 --- /dev/null +++ b/Android/fastlane/Fastfile @@ -0,0 +1,49 @@ +# This file contains the fastlane.tools configuration +# for the Android half of the Skip app. +# You can find the documentation at https://docs.fastlane.tools + +# Load the shared Skip.env properties with the app info +require('dotenv') +Dotenv.load '../../Skip.env' + +default_platform(:android) + +# use the Homebrew gradle rather than expecting a local gradlew +gradle_bin = (ENV['HOMEBREW_PREFIX'] ? ENV['HOMEBREW_PREFIX'] : "/opt/homebrew") + "/bin/gradle" + +default_platform(:android) + +desc "Build Skip Android App" +lane :build do |options| + build_config = (options[:release] ? "Release" : "Debug") + gradle( + task: "build${build_config}", + gradle_path: gradle_bin, + flags: "--warning-mode none -x lint" + ) +end + +desc "Test Skip Android App" +lane :test do + gradle( + task: "test", + gradle_path: gradle_bin + ) +end + +desc "Assemble Skip Android App" +lane :assemble do + gradle( + gradle_path: gradle_bin, + task: "bundleRelease" + ) + # sh "your_script.sh" +end + +desc "Deploy Skip Android App to Google Play" +lane :release do + assemble + upload_to_play_store( + aab: '../.build/Android/app/outputs/bundle/release/app-release.aab' + ) +end diff --git a/Android/fastlane/metadata/android/en-US/full_description.txt b/Android/fastlane/metadata/android/en-US/full_description.txt new file mode 100644 index 0000000..c65ef9c --- /dev/null +++ b/Android/fastlane/metadata/android/en-US/full_description.txt @@ -0,0 +1 @@ +A great new app built with Skip! diff --git a/Android/fastlane/metadata/android/en-US/short_description.txt b/Android/fastlane/metadata/android/en-US/short_description.txt new file mode 100644 index 0000000..c65ef9c --- /dev/null +++ b/Android/fastlane/metadata/android/en-US/short_description.txt @@ -0,0 +1 @@ +A great new app built with Skip! diff --git a/Android/fastlane/metadata/android/en-US/title.txt b/Android/fastlane/metadata/android/en-US/title.txt new file mode 100644 index 0000000..38f0959 --- /dev/null +++ b/Android/fastlane/metadata/android/en-US/title.txt @@ -0,0 +1 @@ +SatsPrice diff --git a/Android/gradle.properties b/Android/gradle.properties new file mode 100644 index 0000000..1b8d060 --- /dev/null +++ b/Android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4g +android.useAndroidX=true +kotlin.code.style=official diff --git a/Android/gradle/wrapper/gradle-wrapper.properties b/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9d2e5f8 --- /dev/null +++ b/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,2 @@ +#Sat Aug 31 09:41:39 EEST 2024 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip diff --git a/Android/settings.gradle.kts b/Android/settings.gradle.kts new file mode 100644 index 0000000..42a54ff --- /dev/null +++ b/Android/settings.gradle.kts @@ -0,0 +1,46 @@ +// This gradle project is part of a conventional Skip app project. +// It invokes the shared build skip plugin logic, which included as part of the skip-unit buildSrc +// When built from Android Studio, it uses the BUILT_PRODUCTS_DIR folder to share the same build outputs as Xcode, otherwise it uses SwiftPM's .build/ folder +pluginManagement { + // local override of BUILT_PRODUCTS_DIR + if (System.getenv("BUILT_PRODUCTS_DIR") == null) { + //System.setProperty("BUILT_PRODUCTS_DIR", "${System.getProperty("user.home")}/Library/Developer/Xcode/DerivedData/MySkipProject-aqywrhrzhkbvfseiqgxuufbdwdft/Build/Products/Debug-iphonesimulator") + } + + // the source for the plugin is linked as part of the SkipUnit transpilation + val skipOutput = System.getenv("BUILT_PRODUCTS_DIR") ?: System.getProperty("BUILT_PRODUCTS_DIR") + + val outputExt = if (skipOutput != null) ".output" else "" // Xcode saves output in package-name.output; SPM has no suffix + val skipOutputs: File = if (skipOutput != null) { + // BUILT_PRODUCTS_DIR is set when building from Xcode, in which case we will use Xcode's DerivedData plugin output + file(skipOutput).resolve("../../../SourcePackages/plugins/") + } else { + exec { + // create transpiled Kotlin and generate Gradle projects from SwiftPM modules + commandLine("swift", "build") + workingDir = file("..") + } + // SPM output folder is a peer of the parent Package.swift + rootDir.resolve("../.build/plugins/outputs/") + } + + // load the Skip plugin (part of the skip-unit project), which handles configuring the Android project + // because this path is a symlink, we need to use the canonical path or gradle will mis-interpret it as a different build source + var pluginSource = skipOutputs.resolve("skip-unit${outputExt}/SkipUnit/skipstone/buildSrc/").canonicalFile + if (!pluginSource.isDirectory) { + // check new SwiftPM6 plugin "destination" folder for command-line builds + pluginSource = skipOutputs.resolve("skip-unit${outputExt}/SkipUnit/destination/skipstone/buildSrc/").canonicalFile + } + + if (!pluginSource.isDirectory) { + throw GradleException("Missing expected Skip output folder: ${pluginSource}. Run `swift build` in the root folder to create, or specify Xcode environment BUILT_PRODUCTS_DIR.") + } + includeBuild(pluginSource.path) { + name = "skip-plugins" + } +} + +plugins { + id("skip-plugin") apply true +} + diff --git a/SatsPrice/Assets.xcassets/AccentColor.colorset/Contents.json b/Darwin/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from SatsPrice/Assets.xcassets/AccentColor.colorset/Contents.json rename to Darwin/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/Contents.json b/Darwin/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Darwin/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-1024-no-alpha 1.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-1024-no-alpha 1.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-1024-no-alpha 1.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-1024-no-alpha 1.png diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-1024-no-alpha.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-1024-no-alpha.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-1024-no-alpha.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-1024-no-alpha.png diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-128-no-alpha.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-128-no-alpha.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-128-no-alpha.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-128-no-alpha.png diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-16-no-alpha.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-16-no-alpha.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-16-no-alpha.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-16-no-alpha.png diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-256-no-alpha 1.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-256-no-alpha 1.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-256-no-alpha 1.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-256-no-alpha 1.png diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-256-no-alpha.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-256-no-alpha.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-256-no-alpha.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-256-no-alpha.png diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-32-no-alpha 1.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-32-no-alpha 1.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-32-no-alpha 1.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-32-no-alpha 1.png diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-32-no-alpha.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-32-no-alpha.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-32-no-alpha.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-32-no-alpha.png diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-512-no-alpha 1.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-512-no-alpha 1.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-512-no-alpha 1.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-512-no-alpha 1.png diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-512-no-alpha.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-512-no-alpha.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-512-no-alpha.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-512-no-alpha.png diff --git a/SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-64-no-alpha.png b/Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-64-no-alpha.png similarity index 100% rename from SatsPrice/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-64-no-alpha.png rename to Darwin/Assets.xcassets/AppIcon.appiconset/bitcoin-calculator-64-no-alpha.png diff --git a/SatsPrice/Assets.xcassets/Contents.json b/Darwin/Assets.xcassets/Contents.json similarity index 100% rename from SatsPrice/Assets.xcassets/Contents.json rename to Darwin/Assets.xcassets/Contents.json diff --git a/SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Darwin/Entitlements.plist similarity index 92% rename from SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to Darwin/Entitlements.plist index 0c67376..6631ffa 100644 --- a/SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ b/Darwin/Entitlements.plist @@ -1,5 +1,6 @@ - + + diff --git a/SatsPrice/SatsPrice.xcdatamodeld/.xccurrentversion b/Darwin/Info.plist similarity index 71% rename from SatsPrice/SatsPrice.xcdatamodeld/.xccurrentversion rename to Darwin/Info.plist index 3f39f17..6d5f8f6 100644 --- a/SatsPrice/SatsPrice.xcdatamodeld/.xccurrentversion +++ b/Darwin/Info.plist @@ -2,7 +2,7 @@ - _XCCurrentVersionName - SatsPrice.xcdatamodel + ITSAppUsesNonExemptEncryption + diff --git a/Darwin/SatsPrice.xcconfig b/Darwin/SatsPrice.xcconfig new file mode 100644 index 0000000..cbd413e --- /dev/null +++ b/Darwin/SatsPrice.xcconfig @@ -0,0 +1,56 @@ +#include "../Skip.env" + +// Set the action that will be executed as part of the Xcode Run Script phase +// Setting to "launch" will build and run the app in the first open Android emulator or device +// Setting to "build" will just run gradle build, but will not launch the app +SKIP_ACTION = launch +//SKIP_ACTION = build + +ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon +ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor + +INFOPLIST_FILE = Info.plist +GENERATE_INFOPLIST_FILE = YES + +// The user-visible name of the app (localizable) +//INFOPLIST_KEY_CFBundleDisplayName = App Name +//INFOPLIST_KEY_LSApplicationCategoryType = public.app-category.utilities + +// iOS-specific Info.plist property keys +INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphone*] = YES +INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphone*] = YES +INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphone*] = YES +INFOPLIST_KEY_UIStatusBarStyle[sdk=iphone*] = UIStatusBarStyleDefault +INFOPLIST_KEY_UISupportedInterfaceOrientations[sdk=iphone*] = UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown + +IPHONEOS_DEPLOYMENT_TARGET = 16.0 +MACOSX_DEPLOYMENT_TARGET = 13.0 +SUPPORTS_MACCATALYST = NO + +// iPhone + iPad +TARGETED_DEVICE_FAMILY = 1,2 + +// iPhone only +// TARGETED_DEVICE_FAMILY = 1 + +SWIFT_EMIT_LOC_STRINGS = YES + +// the name of the product module; this can be anything, but cannot conflict with any Swift module names +PRODUCT_MODULE_NAME = $(PRODUCT_NAME:c99extidentifier)App + +// On-device testing may need to override the bundle ID +// PRODUCT_BUNDLE_IDENTIFIER[config=Debug][sdk=iphoneos*] = cool.beans.BundleIdentifer + +SDKROOT = auto +SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx +SWIFT_EMIT_LOC_STRINGS = YES + +SWIFT_VERSION = 5.0 +//SWIFT_VERSION = 6.0 + +// Development team ID for on-device testing +CODE_SIGNING_REQUIRED = NO +CODE_SIGN_STYLE = Automatic +CODE_SIGN_ENTITLEMENTS = Entitlements.plist +//CODE_SIGNING_IDENTITY = - +//DEVELOPMENT_TEAM = diff --git a/Darwin/SatsPrice.xcodeproj/project.pbxproj b/Darwin/SatsPrice.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b897f1b --- /dev/null +++ b/Darwin/SatsPrice.xcodeproj/project.pbxproj @@ -0,0 +1,294 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 49231BAC2AC5BCEF00F98ADF /* SatsPriceApp in Frameworks */ = {isa = PBXBuildFile; productRef = 49231BAB2AC5BCEF00F98ADF /* SatsPriceApp */; }; + 49231BAD2AC5BCEF00F98ADF /* SatsPriceApp in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 49231BAB2AC5BCEF00F98ADF /* SatsPriceApp */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 496BDBEE2B8A7E9C00C09264 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 496BDBED2B8A7E9C00C09264 /* Localizable.xcstrings */; }; + 499CD43B2AC5B799001AE8D8 /* SatsPriceAppMain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F90C2B2A52156200F06D93 /* SatsPriceAppMain.swift */; }; + 499CD4402AC5B799001AE8D8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 49F90C2F2A52156300F06D93 /* Assets.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 499CD44A2AC5B9C6001AE8D8 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 49231BAD2AC5BCEF00F98ADF /* SatsPriceApp in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 4900101C2BACEA710000DE33 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 493609562A6B7EAE00C401E2 /* SatsPrice */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SatsPrice; path = ..; sourceTree = ""; }; + 496BDBEB2B89A47800C09264 /* SatsPrice.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SatsPrice.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 496BDBED2B8A7E9C00C09264 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = ../Sources/SatsPrice/Resources/Localizable.xcstrings; sourceTree = ""; }; + 496EB72F2A6AE4DE00C1253A /* Skip.env */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Skip.env; path = ../Skip.env; sourceTree = ""; }; + 496EB72F2A6AE4DE00C1253B /* SatsPrice.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = SatsPrice.xcconfig; sourceTree = ""; }; + 496EB72F2A6AE4DE00C1253C /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; + 499AB9082B0581F4005E8330 /* plugins */ = {isa = PBXFileReference; lastKnownFileType = folder; name = plugins; path = ../../../SourcePackages/plugins; sourceTree = BUILT_PRODUCTS_DIR; }; + 49F90C2B2A52156200F06D93 /* SatsPriceAppMain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SatsPriceAppMain.swift; path = Sources/SatsPriceAppMain.swift; sourceTree = SOURCE_ROOT; }; + 49F90C2F2A52156300F06D93 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 49F90C312A52156300F06D93 /* Entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Entitlements.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 499CD43C2AC5B799001AE8D8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 49231BAC2AC5BCEF00F98ADF /* SatsPriceApp in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 496BDBEC2B89A47800C09264 /* Products */ = { + isa = PBXGroup; + children = ( + 496BDBEB2B89A47800C09264 /* SatsPrice.app */, + ); + name = Products; + sourceTree = ""; + }; + 49AB54462B066A7E007B79B2 /* SkipStone */ = { + isa = PBXGroup; + children = ( + 499AB9082B0581F4005E8330 /* plugins */, + ); + name = SkipStone; + sourceTree = ""; + }; + 49F90C1F2A52156200F06D93 = { + isa = PBXGroup; + children = ( + 496EB72F2A6AE4DE00C1253C /* README.md */, + 496EB72F2A6AE4DE00C1253A /* Skip.env */, + 496EB72F2A6AE4DE00C1253B /* SatsPrice.xcconfig */, + 496BDBED2B8A7E9C00C09264 /* Localizable.xcstrings */, + 493609562A6B7EAE00C401E2 /* SatsPrice */, + 49F90C2A2A52156200F06D93 /* App */, + 49AB54462B066A7E007B79B2 /* SkipStone */, + 496BDBEC2B89A47800C09264 /* Products */, + ); + sourceTree = ""; + }; + 49F90C2A2A52156200F06D93 /* App */ = { + isa = PBXGroup; + children = ( + 49F90C2B2A52156200F06D93 /* SatsPriceAppMain.swift */, + 49F90C2F2A52156300F06D93 /* Assets.xcassets */, + 49F90C312A52156300F06D93 /* Entitlements.plist */, + 4900101C2BACEA710000DE33 /* Info.plist */, + ); + name = App; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 499CD4382AC5B799001AE8D8 /* SatsPrice */ = { + isa = PBXNativeTarget; + buildConfigurationList = 499CD4412AC5B799001AE8D8 /* Build configuration list for PBXNativeTarget "SatsPrice" */; + buildPhases = ( + 499CD43A2AC5B799001AE8D8 /* Sources */, + 499CD43C2AC5B799001AE8D8 /* Frameworks */, + 499CD43E2AC5B799001AE8D8 /* Resources */, + 499CD4452AC5B869001AE8D8 /* Run skip gradle */, + 499CD44A2AC5B9C6001AE8D8 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SatsPrice; + packageProductDependencies = ( + 49231BAB2AC5BCEF00F98ADF /* SatsPriceApp */, + ); + productName = App; + productReference = 496BDBEB2B89A47800C09264 /* SatsPrice.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 49F90C202A52156200F06D93 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1540; + }; + buildConfigurationList = 49F90C232A52156200F06D93 /* Build configuration list for PBXProject "SatsPrice" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 49F90C1F2A52156200F06D93; + packageReferences = ( + ); + productRefGroup = 496BDBEC2B89A47800C09264 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 499CD4382AC5B799001AE8D8 /* SatsPrice */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 499CD43E2AC5B799001AE8D8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 499CD4402AC5B799001AE8D8 /* Assets.xcassets in Resources */, + 496BDBEE2B8A7E9C00C09264 /* Localizable.xcstrings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 499CD4452AC5B869001AE8D8 /* Run skip gradle */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run skip gradle"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/sh -e"; + shellScript = "if [ \"${SKIP_ZERO}\" != \"\" ]; then\n\techo \"note: skipping skip due to SKIP_ZERO\"\n\texit 0\nelif [ \"${ENABLE_PREVIEWS}\" == \"YES\" ]; then\n\techo \"note: skipping skip due to ENABLE_PREVIEWS\"\n\texit 0\nelif [ \"${ACTION}\" == \"install\" ]; then\n\techo \"note: skipping skip due to archive install\"\n\texit 0\nelse\n\tSKIP_ACTION=\"${SKIP_ACTION:-launch}\"\nfi\nPATH=${BUILD_ROOT}/Debug:${BUILD_ROOT}/../../SourcePackages/artifacts/skip/skip/skip.artifactbundle/macos:${PATH}:${HOMEBREW_PREFIX:-/opt/homebrew}/bin\necho \"note: running gradle build with: $(which skip) gradle -p ${PWD}/../Android ${SKIP_ACTION:-launch}${CONFIGURATION:-Debug}\"\nskip gradle -p ../Android ${SKIP_ACTION:-launch}${CONFIGURATION:-Debug}\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 499CD43A2AC5B799001AE8D8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 499CD43B2AC5B799001AE8D8 /* SatsPriceAppMain.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 499CD4422AC5B799001AE8D8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEVELOPMENT_TEAM = S99A5B637C; + ENABLE_PREVIEWS = YES; + INFOPLIST_KEY_CFBundleDisplayName = SatsPrice; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MARKETING_VERSION = 0.2.0; + }; + name = Debug; + }; + 499CD4432AC5B799001AE8D8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEVELOPMENT_TEAM = S99A5B637C; + ENABLE_PREVIEWS = YES; + INFOPLIST_KEY_CFBundleDisplayName = SatsPrice; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MARKETING_VERSION = 0.2.0; + }; + name = Release; + }; + 49F90C4B2A52156300F06D93 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 496EB72F2A6AE4DE00C1253B /* SatsPrice.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 49F90C4C2A52156300F06D93 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 496EB72F2A6AE4DE00C1253B /* SatsPrice.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 499CD4412AC5B799001AE8D8 /* Build configuration list for PBXNativeTarget "SatsPrice" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 499CD4422AC5B799001AE8D8 /* Debug */, + 499CD4432AC5B799001AE8D8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 49F90C232A52156200F06D93 /* Build configuration list for PBXProject "SatsPrice" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 49F90C4B2A52156300F06D93 /* Debug */, + 49F90C4C2A52156300F06D93 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + 49231BAB2AC5BCEF00F98ADF /* SatsPriceApp */ = { + isa = XCSwiftPackageProductDependency; + productName = SatsPriceApp; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 49F90C202A52156200F06D93 /* Project object */; +} diff --git a/SatsPrice.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Darwin/SatsPrice.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from SatsPrice.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Darwin/SatsPrice.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Darwin/SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Darwin/SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Darwin/Sources/SatsPriceAppMain.swift b/Darwin/Sources/SatsPriceAppMain.swift new file mode 100644 index 0000000..1126b8e --- /dev/null +++ b/Darwin/Sources/SatsPriceAppMain.swift @@ -0,0 +1,10 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org + +import SwiftUI +import SatsPrice + +/// The entry point to the app simply loads the App implementation from SPM module. +@main struct AppMain: App, SatsPriceApp { +} diff --git a/Darwin/fastlane/AppStore.xcconfig b/Darwin/fastlane/AppStore.xcconfig new file mode 100644 index 0000000..2dd4ae8 --- /dev/null +++ b/Darwin/fastlane/AppStore.xcconfig @@ -0,0 +1,5 @@ +// Additional properties included by the Fastfile build_app + +// This file can be used to override various properties from Skip.env +//PRODUCT_BUNDLE_IDENTIFIER = +//DEVELOPMENT_TEAM = diff --git a/Darwin/fastlane/Appfile b/Darwin/fastlane/Appfile new file mode 100644 index 0000000..2d9792e --- /dev/null +++ b/Darwin/fastlane/Appfile @@ -0,0 +1,8 @@ +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile + +require('dotenv') +Dotenv.load '../../Skip.env' +#app_identifier(ENV['PRODUCT_BUNDLE_IDENTIFIER']) + +# apple_id("my@email") diff --git a/Darwin/fastlane/Deliverfile b/Darwin/fastlane/Deliverfile new file mode 100644 index 0000000..67e07dd --- /dev/null +++ b/Darwin/fastlane/Deliverfile @@ -0,0 +1,28 @@ + +copyright "#{Time.now.year}" +default_language("en-US") + +force(true) # Skip HTML report verification +automatic_release(true) +skip_screenshots(false) +precheck_include_in_app_purchases(false) + +#skip_binary_upload(true) +submit_for_review(true) + +submission_information({ + add_id_info_serves_ads: false, + add_id_info_uses_idfa: false, + add_id_info_tracks_install: false, + add_id_info_tracks_action: false, + add_id_info_limits_tracking: false, + content_rights_has_rights: false, + content_rights_contains_third_party_content: false, + export_compliance_contains_third_party_cryptography: false, + export_compliance_encryption_updated: false, + export_compliance_platform: 'ios', + export_compliance_compliance_required: false, + export_compliance_uses_encryption: false, + export_compliance_is_exempt: false, + export_compliance_contains_proprietary_cryptography: false +}) diff --git a/Darwin/fastlane/Fastfile b/Darwin/fastlane/Fastfile new file mode 100644 index 0000000..874953c --- /dev/null +++ b/Darwin/fastlane/Fastfile @@ -0,0 +1,32 @@ +# This file contains the fastlane.tools configuration +# for the iOS half of the Skip app. +# You can find the documentation at https://docs.fastlane.tools + +default_platform(:ios) + +lane :assemble do |options| + # only build the iOS side of the app + ENV["SKIP_ZERO"] = "true" + build_app( + sdk: "iphoneos", + xcconfig: "fastlane/AppStore.xcconfig", + xcargs: "-skipPackagePluginValidation -skipMacroValidation", + derived_data_path: "../.build/Darwin/DerivedData", + output_directory: "../.build/fastlane/Darwin", + skip_archive: ENV["FASTLANE_SKIP_ARCHIVE"] == "YES", + skip_codesigning: ENV["FASTLANE_SKIP_CODESIGNING"] == "YES" + ) +end + +lane :release do |options| + desc "Build and release app" + + assemble + + upload_to_app_store( + api_key_path: "fastlane/apikey.json", + app_rating_config_path: "fastlane/metadata/rating.json", + release_notes: { default: "Fixes and improvements." } + ) +end + diff --git a/Darwin/fastlane/metadata/en-US/description.txt b/Darwin/fastlane/metadata/en-US/description.txt new file mode 100644 index 0000000..c65ef9c --- /dev/null +++ b/Darwin/fastlane/metadata/en-US/description.txt @@ -0,0 +1 @@ +A great new app built with Skip! diff --git a/Darwin/fastlane/metadata/en-US/keywords.txt b/Darwin/fastlane/metadata/en-US/keywords.txt new file mode 100644 index 0000000..0fb6d6c --- /dev/null +++ b/Darwin/fastlane/metadata/en-US/keywords.txt @@ -0,0 +1 @@ +app,key,words diff --git a/Darwin/fastlane/metadata/en-US/privacy_url.txt b/Darwin/fastlane/metadata/en-US/privacy_url.txt new file mode 100644 index 0000000..575f001 --- /dev/null +++ b/Darwin/fastlane/metadata/en-US/privacy_url.txt @@ -0,0 +1 @@ +https://example.org/privacy/ diff --git a/Darwin/fastlane/metadata/en-US/release_notes.txt b/Darwin/fastlane/metadata/en-US/release_notes.txt new file mode 100644 index 0000000..2e531b6 --- /dev/null +++ b/Darwin/fastlane/metadata/en-US/release_notes.txt @@ -0,0 +1 @@ +Bug fixes and performance improvements. diff --git a/Darwin/fastlane/metadata/en-US/software_url.txt b/Darwin/fastlane/metadata/en-US/software_url.txt new file mode 100644 index 0000000..0ce62b7 --- /dev/null +++ b/Darwin/fastlane/metadata/en-US/software_url.txt @@ -0,0 +1 @@ +https://example.org/app/ diff --git a/Darwin/fastlane/metadata/en-US/subtitle.txt b/Darwin/fastlane/metadata/en-US/subtitle.txt new file mode 100644 index 0000000..2289704 --- /dev/null +++ b/Darwin/fastlane/metadata/en-US/subtitle.txt @@ -0,0 +1 @@ +A new Skip app diff --git a/Darwin/fastlane/metadata/en-US/support_url.txt b/Darwin/fastlane/metadata/en-US/support_url.txt new file mode 100644 index 0000000..fcfa1f7 --- /dev/null +++ b/Darwin/fastlane/metadata/en-US/support_url.txt @@ -0,0 +1 @@ +https://example.org/support/ diff --git a/Darwin/fastlane/metadata/en-US/title.txt b/Darwin/fastlane/metadata/en-US/title.txt new file mode 100644 index 0000000..38f0959 --- /dev/null +++ b/Darwin/fastlane/metadata/en-US/title.txt @@ -0,0 +1 @@ +SatsPrice diff --git a/Darwin/fastlane/metadata/en-US/version_whats_new.txt b/Darwin/fastlane/metadata/en-US/version_whats_new.txt new file mode 100644 index 0000000..5d21c24 --- /dev/null +++ b/Darwin/fastlane/metadata/en-US/version_whats_new.txt @@ -0,0 +1 @@ +New features and better performance. diff --git a/Darwin/fastlane/metadata/rating.json b/Darwin/fastlane/metadata/rating.json new file mode 100644 index 0000000..b003379 --- /dev/null +++ b/Darwin/fastlane/metadata/rating.json @@ -0,0 +1,17 @@ +{ + "alcoholTobaccoOrDrugUseOrReferences": "NONE", + "contests": "NONE", + "gamblingSimulated": "NONE", + "horrorOrFearThemes": "NONE", + "matureOrSuggestiveThemes": "NONE", + "medicalOrTreatmentInformation": "NONE", + "profanityOrCrudeHumor": "NONE", + "sexualContentGraphicAndNudity": "NONE", + "sexualContentOrNudity": "NONE", + "violenceCartoonOrFantasy": "NONE", + "violenceRealisticProlongedGraphicOrSadistic": "NONE", + "violenceRealistic": "NONE", + "gambling": false, + "seventeenPlus": false, + "unrestrictedWebAccess": false +} diff --git a/LICENSE b/LICENSE.GPL similarity index 100% rename from LICENSE rename to LICENSE.GPL diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..695601f --- /dev/null +++ b/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version: 5.9 +// This is a Skip (https://skip.tools) package, +// containing a Swift Package Manager project +// that will use the Skip build plugin to transpile the +// Swift Package, Sources, and Tests into an +// Android Gradle Project with Kotlin sources and JUnit tests. +import PackageDescription + +let package = Package( + name: "sats-price", + defaultLocalization: "en", + platforms: [.iOS(.v16), .macOS(.v13), .tvOS(.v16), .watchOS(.v9), .macCatalyst(.v16)], + products: [ + .library(name: "SatsPriceApp", type: .dynamic, targets: ["SatsPrice"]), + ], + dependencies: [ + .package(url: "https://source.skip.tools/skip.git", from: "1.0.7"), + .package(url: "https://source.skip.tools/skip-ui.git", from: "1.0.0") + ], + targets: [ + .target(name: "SatsPrice", dependencies: [.product(name: "SkipUI", package: "skip-ui")], resources: [.process("Resources")], plugins: [.plugin(name: "skipstone", package: "skip")]), + .testTarget(name: "SatsPriceTests", dependencies: ["SatsPrice", .product(name: "SkipTest", package: "skip")], resources: [.process("Resources")], plugins: [.plugin(name: "skipstone", package: "skip")]), + ] +) diff --git a/README.md b/README.md index 936451d..ccf5f4b 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,49 @@ This app fetches the price of Bitcoin relative to the United States Dollar from multiple sources, and converts inputted amounts between Sats, BTC, and USD. -## Notes +This is a free [Skip](https://skip.tools) dual-platform app project. +It builds a native app for both iOS and Android. -Precision is attempted to be kept up to 20 digits. +## Building -When NaN is displayed, it means "not a number", which can be caused by invalid input or problems fetching the price of Bitcoin from the selected source. +This project is both a stand-alone Swift Package Manager module, +as well as an Xcode project that builds and transpiles the project +into a Kotlin Gradle project for Android using the Skip plugin. + +Building the module requires that Skip be installed using +[Homebrew](https://brew.sh) with `brew install skiptools/skip/skip`. + +This will also install the necessary transpiler prerequisites: +Kotlin, Gradle, and the Android build tools. + +Installation prerequisites can be confirmed by running `skip checkup`. + +## Testing + +The module can be tested using the standard `swift test` command +or by running the test target for the macOS destination in Xcode, +which will run the Swift tests as well as the transpiled +Kotlin JUnit tests in the Robolectric Android simulation environment. + +Parity testing can be performed with `skip test`, +which will output a table of the test results for both platforms. + +## Running + +Xcode and Android Studio must be downloaded and installed in order to +run the app in the iOS simulator / Android emulator. +An Android emulator must already be running, which can be launched from +Android Studio's Device Manager. + +To run both the Swift and Kotlin apps simultaneously, +launch the SatsPriceApp target from Xcode. +A build phases runs the "Launch Android APK" script that +will deploy the transpiled app a running Android emulator or connected device. +Logging output for the iOS app can be viewed in the Xcode console, and in +Android Studio's logcat tab for the transpiled Kotlin app. ## Attribution -The [Bitcoin Calculator](https://www.flaticon.com/free-icons/bitcoin-calculator) icon was created by Icon home and licensed as free for personal and commercial use with attribution. +This project depends on [Skip](https://skip.tools) to build as a multi-platform app. -This project depends on [BigDecimal](https://github.com/mgriebling/BigDecimal), an MIT-licensed package for providing arbitrary-precision and fixed-precision decimal arithmetic in Swift. +The [Bitcoin Calculator](https://www.flaticon.com/free-icons/bitcoin-calculator) icon was created by Icon home and licensed as free for personal and commercial use with attribution. diff --git a/SatsPrice.xcodeproj/project.pbxproj b/SatsPrice.xcodeproj/project.pbxproj deleted file mode 100644 index e352055..0000000 --- a/SatsPrice.xcodeproj/project.pbxproj +++ /dev/null @@ -1,718 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 56; - objects = { - -/* Begin PBXBuildFile section */ - 3A7B2AA32B86407A00ACC4A7 /* FakePriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7B2AA22B86407A00ACC4A7 /* FakePriceFetcher.swift */; }; - 3A8E5DA72C810C9900E9E6BD /* ManualPriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8E5DA62C810C9900E9E6BD /* ManualPriceFetcher.swift */; }; - 3AE2D39D2B83338D00DE5F31 /* SatsPriceApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D39C2B83338D00DE5F31 /* SatsPriceApp.swift */; }; - 3AE2D3A22B83338D00DE5F31 /* SatsPrice.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3A02B83338D00DE5F31 /* SatsPrice.xcdatamodeld */; }; - 3AE2D3A42B83338D00DE5F31 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3A32B83338D00DE5F31 /* ContentView.swift */; }; - 3AE2D3A62B83338D00DE5F31 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3AE2D3A52B83338D00DE5F31 /* Assets.xcassets */; }; - 3AE2D3AA2B83338D00DE5F31 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3AE2D3A92B83338D00DE5F31 /* Preview Assets.xcassets */; }; - 3AE2D3B42B83338E00DE5F31 /* SatsPriceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3B32B83338E00DE5F31 /* SatsPriceTests.swift */; }; - 3AE2D3BE2B83338E00DE5F31 /* SatsPriceUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3BD2B83338E00DE5F31 /* SatsPriceUITests.swift */; }; - 3AE2D3C02B83338E00DE5F31 /* SatsPriceUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3BF2B83338E00DE5F31 /* SatsPriceUITestsLaunchTests.swift */; }; - 3AE2D3D52B83E58500DE5F31 /* SatsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3D42B83E58500DE5F31 /* SatsViewModel.swift */; }; - 3AE2D3DA2B83FE5F00DE5F31 /* SatsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3D92B83FE5F00DE5F31 /* SatsViewModelTests.swift */; }; - 3AE2D3DD2B84020200DE5F31 /* BigDecimal in Frameworks */ = {isa = PBXBuildFile; productRef = 3AE2D3DC2B84020200DE5F31 /* BigDecimal */; }; - 3AE2D3DF2B84597100DE5F31 /* PriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3DE2B84597100DE5F31 /* PriceFetcher.swift */; }; - 3AE2D3E12B8459D600DE5F31 /* CoinbasePriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E02B8459D600DE5F31 /* CoinbasePriceFetcher.swift */; }; - 3AE2D3E32B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E22B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift */; }; - 3AE2D3E52B846A8600DE5F31 /* PriceSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E42B846A8600DE5F31 /* PriceSource.swift */; }; - 3AE2D3E72B85690200DE5F31 /* PriceFetcherDelegator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE2D3E62B85690200DE5F31 /* PriceFetcherDelegator.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 3AE2D3B02B83338E00DE5F31 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 3AE2D3912B83338D00DE5F31 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 3AE2D3982B83338D00DE5F31; - remoteInfo = SatsPrice; - }; - 3AE2D3BA2B83338E00DE5F31 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 3AE2D3912B83338D00DE5F31 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 3AE2D3982B83338D00DE5F31; - remoteInfo = SatsPrice; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 3A7B2AA22B86407A00ACC4A7 /* FakePriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakePriceFetcher.swift; sourceTree = ""; }; - 3A8E5DA62C810C9900E9E6BD /* ManualPriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualPriceFetcher.swift; sourceTree = ""; }; - 3AE2D3992B83338D00DE5F31 /* SatsPrice.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SatsPrice.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 3AE2D39C2B83338D00DE5F31 /* SatsPriceApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsPriceApp.swift; sourceTree = ""; }; - 3AE2D3A12B83338D00DE5F31 /* SatsPrice.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = SatsPrice.xcdatamodel; sourceTree = ""; }; - 3AE2D3A32B83338D00DE5F31 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - 3AE2D3A52B83338D00DE5F31 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 3AE2D3A72B83338D00DE5F31 /* SatsPrice.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SatsPrice.entitlements; sourceTree = ""; }; - 3AE2D3A92B83338D00DE5F31 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 3AE2D3AF2B83338E00DE5F31 /* SatsPriceTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SatsPriceTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 3AE2D3B32B83338E00DE5F31 /* SatsPriceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsPriceTests.swift; sourceTree = ""; }; - 3AE2D3B92B83338E00DE5F31 /* SatsPriceUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SatsPriceUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 3AE2D3BD2B83338E00DE5F31 /* SatsPriceUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsPriceUITests.swift; sourceTree = ""; }; - 3AE2D3BF2B83338E00DE5F31 /* SatsPriceUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsPriceUITestsLaunchTests.swift; sourceTree = ""; }; - 3AE2D3D42B83E58500DE5F31 /* SatsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsViewModel.swift; sourceTree = ""; }; - 3AE2D3D92B83FE5F00DE5F31 /* SatsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsViewModelTests.swift; sourceTree = ""; }; - 3AE2D3DE2B84597100DE5F31 /* PriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceFetcher.swift; sourceTree = ""; }; - 3AE2D3E02B8459D600DE5F31 /* CoinbasePriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinbasePriceFetcher.swift; sourceTree = ""; }; - 3AE2D3E22B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinGeckoPriceFetcher.swift; sourceTree = ""; }; - 3AE2D3E42B846A8600DE5F31 /* PriceSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceSource.swift; sourceTree = ""; }; - 3AE2D3E62B85690200DE5F31 /* PriceFetcherDelegator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceFetcherDelegator.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 3AE2D3962B83338D00DE5F31 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 3AE2D3DD2B84020200DE5F31 /* BigDecimal in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 3AE2D3AC2B83338E00DE5F31 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 3AE2D3B62B83338E00DE5F31 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 3AE2D3902B83338D00DE5F31 = { - isa = PBXGroup; - children = ( - 3AE2D39B2B83338D00DE5F31 /* SatsPrice */, - 3AE2D3B22B83338E00DE5F31 /* SatsPriceTests */, - 3AE2D3BC2B83338E00DE5F31 /* SatsPriceUITests */, - 3AE2D39A2B83338D00DE5F31 /* Products */, - ); - sourceTree = ""; - }; - 3AE2D39A2B83338D00DE5F31 /* Products */ = { - isa = PBXGroup; - children = ( - 3AE2D3992B83338D00DE5F31 /* SatsPrice.app */, - 3AE2D3AF2B83338E00DE5F31 /* SatsPriceTests.xctest */, - 3AE2D3B92B83338E00DE5F31 /* SatsPriceUITests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 3AE2D39B2B83338D00DE5F31 /* SatsPrice */ = { - isa = PBXGroup; - children = ( - 3AE2D3E82B856B1800DE5F31 /* Network */, - 3AE2D3A32B83338D00DE5F31 /* ContentView.swift */, - 3AE2D39C2B83338D00DE5F31 /* SatsPriceApp.swift */, - 3AE2D3D42B83E58500DE5F31 /* SatsViewModel.swift */, - 3AE2D3A52B83338D00DE5F31 /* Assets.xcassets */, - 3AE2D3A72B83338D00DE5F31 /* SatsPrice.entitlements */, - 3AE2D3A02B83338D00DE5F31 /* SatsPrice.xcdatamodeld */, - 3AE2D3A82B83338D00DE5F31 /* Preview Content */, - ); - path = SatsPrice; - sourceTree = ""; - }; - 3AE2D3A82B83338D00DE5F31 /* Preview Content */ = { - isa = PBXGroup; - children = ( - 3AE2D3A92B83338D00DE5F31 /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; - 3AE2D3B22B83338E00DE5F31 /* SatsPriceTests */ = { - isa = PBXGroup; - children = ( - 3AE2D3B32B83338E00DE5F31 /* SatsPriceTests.swift */, - 3AE2D3D92B83FE5F00DE5F31 /* SatsViewModelTests.swift */, - ); - path = SatsPriceTests; - sourceTree = ""; - }; - 3AE2D3BC2B83338E00DE5F31 /* SatsPriceUITests */ = { - isa = PBXGroup; - children = ( - 3AE2D3BD2B83338E00DE5F31 /* SatsPriceUITests.swift */, - 3AE2D3BF2B83338E00DE5F31 /* SatsPriceUITestsLaunchTests.swift */, - ); - path = SatsPriceUITests; - sourceTree = ""; - }; - 3AE2D3E82B856B1800DE5F31 /* Network */ = { - isa = PBXGroup; - children = ( - 3AE2D3E02B8459D600DE5F31 /* CoinbasePriceFetcher.swift */, - 3AE2D3E22B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift */, - 3A7B2AA22B86407A00ACC4A7 /* FakePriceFetcher.swift */, - 3A8E5DA62C810C9900E9E6BD /* ManualPriceFetcher.swift */, - 3AE2D3DE2B84597100DE5F31 /* PriceFetcher.swift */, - 3AE2D3E62B85690200DE5F31 /* PriceFetcherDelegator.swift */, - 3AE2D3E42B846A8600DE5F31 /* PriceSource.swift */, - ); - path = Network; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 3AE2D3982B83338D00DE5F31 /* SatsPrice */ = { - isa = PBXNativeTarget; - buildConfigurationList = 3AE2D3C32B83338E00DE5F31 /* Build configuration list for PBXNativeTarget "SatsPrice" */; - buildPhases = ( - 3AE2D3952B83338D00DE5F31 /* Sources */, - 3AE2D3962B83338D00DE5F31 /* Frameworks */, - 3AE2D3972B83338D00DE5F31 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SatsPrice; - packageProductDependencies = ( - 3AE2D3DC2B84020200DE5F31 /* BigDecimal */, - ); - productName = SatsPrice; - productReference = 3AE2D3992B83338D00DE5F31 /* SatsPrice.app */; - productType = "com.apple.product-type.application"; - }; - 3AE2D3AE2B83338E00DE5F31 /* SatsPriceTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 3AE2D3C62B83338E00DE5F31 /* Build configuration list for PBXNativeTarget "SatsPriceTests" */; - buildPhases = ( - 3AE2D3AB2B83338E00DE5F31 /* Sources */, - 3AE2D3AC2B83338E00DE5F31 /* Frameworks */, - 3AE2D3AD2B83338E00DE5F31 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 3AE2D3B12B83338E00DE5F31 /* PBXTargetDependency */, - ); - name = SatsPriceTests; - productName = SatsPriceTests; - productReference = 3AE2D3AF2B83338E00DE5F31 /* SatsPriceTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 3AE2D3B82B83338E00DE5F31 /* SatsPriceUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 3AE2D3C92B83338E00DE5F31 /* Build configuration list for PBXNativeTarget "SatsPriceUITests" */; - buildPhases = ( - 3AE2D3B52B83338E00DE5F31 /* Sources */, - 3AE2D3B62B83338E00DE5F31 /* Frameworks */, - 3AE2D3B72B83338E00DE5F31 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 3AE2D3BB2B83338E00DE5F31 /* PBXTargetDependency */, - ); - name = SatsPriceUITests; - productName = SatsPriceUITests; - productReference = 3AE2D3B92B83338E00DE5F31 /* SatsPriceUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 3AE2D3912B83338D00DE5F31 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1520; - LastUpgradeCheck = 1520; - TargetAttributes = { - 3AE2D3982B83338D00DE5F31 = { - CreatedOnToolsVersion = 15.2; - }; - 3AE2D3AE2B83338E00DE5F31 = { - CreatedOnToolsVersion = 15.2; - TestTargetID = 3AE2D3982B83338D00DE5F31; - }; - 3AE2D3B82B83338E00DE5F31 = { - CreatedOnToolsVersion = 15.2; - TestTargetID = 3AE2D3982B83338D00DE5F31; - }; - }; - }; - buildConfigurationList = 3AE2D3942B83338D00DE5F31 /* Build configuration list for PBXProject "SatsPrice" */; - compatibilityVersion = "Xcode 14.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 3AE2D3902B83338D00DE5F31; - packageReferences = ( - 3AE2D3DB2B84020200DE5F31 /* XCRemoteSwiftPackageReference "BigDecimal" */, - ); - productRefGroup = 3AE2D39A2B83338D00DE5F31 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 3AE2D3982B83338D00DE5F31 /* SatsPrice */, - 3AE2D3AE2B83338E00DE5F31 /* SatsPriceTests */, - 3AE2D3B82B83338E00DE5F31 /* SatsPriceUITests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 3AE2D3972B83338D00DE5F31 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3AE2D3AA2B83338D00DE5F31 /* Preview Assets.xcassets in Resources */, - 3AE2D3A62B83338D00DE5F31 /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 3AE2D3AD2B83338E00DE5F31 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 3AE2D3B72B83338E00DE5F31 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 3AE2D3952B83338D00DE5F31 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3AE2D3E52B846A8600DE5F31 /* PriceSource.swift in Sources */, - 3AE2D3E72B85690200DE5F31 /* PriceFetcherDelegator.swift in Sources */, - 3A7B2AA32B86407A00ACC4A7 /* FakePriceFetcher.swift in Sources */, - 3AE2D3E12B8459D600DE5F31 /* CoinbasePriceFetcher.swift in Sources */, - 3AE2D39D2B83338D00DE5F31 /* SatsPriceApp.swift in Sources */, - 3AE2D3A22B83338D00DE5F31 /* SatsPrice.xcdatamodeld in Sources */, - 3AE2D3A42B83338D00DE5F31 /* ContentView.swift in Sources */, - 3A8E5DA72C810C9900E9E6BD /* ManualPriceFetcher.swift in Sources */, - 3AE2D3D52B83E58500DE5F31 /* SatsViewModel.swift in Sources */, - 3AE2D3E32B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift in Sources */, - 3AE2D3DF2B84597100DE5F31 /* PriceFetcher.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 3AE2D3AB2B83338E00DE5F31 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3AE2D3DA2B83FE5F00DE5F31 /* SatsViewModelTests.swift in Sources */, - 3AE2D3B42B83338E00DE5F31 /* SatsPriceTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 3AE2D3B52B83338E00DE5F31 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3AE2D3BE2B83338E00DE5F31 /* SatsPriceUITests.swift in Sources */, - 3AE2D3C02B83338E00DE5F31 /* SatsPriceUITestsLaunchTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 3AE2D3B12B83338E00DE5F31 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 3AE2D3982B83338D00DE5F31 /* SatsPrice */; - targetProxy = 3AE2D3B02B83338E00DE5F31 /* PBXContainerItemProxy */; - }; - 3AE2D3BB2B83338E00DE5F31 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 3AE2D3982B83338D00DE5F31 /* SatsPrice */; - targetProxy = 3AE2D3BA2B83338E00DE5F31 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 3AE2D3C12B83338E00DE5F31 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = "xrsimulator xros watchsimulator watchos macosx iphonesimulator iphoneos driverkit appletvsimulator appletvos"; - SUPPORTS_MACCATALYST = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 3AE2D3C22B83338E00DE5F31 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = "xrsimulator xros watchsimulator watchos macosx iphonesimulator iphoneos driverkit appletvsimulator appletvos"; - SUPPORTS_MACCATALYST = YES; - SWIFT_COMPILATION_MODE = wholemodule; - }; - name = Release; - }; - 3AE2D3C42B83338E00DE5F31 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = SatsPrice/SatsPrice.entitlements; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_ASSET_PATHS = "\"SatsPrice/Preview Content\""; - DEVELOPMENT_TEAM = S99A5B637C; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_CFBundleDisplayName = SatsPrice; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; - "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 0.1; - PRODUCT_BUNDLE_IDENTIFIER = xyz.tyiu.SatsPrice; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SUPPORTS_MACCATALYST = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 3AE2D3C52B83338E00DE5F31 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = SatsPrice/SatsPrice.entitlements; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_ASSET_PATHS = "\"SatsPrice/Preview Content\""; - DEVELOPMENT_TEAM = S99A5B637C; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_CFBundleDisplayName = SatsPrice; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; - "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 0.1; - PRODUCT_BUNDLE_IDENTIFIER = xyz.tyiu.SatsPrice; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SUPPORTS_MACCATALYST = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - 3AE2D3C72B83338E00DE5F31 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = xyz.tyiu.SatsPriceTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SatsPrice.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/SatsPrice"; - }; - name = Debug; - }; - 3AE2D3C82B83338E00DE5F31 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = xyz.tyiu.SatsPriceTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SatsPrice.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/SatsPrice"; - }; - name = Release; - }; - 3AE2D3CA2B83338E00DE5F31 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = xyz.tyiu.SatsPriceUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = SatsPrice; - }; - name = Debug; - }; - 3AE2D3CB2B83338E00DE5F31 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = xyz.tyiu.SatsPriceUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = SatsPrice; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 3AE2D3942B83338D00DE5F31 /* Build configuration list for PBXProject "SatsPrice" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3AE2D3C12B83338E00DE5F31 /* Debug */, - 3AE2D3C22B83338E00DE5F31 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 3AE2D3C32B83338E00DE5F31 /* Build configuration list for PBXNativeTarget "SatsPrice" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3AE2D3C42B83338E00DE5F31 /* Debug */, - 3AE2D3C52B83338E00DE5F31 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 3AE2D3C62B83338E00DE5F31 /* Build configuration list for PBXNativeTarget "SatsPriceTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3AE2D3C72B83338E00DE5F31 /* Debug */, - 3AE2D3C82B83338E00DE5F31 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 3AE2D3C92B83338E00DE5F31 /* Build configuration list for PBXNativeTarget "SatsPriceUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3AE2D3CA2B83338E00DE5F31 /* Debug */, - 3AE2D3CB2B83338E00DE5F31 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 3AE2D3DB2B84020200DE5F31 /* XCRemoteSwiftPackageReference "BigDecimal" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/mgriebling/BigDecimal.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.2.3; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 3AE2D3DC2B84020200DE5F31 /* BigDecimal */ = { - isa = XCSwiftPackageProductDependency; - package = 3AE2D3DB2B84020200DE5F31 /* XCRemoteSwiftPackageReference "BigDecimal" */; - productName = BigDecimal; - }; -/* End XCSwiftPackageProductDependency section */ - -/* Begin XCVersionGroup section */ - 3AE2D3A02B83338D00DE5F31 /* SatsPrice.xcdatamodeld */ = { - isa = XCVersionGroup; - children = ( - 3AE2D3A12B83338D00DE5F31 /* SatsPrice.xcdatamodel */, - ); - currentVersion = 3AE2D3A12B83338D00DE5F31 /* SatsPrice.xcdatamodel */; - path = SatsPrice.xcdatamodeld; - sourceTree = ""; - versionGroupType = wrapper.xcdatamodel; - }; -/* End XCVersionGroup section */ - }; - rootObject = 3AE2D3912B83338D00DE5F31 /* Project object */; -} diff --git a/SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 0b5df84..0000000 --- a/SatsPrice.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,32 +0,0 @@ -{ - "pins" : [ - { - "identity" : "bigdecimal", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mgriebling/BigDecimal.git", - "state" : { - "revision" : "c4e8348c7fbc90f29225d5f8681ce33a16ab33a2", - "version" : "2.2.3" - } - }, - { - "identity" : "bigint", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mgriebling/BigInt.git", - "state" : { - "revision" : "498d4d290658d7c43a24b9e309c321592dc294f2", - "version" : "2.0.10" - } - }, - { - "identity" : "uint128", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mgriebling/UInt128.git", - "state" : { - "revision" : "59dac4f14d657fd60bacfdfb7398d38b450af74f", - "version" : "3.1.5" - } - } - ], - "version" : 2 -} diff --git a/SatsPrice.xcodeproj/project.xcworkspace/xcuserdata/tyiu.xcuserdatad/WorkspaceSettings.xcsettings b/SatsPrice.xcodeproj/project.xcworkspace/xcuserdata/tyiu.xcuserdatad/WorkspaceSettings.xcsettings deleted file mode 100644 index bbfef02..0000000 --- a/SatsPrice.xcodeproj/project.xcworkspace/xcuserdata/tyiu.xcuserdatad/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,14 +0,0 @@ - - - - - BuildLocationStyle - UseAppPreferences - CustomBuildLocationType - RelativeToDerivedData - DerivedDataLocationStyle - Default - ShowSharedSchemesAutomaticallyEnabled - - - diff --git a/SatsPrice.xcodeproj/xcuserdata/tyiu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SatsPrice.xcodeproj/xcuserdata/tyiu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index 6edebda..0000000 --- a/SatsPrice.xcodeproj/xcuserdata/tyiu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/SatsPrice.xcodeproj/xcuserdata/tyiu.xcuserdatad/xcschemes/xcschememanagement.plist b/SatsPrice.xcodeproj/xcuserdata/tyiu.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index b7ab8b4..0000000 --- a/SatsPrice.xcodeproj/xcuserdata/tyiu.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,35 +0,0 @@ - - - - - SchemeUserState - - Demo (Playground) 1.xcscheme - - isShown - - orderHint - 2 - - Demo (Playground) 2.xcscheme - - isShown - - orderHint - 3 - - Demo (Playground).xcscheme - - isShown - - orderHint - 1 - - SatsPrice.xcscheme_^#shared#^_ - - orderHint - 0 - - - - diff --git a/SatsPrice/Network/PriceFetcher.swift b/SatsPrice/Network/PriceFetcher.swift deleted file mode 100644 index 341ddc6..0000000 --- a/SatsPrice/Network/PriceFetcher.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// PriceFetcher.swift -// SatsPrice -// -// Created by Terry Yiu on 2/19/24. -// - -import Foundation -import BigDecimal - -protocol PriceFetcher { - func btcToUsd() async throws -> BigDecimal? -} diff --git a/SatsPrice/SatsPrice.entitlements b/SatsPrice/SatsPrice.entitlements deleted file mode 100644 index ee95ab7..0000000 --- a/SatsPrice/SatsPrice.entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.network.client - - - diff --git a/SatsPrice/SatsPrice.xcdatamodeld/SatsPrice.xcdatamodel/contents b/SatsPrice/SatsPrice.xcdatamodeld/SatsPrice.xcdatamodel/contents deleted file mode 100644 index 9ed2921..0000000 --- a/SatsPrice/SatsPrice.xcdatamodeld/SatsPrice.xcdatamodel/contents +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/SatsPrice/SatsPriceApp.swift b/SatsPrice/SatsPriceApp.swift deleted file mode 100644 index f11b8b0..0000000 --- a/SatsPrice/SatsPriceApp.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// SatsPriceApp.swift -// SatsPrice -// -// Created by Terry Yiu on 2/19/24. -// - -import SwiftUI - -@main -struct SatsPriceApp: App { - var body: some Scene { - WindowGroup { - ContentView(.coinbase) - } - } -} diff --git a/SatsPrice/SatsViewModel.swift b/SatsPrice/SatsViewModel.swift deleted file mode 100644 index 2c5a528..0000000 --- a/SatsPrice/SatsViewModel.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// SatsViewModel.swift -// SatsPrice -// -// Created by Terry Yiu on 2/19/24. -// - -import Foundation -import BigDecimal - -class SatsViewModel: ObservableObject { - @Published private(set) var btcToUsd: BigDecimal = BigDecimal.nan - @Published var lastUpdated: Date = Date.now - - @Published private(set) var sats: BigDecimal = 0 - @Published private(set) var btc: BigDecimal = 0 - @Published private(set) var usd: BigDecimal = 0 - - var btcToUsdString: String { - get { btcToUsd.asString(.plain) } - set { - self.btcToUsd = BigDecimal(newValue) - self.usd = btc.multiply(btcToUsd, Rounding(.down, 20)) - } - } - - var satsString: String { - get { sats.asString(.plain) } - set { - self.sats = BigDecimal(newValue) - - let preciseDivide = sats.divide(100000000) - if preciseDivide.isNaN { - self.btc = sats.divide(100000000, Rounding(.down, 20)) - } else { - self.btc = sats.divide(100000000) - } - - self.usd = btc.multiply(btcToUsd, Rounding(.down, 20)) - } - } - - var btcString: String { - get { btc.asString(.plain) } - set { - self.btc = BigDecimal(newValue) - self.sats = btc.multiply(100000000, Rounding(.down, 20)) - self.usd = btc.multiply(btcToUsd, Rounding(.down, 20)) - } - } - - var usdString: String { - get { usd.asString(.plain) } - set { - self.usd = BigDecimal(newValue) - - let preciseDivide = usd.divide(btcToUsd) - if preciseDivide.isNaN { - self.btc = usd.divide(btcToUsd, Rounding(.down, 20)) - } else { - self.btc = usd.divide(btcToUsd) - } - - self.sats = btc.multiply(100000000, Rounding(.down, 20)) - } - } -} diff --git a/SatsPriceTests/SatsPriceTests.swift b/SatsPriceTests/SatsPriceTests.swift deleted file mode 100644 index 751f057..0000000 --- a/SatsPriceTests/SatsPriceTests.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// SatsPriceTests.swift -// SatsPriceTests -// -// Created by Terry Yiu on 2/19/24. -// - -import XCTest - -final class SatsPriceTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/SatsPriceUITests/SatsPriceUITests.swift b/SatsPriceUITests/SatsPriceUITests.swift deleted file mode 100644 index 3320e60..0000000 --- a/SatsPriceUITests/SatsPriceUITests.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// SatsPriceUITests.swift -// SatsPriceUITests -// -// Created by Terry Yiu on 2/19/24. -// - -import XCTest - -final class SatsPriceUITests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testLaunchPerformance() throws { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { - // This measures how long it takes to launch your application. - measure(metrics: [XCTApplicationLaunchMetric()]) { - XCUIApplication().launch() - } - } - } -} diff --git a/SatsPriceUITests/SatsPriceUITestsLaunchTests.swift b/SatsPriceUITests/SatsPriceUITestsLaunchTests.swift deleted file mode 100644 index 5fbea58..0000000 --- a/SatsPriceUITests/SatsPriceUITestsLaunchTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// SatsPriceUITestsLaunchTests.swift -// SatsPriceUITests -// -// Created by Terry Yiu on 2/19/24. -// - -import XCTest - -final class SatsPriceUITestsLaunchTests: XCTestCase { - - override class var runsForEachTargetApplicationUIConfiguration: Bool { - true - } - - override func setUpWithError() throws { - continueAfterFailure = false - } - - func testLaunch() throws { - let app = XCUIApplication() - app.launch() - - // Insert steps here to perform after app launch but before taking a screenshot, - // such as logging into a test account or navigating somewhere in the app - - let attachment = XCTAttachment(screenshot: app.screenshot()) - attachment.name = "Launch Screen" - attachment.lifetime = .keepAlways - add(attachment) - } -} diff --git a/Skip.env b/Skip.env new file mode 100644 index 0000000..c187154 --- /dev/null +++ b/Skip.env @@ -0,0 +1,20 @@ +// The configuration file for your Skip App (https://skip.tools). +// Properties specified here are shared between +// Darwin/SatsPrice.xcconfig and Android/settings.gradle.kts +// and will be included in the app's metadata files +// Info.plist and AndroidManifest.xml + +// PRODUCT_NAME is the default title of the app, which must match the app's Swift module name +PRODUCT_NAME = SatsPrice + +// PRODUCT_BUNDLE_IDENTIFIER is the unique id for both the iOS and Android app +PRODUCT_BUNDLE_IDENTIFIER = xyz.tyiu.SatsPrice + +// The semantic version of the app +MARKETING_VERSION = 0.2.0 + +// The build number specifying the internal app version +CURRENT_PROJECT_VERSION = 1 + +// The package name for the Android entry point, referenced by the AndroidManifest.xml +ANDROID_PACKAGE_NAME = sats.price diff --git a/SatsPrice/ContentView.swift b/Sources/SatsPrice/ContentView.swift similarity index 83% rename from SatsPrice/ContentView.swift rename to Sources/SatsPrice/ContentView.swift index 7864bc2..5e0a8ad 100644 --- a/SatsPrice/ContentView.swift +++ b/Sources/SatsPrice/ContentView.swift @@ -1,15 +1,11 @@ -// -// ContentView.swift -// SatsPrice -// -// Created by Terry Yiu on 2/19/24. -// +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org -import SwiftUI -import BigDecimal import Combine +import SwiftUI -struct ContentView: View { +public struct ContentView: View { @ObservedObject private var satsViewModel = SatsViewModel() @State private var priceSource: PriceSource @@ -42,7 +38,7 @@ struct ContentView: View { satsViewModel.lastUpdated = Date.now } - var body: some View { + public var body: some View { Form { Section { Picker("Price Source", selection: $priceSource) { @@ -54,7 +50,7 @@ struct ContentView: View { HStack { TextField("", text: $satsViewModel.btcToUsdString) .disabled(priceSource != .manual) -#if os(iOS) +#if os(iOS) || SKIP .keyboardType(.decimalPad) #endif if priceSource != .manual { @@ -63,19 +59,21 @@ struct ContentView: View { await updatePrice() } }) { - Image(systemName: "arrow.clockwise") + Image(systemName: "arrow.clockwise.circle") } } } } header: { Text("1 BTC to USD") } footer: { - Text("Last updated: \(dateFormatter.string(from: satsViewModel.lastUpdated))") + if priceSource != .manual { + Text("Last updated: \(dateFormatter.string(from: satsViewModel.lastUpdated))") + } } Section { TextField("", text: $satsViewModel.satsString) -#if os(iOS) +#if os(iOS) || SKIP .keyboardType(.numberPad) #endif } header: { @@ -84,7 +82,7 @@ struct ContentView: View { Section { TextField("", text: $satsViewModel.btcString) -#if os(iOS) +#if os(iOS) || SKIP .keyboardType(.decimalPad) #endif } header: { @@ -93,7 +91,7 @@ struct ContentView: View { Section { TextField("", text: $satsViewModel.usdString) -#if os(iOS) +#if os(iOS) || SKIP .keyboardType(.decimalPad) #endif } header: { diff --git a/SatsPrice/Network/CoinGeckoPriceFetcher.swift b/Sources/SatsPrice/Network/CoinGeckoPriceFetcher.swift similarity index 71% rename from SatsPrice/Network/CoinGeckoPriceFetcher.swift rename to Sources/SatsPrice/Network/CoinGeckoPriceFetcher.swift index 5cc20fd..8a4658a 100644 --- a/SatsPrice/Network/CoinGeckoPriceFetcher.swift +++ b/Sources/SatsPrice/Network/CoinGeckoPriceFetcher.swift @@ -1,3 +1,6 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org // // CoinGeckoPriceFetcher.swift // SatsPrice @@ -6,20 +9,23 @@ // import Foundation -import BigDecimal private struct CoinGeckoPriceResponse: Codable { let bitcoin: CoinGeckoPrice } private struct CoinGeckoPrice: Codable { +#if !SKIP let usd: Decimal +#else + let usd: String +#endif } class CoinGeckoPriceFetcher : PriceFetcher { private static let urlString = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&precision=18" - func btcToUsd() async throws -> BigDecimal? { + func btcToUsd() async throws -> Decimal? { do { guard let urlComponents = URLComponents(string: CoinGeckoPriceFetcher.urlString), let url = urlComponents.url else { return nil @@ -30,7 +36,11 @@ class CoinGeckoPriceFetcher : PriceFetcher { let priceResponse = try JSONDecoder().decode(CoinGeckoPriceResponse.self, from: data) let price = priceResponse.bitcoin - return BigDecimal(price.usd) +#if !SKIP + return price.usd +#else + return Decimal(price.usd) +#endif } catch { return nil } diff --git a/SatsPrice/Network/CoinbasePriceFetcher.swift b/Sources/SatsPrice/Network/CoinbasePriceFetcher.swift similarity index 74% rename from SatsPrice/Network/CoinbasePriceFetcher.swift rename to Sources/SatsPrice/Network/CoinbasePriceFetcher.swift index 01a2855..b3add15 100644 --- a/SatsPrice/Network/CoinbasePriceFetcher.swift +++ b/Sources/SatsPrice/Network/CoinbasePriceFetcher.swift @@ -1,3 +1,6 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org // // CoinbasePriceFetcher.swift // SatsPrice @@ -6,7 +9,6 @@ // import Foundation -import BigDecimal private struct CoinbasePriceResponse: Codable { let data: CoinbasePrice @@ -23,7 +25,7 @@ class CoinbasePriceFetcher : PriceFetcher { private static let btc = "BTC" private static let usd = "USD" - func btcToUsd() async throws -> BigDecimal? { + func btcToUsd() async throws -> Decimal? { do { guard let urlComponents = URLComponents(string: CoinbasePriceFetcher.urlString), let url = urlComponents.url else { return nil @@ -38,7 +40,11 @@ class CoinbasePriceFetcher : PriceFetcher { return nil } - return BigDecimal(coinbasePrice.amount) + #if !SKIP + return Decimal(string: coinbasePrice.amount) + #else + return Decimal(coinbasePrice.amount) + #endif } catch { return nil } diff --git a/SatsPrice/Network/FakePriceFetcher.swift b/Sources/SatsPrice/Network/FakePriceFetcher.swift similarity index 50% rename from SatsPrice/Network/FakePriceFetcher.swift rename to Sources/SatsPrice/Network/FakePriceFetcher.swift index 6e90d05..400d02a 100644 --- a/SatsPrice/Network/FakePriceFetcher.swift +++ b/Sources/SatsPrice/Network/FakePriceFetcher.swift @@ -1,3 +1,6 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org // // FakePriceFetcher.swift // SatsPrice @@ -7,12 +10,11 @@ #if DEBUG import Foundation -import BigDecimal /// Fake price fetcher that returns a randomized price. Useful for development testing without requiring a network call. class FakePriceFetcher: PriceFetcher { - func btcToUsd() async throws -> BigDecimal? { - BigDecimal(Double.random(in: 10000...100000)) + func btcToUsd() async throws -> Decimal? { + Decimal(Double.random(in: 10000...100000)) } } #endif diff --git a/SatsPrice/Network/ManualPriceFetcher.swift b/Sources/SatsPrice/Network/ManualPriceFetcher.swift similarity index 52% rename from SatsPrice/Network/ManualPriceFetcher.swift rename to Sources/SatsPrice/Network/ManualPriceFetcher.swift index 23d6c39..6dc9cb9 100644 --- a/SatsPrice/Network/ManualPriceFetcher.swift +++ b/Sources/SatsPrice/Network/ManualPriceFetcher.swift @@ -1,3 +1,6 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org // // ManualPriceFetcher.swift // SatsPrice @@ -6,13 +9,12 @@ // import Foundation -import BigDecimal /// Fake price fetcher that returns a randomized price. Useful for development testing without requiring a network call. class ManualPriceFetcher: PriceFetcher { - var price: BigDecimal = 1 + var price: Decimal = Decimal(1) - func btcToUsd() async throws -> BigDecimal? { + func btcToUsd() async throws -> Decimal? { return price } } diff --git a/Sources/SatsPrice/Network/PriceFetcher.swift b/Sources/SatsPrice/Network/PriceFetcher.swift new file mode 100644 index 0000000..2a5e14d --- /dev/null +++ b/Sources/SatsPrice/Network/PriceFetcher.swift @@ -0,0 +1,15 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org +// +// PriceFetcher.swift +// SatsPrice +// +// Created by Terry Yiu on 2/19/24. +// + +import Foundation + +protocol PriceFetcher { + func btcToUsd() async throws -> Decimal? +} diff --git a/SatsPrice/Network/PriceFetcherDelegator.swift b/Sources/SatsPrice/Network/PriceFetcherDelegator.swift similarity index 79% rename from SatsPrice/Network/PriceFetcherDelegator.swift rename to Sources/SatsPrice/Network/PriceFetcherDelegator.swift index 21e7a6d..b944c14 100644 --- a/SatsPrice/Network/PriceFetcherDelegator.swift +++ b/Sources/SatsPrice/Network/PriceFetcherDelegator.swift @@ -1,3 +1,6 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org // // PriceFetcherDelegator.swift // SatsPrice @@ -6,7 +9,6 @@ // import Foundation -import BigDecimal class PriceFetcherDelegator: PriceFetcher { private let coinbasePriceFetcher = CoinbasePriceFetcher() @@ -37,7 +39,7 @@ class PriceFetcherDelegator: PriceFetcher { } } - func btcToUsd() async throws -> BigDecimal? { + func btcToUsd() async throws -> Decimal? { return try await delegate.btcToUsd() } } diff --git a/SatsPrice/Network/PriceSource.swift b/Sources/SatsPrice/Network/PriceSource.swift similarity index 78% rename from SatsPrice/Network/PriceSource.swift rename to Sources/SatsPrice/Network/PriceSource.swift index a2e2809..172fb85 100644 --- a/SatsPrice/Network/PriceSource.swift +++ b/Sources/SatsPrice/Network/PriceSource.swift @@ -1,3 +1,6 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org // // PriceSource.swift // SatsPrice diff --git a/Sources/SatsPrice/Resources/Localizable.xcstrings b/Sources/SatsPrice/Resources/Localizable.xcstrings new file mode 100644 index 0000000..e866dfc --- /dev/null +++ b/Sources/SatsPrice/Resources/Localizable.xcstrings @@ -0,0 +1,27 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "" : { + + }, + "1 BTC to USD" : { + + }, + "BTC" : { + + }, + "Last updated: %@" : { + + }, + "Price Source" : { + + }, + "Sats" : { + + }, + "USD" : { + + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/SatsPrice/Preview Content/Preview Assets.xcassets/Contents.json b/Sources/SatsPrice/Resources/Module.xcassets/Contents.json similarity index 100% rename from SatsPrice/Preview Content/Preview Assets.xcassets/Contents.json rename to Sources/SatsPrice/Resources/Module.xcassets/Contents.json diff --git a/Sources/SatsPrice/SatsPrice.swift b/Sources/SatsPrice/SatsPrice.swift new file mode 100644 index 0000000..ef5a551 --- /dev/null +++ b/Sources/SatsPrice/SatsPrice.swift @@ -0,0 +1,6 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org + +public class SatsPriceModule { +} diff --git a/Sources/SatsPrice/SatsPriceApp.swift b/Sources/SatsPrice/SatsPriceApp.swift new file mode 100644 index 0000000..d78e7a0 --- /dev/null +++ b/Sources/SatsPrice/SatsPriceApp.swift @@ -0,0 +1,43 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org + +import Foundation +import OSLog +import SwiftUI + +let logger: Logger = Logger(subsystem: "xyz.tyiu.SatsPrice", category: "SatsPrice") + +/// The Android SDK number we are running against, or `nil` if not running on Android +let androidSDK = ProcessInfo.processInfo.environment["android.os.Build.VERSION.SDK_INT"].flatMap({ Int($0) }) + +/// The shared top-level view for the app, loaded from the platform-specific App delegates below. +/// +/// The default implementation merely loads the `ContentView` for the app and logs a message. +public struct RootView : View { + public init() { + } + + public var body: some View { + ContentView(.coinbase) + .task { + logger.log("Welcome to Skip on \(androidSDK != nil ? "Android" : "Darwin")!") + logger.warning("Skip app logs are viewable in the Xcode console for iOS; Android logs can be viewed in Studio or using adb logcat") + } + } +} + +#if !SKIP +public protocol SatsPriceApp : App { +} + +/// The entry point to the SatsPrice app. +/// The concrete implementation is in the SatsPriceApp module. +public extension SatsPriceApp { + var body: some Scene { + WindowGroup { + RootView() + } + } +} +#endif diff --git a/Sources/SatsPrice/SatsViewModel.swift b/Sources/SatsPrice/SatsViewModel.swift new file mode 100644 index 0000000..c4a1ea2 --- /dev/null +++ b/Sources/SatsPrice/SatsViewModel.swift @@ -0,0 +1,173 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org +// +// +// SatsViewModel.swift +// SatsPrice +// +// Created by Terry Yiu on 2/19/24. +// + +import Foundation +import SwiftUI + +class SatsViewModel: ObservableObject { + @Published var lastUpdated: Date = Date.now + + @Published var btcToUsdStringInternal: String = "" + @Published var satsStringInternal: String = "" + @Published var btcStringInternal: String = "" + @Published var usdStringInternal: String = "" + + var btcToUsdString: String { + get { + btcToUsdStringInternal + } + set { + btcToUsdStringInternal = newValue + + if let btc, let btcToUsd { + usdStringInternal = (btc * btcToUsd).formatString() + } else { + usdStringInternal = "" + } + } + } + + var satsString: String { + get { + satsStringInternal + } + set { + satsStringInternal = newValue + + if let sats { +#if !SKIP + let btc = sats / Decimal(100000000) +#else + let btc = sats.divide(Decimal(100000000), 20, java.math.RoundingMode.DOWN) +#endif + btcStringInternal = btc.formatString() + if let btcToUsd { + usdStringInternal = (btc * btcToUsd).formatString() + } else { + usdStringInternal = "" + } + } else { + btcStringInternal = "" + usdStringInternal = "" + } + } + } + + var btcString: String { + get { + btcStringInternal + } + set { + btcStringInternal = newValue + + if let btc { + let sats = btc * Decimal(100000000) + satsStringInternal = sats.formatString() + + if let btcToUsd { + usdStringInternal = (btc * btcToUsd).formatString() + } else { + usdStringInternal = "" + } + } else { + satsStringInternal = "" + usdStringInternal = "" + } + } + } + + var usdString: String { + get { + usdStringInternal + } + set { + usdStringInternal = newValue + + if let usd { + if let btcToUsd { +#if !SKIP + let btc = usd / btcToUsd +#else + let btc = usd.divide(btcToUsd, 20, java.math.RoundingMode.DOWN) +#endif + btcStringInternal = btc.formatString() + + let sats = btc * Decimal(100000000) + satsStringInternal = sats.formatString() + } else { + satsStringInternal = "" + usdStringInternal = "" + } + } else { + satsStringInternal = "" + usdStringInternal = "" + } + } + } + + var btcToUsd: Decimal? { +#if !SKIP + return Decimal(string: btcToUsdStringInternal) +#else + do { + return Decimal(btcToUsdStringInternal) + } catch { + return nil + } +#endif + } + + var sats: Decimal? { +#if !SKIP + return Decimal(string: satsStringInternal) +#else + do { + return Decimal(satsStringInternal) + } catch { + return nil + } +#endif + } + + var btc: Decimal? { +#if !SKIP + return Decimal(string: btcStringInternal) +#else + do { + return Decimal(btcStringInternal) + } catch { + return nil + } +#endif + } + + var usd: Decimal? { +#if !SKIP + return Decimal(string: usdStringInternal) +#else + do { + return Decimal(usdStringInternal) + } catch { + return nil + } +#endif + } +} + +extension Decimal { + func formatString() -> String { +#if !SKIP + return String(describing: self) +#else + return stripTrailingZeros().toPlainString() +#endif + } +} diff --git a/Sources/SatsPrice/Skip/skip.yml b/Sources/SatsPrice/Skip/skip.yml new file mode 100644 index 0000000..f0bf5ee --- /dev/null +++ b/Sources/SatsPrice/Skip/skip.yml @@ -0,0 +1,3 @@ +# Configuration file for https://skip.tools project +build: + contents: diff --git a/SatsPriceTests/SatsViewModelTests.swift b/Tests/SatsPriceTests/SatsViewModelTests.swift similarity index 52% rename from SatsPriceTests/SatsViewModelTests.swift rename to Tests/SatsPriceTests/SatsViewModelTests.swift index 867e787..d32562a 100644 --- a/SatsPriceTests/SatsViewModelTests.swift +++ b/Tests/SatsPriceTests/SatsViewModelTests.swift @@ -1,3 +1,6 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org // // SatsViewModelTests.swift // SatsPriceTests @@ -6,66 +9,57 @@ // import XCTest -import BigDecimal @testable import SatsPrice final class SatsViewModelTests: XCTestCase { - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - func testSatsViewModel() { let satsViewModel = SatsViewModel() satsViewModel.btcToUsdString = "54321" // Test BTC updates. satsViewModel.btcString = "1" - XCTAssertEqual(satsViewModel.btc, BigDecimal("1")) + XCTAssertEqual(satsViewModel.btc, Decimal(string: "1")) XCTAssertEqual(satsViewModel.btcString, "1") - XCTAssertEqual(satsViewModel.sats, BigDecimal("100000000")) + XCTAssertEqual(satsViewModel.sats, Decimal(string: "100000000")) XCTAssertEqual(satsViewModel.satsString, "100000000") - XCTAssertEqual(satsViewModel.usd, BigDecimal("54321")) + XCTAssertEqual(satsViewModel.usd, Decimal(string: "54321")) XCTAssertEqual(satsViewModel.usdString, "54321") // Test Sats updates. satsViewModel.satsString = "200000000" - XCTAssertEqual(satsViewModel.btc, BigDecimal("2")) + XCTAssertEqual(satsViewModel.btc, Decimal(string: "2")) XCTAssertEqual(satsViewModel.btcString, "2") - XCTAssertEqual(satsViewModel.sats, BigDecimal("200000000")) + XCTAssertEqual(satsViewModel.sats, Decimal(string: "200000000")) XCTAssertEqual(satsViewModel.satsString, "200000000") - XCTAssertEqual(satsViewModel.usd, BigDecimal("108642")) + XCTAssertEqual(satsViewModel.usd, Decimal(string: "108642")) XCTAssertEqual(satsViewModel.usdString, "108642") // Test USD updates. satsViewModel.usdString = "162963" - XCTAssertEqual(satsViewModel.btc, BigDecimal("3")) + XCTAssertEqual(satsViewModel.btc, Decimal(string: "3")) XCTAssertEqual(satsViewModel.btcString, "3") - XCTAssertEqual(satsViewModel.sats, BigDecimal("300000000")) + XCTAssertEqual(satsViewModel.sats, Decimal(string: "300000000")) XCTAssertEqual(satsViewModel.satsString, "300000000") - XCTAssertEqual(satsViewModel.usd, BigDecimal("162963")) + XCTAssertEqual(satsViewModel.usd, Decimal(string: "162963")) XCTAssertEqual(satsViewModel.usdString, "162963") // Test fractional amounts. satsViewModel.usdString = "1" - XCTAssertEqual(satsViewModel.btc, BigDecimal("0.000018409086725207562452")) - XCTAssertEqual(satsViewModel.btcString, "0.000018409086725207562452") - XCTAssertEqual(satsViewModel.sats, BigDecimal("1840.9086725207562452")) - XCTAssertEqual(satsViewModel.satsString, "1840.9086725207562452") - XCTAssertEqual(satsViewModel.usd, BigDecimal("1")) + XCTAssertEqual(satsViewModel.btc, Decimal(string: "0.00001840908672520756245282671526665562")) + XCTAssertEqual(satsViewModel.btcString, "0.00001840908672520756245282671526665562") + XCTAssertEqual(satsViewModel.sats, Decimal(string: "1840.908672520756245282671526665562")) + XCTAssertEqual(satsViewModel.satsString, "1840.908672520756245282671526665562") + XCTAssertEqual(satsViewModel.usd, Decimal(string: "1")) XCTAssertEqual(satsViewModel.usdString, "1") // Test large amounts that exceed the cap of 21M BTC. satsViewModel.usdString = "11407419999999" - XCTAssertEqual(satsViewModel.btc, BigDecimal("210000184.09084884298")) - XCTAssertEqual(satsViewModel.btcString, "210000184.09084884298") - XCTAssertEqual(satsViewModel.sats, BigDecimal("21000018409084884.298")) - XCTAssertEqual(satsViewModel.satsString, "21000018409084884.298") - XCTAssertEqual(satsViewModel.usd, BigDecimal("11407419999999")) + XCTAssertEqual(satsViewModel.btc, Decimal(string: "210000184.09084884298889932070469983984")) + XCTAssertEqual(satsViewModel.btcString, "210000184.09084884298889932070469983984") + XCTAssertEqual(satsViewModel.sats, Decimal(string: "21000018409084884.298889932070469983984")) + XCTAssertEqual(satsViewModel.satsString, "21000018409084884.298889932070469983984") + XCTAssertEqual(satsViewModel.usd, Decimal(string: "11407419999999")) XCTAssertEqual(satsViewModel.usdString, "11407419999999") } diff --git a/Tests/SatsPriceTests/Skip/skip.yml b/Tests/SatsPriceTests/Skip/skip.yml new file mode 100644 index 0000000..f7eb021 --- /dev/null +++ b/Tests/SatsPriceTests/Skip/skip.yml @@ -0,0 +1,3 @@ +# Configuration file for https://skip.tools project +#build: +# contents: \ No newline at end of file diff --git a/Tests/SatsPriceTests/XCSkipTests.swift b/Tests/SatsPriceTests/XCSkipTests.swift new file mode 100644 index 0000000..d9edbba --- /dev/null +++ b/Tests/SatsPriceTests/XCSkipTests.swift @@ -0,0 +1,32 @@ +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org + +import Foundation +#if os(macOS) // Skip transpiled tests only run on macOS targets +import SkipTest + +/// This test case will run the transpiled tests for the Skip module. +@available(macOS 13, macCatalyst 16, *) +final class XCSkipTests: XCTestCase, XCGradleHarness { + public func testSkipModule() async throws { + // Run the transpiled JUnit tests for the current test module. + // These tests will be executed locally using Robolectric. + // Connected device or emulator tests can be run by setting the + // `ANDROID_SERIAL` environment variable to an `adb devices` + // ID in the scheme's Run settings. + // + // Note that it isn't currently possible to filter the tests to run. + try await runGradleTests() + } +} +#endif + +/// True when running in a transpiled Java runtime environment +let isJava = ProcessInfo.processInfo.environment["java.io.tmpdir"] != nil +/// True when running within an Android environment (either an emulator or device) +let isAndroid = isJava && ProcessInfo.processInfo.environment["ANDROID_ROOT"] != nil +/// True is the transpiled code is currently running in the local Robolectric test environment +let isRobolectric = isJava && !isAndroid +/// True if the system's `Int` type is 32-bit. +let is32BitInteger = Int64(Int.max) == Int64(Int32.max)