Convert SatsPrice to a Skip multiplatform app
104
.gitignore
vendored
Normal file
@@ -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
|
||||
|
||||
64
Android/app/build.gradle.kts
Normal file
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
4
Android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
-keeppackagenames **
|
||||
-keep class skip.** { *; }
|
||||
-keep class com.sun.jna.Pointer { *; }
|
||||
-keep class sats.price.** { *; }
|
||||
30
Android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This AndroidManifest.xml template was generated by Skip -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- example permissions for using device location -->
|
||||
<!-- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> -->
|
||||
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> -->
|
||||
|
||||
<!-- permissions needed for using the internet or an embedded WebKit browser -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->
|
||||
|
||||
<application
|
||||
android:label="${PRODUCT_NAME}"
|
||||
android:name=".AndroidAppMain"
|
||||
android:supportsRtl="true"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|mnc|colorMode|density|fontScale|fontWeightAdjustment|keyboard|layoutDirection|locale|mcc|navigation|smallestScreenSize|touchscreen|uiMode"
|
||||
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
BIN
Android/app/src/main/ic_launcher-playstore.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
125
Android/app/src/main/kotlin/sats/price/Main.kt
Normal file
@@ -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<String>, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
BIN
Android/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
Android/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
BIN
Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
BIN
Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
BIN
Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
11
Android/fastlane/Appfile
Normal file
@@ -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")
|
||||
49
Android/fastlane/Fastfile
Normal file
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
A great new app built with Skip!
|
||||
@@ -0,0 +1 @@
|
||||
A great new app built with Skip!
|
||||
1
Android/fastlane/metadata/android/en-US/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
SatsPrice
|
||||
3
Android/gradle.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx4g
|
||||
android.useAndroidX=true
|
||||
kotlin.code.style=official
|
||||
2
Android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#Sat Aug 31 09:41:39 EEST 2024
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
46
Android/settings.gradle.kts
Normal file
@@ -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
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
<dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -2,7 +2,7 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>SatsPrice.xcdatamodel</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
56
Darwin/SatsPrice.xcconfig
Normal file
@@ -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 =
|
||||
294
Darwin/SatsPrice.xcodeproj/project.pbxproj
Normal file
@@ -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 = "<group>"; };
|
||||
493609562A6B7EAE00C401E2 /* SatsPrice */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SatsPrice; path = ..; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
496EB72F2A6AE4DE00C1253A /* Skip.env */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Skip.env; path = ../Skip.env; sourceTree = "<group>"; };
|
||||
496EB72F2A6AE4DE00C1253B /* SatsPrice.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = SatsPrice.xcconfig; sourceTree = "<group>"; };
|
||||
496EB72F2A6AE4DE00C1253C /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
49F90C312A52156300F06D93 /* Entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Entitlements.plist; sourceTree = "<group>"; };
|
||||
/* 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 = "<group>";
|
||||
};
|
||||
49AB54462B066A7E007B79B2 /* SkipStone */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
499AB9082B0581F4005E8330 /* plugins */,
|
||||
);
|
||||
name = SkipStone;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
49F90C2A2A52156200F06D93 /* App */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
49F90C2B2A52156200F06D93 /* SatsPriceAppMain.swift */,
|
||||
49F90C2F2A52156300F06D93 /* Assets.xcassets */,
|
||||
49F90C312A52156300F06D93 /* Entitlements.plist */,
|
||||
4900101C2BACEA710000DE33 /* Info.plist */,
|
||||
);
|
||||
name = App;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* 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 */;
|
||||
}
|
||||
10
Darwin/Sources/SatsPriceAppMain.swift
Normal file
@@ -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 {
|
||||
}
|
||||
5
Darwin/fastlane/AppStore.xcconfig
Normal file
@@ -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 =
|
||||
8
Darwin/fastlane/Appfile
Normal file
@@ -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")
|
||||
28
Darwin/fastlane/Deliverfile
Normal file
@@ -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
|
||||
})
|
||||
32
Darwin/fastlane/Fastfile
Normal file
@@ -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
|
||||
|
||||
1
Darwin/fastlane/metadata/en-US/description.txt
Normal file
@@ -0,0 +1 @@
|
||||
A great new app built with Skip!
|
||||
1
Darwin/fastlane/metadata/en-US/keywords.txt
Normal file
@@ -0,0 +1 @@
|
||||
app,key,words
|
||||
1
Darwin/fastlane/metadata/en-US/privacy_url.txt
Normal file
@@ -0,0 +1 @@
|
||||
https://example.org/privacy/
|
||||
1
Darwin/fastlane/metadata/en-US/release_notes.txt
Normal file
@@ -0,0 +1 @@
|
||||
Bug fixes and performance improvements.
|
||||
1
Darwin/fastlane/metadata/en-US/software_url.txt
Normal file
@@ -0,0 +1 @@
|
||||
https://example.org/app/
|
||||
1
Darwin/fastlane/metadata/en-US/subtitle.txt
Normal file
@@ -0,0 +1 @@
|
||||
A new Skip app
|
||||
1
Darwin/fastlane/metadata/en-US/support_url.txt
Normal file
@@ -0,0 +1 @@
|
||||
https://example.org/support/
|
||||
1
Darwin/fastlane/metadata/en-US/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
SatsPrice
|
||||
1
Darwin/fastlane/metadata/en-US/version_whats_new.txt
Normal file
@@ -0,0 +1 @@
|
||||
New features and better performance.
|
||||
17
Darwin/fastlane/metadata/rating.json
Normal file
@@ -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
|
||||
}
|
||||
24
Package.swift
Normal file
@@ -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")]),
|
||||
]
|
||||
)
|
||||
45
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.
|
||||
|
||||
@@ -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 = "<group>"; };
|
||||
3A8E5DA62C810C9900E9E6BD /* ManualPriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualPriceFetcher.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
3AE2D3A12B83338D00DE5F31 /* SatsPrice.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = SatsPrice.xcdatamodel; sourceTree = "<group>"; };
|
||||
3AE2D3A32B83338D00DE5F31 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
3AE2D3A52B83338D00DE5F31 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
3AE2D3A72B83338D00DE5F31 /* SatsPrice.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SatsPrice.entitlements; sourceTree = "<group>"; };
|
||||
3AE2D3A92B83338D00DE5F31 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
3AE2D3BF2B83338E00DE5F31 /* SatsPriceUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsPriceUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
||||
3AE2D3D42B83E58500DE5F31 /* SatsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsViewModel.swift; sourceTree = "<group>"; };
|
||||
3AE2D3D92B83FE5F00DE5F31 /* SatsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatsViewModelTests.swift; sourceTree = "<group>"; };
|
||||
3AE2D3DE2B84597100DE5F31 /* PriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceFetcher.swift; sourceTree = "<group>"; };
|
||||
3AE2D3E02B8459D600DE5F31 /* CoinbasePriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinbasePriceFetcher.swift; sourceTree = "<group>"; };
|
||||
3AE2D3E22B8461FE00DE5F31 /* CoinGeckoPriceFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinGeckoPriceFetcher.swift; sourceTree = "<group>"; };
|
||||
3AE2D3E42B846A8600DE5F31 /* PriceSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceSource.swift; sourceTree = "<group>"; };
|
||||
3AE2D3E62B85690200DE5F31 /* PriceFetcherDelegator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceFetcherDelegator.swift; sourceTree = "<group>"; };
|
||||
/* 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 = "<group>";
|
||||
};
|
||||
3AE2D39A2B83338D00DE5F31 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3AE2D3992B83338D00DE5F31 /* SatsPrice.app */,
|
||||
3AE2D3AF2B83338E00DE5F31 /* SatsPriceTests.xctest */,
|
||||
3AE2D3B92B83338E00DE5F31 /* SatsPriceUITests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
3AE2D3A82B83338D00DE5F31 /* Preview Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3AE2D3A92B83338D00DE5F31 /* Preview Assets.xcassets */,
|
||||
);
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3AE2D3B22B83338E00DE5F31 /* SatsPriceTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3AE2D3B32B83338E00DE5F31 /* SatsPriceTests.swift */,
|
||||
3AE2D3D92B83FE5F00DE5F31 /* SatsViewModelTests.swift */,
|
||||
);
|
||||
path = SatsPriceTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3AE2D3BC2B83338E00DE5F31 /* SatsPriceUITests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3AE2D3BD2B83338E00DE5F31 /* SatsPriceUITests.swift */,
|
||||
3AE2D3BF2B83338E00DE5F31 /* SatsPriceUITestsLaunchTests.swift */,
|
||||
);
|
||||
path = SatsPriceUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
/* 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 = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
};
|
||||
/* End XCVersionGroup section */
|
||||
};
|
||||
rootObject = 3AE2D3912B83338D00DE5F31 /* Project object */;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildLocationStyle</key>
|
||||
<string>UseAppPreferences</string>
|
||||
<key>CustomBuildLocationType</key>
|
||||
<string>RelativeToDerivedData</string>
|
||||
<key>DerivedDataLocationStyle</key>
|
||||
<string>Default</string>
|
||||
<key>ShowSharedSchemesAutomaticallyEnabled</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
uuid = "A2E66E20-4DE4-4B6E-A647-48299476A936"
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
</Bucket>
|
||||
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>Demo (Playground) 1.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>Demo (Playground) 2.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
<key>Demo (Playground).xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>SatsPrice.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -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?
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1" systemVersion="11A491" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="false" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Item" representedClassName="Item" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="Item" positionX="-63" positionY="-18" width="128" height="44"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
20
Skip.env
Normal file
@@ -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
|
||||
@@ -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: {
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
15
Sources/SatsPrice/Network/PriceFetcher.swift
Normal file
@@ -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?
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
27
Sources/SatsPrice/Resources/Localizable.xcstrings
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"" : {
|
||||
|
||||
},
|
||||
"1 BTC to USD" : {
|
||||
|
||||
},
|
||||
"BTC" : {
|
||||
|
||||
},
|
||||
"Last updated: %@" : {
|
||||
|
||||
},
|
||||
"Price Source" : {
|
||||
|
||||
},
|
||||
"Sats" : {
|
||||
|
||||
},
|
||||
"USD" : {
|
||||
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
6
Sources/SatsPrice/SatsPrice.swift
Normal file
@@ -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 {
|
||||
}
|
||||
43
Sources/SatsPrice/SatsPriceApp.swift
Normal file
@@ -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
|
||||
173
Sources/SatsPrice/SatsViewModel.swift
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
3
Sources/SatsPrice/Skip/skip.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Configuration file for https://skip.tools project
|
||||
build:
|
||||
contents:
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
3
Tests/SatsPriceTests/Skip/skip.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Configuration file for https://skip.tools project
|
||||
#build:
|
||||
# contents:
|
||||