From 49182561f9c8085ab9828cd1811708b4394d0086 Mon Sep 17 00:00:00 2001 From: JayShortway <29483617+JayShortway@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:00:50 +0200 Subject: [PATCH] Adds Paywalls (#77) * Adds a new :ui module. * Renames :ui to :paywalls. Apparently :ui conflicts with compose:ui, causing compile errors. * Increases heap space to 4 GiB. * Uses the kobankat-library plugin, and sets the package to ui.revenuecatui for consistency. * Adds the correct ui dependencies. * Adds PaywallOptions. * Adds Paywall. * Adds PaywallFooter. * Minimizes dependencies. * Fixes restoring purchases on Android. * Fixes toAndroidPaywallOptions(). * Fixes the minSdk for paywalls. * The sample app can now show paywalls. * Adds footer paywall buttons. * Fixes showing footer paywalls. * Adds the RevenueCatUI pod to iosApp. * Fixes "unrecognized selector sent to class" by using the correct (PHC) pods in iosApp. * Removes useless receiver. * Updates to Kotlin 2.0.0. Can revert later if needed. * Adds Kotlin code to the Xcode workspace. * Makes platform-to-common error conversion public. Could be made internal later, with an annotation? * Adds .kotlin to .gitignore. * Adds PaywallListener. * Adds PaywallListener to PaywallOptions. * Adds shouldDisplayDismissButton to PaywallOptions. * Adds and uses UIKitPaywall. * Propagates shouldDisplayDismissButton on Android. * Deletes PaywallViewControllerDelegate. * Sets shouldDisplayDismissButton to true. * Updates some Xcode files. * Adds a back button to CustomPaywallContent. * Fixes the height of PaywallFooter by animating it. * PaywallFooter now also works without a listener set. * UIKitPaywall uses UIKitViewController instead of UIKitView. * Adds docs to PaywallListener to fix Detekt. * Updates public api dump. * Fixes api-tester. * Adds 2 run configurations that mimic CI. * Adds public api dump for paywalls. --- .gitignore | 4 +- .run/kobankat [_apiTester_assemble].run.xml | 25 +++ .run/kobankat [detektCommonMain].run.xml | 25 +++ .../kobankat/apitester/StoreProductAPI.kt | 12 -- build.gradle.kts | 1 + composeApp/build.gradle.kts | 4 +- .../kotlin/io/shortway/kobankat/sample/App.kt | 100 +++++----- .../io/shortway/kobankat/sample/LogView.kt | 41 ++++ .../io/shortway/kobankat/sample/MainScreen.kt | 181 ++++++++++++++++++ core/api/core.api | 17 +- core/api/core.klib.api | 51 +++-- core/core.podspec | 6 +- .../io/shortway/kobankat/errors.android.kt | 4 +- .../shortway/kobankat/PurchasesFactory.ios.kt | 2 +- .../kotlin/io/shortway/kobankat/errors.ios.kt | 5 +- either/api/either.klib.api | 10 +- gradle.properties | 4 +- gradle/libs.versions.toml | 4 +- iosApp/Podfile | 3 +- iosApp/Podfile.lock | 22 ++- iosApp/iosApp.xcodeproj/project.pbxproj | 29 ++- .../contents.xcworkspacedata | 20 ++ iosApp/iosApp/Info.plist | 4 +- paywalls/.gitignore | 1 + paywalls/api/paywalls.api | 57 ++++++ paywalls/api/paywalls.klib.api | 50 +++++ paywalls/build.gradle.kts | 51 +++++ paywalls/paywalls.podspec | 54 ++++++ .../kobankat/ui/revenuecatui/Paywall.kt | 10 + .../kobankat/ui/revenuecatui/PaywallFooter.kt | 20 ++ .../ui/revenuecatui/PaywallOptionsKtx.kt | 48 +++++ .../kobankat/ui/revenuecatui/Paywall.kt | 9 + .../kobankat/ui/revenuecatui/PaywallFooter.kt | 13 ++ .../ui/revenuecatui/PaywallListener.kt | 54 ++++++ .../ui/revenuecatui/PaywallOptions.kt | 86 +++++++++ .../kobankat/ui/revenuecatui/Paywall.kt | 13 ++ .../kobankat/ui/revenuecatui/PaywallFooter.kt | 66 +++++++ .../ui/revenuecatui/PaywallOptionsKtx.kt | 89 +++++++++ .../kobankat/ui/revenuecatui/UIKitPaywall.kt | 53 +++++ result/api/result.klib.api | 8 +- settings.gradle.kts | 1 + 41 files changed, 1123 insertions(+), 134 deletions(-) create mode 100644 .run/kobankat [_apiTester_assemble].run.xml create mode 100644 .run/kobankat [detektCommonMain].run.xml create mode 100644 composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/LogView.kt create mode 100644 composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/MainScreen.kt create mode 100644 paywalls/.gitignore create mode 100644 paywalls/api/paywalls.api create mode 100644 paywalls/api/paywalls.klib.api create mode 100644 paywalls/build.gradle.kts create mode 100644 paywalls/paywalls.podspec create mode 100644 paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt create mode 100644 paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt create mode 100644 paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptionsKtx.kt create mode 100644 paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt create mode 100644 paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt create mode 100644 paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallListener.kt create mode 100644 paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptions.kt create mode 100644 paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt create mode 100644 paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt create mode 100644 paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptionsKtx.kt create mode 100644 paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/UIKitPaywall.kt diff --git a/.gitignore b/.gitignore index 0e29b0d5..847f6a47 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ captures .cxx local.properties xcuserdata -Pods \ No newline at end of file +Pods +.kotlin +!.run diff --git a/.run/kobankat [_apiTester_assemble].run.xml b/.run/kobankat [_apiTester_assemble].run.xml new file mode 100644 index 00000000..d4f89ed6 --- /dev/null +++ b/.run/kobankat [_apiTester_assemble].run.xml @@ -0,0 +1,25 @@ + + + + + + + true + true + false + false + + + diff --git a/.run/kobankat [detektCommonMain].run.xml b/.run/kobankat [detektCommonMain].run.xml new file mode 100644 index 00000000..cf7b5286 --- /dev/null +++ b/.run/kobankat [detektCommonMain].run.xml @@ -0,0 +1,25 @@ + + + + + + + true + true + false + false + + + diff --git a/apiTester/src/commonMain/kotlin/io/shortway/kobankat/apitester/StoreProductAPI.kt b/apiTester/src/commonMain/kotlin/io/shortway/kobankat/apitester/StoreProductAPI.kt index 6e46b9d3..b0f43219 100644 --- a/apiTester/src/commonMain/kotlin/io/shortway/kobankat/apitester/StoreProductAPI.kt +++ b/apiTester/src/commonMain/kotlin/io/shortway/kobankat/apitester/StoreProductAPI.kt @@ -13,15 +13,11 @@ import io.shortway.kobankat.models.SubscriptionOptions import io.shortway.kobankat.models.defaultOption import io.shortway.kobankat.models.description import io.shortway.kobankat.models.discounts -import io.shortway.kobankat.models.formattedPricePerMonth import io.shortway.kobankat.models.id import io.shortway.kobankat.models.introductoryDiscount import io.shortway.kobankat.models.period import io.shortway.kobankat.models.presentedOfferingContext import io.shortway.kobankat.models.price -import io.shortway.kobankat.models.pricePerMonth -import io.shortway.kobankat.models.pricePerWeek -import io.shortway.kobankat.models.pricePerYear import io.shortway.kobankat.models.purchasingData import io.shortway.kobankat.models.subscriptionOptions import io.shortway.kobankat.models.title @@ -35,14 +31,6 @@ private class StoreProductAPI { val storeProductId: String = id val type: ProductType = type val price: Price = price - val formattedPricePerMonth: String? = formattedPricePerMonth(locale) - val formattedPricePerMonthNoLocale: String? = formattedPricePerMonth() - val pricePerWeek: Price? = pricePerWeek(locale) - val pricePerMonth: Price? = pricePerMonth(locale) - val pricePerYear: Price? = pricePerYear(locale) - val pricePerWeekNoLocale: Price? = pricePerYear() - val pricePerMonthNoLocale: Price? = pricePerMonth() - val pricePerYearNoLocale: Price? = pricePerYear() val title: String = title val description: String? = description val period: Period? = period diff --git a/build.gradle.kts b/build.gradle.kts index ca10dd92..850f4c8b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,6 +12,7 @@ plugins { alias(libs.plugins.android.application).apply(false) alias(libs.plugins.android.library).apply(false) alias(libs.plugins.jetbrains.compose).apply(false) + alias(libs.plugins.compose.compiler).apply(false) alias(libs.plugins.kotlin.multiplatform).apply(false) alias(libs.plugins.kotlin.cocoapods).apply(false) alias(libs.plugins.kotlinx.binaryCompatibilityValidator) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index c01f757d..d99dc927 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) } kotlin { @@ -45,6 +46,7 @@ kotlin { implementation(projects.result) implementation(projects.either) implementation(projects.datetime) + implementation(projects.paywalls) } androidMain.dependencies { implementation(libs.androidx.compose.ui.tooling.preview) @@ -63,7 +65,7 @@ android { defaultConfig { applicationId = "io.shortway.kobankat.sample" - minSdk = libs.versions.android.minSdk.get().toInt() + minSdk = 24 targetSdk = libs.versions.android.targetSdk.get().toInt() versionCode = 1 versionName = "1.0" diff --git a/composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/App.kt b/composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/App.kt index 89e5a216..5fcc3372 100644 --- a/composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/App.kt +++ b/composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/App.kt @@ -1,87 +1,83 @@ package io.shortway.kobankat.sample import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.unit.dp -import io.shortway.kobankat.LogLevel -import io.shortway.kobankat.PurchasesConfiguration -import io.shortway.kobankat.PurchasesFactory +import androidx.compose.ui.text.style.TextAlign +import io.shortway.kobankat.Offering +import io.shortway.kobankat.ui.revenuecatui.Paywall +import io.shortway.kobankat.ui.revenuecatui.PaywallFooter +import io.shortway.kobankat.ui.revenuecatui.PaywallOptions @Composable fun App() { - val logs = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - // In a real app, you'd probably have a class dedicated to app initialization logic. - PurchasesFactory.configure( - PurchasesConfiguration( - apiKey = "YOUR-REVENUECAT-API-KEY", - ) - ) - PurchasesFactory.logLevel = LogLevel.VERBOSE - PurchasesFactory.logHandler = SimpleLogHandler { logs.add(it) } - } - MaterialTheme { Column( modifier = Modifier .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally ) { - Text( - text = "KobanKat logs", - modifier = Modifier.padding(all = 16.dp), - style = MaterialTheme.typography.h4 - ) + var showPaywallAsFooter by remember { mutableStateOf(false) } + var paywallOffering: Offering? by remember { mutableStateOf(null) } + + if (paywallOffering == null) MainScreen( + onShowPaywallClick = { offering, footer -> + showPaywallAsFooter = footer + paywallOffering = offering + }, + modifier = Modifier.fillMaxSize() + ) else { + val options = PaywallOptions(dismissRequest = { paywallOffering = null }) { + offering = paywallOffering + shouldDisplayDismissButton = true + } + + if (showPaywallAsFooter) PaywallFooter(options) { contentPadding -> + CustomPaywallContent( + onBackClick = { paywallOffering = null }, + modifier = Modifier + .fillMaxSize() + .background(Color.Magenta) + .padding(contentPadding), + ) + } + else Paywall(options) + } - LogView( - logs = logs, - modifier = Modifier - .weight(1f) - .background(Color.LightGray), - ) } } } @Composable -private fun LogView( - logs: SnapshotStateList, +private fun CustomPaywallContent( + onBackClick: () -> Unit, modifier: Modifier = Modifier, ) { - val listState = rememberLazyListState() - LaunchedEffect(logs.size) { - listState.animateScrollToItem(index = logs.lastIndex.coerceAtLeast(0)) - } - - LazyColumn( + Column( modifier = modifier, - state = listState, - contentPadding = PaddingValues(all = 16.dp) + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, ) { - items(logs) { - Text( - text = it, - modifier = Modifier.padding(vertical = 4.dp), - fontFamily = FontFamily.Monospace, - style = MaterialTheme.typography.caption - ) + Text( + text = "Custom paywall content!", + textAlign = TextAlign.Center, + style = MaterialTheme.typography.h4, + ) + Button(onClick = onBackClick) { + Text("Go back") } } } diff --git a/composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/LogView.kt b/composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/LogView.kt new file mode 100644 index 00000000..ceceef04 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/LogView.kt @@ -0,0 +1,41 @@ +package io.shortway.kobankat.sample + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.dp + +@Composable +internal fun LogView( + logs: SnapshotStateList, + modifier: Modifier = Modifier, +) { + val listState = rememberLazyListState() + LaunchedEffect(logs.size) { + listState.animateScrollToItem(index = logs.lastIndex.coerceAtLeast(0)) + } + + LazyColumn( + modifier = modifier, + state = listState, + contentPadding = PaddingValues(all = 16.dp) + ) { + items(logs) { + Text( + text = it, + modifier = Modifier.padding(vertical = 4.dp), + fontFamily = FontFamily.Monospace, + style = MaterialTheme.typography.caption + ) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/MainScreen.kt b/composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/MainScreen.kt new file mode 100644 index 00000000..d52a2c7b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/io/shortway/kobankat/sample/MainScreen.kt @@ -0,0 +1,181 @@ +package io.shortway.kobankat.sample + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.Button +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.LocalTextStyle +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import arrow.core.Either +import io.shortway.kobankat.Offering +import io.shortway.kobankat.Offerings +import io.shortway.kobankat.PurchasesConfiguration +import io.shortway.kobankat.PurchasesFactory +import io.shortway.kobankat.all +import io.shortway.kobankat.current +import io.shortway.kobankat.either.awaitOfferingsEither +import io.shortway.kobankat.identifier + +@Composable +fun MainScreen( + onShowPaywallClick: (offering: Offering, footer: Boolean) -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier.padding(all = 16.dp) + ) { + Text( + text = "Configuration", + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.h6, + ) + + var isConfigured by remember { mutableStateOf(PurchasesFactory.isConfigured) } + var configuration by remember { mutableStateOf(Configuration(apiKey = "", userId = "")) } + ConfigurationSettings( + configuration = configuration, + onConfigurationChanged = { configuration = it }, + modifier = Modifier.fillMaxWidth() + ) + + if (!isConfigured) { + Spacer(modifier = Modifier.size(8.dp)) + Button( + onClick = { + PurchasesFactory.configure(configuration.toPurchasesConfiguration()) + isConfigured = PurchasesFactory.isConfigured + }, + modifier = Modifier.align(Alignment.CenterHorizontally), + enabled = configuration.userId.isNotBlank() && configuration.apiKey.isNotBlank(), + ) { + Text("Configure") + } + } else { + Spacer(modifier = Modifier.size(16.dp)) + Text( + text = "Paywalls", + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.h6, + ) + + PaywallsSection( + onShowPaywallClick = onShowPaywallClick, + modifier = Modifier.fillMaxWidth(), + ) + } + } +} + +@Composable +private fun ConfigurationSettings( + configuration: Configuration, + onConfigurationChanged: (Configuration) -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier) { + Text( + text = "API key", + modifier = Modifier.fillMaxWidth() + ) + TextField( + value = configuration.apiKey, + onValueChange = { onConfigurationChanged(configuration.copy(apiKey = it)) }, + modifier = Modifier.fillMaxWidth(), + textStyle = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace) + ) + Text( + text = "user ID", + modifier = Modifier.fillMaxWidth() + ) + TextField( + value = configuration.userId, + onValueChange = { onConfigurationChanged(configuration.copy(userId = it)) }, + modifier = Modifier.fillMaxWidth(), + textStyle = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace) + ) + } +} + +@Composable +private fun PaywallsSection( + onShowPaywallClick: (offering: Offering, footer: Boolean) -> Unit, + modifier: Modifier = Modifier +) { + var offeringsState: OfferingsState by remember { mutableStateOf(OfferingsState.Loading) } + LaunchedEffect(Unit) { + offeringsState = + when (val offerings = PurchasesFactory.sharedInstance.awaitOfferingsEither()) { + is Either.Left -> OfferingsState.Error + is Either.Right -> OfferingsState.Loaded(offerings.value) + } + } + + Column( + modifier = modifier, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + + when (val currentOfferingsState = offeringsState) { + is OfferingsState.Loading -> CircularProgressIndicator() + is OfferingsState.Error -> Text("Failed to get offerings") + is OfferingsState.Loaded -> { + + currentOfferingsState.offerings.all.forEach { (id, offering) -> + val isCurrent = + id == currentOfferingsState.offerings.current?.identifier + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = "${if (isCurrent) "\uD83D\uDC49" else ""} $id", + modifier = Modifier.weight(1f), + ) + Button(onClick = { onShowPaywallClick(offering, true) }) { + Text("Footer") + } + Spacer(modifier = Modifier.size(4.dp)) + Button(onClick = { onShowPaywallClick(offering, false) }) { + Text("Fullscreen") + } + } + } + } + } + } +} + +private data class Configuration(val apiKey: String, val userId: String) { + fun toPurchasesConfiguration(builder: PurchasesConfiguration.Builder.() -> Unit = { }) = + PurchasesConfiguration(apiKey) { + appUserId = userId + apply(builder) + } +} + +private sealed interface OfferingsState { + data object Loading : OfferingsState + data class Loaded(val offerings: Offerings) : OfferingsState + data object Error : OfferingsState +} diff --git a/core/api/core.api b/core/api/core.api index cdf074f4..eb96b58e 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -63,6 +63,10 @@ public final class io/shortway/kobankat/EntitlementVerificationMode : java/lang/ public static fun values ()[Lio/shortway/kobankat/EntitlementVerificationMode; } +public final class io/shortway/kobankat/Errors_androidKt { + public static final fun toPurchasesError (Lcom/revenuecat/purchases/PurchasesError;)Lio/shortway/kobankat/PurchasesError; +} + public final class io/shortway/kobankat/OfferingKt { public static final fun get (Lcom/revenuecat/purchases/Offering;Ljava/lang/String;)Lcom/revenuecat/purchases/Package; public static final fun getMetadataString (Lcom/revenuecat/purchases/Offering;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; @@ -240,7 +244,6 @@ public final class io/shortway/kobankat/PurchasesFactory { public static synthetic fun canMakePayments$default (Ljava/util/List;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public final fun configure (Lio/shortway/kobankat/PurchasesConfiguration;)Lcom/revenuecat/purchases/Purchases; public static final fun getForceUniversalAppStore ()Z - public static final fun getFrameworkVersion ()Ljava/lang/String; public static final fun getLogHandler ()Lcom/revenuecat/purchases/LogHandler; public static final fun getLogLevel ()Lcom/revenuecat/purchases/LogLevel; public static final fun getProxyURL ()Ljava/lang/String; @@ -254,6 +257,10 @@ public final class io/shortway/kobankat/PurchasesFactory { public static final fun setSimulatesAskToBuyInSandbox (Z)V } +public final class io/shortway/kobankat/PurchasesFactoryKt { + public static final fun getFrameworkVersion (Lio/shortway/kobankat/PurchasesFactory;)Ljava/lang/String; +} + public final class io/shortway/kobankat/PurchasesTransactionException : io/shortway/kobankat/PurchasesException { public fun (Lio/shortway/kobankat/PurchasesError;Z)V public final fun getUserCancelled ()Z @@ -484,8 +491,6 @@ public final class io/shortway/kobankat/models/StoreProductDiscount_androidKt { } public final class io/shortway/kobankat/models/StoreProduct_androidKt { - public static final fun formattedPricePerMonth (Lio/shortway/kobankat/models/StoreProduct;Lio/shortway/kobankat/i18n/Locale;)Ljava/lang/String; - public static synthetic fun formattedPricePerMonth$default (Lio/shortway/kobankat/models/StoreProduct;Lio/shortway/kobankat/i18n/Locale;ILjava/lang/Object;)Ljava/lang/String; public static final fun getDefaultOption (Lio/shortway/kobankat/models/StoreProduct;)Lcom/revenuecat/purchases/models/SubscriptionOption; public static final fun getDescription (Lio/shortway/kobankat/models/StoreProduct;)Ljava/lang/String; public static final fun getDiscounts (Lio/shortway/kobankat/models/StoreProduct;)Ljava/util/List; @@ -498,12 +503,6 @@ public final class io/shortway/kobankat/models/StoreProduct_androidKt { public static final fun getSubscriptionOptions (Lio/shortway/kobankat/models/StoreProduct;)Lcom/revenuecat/purchases/models/SubscriptionOptions; public static final fun getTitle (Lio/shortway/kobankat/models/StoreProduct;)Ljava/lang/String; public static final fun getType (Lio/shortway/kobankat/models/StoreProduct;)Lcom/revenuecat/purchases/ProductType; - public static final fun pricePerMonth (Lio/shortway/kobankat/models/StoreProduct;Lio/shortway/kobankat/i18n/Locale;)Lcom/revenuecat/purchases/models/Price; - public static synthetic fun pricePerMonth$default (Lio/shortway/kobankat/models/StoreProduct;Lio/shortway/kobankat/i18n/Locale;ILjava/lang/Object;)Lcom/revenuecat/purchases/models/Price; - public static final fun pricePerWeek (Lio/shortway/kobankat/models/StoreProduct;Lio/shortway/kobankat/i18n/Locale;)Lcom/revenuecat/purchases/models/Price; - public static synthetic fun pricePerWeek$default (Lio/shortway/kobankat/models/StoreProduct;Lio/shortway/kobankat/i18n/Locale;ILjava/lang/Object;)Lcom/revenuecat/purchases/models/Price; - public static final fun pricePerYear (Lio/shortway/kobankat/models/StoreProduct;Lio/shortway/kobankat/i18n/Locale;)Lcom/revenuecat/purchases/models/Price; - public static synthetic fun pricePerYear$default (Lio/shortway/kobankat/models/StoreProduct;Lio/shortway/kobankat/i18n/Locale;ILjava/lang/Object;)Lcom/revenuecat/purchases/models/Price; } public final class io/shortway/kobankat/models/StoreTransaction_androidKt { diff --git a/core/api/core.klib.api b/core/api/core.klib.api index 74a370f7..83dc798a 100644 --- a/core/api/core.klib.api +++ b/core/api/core.klib.api @@ -54,7 +54,7 @@ final class io.shortway.kobankat.ktx/SuccessfulLogin { // io.shortway.kobankat.k constructor (cocoapods.PurchasesHybridCommon/RCCustomerInfo, kotlin/Boolean) // io.shortway.kobankat.ktx/SuccessfulLogin.|(cocoapods.PurchasesHybridCommon.RCCustomerInfo;kotlin.Boolean){}[0] final fun component1(): cocoapods.PurchasesHybridCommon/RCCustomerInfo // io.shortway.kobankat.ktx/SuccessfulLogin.component1|component1(){}[0] final fun component2(): kotlin/Boolean // io.shortway.kobankat.ktx/SuccessfulLogin.component2|component2(){}[0] - final fun copy(cocoapods.PurchasesHybridCommon/RCCustomerInfo =..., kotlin/Boolean =...): io.shortway.kobankat.ktx/SuccessfulLogin // io.shortway.kobankat.ktx/SuccessfulLogin.copy|copy(cocoapods.PurchasesHybridCommon.RCCustomerInfo;kotlin.Boolean){}[0] + final fun copy(cocoapods.PurchasesHybridCommon/RCCustomerInfo = ..., kotlin/Boolean = ...): io.shortway.kobankat.ktx/SuccessfulLogin // io.shortway.kobankat.ktx/SuccessfulLogin.copy|copy(cocoapods.PurchasesHybridCommon.RCCustomerInfo;kotlin.Boolean){}[0] final fun equals(kotlin/Any?): kotlin/Boolean // io.shortway.kobankat.ktx/SuccessfulLogin.equals|equals(kotlin.Any?){}[0] final fun hashCode(): kotlin/Int // io.shortway.kobankat.ktx/SuccessfulLogin.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // io.shortway.kobankat.ktx/SuccessfulLogin.toString|toString(){}[0] @@ -67,7 +67,7 @@ final class io.shortway.kobankat.ktx/SuccessfulPurchase { // io.shortway.kobanka constructor (cocoapods.PurchasesHybridCommon/RCStoreTransaction, cocoapods.PurchasesHybridCommon/RCCustomerInfo) // io.shortway.kobankat.ktx/SuccessfulPurchase.|(cocoapods.PurchasesHybridCommon.RCStoreTransaction;cocoapods.PurchasesHybridCommon.RCCustomerInfo){}[0] final fun component1(): cocoapods.PurchasesHybridCommon/RCStoreTransaction // io.shortway.kobankat.ktx/SuccessfulPurchase.component1|component1(){}[0] final fun component2(): cocoapods.PurchasesHybridCommon/RCCustomerInfo // io.shortway.kobankat.ktx/SuccessfulPurchase.component2|component2(){}[0] - final fun copy(cocoapods.PurchasesHybridCommon/RCStoreTransaction =..., cocoapods.PurchasesHybridCommon/RCCustomerInfo =...): io.shortway.kobankat.ktx/SuccessfulPurchase // io.shortway.kobankat.ktx/SuccessfulPurchase.copy|copy(cocoapods.PurchasesHybridCommon.RCStoreTransaction;cocoapods.PurchasesHybridCommon.RCCustomerInfo){}[0] + final fun copy(cocoapods.PurchasesHybridCommon/RCStoreTransaction = ..., cocoapods.PurchasesHybridCommon/RCCustomerInfo = ...): io.shortway.kobankat.ktx/SuccessfulPurchase // io.shortway.kobankat.ktx/SuccessfulPurchase.copy|copy(cocoapods.PurchasesHybridCommon.RCStoreTransaction;cocoapods.PurchasesHybridCommon.RCCustomerInfo){}[0] final fun equals(kotlin/Any?): kotlin/Boolean // io.shortway.kobankat.ktx/SuccessfulPurchase.equals|equals(kotlin.Any?){}[0] final fun hashCode(): kotlin/Int // io.shortway.kobankat.ktx/SuccessfulPurchase.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // io.shortway.kobankat.ktx/SuccessfulPurchase.toString|toString(){}[0] @@ -81,7 +81,7 @@ final class io.shortway.kobankat.models/Price { // io.shortway.kobankat.models/P final fun component1(): kotlin/String // io.shortway.kobankat.models/Price.component1|component1(){}[0] final fun component2(): kotlin/Long // io.shortway.kobankat.models/Price.component2|component2(){}[0] final fun component3(): kotlin/String // io.shortway.kobankat.models/Price.component3|component3(){}[0] - final fun copy(kotlin/String =..., kotlin/Long =..., kotlin/String =...): io.shortway.kobankat.models/Price // io.shortway.kobankat.models/Price.copy|copy(kotlin.String;kotlin.Long;kotlin.String){}[0] + final fun copy(kotlin/String = ..., kotlin/Long = ..., kotlin/String = ...): io.shortway.kobankat.models/Price // io.shortway.kobankat.models/Price.copy|copy(kotlin.String;kotlin.Long;kotlin.String){}[0] final fun equals(kotlin/Any?): kotlin/Boolean // io.shortway.kobankat.models/Price.equals|equals(kotlin.Any?){}[0] final fun hashCode(): kotlin/Int // io.shortway.kobankat.models/Price.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // io.shortway.kobankat.models/Price.toString|toString(){}[0] @@ -99,7 +99,7 @@ final class io.shortway.kobankat.models/PricingPhase { // io.shortway.kobankat.m final fun component3(): kotlin/Int? // io.shortway.kobankat.models/PricingPhase.component3|component3(){}[0] final fun component4(): io.shortway.kobankat.models/Price // io.shortway.kobankat.models/PricingPhase.component4|component4(){}[0] final fun component5(): io.shortway.kobankat.models/OfferPaymentMode? // io.shortway.kobankat.models/PricingPhase.component5|component5(){}[0] - final fun copy(cocoapods.PurchasesHybridCommon/RCSubscriptionPeriod =..., io.shortway.kobankat.models/RecurrenceMode =..., kotlin/Int? =..., io.shortway.kobankat.models/Price =..., io.shortway.kobankat.models/OfferPaymentMode? =...): io.shortway.kobankat.models/PricingPhase // io.shortway.kobankat.models/PricingPhase.copy|copy(cocoapods.PurchasesHybridCommon.RCSubscriptionPeriod;io.shortway.kobankat.models.RecurrenceMode;kotlin.Int?;io.shortway.kobankat.models.Price;io.shortway.kobankat.models.OfferPaymentMode?){}[0] + final fun copy(cocoapods.PurchasesHybridCommon/RCSubscriptionPeriod = ..., io.shortway.kobankat.models/RecurrenceMode = ..., kotlin/Int? = ..., io.shortway.kobankat.models/Price = ..., io.shortway.kobankat.models/OfferPaymentMode? = ...): io.shortway.kobankat.models/PricingPhase // io.shortway.kobankat.models/PricingPhase.copy|copy(cocoapods.PurchasesHybridCommon.RCSubscriptionPeriod;io.shortway.kobankat.models.RecurrenceMode;kotlin.Int?;io.shortway.kobankat.models.Price;io.shortway.kobankat.models.OfferPaymentMode?){}[0] final fun equals(kotlin/Any?): kotlin/Boolean // io.shortway.kobankat.models/PricingPhase.equals|equals(kotlin.Any?){}[0] final fun hashCode(): kotlin/Int // io.shortway.kobankat.models/PricingPhase.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // io.shortway.kobankat.models/PricingPhase.toString|toString(){}[0] @@ -126,9 +126,9 @@ final class io.shortway.kobankat.models/SubscriptionOptions { // io.shortway.kob final fun (): io.shortway.kobankat.models/SubscriptionOption? // io.shortway.kobankat.models/SubscriptionOptions.introOffer.|(){}[0] } final class io.shortway.kobankat/DangerousSettings { // io.shortway.kobankat/DangerousSettings|null[0] - constructor (kotlin/Boolean =...) // io.shortway.kobankat/DangerousSettings.|(kotlin.Boolean){}[0] + constructor (kotlin/Boolean = ...) // io.shortway.kobankat/DangerousSettings.|(kotlin.Boolean){}[0] final fun component1(): kotlin/Boolean // io.shortway.kobankat/DangerousSettings.component1|component1(){}[0] - final fun copy(kotlin/Boolean =...): io.shortway.kobankat/DangerousSettings // io.shortway.kobankat/DangerousSettings.copy|copy(kotlin.Boolean){}[0] + final fun copy(kotlin/Boolean = ...): io.shortway.kobankat/DangerousSettings // io.shortway.kobankat/DangerousSettings.copy|copy(kotlin.Boolean){}[0] final fun equals(kotlin/Any?): kotlin/Boolean // io.shortway.kobankat/DangerousSettings.equals|equals(kotlin.Any?){}[0] final fun hashCode(): kotlin/Int // io.shortway.kobankat/DangerousSettings.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // io.shortway.kobankat/DangerousSettings.toString|toString(){}[0] @@ -197,7 +197,7 @@ final class io.shortway.kobankat/PurchasesConfiguration { // io.shortway.kobanka final fun (): io.shortway.kobankat/EntitlementVerificationMode // io.shortway.kobankat/PurchasesConfiguration.verificationMode.|(){}[0] } final class io.shortway.kobankat/PurchasesError { // io.shortway.kobankat/PurchasesError|null[0] - constructor (io.shortway.kobankat/PurchasesErrorCode, kotlin/String? =...) // io.shortway.kobankat/PurchasesError.|(io.shortway.kobankat.PurchasesErrorCode;kotlin.String?){}[0] + constructor (io.shortway.kobankat/PurchasesErrorCode, kotlin/String? = ...) // io.shortway.kobankat/PurchasesError.|(io.shortway.kobankat.PurchasesErrorCode;kotlin.String?){}[0] final fun toString(): kotlin/String // io.shortway.kobankat/PurchasesError.toString|toString(){}[0] final val code // io.shortway.kobankat/PurchasesError.code|{}code[0] final fun (): io.shortway.kobankat/PurchasesErrorCode // io.shortway.kobankat/PurchasesError.code.|(){}[0] @@ -402,7 +402,7 @@ final enum class io.shortway.kobankat/PurchasesErrorCode : kotlin/Enum // io.shortway.kobankat/PurchasesErrorCode.values|values#static(){}[0] final val code // io.shortway.kobankat/PurchasesErrorCode.code|{}code[0] final fun (): kotlin/Int // io.shortway.kobankat/PurchasesErrorCode.code.|(){}[0] - final val description // io.shortway.kobankat/PurchasesErrorCode.description|(){}[0] + final val description // io.shortway.kobankat/PurchasesErrorCode.description|{}description[0] final fun (): kotlin/String // io.shortway.kobankat/PurchasesErrorCode.description.|(){}[0] final val entries // io.shortway.kobankat/PurchasesErrorCode.entries|#static{}entries[0] final fun (): kotlin.enums/EnumEntries // io.shortway.kobankat/PurchasesErrorCode.entries.|#static(){}[0] @@ -441,7 +441,7 @@ final fun (cocoapods.PurchasesHybridCommon/RCOfferings).io.shortway.kobankat/get final fun (cocoapods.PurchasesHybridCommon/RCOfferings).io.shortway.kobankat/getOffering(kotlin/String): cocoapods.PurchasesHybridCommon/RCOffering? // io.shortway.kobankat/getOffering|getOffering@cocoapods.PurchasesHybridCommon.RCOfferings(kotlin.String){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/close() // io.shortway.kobankat/close|close@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/collectDeviceIdentifiers() // io.shortway.kobankat/collectDeviceIdentifiers|collectDeviceIdentifiers@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] -final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/getCustomerInfo(io.shortway.kobankat/CacheFetchPolicy =..., kotlin/Function1, kotlin/Function1) // io.shortway.kobankat/getCustomerInfo|getCustomerInfo@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.CacheFetchPolicy;kotlin.Function1;kotlin.Function1){}[0] +final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/getCustomerInfo(io.shortway.kobankat/CacheFetchPolicy = ..., kotlin/Function1, kotlin/Function1) // io.shortway.kobankat/getCustomerInfo|getCustomerInfo@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.CacheFetchPolicy;kotlin.Function1;kotlin.Function1){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/getOfferings(kotlin/Function1, kotlin/Function1) // io.shortway.kobankat/getOfferings|getOfferings@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.Function1;kotlin.Function1){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/getProducts(kotlin.collections/List, kotlin/Function1, kotlin/Function1, kotlin/Unit>) // io.shortway.kobankat/getProducts|getProducts@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.collections.List;kotlin.Function1;kotlin.Function1,kotlin.Unit>){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/getPromotionalOffer(cocoapods.PurchasesHybridCommon/RCStoreProductDiscount, cocoapods.PurchasesHybridCommon/RCStoreProduct, kotlin/Function1, kotlin/Function1) // io.shortway.kobankat/getPromotionalOffer|getPromotionalOffer@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProductDiscount;cocoapods.PurchasesHybridCommon.RCStoreProduct;kotlin.Function1;kotlin.Function1){}[0] @@ -449,10 +449,10 @@ final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/inv final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/logIn(kotlin/String, kotlin/Function1, kotlin/Function2) // io.shortway.kobankat/logIn|logIn@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.String;kotlin.Function1;kotlin.Function2){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/logOut(kotlin/Function1, kotlin/Function1) // io.shortway.kobankat/logOut|logOut@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.Function1;kotlin.Function1){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/purchase(cocoapods.PurchasesHybridCommon/RCPackage, cocoapods.PurchasesHybridCommon/RCPromotionalOffer, kotlin/Function2, kotlin/Function2) // io.shortway.kobankat/purchase|purchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;cocoapods.PurchasesHybridCommon.RCPromotionalOffer;kotlin.Function2;kotlin.Function2){}[0] -final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/purchase(cocoapods.PurchasesHybridCommon/RCPackage, kotlin/Function2, kotlin/Function2, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...) // io.shortway.kobankat/purchase|purchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;kotlin.Function2;kotlin.Function2;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/purchase(cocoapods.PurchasesHybridCommon/RCPackage, kotlin/Function2, kotlin/Function2, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...) // io.shortway.kobankat/purchase|purchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;kotlin.Function2;kotlin.Function2;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/purchase(cocoapods.PurchasesHybridCommon/RCStoreProduct, cocoapods.PurchasesHybridCommon/RCPromotionalOffer, kotlin/Function2, kotlin/Function2) // io.shortway.kobankat/purchase|purchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;cocoapods.PurchasesHybridCommon.RCPromotionalOffer;kotlin.Function2;kotlin.Function2){}[0] -final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/purchase(cocoapods.PurchasesHybridCommon/RCStoreProduct, kotlin/Function2, kotlin/Function2, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...) // io.shortway.kobankat/purchase|purchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;kotlin.Function2;kotlin.Function2;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] -final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/purchase(io.shortway.kobankat.models/SubscriptionOption, kotlin/Function2, kotlin/Function2, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...) // io.shortway.kobankat/purchase|purchase@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.models.SubscriptionOption;kotlin.Function2;kotlin.Function2;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/purchase(cocoapods.PurchasesHybridCommon/RCStoreProduct, kotlin/Function2, kotlin/Function2, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...) // io.shortway.kobankat/purchase|purchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;kotlin.Function2;kotlin.Function2;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/purchase(io.shortway.kobankat.models/SubscriptionOption, kotlin/Function2, kotlin/Function2, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...) // io.shortway.kobankat/purchase|purchase@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.models.SubscriptionOption;kotlin.Function2;kotlin.Function2;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/restorePurchases(kotlin/Function1, kotlin/Function1) // io.shortway.kobankat/restorePurchases|restorePurchases@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.Function1;kotlin.Function1){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/setAd(kotlin/String?) // io.shortway.kobankat/setAd|setAd@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.String?){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/setAdGroup(kotlin/String?) // io.shortway.kobankat/setAdGroup|setAdGroup@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.String?){}[0] @@ -475,24 +475,19 @@ final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/set final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/setOnesignalUserID(kotlin/String?) // io.shortway.kobankat/setOnesignalUserID|setOnesignalUserID@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.String?){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/setPhoneNumber(kotlin/String?) // io.shortway.kobankat/setPhoneNumber|setPhoneNumber@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.String?){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/setPushToken(kotlin/String?) // io.shortway.kobankat/setPushToken|setPushToken@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.String?){}[0] -final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/showInAppMessagesIfNeeded(kotlin.collections/List =...) // io.shortway.kobankat/showInAppMessagesIfNeeded|showInAppMessagesIfNeeded@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.collections.List){}[0] +final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/showInAppMessagesIfNeeded(kotlin.collections/List = ...) // io.shortway.kobankat/showInAppMessagesIfNeeded|showInAppMessagesIfNeeded@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.collections.List){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/syncAttributesAndOfferingsIfNeeded(kotlin/Function1, kotlin/Function1) // io.shortway.kobankat/syncAttributesAndOfferingsIfNeeded|syncAttributesAndOfferingsIfNeeded@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.Function1;kotlin.Function1){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/syncObserverModeAmazonPurchase(kotlin/String, kotlin/String, kotlin/String, kotlin/String?, kotlin/Double?) // io.shortway.kobankat/syncObserverModeAmazonPurchase|syncObserverModeAmazonPurchase@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.String;kotlin.String;kotlin.String;kotlin.String?;kotlin.Double?){}[0] final fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat/syncPurchases(kotlin/Function1, kotlin/Function1) // io.shortway.kobankat/syncPurchases|syncPurchases@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.Function1;kotlin.Function1){}[0] -final fun (cocoapods.PurchasesHybridCommon/RCStoreProduct).io.shortway.kobankat.models/formattedPricePerMonth(io.shortway.kobankat.i18n/Locale =...): kotlin/String? // io.shortway.kobankat.models/formattedPricePerMonth|formattedPricePerMonth@cocoapods.PurchasesHybridCommon.RCStoreProduct(io.shortway.kobankat.i18n.Locale){}[0] -final fun (cocoapods.PurchasesHybridCommon/RCStoreProduct).io.shortway.kobankat.models/pricePerMonth(io.shortway.kobankat.i18n/Locale =...): io.shortway.kobankat.models/Price? // io.shortway.kobankat.models/pricePerMonth|pricePerMonth@cocoapods.PurchasesHybridCommon.RCStoreProduct(io.shortway.kobankat.i18n.Locale){}[0] -final fun (cocoapods.PurchasesHybridCommon/RCStoreProduct).io.shortway.kobankat.models/pricePerWeek(io.shortway.kobankat.i18n/Locale =...): io.shortway.kobankat.models/Price? // io.shortway.kobankat.models/pricePerWeek|pricePerWeek@cocoapods.PurchasesHybridCommon.RCStoreProduct(io.shortway.kobankat.i18n.Locale){}[0] -final fun (cocoapods.PurchasesHybridCommon/RCStoreProduct).io.shortway.kobankat.models/pricePerYear(io.shortway.kobankat.i18n/Locale =...): io.shortway.kobankat.models/Price? // io.shortway.kobankat.models/pricePerYear|pricePerYear@cocoapods.PurchasesHybridCommon.RCStoreProduct(io.shortway.kobankat.i18n.Locale){}[0] final fun (cocoapods.PurchasesHybridCommon/RCStoreProductDiscount).io.shortway.kobankat.models/price(cocoapods.PurchasesHybridCommon/RCStoreProduct): io.shortway.kobankat.models/Price // io.shortway.kobankat.models/price|price@cocoapods.PurchasesHybridCommon.RCStoreProductDiscount(cocoapods.PurchasesHybridCommon.RCStoreProduct){}[0] final fun (io.shortway.kobankat.i18n/Locale).io.shortway.kobankat.i18n/toNsLocale(): platform.Foundation/NSLocale // io.shortway.kobankat.i18n/toNsLocale|toNsLocale@io.shortway.kobankat.i18n.Locale(){}[0] -final fun (io.shortway.kobankat/PurchasesFactory).io.shortway.kobankat/configure(kotlin/String, kotlin/Function1 =...): cocoapods.PurchasesHybridCommon/RCPurchases // io.shortway.kobankat/configure|configure@io.shortway.kobankat.PurchasesFactory(kotlin.String;kotlin.Function1){}[0] +final fun (io.shortway.kobankat/PurchasesFactory).io.shortway.kobankat/configure(kotlin/String, kotlin/Function1 = ...): cocoapods.PurchasesHybridCommon/RCPurchases // io.shortway.kobankat/configure|configure@io.shortway.kobankat.PurchasesFactory(kotlin.String;kotlin.Function1){}[0] +final fun (platform.Foundation/NSError).io.shortway.kobankat/toPurchasesErrorOrThrow(): io.shortway.kobankat/PurchasesError // io.shortway.kobankat/toPurchasesErrorOrThrow|toPurchasesErrorOrThrow@platform.Foundation.NSError(){}[0] final fun (platform.Foundation/NSLocale).io.shortway.kobankat.i18n/toLocale(): io.shortway.kobankat.i18n/Locale // io.shortway.kobankat.i18n/toLocale|toLocale@platform.Foundation.NSLocale(){}[0] -final fun io.shortway.kobankat/PurchasesConfiguration(kotlin/String, kotlin/Function1 =...): io.shortway.kobankat/PurchasesConfiguration // io.shortway.kobankat/PurchasesConfiguration|PurchasesConfiguration(kotlin.String;kotlin.Function1){}[0] +final fun io.shortway.kobankat/PurchasesConfiguration(kotlin/String, kotlin/Function1 = ...): io.shortway.kobankat/PurchasesConfiguration // io.shortway.kobankat/PurchasesConfiguration|PurchasesConfiguration(kotlin.String;kotlin.Function1){}[0] final object io.shortway.kobankat/PurchasesFactory { // io.shortway.kobankat/PurchasesFactory|null[0] - final fun canMakePayments(kotlin.collections/List =..., kotlin/Function1) // io.shortway.kobankat/PurchasesFactory.canMakePayments|canMakePayments(kotlin.collections.List;kotlin.Function1){}[0] + final fun canMakePayments(kotlin.collections/List = ..., kotlin/Function1) // io.shortway.kobankat/PurchasesFactory.canMakePayments|canMakePayments(kotlin.collections.List;kotlin.Function1){}[0] final fun configure(io.shortway.kobankat/PurchasesConfiguration): cocoapods.PurchasesHybridCommon/RCPurchases // io.shortway.kobankat/PurchasesFactory.configure|configure(io.shortway.kobankat.PurchasesConfiguration){}[0] - final val frameworkVersion // io.shortway.kobankat/PurchasesFactory.frameworkVersion|{}frameworkVersion[0] - final fun (): kotlin/String // io.shortway.kobankat/PurchasesFactory.frameworkVersion.|(){}[0] final val isConfigured // io.shortway.kobankat/PurchasesFactory.isConfigured|{}isConfigured[0] final fun (): kotlin/Boolean // io.shortway.kobankat/PurchasesFactory.isConfigured.|(){}[0] final val sharedInstance // io.shortway.kobankat/PurchasesFactory.sharedInstance|{}sharedInstance[0] @@ -513,17 +508,17 @@ final object io.shortway.kobankat/PurchasesFactory { // io.shortway.kobankat/Pur final fun (): kotlin/Boolean // io.shortway.kobankat/PurchasesFactory.simulatesAskToBuyInSandbox.|(){}[0] final fun (kotlin/Boolean) // io.shortway.kobankat/PurchasesFactory.simulatesAskToBuyInSandbox.|(kotlin.Boolean){}[0] } -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitCustomerInfo(io.shortway.kobankat/CacheFetchPolicy =...): cocoapods.PurchasesHybridCommon/RCCustomerInfo // io.shortway.kobankat.ktx/awaitCustomerInfo|awaitCustomerInfo@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.CacheFetchPolicy){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitCustomerInfo(io.shortway.kobankat/CacheFetchPolicy = ...): cocoapods.PurchasesHybridCommon/RCCustomerInfo // io.shortway.kobankat.ktx/awaitCustomerInfo|awaitCustomerInfo@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.CacheFetchPolicy){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitGetProducts(kotlin.collections/List): kotlin.collections/List // io.shortway.kobankat.ktx/awaitGetProducts|awaitGetProducts@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.collections.List){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitLogIn(kotlin/String): io.shortway.kobankat.ktx/SuccessfulLogin // io.shortway.kobankat.ktx/awaitLogIn|awaitLogIn@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.String){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitLogOut(): cocoapods.PurchasesHybridCommon/RCCustomerInfo // io.shortway.kobankat.ktx/awaitLogOut|awaitLogOut@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitOfferings(): cocoapods.PurchasesHybridCommon/RCOfferings // io.shortway.kobankat.ktx/awaitOfferings|awaitOfferings@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitPromotionalOffer(cocoapods.PurchasesHybridCommon/RCStoreProductDiscount, cocoapods.PurchasesHybridCommon/RCStoreProduct): cocoapods.PurchasesHybridCommon/RCPromotionalOffer // io.shortway.kobankat.ktx/awaitPromotionalOffer|awaitPromotionalOffer@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProductDiscount;cocoapods.PurchasesHybridCommon.RCStoreProduct){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitPurchase(cocoapods.PurchasesHybridCommon/RCPackage, cocoapods.PurchasesHybridCommon/RCPromotionalOffer): io.shortway.kobankat.ktx/SuccessfulPurchase // io.shortway.kobankat.ktx/awaitPurchase|awaitPurchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;cocoapods.PurchasesHybridCommon.RCPromotionalOffer){}[0] -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitPurchase(cocoapods.PurchasesHybridCommon/RCPackage, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...): io.shortway.kobankat.ktx/SuccessfulPurchase // io.shortway.kobankat.ktx/awaitPurchase|awaitPurchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitPurchase(cocoapods.PurchasesHybridCommon/RCPackage, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...): io.shortway.kobankat.ktx/SuccessfulPurchase // io.shortway.kobankat.ktx/awaitPurchase|awaitPurchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitPurchase(cocoapods.PurchasesHybridCommon/RCStoreProduct, cocoapods.PurchasesHybridCommon/RCPromotionalOffer): io.shortway.kobankat.ktx/SuccessfulPurchase // io.shortway.kobankat.ktx/awaitPurchase|awaitPurchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;cocoapods.PurchasesHybridCommon.RCPromotionalOffer){}[0] -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitPurchase(cocoapods.PurchasesHybridCommon/RCStoreProduct, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...): io.shortway.kobankat.ktx/SuccessfulPurchase // io.shortway.kobankat.ktx/awaitPurchase|awaitPurchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitPurchase(io.shortway.kobankat.models/SubscriptionOption, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...): io.shortway.kobankat.ktx/SuccessfulPurchase // io.shortway.kobankat.ktx/awaitPurchase|awaitPurchase@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.models.SubscriptionOption;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitPurchase(cocoapods.PurchasesHybridCommon/RCStoreProduct, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...): io.shortway.kobankat.ktx/SuccessfulPurchase // io.shortway.kobankat.ktx/awaitPurchase|awaitPurchase@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitPurchase(io.shortway.kobankat.models/SubscriptionOption, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...): io.shortway.kobankat.ktx/SuccessfulPurchase // io.shortway.kobankat.ktx/awaitPurchase|awaitPurchase@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.models.SubscriptionOption;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitRestore(): cocoapods.PurchasesHybridCommon/RCCustomerInfo // io.shortway.kobankat.ktx/awaitRestore|awaitRestore@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitSyncAttributesAndOfferingsIfNeeded(): cocoapods.PurchasesHybridCommon/RCOfferings // io.shortway.kobankat.ktx/awaitSyncAttributesAndOfferingsIfNeeded|awaitSyncAttributesAndOfferingsIfNeeded@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.ktx/awaitSyncPurchases(): cocoapods.PurchasesHybridCommon/RCCustomerInfo // io.shortway.kobankat.ktx/awaitSyncPurchases|awaitSyncPurchases@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] @@ -539,7 +534,7 @@ final val io.shortway.kobankat.models/discounts // io.shortway.kobankat.models/d final fun (cocoapods.PurchasesHybridCommon/RCStoreProduct).(): kotlin.collections/List // io.shortway.kobankat.models/discounts.|@cocoapods.PurchasesHybridCommon.RCStoreProduct(){}[0] final val io.shortway.kobankat.models/freePhase // io.shortway.kobankat.models/freePhase|@io.shortway.kobankat.models.SubscriptionOption{}freePhase[0] final fun (io.shortway.kobankat.models/SubscriptionOption).(): io.shortway.kobankat.models/PricingPhase? // io.shortway.kobankat.models/freePhase.|@io.shortway.kobankat.models.SubscriptionOption(){}[0] -final val io.shortway.kobankat.models/fullPricePhase // io.shortway.kobankat.models/fullPricePhase|@io.shortway.kobankat.models.SubscriptionOption(){}[0] +final val io.shortway.kobankat.models/fullPricePhase // io.shortway.kobankat.models/fullPricePhase|@io.shortway.kobankat.models.SubscriptionOption{}fullPricePhase[0] final fun (io.shortway.kobankat.models/SubscriptionOption).(): io.shortway.kobankat.models/PricingPhase? // io.shortway.kobankat.models/fullPricePhase.|@io.shortway.kobankat.models.SubscriptionOption(){}[0] final val io.shortway.kobankat.models/id // io.shortway.kobankat.models/id|@cocoapods.PurchasesHybridCommon.RCStoreProduct{}id[0] final fun (cocoapods.PurchasesHybridCommon/RCStoreProduct).(): kotlin/String // io.shortway.kobankat.models/id.|@cocoapods.PurchasesHybridCommon.RCStoreProduct(){}[0] @@ -629,6 +624,8 @@ final val io.shortway.kobankat/expirationDateMillis // io.shortway.kobankat/expi final fun (cocoapods.PurchasesHybridCommon/RCEntitlementInfo).(): kotlin/Long? // io.shortway.kobankat/expirationDateMillis.|@cocoapods.PurchasesHybridCommon.RCEntitlementInfo(){}[0] final val io.shortway.kobankat/firstSeenMillis // io.shortway.kobankat/firstSeenMillis|@cocoapods.PurchasesHybridCommon.RCCustomerInfo{}firstSeenMillis[0] final fun (cocoapods.PurchasesHybridCommon/RCCustomerInfo).(): kotlin/Long // io.shortway.kobankat/firstSeenMillis.|@cocoapods.PurchasesHybridCommon.RCCustomerInfo(){}[0] +final val io.shortway.kobankat/frameworkVersion // io.shortway.kobankat/frameworkVersion|@io.shortway.kobankat.PurchasesFactory{}frameworkVersion[0] + final fun (io.shortway.kobankat/PurchasesFactory).(): kotlin/String // io.shortway.kobankat/frameworkVersion.|@io.shortway.kobankat.PurchasesFactory(){}[0] final val io.shortway.kobankat/identifier // io.shortway.kobankat/identifier|@cocoapods.PurchasesHybridCommon.RCEntitlementInfo{}identifier[0] final fun (cocoapods.PurchasesHybridCommon/RCEntitlementInfo).(): kotlin/String // io.shortway.kobankat/identifier.|@cocoapods.PurchasesHybridCommon.RCEntitlementInfo(){}[0] final val io.shortway.kobankat/identifier // io.shortway.kobankat/identifier|@cocoapods.PurchasesHybridCommon.RCOffering{}identifier[0] diff --git a/core/core.podspec b/core/core.podspec index d43943cf..0e3dcc10 100644 --- a/core/core.podspec +++ b/core/core.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |spec| spec.summary = '' spec.vendored_frameworks = 'build/cocoapods/framework/KobanKat.framework' spec.libraries = 'c++' - spec.ios.deployment_target = '11.0' + spec.ios.deployment_target = '11.0' spec.dependency 'PurchasesHybridCommon', '10.7.0' if !Dir.exist?('build/cocoapods/framework/KobanKat.framework') || Dir.empty?('build/cocoapods/framework/KobanKat.framework') @@ -22,6 +22,10 @@ Pod::Spec.new do |spec| Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)" end + spec.xcconfig = { + 'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO', + } + spec.pod_target_xcconfig = { 'KOTLIN_PROJECT_PATH' => ':core', 'PRODUCT_MODULE_NAME' => 'KobanKat', diff --git a/core/src/androidMain/kotlin/io/shortway/kobankat/errors.android.kt b/core/src/androidMain/kotlin/io/shortway/kobankat/errors.android.kt index 0a1f9e5a..0808b07e 100644 --- a/core/src/androidMain/kotlin/io/shortway/kobankat/errors.android.kt +++ b/core/src/androidMain/kotlin/io/shortway/kobankat/errors.android.kt @@ -3,7 +3,7 @@ package io.shortway.kobankat import com.revenuecat.purchases.PurchasesError as RcPurchasesError import com.revenuecat.purchases.PurchasesErrorCode as RcPurchasesErrorCode -internal fun RcPurchasesError.toPurchasesError(): PurchasesError = +public fun RcPurchasesError.toPurchasesError(): PurchasesError = PurchasesError( code = code.toPurchasesErrorCode(), underlyingErrorMessage = underlyingErrorMessage @@ -38,4 +38,4 @@ internal fun RcPurchasesErrorCode.toPurchasesErrorCode(): PurchasesErrorCode = RcPurchasesErrorCode.EmptySubscriberAttributesError -> PurchasesErrorCode.EmptySubscriberAttributesError RcPurchasesErrorCode.CustomerInfoError -> PurchasesErrorCode.CustomerInfoError RcPurchasesErrorCode.SignatureVerificationError -> PurchasesErrorCode.SignatureVerificationError - } \ No newline at end of file + } diff --git a/core/src/iosMain/kotlin/io/shortway/kobankat/PurchasesFactory.ios.kt b/core/src/iosMain/kotlin/io/shortway/kobankat/PurchasesFactory.ios.kt index c262fb81..a1bdfba1 100644 --- a/core/src/iosMain/kotlin/io/shortway/kobankat/PurchasesFactory.ios.kt +++ b/core/src/iosMain/kotlin/io/shortway/kobankat/PurchasesFactory.ios.kt @@ -49,7 +49,7 @@ public actual object PurchasesFactory { platformFlavor = BuildKonfig.platformFlavor, platformFlavorVersion = frameworkVersion, usesStoreKit2IfAvailable = false, // In Flutter it's deprecated & defaults to false. - dangerousSettings = configuration.dangerousSettings.toRCDangerousSettings(), + dangerousSettings = dangerousSettings.toRCDangerousSettings(), shouldShowInAppMessagesAutomatically = showInAppMessagesAutomatically, verificationMode = verificationMode.name, ) diff --git a/core/src/iosMain/kotlin/io/shortway/kobankat/errors.ios.kt b/core/src/iosMain/kotlin/io/shortway/kobankat/errors.ios.kt index 10c85cba..67e118f6 100644 --- a/core/src/iosMain/kotlin/io/shortway/kobankat/errors.ios.kt +++ b/core/src/iosMain/kotlin/io/shortway/kobankat/errors.ios.kt @@ -3,8 +3,7 @@ package io.shortway.kobankat import platform.Foundation.NSError - -internal fun NSError.toPurchasesErrorOrThrow(): PurchasesError = +public fun NSError.toPurchasesErrorOrThrow(): PurchasesError = PurchasesError( code = code().toPurchasesErrorCode(), underlyingErrorMessage = localizedDescription(), @@ -50,4 +49,4 @@ private fun Long.toPurchasesErrorCode(): PurchasesErrorCode = 36L -> PurchasesErrorCode.FeatureNotAvailableInCustomEntitlementsComputationMode 37L -> PurchasesErrorCode.SignatureVerificationError else -> error("Unexpected ErrorCode: $this") -} \ No newline at end of file +} diff --git a/either/api/either.klib.api b/either/api/either.klib.api index f480ccbc..f506c903 100644 --- a/either/api/either.klib.api +++ b/either/api/either.klib.api @@ -10,7 +10,7 @@ final class io.shortway.kobankat.either/FailedPurchase { // io.shortway.kobankat constructor (io.shortway.kobankat/PurchasesError, kotlin/Boolean) // io.shortway.kobankat.either/FailedPurchase.|(io.shortway.kobankat.PurchasesError;kotlin.Boolean){}[0] final fun component1(): io.shortway.kobankat/PurchasesError // io.shortway.kobankat.either/FailedPurchase.component1|component1(){}[0] final fun component2(): kotlin/Boolean // io.shortway.kobankat.either/FailedPurchase.component2|component2(){}[0] - final fun copy(io.shortway.kobankat/PurchasesError =..., kotlin/Boolean =...): io.shortway.kobankat.either/FailedPurchase // io.shortway.kobankat.either/FailedPurchase.copy|copy(io.shortway.kobankat.PurchasesError;kotlin.Boolean){}[0] + final fun copy(io.shortway.kobankat/PurchasesError = ..., kotlin/Boolean = ...): io.shortway.kobankat.either/FailedPurchase // io.shortway.kobankat.either/FailedPurchase.copy|copy(io.shortway.kobankat.PurchasesError;kotlin.Boolean){}[0] final fun equals(kotlin/Any?): kotlin/Boolean // io.shortway.kobankat.either/FailedPurchase.equals|equals(kotlin.Any?){}[0] final fun hashCode(): kotlin/Int // io.shortway.kobankat.either/FailedPurchase.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // io.shortway.kobankat.either/FailedPurchase.toString|toString(){}[0] @@ -19,17 +19,17 @@ final class io.shortway.kobankat.either/FailedPurchase { // io.shortway.kobankat final val userCancelled // io.shortway.kobankat.either/FailedPurchase.userCancelled|{}userCancelled[0] final fun (): kotlin/Boolean // io.shortway.kobankat.either/FailedPurchase.userCancelled.|(){}[0] } -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitCustomerInfoEither(io.shortway.kobankat/CacheFetchPolicy =...): arrow.core/Either // io.shortway.kobankat.either/awaitCustomerInfoEither|awaitCustomerInfoEither@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.CacheFetchPolicy){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitCustomerInfoEither(io.shortway.kobankat/CacheFetchPolicy = ...): arrow.core/Either // io.shortway.kobankat.either/awaitCustomerInfoEither|awaitCustomerInfoEither@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.CacheFetchPolicy){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitGetProductsEither(kotlin.collections/List): arrow.core/Either> // io.shortway.kobankat.either/awaitGetProductsEither|awaitGetProductsEither@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.collections.List){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitLogInEither(kotlin/String): arrow.core/Either // io.shortway.kobankat.either/awaitLogInEither|awaitLogInEither@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.String){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitLogOutEither(): arrow.core/Either // io.shortway.kobankat.either/awaitLogOutEither|awaitLogOutEither@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitOfferingsEither(): arrow.core/Either // io.shortway.kobankat.either/awaitOfferingsEither|awaitOfferingsEither@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitPromotionalOfferEither(cocoapods.PurchasesHybridCommon/RCStoreProductDiscount, cocoapods.PurchasesHybridCommon/RCStoreProduct): arrow.core/Either // io.shortway.kobankat.either/awaitPromotionalOfferEither|awaitPromotionalOfferEither@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProductDiscount;cocoapods.PurchasesHybridCommon.RCStoreProduct){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitPurchaseEither(cocoapods.PurchasesHybridCommon/RCPackage, cocoapods.PurchasesHybridCommon/RCPromotionalOffer): arrow.core/Either // io.shortway.kobankat.either/awaitPurchaseEither|awaitPurchaseEither@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;cocoapods.PurchasesHybridCommon.RCPromotionalOffer){}[0] -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitPurchaseEither(cocoapods.PurchasesHybridCommon/RCPackage, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...): arrow.core/Either // io.shortway.kobankat.either/awaitPurchaseEither|awaitPurchaseEither@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitPurchaseEither(cocoapods.PurchasesHybridCommon/RCPackage, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...): arrow.core/Either // io.shortway.kobankat.either/awaitPurchaseEither|awaitPurchaseEither@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitPurchaseEither(cocoapods.PurchasesHybridCommon/RCStoreProduct, cocoapods.PurchasesHybridCommon/RCPromotionalOffer): arrow.core/Either // io.shortway.kobankat.either/awaitPurchaseEither|awaitPurchaseEither@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;cocoapods.PurchasesHybridCommon.RCPromotionalOffer){}[0] -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitPurchaseEither(cocoapods.PurchasesHybridCommon/RCStoreProduct, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...): arrow.core/Either // io.shortway.kobankat.either/awaitPurchaseEither|awaitPurchaseEither@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitPurchaseEither(io.shortway.kobankat.models/SubscriptionOption, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...): arrow.core/Either // io.shortway.kobankat.either/awaitPurchaseEither|awaitPurchaseEither@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.models.SubscriptionOption;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitPurchaseEither(cocoapods.PurchasesHybridCommon/RCStoreProduct, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...): arrow.core/Either // io.shortway.kobankat.either/awaitPurchaseEither|awaitPurchaseEither@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitPurchaseEither(io.shortway.kobankat.models/SubscriptionOption, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...): arrow.core/Either // io.shortway.kobankat.either/awaitPurchaseEither|awaitPurchaseEither@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.models.SubscriptionOption;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitRestoreEither(): arrow.core/Either // io.shortway.kobankat.either/awaitRestoreEither|awaitRestoreEither@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitSyncAttributesAndOfferingsIfNeededEither(): arrow.core/Either // io.shortway.kobankat.either/awaitSyncAttributesAndOfferingsIfNeededEither|awaitSyncAttributesAndOfferingsIfNeededEither@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.either/awaitSyncPurchasesEither(): arrow.core/Either // io.shortway.kobankat.either/awaitSyncPurchasesEither|awaitSyncPurchasesEither@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] diff --git a/gradle.properties b/gradle.properties index 7f53ad43..34929696 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ #Gradle -org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" +org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx4096M" org.gradle.caching=true org.gradle.configuration-cache=true @@ -8,4 +8,4 @@ kotlin.code.style=official #Android android.useAndroidX=true -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index de540907..17d05b0c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ android-minSdk = "21" android-targetSdk = "34" compose = "1.6.7" java = "1.8" -kotlin = "1.9.24" +kotlin = "2.0.0" revenuecat-common = "10.7.0" revenuecat-kmp = "0.5.0-SNAPSHOT" @@ -20,6 +20,7 @@ kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", ve kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version = "0.6.0" } revenuecat-common = { module = "com.revenuecat.purchases:purchases-hybrid-common", version.ref = "revenuecat-common" } +revenuecat-commonUi = { module = "com.revenuecat.purchases:purchases-hybrid-common-ui", version.ref = "revenuecat-common" } [plugins] adamko-dokkatoo-html = { id = "dev.adamko.dokkatoo-html", version = "2.3.1" } @@ -27,6 +28,7 @@ arturbosch-detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.23.6" } android-application = { id = "com.android.application", version.ref = "agp" } android-library = { id = "com.android.library", version.ref = "agp" } codingfeline-buildkonfig = { id = "com.codingfeline.buildkonfig", version = "0.15.1" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } gradleup-nmcp = { id = "com.gradleup.nmcp", version = "0.0.4" } jetbrains-compose = { id = "org.jetbrains.compose", version = "1.6.10" } kotlin-cocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" } diff --git a/iosApp/Podfile b/iosApp/Podfile index 29cc61f5..83bf00bc 100644 --- a/iosApp/Podfile +++ b/iosApp/Podfile @@ -6,5 +6,6 @@ target 'iosApp' do # use_frameworks! # Pods for iosApp - pod 'RevenueCat' + pod 'PurchasesHybridCommon' + pod 'PurchasesHybridCommonUI' end diff --git a/iosApp/Podfile.lock b/iosApp/Podfile.lock index ce29a202..3822dfa8 100644 --- a/iosApp/Podfile.lock +++ b/iosApp/Podfile.lock @@ -1,16 +1,30 @@ PODS: - - RevenueCat (4.37.0) + - PurchasesHybridCommon (10.7.0): + - RevenueCat (= 4.41.2) + - PurchasesHybridCommonUI (10.7.0): + - PurchasesHybridCommon (= 10.7.0) + - RevenueCatUI (= 4.41.2) + - RevenueCat (4.41.2) + - RevenueCatUI (4.41.2): + - RevenueCat (= 4.41.2) DEPENDENCIES: - - RevenueCat + - PurchasesHybridCommon + - PurchasesHybridCommonUI SPEC REPOS: trunk: + - PurchasesHybridCommon + - PurchasesHybridCommonUI - RevenueCat + - RevenueCatUI SPEC CHECKSUMS: - RevenueCat: b4fb058d7ced6a37327f6bc9636752dbc4a7235b + PurchasesHybridCommon: 01b102a271605eb5857a6cbf27e3fde31dae0f61 + PurchasesHybridCommonUI: f3a7ef56aa41a7fd602b4f450393cc7897fe5815 + RevenueCat: 0250867579677899de94f96ad9be342be865560f + RevenueCatUI: e3bac7e40db387f53e7b3579175cca749656bee7 -PODFILE CHECKSUM: af60a63696dae160d0e392872d37dfe1bf3efed7 +PODFILE CHECKSUM: 94b98173b57ff2909b9b4bdb218a9abcceef39fe COCOAPODS: 1.15.2 diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 2ed8a3ec..a3c1b5e9 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -18,7 +18,7 @@ 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; - 7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF7B242A565900829871 /* KobanKat Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "KobanKat Sample.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 7970694DAED6D108F4AB7D20 /* libPods-iosApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-iosApp.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -53,7 +53,6 @@ ADB7642423335C54609E9121 /* Pods-iosApp.debug.xcconfig */, E4E97109987A6338537554BE /* Pods-iosApp.release.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -79,7 +78,7 @@ 7555FF7C242A565900829871 /* Products */ = { isa = PBXGroup; children = ( - 7555FF7B242A565900829871 /* iosApp.app */, + 7555FF7B242A565900829871 /* KobanKat Sample.app */, ); name = Products; sourceTree = ""; @@ -116,6 +115,7 @@ 7555FF77242A565900829871 /* Sources */, B92378962B6B1156000C7307 /* Frameworks */, 7555FF79242A565900829871 /* Resources */, + BADF33751A0CFC72A3F9017A /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -123,7 +123,7 @@ ); name = iosApp; productName = iosApp; - productReference = 7555FF7B242A565900829871 /* iosApp.app */; + productReference = 7555FF7B242A565900829871 /* KobanKat Sample.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -172,6 +172,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + BADF33751A0CFC72A3F9017A /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; E546C444910C3630A98ADF58 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -353,7 +370,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; - DEVELOPMENT_TEAM = "${TEAM_ID}"; + DEVELOPMENT_TEAM = 8SXR2327BM; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)"; INFOPLIST_FILE = iosApp/Info.plist; @@ -381,7 +398,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; DEVELOPMENT_TEAM = "${TEAM_ID}"; ENABLE_PREVIEWS = YES; diff --git a/iosApp/iosApp.xcworkspace/contents.xcworkspacedata b/iosApp/iosApp.xcworkspace/contents.xcworkspacedata index c009e7d7..1b36fe93 100644 --- a/iosApp/iosApp.xcworkspace/contents.xcworkspacedata +++ b/iosApp/iosApp.xcworkspace/contents.xcworkspacedata @@ -1,6 +1,26 @@ + + + + + + + + + + + + diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index 412e3781..aaf5e182 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -2,6 +2,8 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -20,8 +22,6 @@ 1 LSRequiresIPhoneOS - CADisableMinimumFrameDurationOnPhone - UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/paywalls/.gitignore b/paywalls/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/paywalls/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/paywalls/api/paywalls.api b/paywalls/api/paywalls.api new file mode 100644 index 00000000..dd62eec3 --- /dev/null +++ b/paywalls/api/paywalls.api @@ -0,0 +1,57 @@ +public final class io/shortway/kobankat/ui/revenuecatui/PaywallFooterKt { + public static final fun PaywallFooter (Lio/shortway/kobankat/ui/revenuecatui/PaywallOptions;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V +} + +public final class io/shortway/kobankat/ui/revenuecatui/PaywallKt { + public static final fun Paywall (Lio/shortway/kobankat/ui/revenuecatui/PaywallOptions;Landroidx/compose/runtime/Composer;I)V +} + +public abstract interface class io/shortway/kobankat/ui/revenuecatui/PaywallListener { + public abstract fun onPurchaseCancelled ()V + public abstract fun onPurchaseCompleted (Lcom/revenuecat/purchases/CustomerInfo;Lcom/revenuecat/purchases/models/StoreTransaction;)V + public abstract fun onPurchaseError (Lio/shortway/kobankat/PurchasesError;)V + public abstract fun onPurchaseStarted (Lcom/revenuecat/purchases/Package;)V + public abstract fun onRestoreCompleted (Lcom/revenuecat/purchases/CustomerInfo;)V + public abstract fun onRestoreError (Lio/shortway/kobankat/PurchasesError;)V + public abstract fun onRestoreStarted ()V +} + +public final class io/shortway/kobankat/ui/revenuecatui/PaywallListener$DefaultImpls { + public static fun onPurchaseCancelled (Lio/shortway/kobankat/ui/revenuecatui/PaywallListener;)V + public static fun onPurchaseCompleted (Lio/shortway/kobankat/ui/revenuecatui/PaywallListener;Lcom/revenuecat/purchases/CustomerInfo;Lcom/revenuecat/purchases/models/StoreTransaction;)V + public static fun onPurchaseError (Lio/shortway/kobankat/ui/revenuecatui/PaywallListener;Lio/shortway/kobankat/PurchasesError;)V + public static fun onPurchaseStarted (Lio/shortway/kobankat/ui/revenuecatui/PaywallListener;Lcom/revenuecat/purchases/Package;)V + public static fun onRestoreCompleted (Lio/shortway/kobankat/ui/revenuecatui/PaywallListener;Lcom/revenuecat/purchases/CustomerInfo;)V + public static fun onRestoreError (Lio/shortway/kobankat/ui/revenuecatui/PaywallListener;Lio/shortway/kobankat/PurchasesError;)V + public static fun onRestoreStarted (Lio/shortway/kobankat/ui/revenuecatui/PaywallListener;)V +} + +public final class io/shortway/kobankat/ui/revenuecatui/PaywallOptions { + public static final field $stable I + public synthetic fun (Lcom/revenuecat/purchases/Offering;ZLio/shortway/kobankat/ui/revenuecatui/PaywallListener;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getDismissRequest ()Lkotlin/jvm/functions/Function0; + public final fun getListener ()Lio/shortway/kobankat/ui/revenuecatui/PaywallListener; + public final fun getOffering ()Lcom/revenuecat/purchases/Offering; + public final fun getShouldDisplayDismissButton ()Z + public fun toString ()Ljava/lang/String; +} + +public final class io/shortway/kobankat/ui/revenuecatui/PaywallOptions$Builder { + public static final field $stable I + public fun (Lkotlin/jvm/functions/Function0;)V + public final fun build ()Lio/shortway/kobankat/ui/revenuecatui/PaywallOptions; + public final fun getDismissRequest ()Lkotlin/jvm/functions/Function0; + public final fun getListener ()Lio/shortway/kobankat/ui/revenuecatui/PaywallListener; + public final fun getOffering ()Lcom/revenuecat/purchases/Offering; + public final fun getShouldDisplayDismissButton ()Z + public final fun setDismissRequest (Lkotlin/jvm/functions/Function0;)V + public final fun setListener (Lio/shortway/kobankat/ui/revenuecatui/PaywallListener;)V + public final fun setOffering (Lcom/revenuecat/purchases/Offering;)V + public final fun setShouldDisplayDismissButton (Z)V +} + +public final class io/shortway/kobankat/ui/revenuecatui/PaywallOptionsKt { + public static final synthetic fun PaywallOptions (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Lio/shortway/kobankat/ui/revenuecatui/PaywallOptions; + public static synthetic fun PaywallOptions$default (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lio/shortway/kobankat/ui/revenuecatui/PaywallOptions; +} + diff --git a/paywalls/api/paywalls.klib.api b/paywalls/api/paywalls.klib.api new file mode 100644 index 00000000..33ca4b7b --- /dev/null +++ b/paywalls/api/paywalls.klib.api @@ -0,0 +1,50 @@ +// Klib ABI Dump +// Targets: [iosArm64, iosSimulatorArm64, iosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract interface io.shortway.kobankat.ui.revenuecatui/PaywallListener { // io.shortway.kobankat.ui.revenuecatui/PaywallListener|null[0] + open fun onPurchaseCancelled() // io.shortway.kobankat.ui.revenuecatui/PaywallListener.onPurchaseCancelled|onPurchaseCancelled(){}[0] + open fun onPurchaseCompleted(cocoapods.PurchasesHybridCommon/RCCustomerInfo, cocoapods.PurchasesHybridCommon/RCStoreTransaction) // io.shortway.kobankat.ui.revenuecatui/PaywallListener.onPurchaseCompleted|onPurchaseCompleted(cocoapods.PurchasesHybridCommon.RCCustomerInfo;cocoapods.PurchasesHybridCommon.RCStoreTransaction){}[0] + open fun onPurchaseError(io.shortway.kobankat/PurchasesError) // io.shortway.kobankat.ui.revenuecatui/PaywallListener.onPurchaseError|onPurchaseError(io.shortway.kobankat.PurchasesError){}[0] + open fun onPurchaseStarted(cocoapods.PurchasesHybridCommon/RCPackage) // io.shortway.kobankat.ui.revenuecatui/PaywallListener.onPurchaseStarted|onPurchaseStarted(cocoapods.PurchasesHybridCommon.RCPackage){}[0] + open fun onRestoreCompleted(cocoapods.PurchasesHybridCommon/RCCustomerInfo) // io.shortway.kobankat.ui.revenuecatui/PaywallListener.onRestoreCompleted|onRestoreCompleted(cocoapods.PurchasesHybridCommon.RCCustomerInfo){}[0] + open fun onRestoreError(io.shortway.kobankat/PurchasesError) // io.shortway.kobankat.ui.revenuecatui/PaywallListener.onRestoreError|onRestoreError(io.shortway.kobankat.PurchasesError){}[0] + open fun onRestoreStarted() // io.shortway.kobankat.ui.revenuecatui/PaywallListener.onRestoreStarted|onRestoreStarted(){}[0] +} +final class io.shortway.kobankat.ui.revenuecatui/PaywallOptions { // io.shortway.kobankat.ui.revenuecatui/PaywallOptions|null[0] + final class Builder { // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder|null[0] + constructor (kotlin/Function0) // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.|(kotlin.Function0){}[0] + final fun build(): io.shortway.kobankat.ui.revenuecatui/PaywallOptions // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.build|build(){}[0] + final var dismissRequest // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.dismissRequest|{}dismissRequest[0] + final fun (): kotlin/Function0 // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.dismissRequest.|(){}[0] + final fun (kotlin/Function0) // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.dismissRequest.|(kotlin.Function0){}[0] + final var listener // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.listener|{}listener[0] + final fun (): io.shortway.kobankat.ui.revenuecatui/PaywallListener? // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.listener.|(){}[0] + final fun (io.shortway.kobankat.ui.revenuecatui/PaywallListener?) // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.listener.|(io.shortway.kobankat.ui.revenuecatui.PaywallListener?){}[0] + final var offering // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.offering|{}offering[0] + final fun (): cocoapods.PurchasesHybridCommon/RCOffering? // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.offering.|(){}[0] + final fun (cocoapods.PurchasesHybridCommon/RCOffering?) // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.offering.|(cocoapods.PurchasesHybridCommon.RCOffering?){}[0] + final var shouldDisplayDismissButton // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.shouldDisplayDismissButton|{}shouldDisplayDismissButton[0] + final fun (): kotlin/Boolean // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.shouldDisplayDismissButton.|(){}[0] + final fun (kotlin/Boolean) // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.Builder.shouldDisplayDismissButton.|(kotlin.Boolean){}[0] + } + final fun toString(): kotlin/String // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.toString|toString(){}[0] + final val dismissRequest // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.dismissRequest|{}dismissRequest[0] + final fun (): kotlin/Function0 // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.dismissRequest.|(){}[0] + final val listener // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.listener|{}listener[0] + final fun (): io.shortway.kobankat.ui.revenuecatui/PaywallListener? // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.listener.|(){}[0] + final val offering // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.offering|{}offering[0] + final fun (): cocoapods.PurchasesHybridCommon/RCOffering? // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.offering.|(){}[0] + final val shouldDisplayDismissButton // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.shouldDisplayDismissButton|{}shouldDisplayDismissButton[0] + final fun (): kotlin/Boolean // io.shortway.kobankat.ui.revenuecatui/PaywallOptions.shouldDisplayDismissButton.|(){}[0] +} +final fun io.shortway.kobankat.ui.revenuecatui/Paywall(io.shortway.kobankat.ui.revenuecatui/PaywallOptions, androidx.compose.runtime/Composer?, kotlin/Int) // io.shortway.kobankat.ui.revenuecatui/Paywall|Paywall(io.shortway.kobankat.ui.revenuecatui.PaywallOptions;androidx.compose.runtime.Composer?;kotlin.Int){}[0] +final fun io.shortway.kobankat.ui.revenuecatui/PaywallFooter(io.shortway.kobankat.ui.revenuecatui/PaywallOptions, kotlin/Function3?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // io.shortway.kobankat.ui.revenuecatui/PaywallFooter|PaywallFooter(io.shortway.kobankat.ui.revenuecatui.PaywallOptions;kotlin.Function3?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] +final fun io.shortway.kobankat.ui.revenuecatui/PaywallOptions(kotlin/Function0, kotlin/Function1 = ...): io.shortway.kobankat.ui.revenuecatui/PaywallOptions // io.shortway.kobankat.ui.revenuecatui/PaywallOptions|PaywallOptions(kotlin.Function0;kotlin.Function1){}[0] +final val io.shortway.kobankat.ui.revenuecatui/io_shortway_kobankat_ui_revenuecatui_IosPaywallDelegate$stableprop // io.shortway.kobankat.ui.revenuecatui/io_shortway_kobankat_ui_revenuecatui_IosPaywallDelegate$stableprop|#static{}io_shortway_kobankat_ui_revenuecatui_IosPaywallDelegate$stableprop[0] +final val io.shortway.kobankat.ui.revenuecatui/io_shortway_kobankat_ui_revenuecatui_PaywallOptions$stableprop // io.shortway.kobankat.ui.revenuecatui/io_shortway_kobankat_ui_revenuecatui_PaywallOptions$stableprop|#static{}io_shortway_kobankat_ui_revenuecatui_PaywallOptions$stableprop[0] +final val io.shortway.kobankat.ui.revenuecatui/io_shortway_kobankat_ui_revenuecatui_PaywallOptions_Builder$stableprop // io.shortway.kobankat.ui.revenuecatui/io_shortway_kobankat_ui_revenuecatui_PaywallOptions_Builder$stableprop|#static{}io_shortway_kobankat_ui_revenuecatui_PaywallOptions_Builder$stableprop[0] diff --git a/paywalls/build.gradle.kts b/paywalls/build.gradle.kts new file mode 100644 index 00000000..efdea795 --- /dev/null +++ b/paywalls/build.gradle.kts @@ -0,0 +1,51 @@ +plugins { + id("kobankat-library") + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) + alias(libs.plugins.kotlin.cocoapods) +} + +kotlin { + sourceSets { + commonMain.dependencies { + api(projects.core) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.ui) + } + androidMain.dependencies { + implementation(libs.androidx.compose.ui.tooling.preview) + implementation(libs.revenuecat.commonUi) + } + } + + cocoapods { + version = "1.0" + ios.deploymentTarget = "11.0" + + pod("PurchasesHybridCommonUI") { + version = libs.versions.revenuecat.common.get() + extraOpts += listOf("-compiler-option", "-fmodules") + } + } +} + +android { + namespace = "io.shortway.kobankat.ui.revenuecatui" + + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + sourceSets["main"].res.srcDirs("src/androidMain/res") + sourceSets["main"].resources.srcDirs("src/commonMain/resources") + + defaultConfig { + minSdk = 24 + } + buildTypes { + getByName("release") { + isMinifyEnabled = false + } + } + dependencies { + debugImplementation(libs.androidx.compose.ui.tooling) + } +} diff --git a/paywalls/paywalls.podspec b/paywalls/paywalls.podspec new file mode 100644 index 00000000..9bd46b1a --- /dev/null +++ b/paywalls/paywalls.podspec @@ -0,0 +1,54 @@ +Pod::Spec.new do |spec| + spec.name = 'paywalls' + spec.version = '1.0' + spec.homepage = '' + spec.source = { :http=> ''} + spec.authors = '' + spec.license = '' + spec.summary = '' + spec.vendored_frameworks = 'build/cocoapods/framework/paywalls.framework' + spec.libraries = 'c++' + spec.ios.deployment_target = '11.0' + spec.dependency 'PurchasesHybridCommonUI', '10.7.0' + + if !Dir.exist?('build/cocoapods/framework/paywalls.framework') || Dir.empty?('build/cocoapods/framework/paywalls.framework') + raise " + + Kotlin framework 'paywalls' doesn't exist yet, so a proper Xcode project can't be generated. + 'pod install' should be executed after running ':generateDummyFramework' Gradle task: + + ./gradlew :paywalls:generateDummyFramework + + Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)" + end + + spec.xcconfig = { + 'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO', + } + + spec.pod_target_xcconfig = { + 'KOTLIN_PROJECT_PATH' => ':paywalls', + 'PRODUCT_MODULE_NAME' => 'paywalls', + } + + spec.script_phases = [ + { + :name => 'Build paywalls', + :execution_position => :before_compile, + :shell_path => '/bin/sh', + :script => <<-SCRIPT + if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then + echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"" + exit 0 + fi + set -ev + REPO_ROOT="$PODS_TARGET_SRCROOT" + "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ + -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ + -Pkotlin.native.cocoapods.archs="$ARCHS" \ + -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" + SCRIPT + } + ] + spec.resources = ['build/compose/cocoapods/compose-resources'] +end \ No newline at end of file diff --git a/paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt b/paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt new file mode 100644 index 00000000..ca5f582e --- /dev/null +++ b/paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt @@ -0,0 +1,10 @@ +package io.shortway.kobankat.ui.revenuecatui + +import androidx.compose.runtime.Composable +import com.revenuecat.purchases.ui.revenuecatui.ExperimentalPreviewRevenueCatUIPurchasesAPI +import com.revenuecat.purchases.ui.revenuecatui.Paywall as RcPaywall + +@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class) +@Composable +public actual fun Paywall(options: PaywallOptions): Unit = + RcPaywall(options.toAndroidPaywallOptions()) diff --git a/paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt b/paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt new file mode 100644 index 00000000..26562759 --- /dev/null +++ b/paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt @@ -0,0 +1,20 @@ +package io.shortway.kobankat.ui.revenuecatui + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.Composable +import com.revenuecat.purchases.ui.revenuecatui.ExperimentalPreviewRevenueCatUIPurchasesAPI +import com.revenuecat.purchases.ui.revenuecatui.PaywallFooter as AndroidPaywallFooter + +/** + * A Paywall footer. + */ +@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class) +@Composable +public actual fun PaywallFooter( + options: PaywallOptions, + mainContent: @Composable ((PaddingValues) -> Unit)?, +): Unit = + AndroidPaywallFooter( + options = options.toAndroidPaywallOptions(), + mainContent = mainContent + ) diff --git a/paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptionsKtx.kt b/paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptionsKtx.kt new file mode 100644 index 00000000..3681ad16 --- /dev/null +++ b/paywalls/src/androidMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptionsKtx.kt @@ -0,0 +1,48 @@ +package io.shortway.kobankat.ui.revenuecatui + +import com.revenuecat.purchases.CustomerInfo +import com.revenuecat.purchases.Package +import com.revenuecat.purchases.PurchasesError +import com.revenuecat.purchases.models.StoreTransaction +import com.revenuecat.purchases.ui.revenuecatui.ExperimentalPreviewRevenueCatUIPurchasesAPI +import io.shortway.kobankat.toPurchasesError +import com.revenuecat.purchases.ui.revenuecatui.PaywallListener as AndroidPaywallListener +import com.revenuecat.purchases.ui.revenuecatui.PaywallOptions as AndroidPaywallOptions + +@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class) +internal fun PaywallOptions.toAndroidPaywallOptions(): AndroidPaywallOptions = + AndroidPaywallOptions.Builder(dismissRequest) + .setOffering(offering) + .setShouldDisplayDismissButton(shouldDisplayDismissButton) + .setListener(listener?.toAndroidPaywallListener()) + .build() + +@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class) +private fun PaywallListener.toAndroidPaywallListener(): AndroidPaywallListener = + PaywallListenerWrapper(this) + +@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class) +private class PaywallListenerWrapper(private val listener: PaywallListener) : + AndroidPaywallListener { + override fun onPurchaseCancelled() = + listener.onPurchaseCancelled() + + override fun onPurchaseCompleted( + customerInfo: CustomerInfo, + storeTransaction: StoreTransaction + ) = listener.onPurchaseCompleted(customerInfo, storeTransaction) + + override fun onPurchaseError(error: PurchasesError) = + listener.onPurchaseError(error.toPurchasesError()) + + override fun onPurchaseStarted(rcPackage: Package) = + listener.onPurchaseStarted(rcPackage) + + override fun onRestoreCompleted(customerInfo: CustomerInfo) = + listener.onRestoreCompleted(customerInfo) + + override fun onRestoreError(error: PurchasesError) = + listener.onRestoreError(error.toPurchasesError()) + + override fun onRestoreStarted() = listener.onRestoreStarted() +} diff --git a/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt b/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt new file mode 100644 index 00000000..04b5ce5e --- /dev/null +++ b/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt @@ -0,0 +1,9 @@ +package io.shortway.kobankat.ui.revenuecatui + +import androidx.compose.runtime.Composable + +/** + * A full-screen Paywall. + */ +@Composable +public expect fun Paywall(options: PaywallOptions) diff --git a/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt b/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt new file mode 100644 index 00000000..1e799695 --- /dev/null +++ b/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt @@ -0,0 +1,13 @@ +package io.shortway.kobankat.ui.revenuecatui + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.Composable + +/** + * A Paywall footer. + */ +@Composable +public expect fun PaywallFooter( + options: PaywallOptions, + mainContent: @Composable ((PaddingValues) -> Unit)? = null, +) diff --git a/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallListener.kt b/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallListener.kt new file mode 100644 index 00000000..2e8fba5d --- /dev/null +++ b/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallListener.kt @@ -0,0 +1,54 @@ +package io.shortway.kobankat.ui.revenuecatui + +import io.shortway.kobankat.CustomerInfo +import io.shortway.kobankat.Package +import io.shortway.kobankat.PurchasesError +import io.shortway.kobankat.models.StoreTransaction + +/** + * Provide an implementation of this interface to [PaywallOptions] to listen for Paywall events. + * Every function has a default no-op implementation. + */ +public interface PaywallListener { + /** + * Callback that gets called when a purchase is started. + * + * @param rcPackage The package being purchased. + */ + public fun onPurchaseStarted(rcPackage: Package) {} + + /** + * Callback that gets called when a purchase is completed. + */ + public fun onPurchaseCompleted( + customerInfo: CustomerInfo, + storeTransaction: StoreTransaction + ) { + } + + /** + * Callback that gets called when a purchase fails. + */ + public fun onPurchaseError(error: PurchasesError) {} + + /** + * Callback that gets called when a purchase is cancelled. + */ + public fun onPurchaseCancelled() {} + + /** + * Callback that gets called when a restore is started. + */ + public fun onRestoreStarted() {} + + /** + * Callback that gets called when a restore is completed. Note that this may get called even + * if no entitlements have been granted in case no relevant purchases were found. + */ + public fun onRestoreCompleted(customerInfo: CustomerInfo) {} + + /** + * Callback that gets called when a restore fails. + */ + public fun onRestoreError(error: PurchasesError) {} +} diff --git a/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptions.kt b/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptions.kt new file mode 100644 index 00000000..9d0911e6 --- /dev/null +++ b/paywalls/src/commonMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptions.kt @@ -0,0 +1,86 @@ +package io.shortway.kobankat.ui.revenuecatui + +import io.shortway.kobankat.Offering +import kotlin.jvm.JvmSynthetic + +/** + * Options for the Paywall. + */ +public class PaywallOptions private constructor( + /** + * Optional Offering object obtained through `getOfferings()`. + */ + public val offering: Offering?, + /** + * Whether to display a close button on the paywall screen. Only available when using + * [Paywall]. Ignored when using [PaywallFooter]. Defaults to false. + */ + public val shouldDisplayDismissButton: Boolean, + /** + * Optional listener, called for various purchase and restore events. + */ + public val listener: PaywallListener?, + /** + * Dismiss the paywall, i.e. remove the view, navigate to another screen, etc. Will be called + * when the close button is pressed (if enabled) or when a purchase succeeds. + */ + public val dismissRequest: () -> Unit, +) { + override fun toString(): String = + "PaywallOptions(" + + "offering=$offering, " + + "shouldDisplayDismissButton=$shouldDisplayDismissButton, " + + "listener=$listener, " + + "dismissRequest=$dismissRequest" + + ")" + + /** + * Use this builder to create an instance of [PaywallOptions]. + */ + public class Builder( + /** + * Dismiss the paywall, i.e. remove the view, navigate to another screen, etc. Will be + * called when the close button is pressed (if enabled) or when a purchase succeeds. + */ + public var dismissRequest: () -> Unit + ) { + /** + * Optional Offering object obtained through `getOfferings()`. + */ + public var offering: Offering? = null + + /** + * Whether to display a close button on the paywall screen. Only available when using + * [Paywall]. Ignored when using [PaywallFooter]. Defaults to false. + */ + public var shouldDisplayDismissButton: Boolean = false + + /** + * Optional listener, called for various purchase and restore events. + */ + public var listener: PaywallListener? = null + + /** + * Creates a [PaywallOptions] instance with the specified properties. + */ + public fun build(): PaywallOptions = PaywallOptions( + offering = offering, + shouldDisplayDismissButton = shouldDisplayDismissButton, + listener = listener, + dismissRequest = dismissRequest, + ) + } + +} + +/** + * Options for the Paywall. + */ +@JvmSynthetic +public fun PaywallOptions( + dismissRequest: () -> Unit, + builder: PaywallOptions.Builder.() -> Unit = { }, +): PaywallOptions = + PaywallOptions.Builder(dismissRequest) + .apply(builder) + .build() diff --git a/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt b/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt new file mode 100644 index 00000000..bf9406dd --- /dev/null +++ b/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/Paywall.kt @@ -0,0 +1,13 @@ +package io.shortway.kobankat.ui.revenuecatui + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +public actual fun Paywall(options: PaywallOptions): Unit = + UIKitPaywall( + options = options, + footer = false, + modifier = Modifier.fillMaxSize() + ) diff --git a/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt b/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt new file mode 100644 index 00000000..d248c09f --- /dev/null +++ b/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallFooter.kt @@ -0,0 +1,66 @@ +package io.shortway.kobankat.ui.revenuecatui + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp + +/** + * A Paywall footer. + */ +@Composable +public actual fun PaywallFooter( + options: PaywallOptions, + mainContent: @Composable ((PaddingValues) -> Unit)?, +) { + var height by remember { mutableStateOf(0.dp) } + val paywallComposable = @Composable { + UIKitPaywall( + options = options, + footer = true, + modifier = Modifier + .fillMaxWidth() + .clip( + RoundedCornerShape(topStart = DefaultCornerRadius, topEnd = DefaultCornerRadius) + ) + .animateContentSize() + .height(height), + onHeightChange = { newHeight -> height = newHeight.dp } + ) + } + + // Largely the same as PaywallFooter in purchases:ui on Android. + if (mainContent == null) { + paywallComposable() + } else { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(-DefaultCornerRadius), + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + ) { + mainContent(PaddingValues(bottom = DefaultCornerRadius)) + } + paywallComposable() + } + } + +} + +private val DefaultCornerRadius = 20.dp diff --git a/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptionsKtx.kt b/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptionsKtx.kt new file mode 100644 index 00000000..8df053db --- /dev/null +++ b/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/PaywallOptionsKtx.kt @@ -0,0 +1,89 @@ +package io.shortway.kobankat.ui.revenuecatui + +import cocoapods.PurchasesHybridCommonUI.RCPaywallViewController +import cocoapods.PurchasesHybridCommonUI.RCPaywallViewControllerDelegateProtocol +import io.shortway.kobankat.CustomerInfo +import io.shortway.kobankat.Package +import io.shortway.kobankat.models.StoreTransaction +import io.shortway.kobankat.toPurchasesErrorOrThrow +import kotlinx.cinterop.CValue +import kotlinx.cinterop.ObjCSignatureOverride +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.pointed +import objcnames.classes.RCCustomerInfo +import objcnames.classes.RCPackage +import objcnames.classes.RCStoreTransaction +import platform.CoreGraphics.CGSize +import platform.Foundation.NSError +import platform.darwin.NSObject + +internal class IosPaywallDelegate( + private val listener: PaywallListener?, + private val onHeightChange: (Int) -> Unit +) : RCPaywallViewControllerDelegateProtocol, + NSObject() { + + @Suppress("CAST_NEVER_SUCCEEDS") + override fun paywallViewController( + controller: RCPaywallViewController, + didStartPurchaseWithPackage: RCPackage + ) { + listener?.onPurchaseStarted(didStartPurchaseWithPackage as Package) + } + + @Suppress("CAST_NEVER_SUCCEEDS") + override fun paywallViewController( + controller: RCPaywallViewController, + didFinishPurchasingWithCustomerInfo: RCCustomerInfo, + transaction: RCStoreTransaction? + ) { + listener?.onPurchaseCompleted( + didFinishPurchasingWithCustomerInfo as CustomerInfo, + transaction as StoreTransaction + ) + } + + @Suppress("CONFLICTING_OVERLOADS", "PARAMETER_NAME_CHANGED_ON_OVERRIDE") + @ObjCSignatureOverride + override fun paywallViewController( + controller: RCPaywallViewController, + didFailPurchasingWithError: NSError + ) { + listener?.onPurchaseError(didFailPurchasingWithError.toPurchasesErrorOrThrow()) + } + + override fun paywallViewControllerDidCancelPurchase(controller: RCPaywallViewController) { + listener?.onPurchaseCancelled() + } + + override fun paywallViewControllerDidStartRestore(controller: RCPaywallViewController) { + listener?.onRestoreStarted() + } + + @Suppress("CAST_NEVER_SUCCEEDS", "PARAMETER_NAME_CHANGED_ON_OVERRIDE") + override fun paywallViewController( + controller: RCPaywallViewController, + didFinishRestoringWithCustomerInfo: RCCustomerInfo + ) { + listener?.onRestoreCompleted(didFinishRestoringWithCustomerInfo as CustomerInfo) + } + + @Suppress("CONFLICTING_OVERLOADS", "PARAMETER_NAME_CHANGED_ON_OVERRIDE") + @ObjCSignatureOverride + override fun paywallViewController( + controller: RCPaywallViewController, + didFailRestoringWithError: NSError + ) { + listener?.onRestoreError(didFailRestoringWithError.toPurchasesErrorOrThrow()) + } + + + override fun paywallViewController( + controller: RCPaywallViewController, + didChangeSizeTo: CValue + ) { + var height: Int? = null + memScoped { height = didChangeSizeTo.ptr.pointed.height.toInt() } + onHeightChange(height!!) + } +} diff --git a/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/UIKitPaywall.kt b/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/UIKitPaywall.kt new file mode 100644 index 00000000..d8ef9967 --- /dev/null +++ b/paywalls/src/iosMain/kotlin/io/shortway/kobankat/ui/revenuecatui/UIKitPaywall.kt @@ -0,0 +1,53 @@ +package io.shortway.kobankat.ui.revenuecatui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.interop.UIKitViewController +import cocoapods.PurchasesHybridCommonUI.RCPaywallFooterViewController +import cocoapods.PurchasesHybridCommonUI.RCPaywallViewController +import objcnames.classes.RCOffering + +@Composable +internal fun UIKitPaywall( + options: PaywallOptions, + footer: Boolean, + modifier: Modifier = Modifier, + onHeightChange: (Int) -> Unit = { }, +) { + // Keeping references to avoid them being deallocated. + val dismissRequestedHandler: (RCPaywallViewController?) -> Unit = + remember(options.dismissRequest) { { options.dismissRequest() } } + val delegate = remember(options.listener, onHeightChange) { + IosPaywallDelegate(options.listener, onHeightChange) + } + // We remember this wrapper so we can keep a reference to RCPaywallViewController, even during + // recompositions. RCPaywallViewController itself is not yet instantiated here. + val viewControllerWrapper = remember { ViewControllerWrapper(null) } + UIKitViewController( + modifier = modifier, + factory = { + val viewController = if (footer) RCPaywallFooterViewController( + offering = options.offering as RCOffering, + displayCloseButton = options.shouldDisplayDismissButton, + shouldBlockTouchEvents = false, + dismissRequestedHandler = dismissRequestedHandler, + ) else RCPaywallViewController( + offering = options.offering as RCOffering, + displayCloseButton = options.shouldDisplayDismissButton, + shouldBlockTouchEvents = false, + dismissRequestedHandler = dismissRequestedHandler, + ) + + viewController + .apply { setDelegate(delegate) } + .also { viewControllerWrapper.value = it } + }, + ) +} + +/** + * Can be [remembered][remember] before the RCPaywallViewController is instantiated, so as to + * "reserve" a spot in the Compose slot table. + */ +private class ViewControllerWrapper(var value: RCPaywallViewController?) diff --git a/result/api/result.klib.api b/result/api/result.klib.api index 76704700..2ee8d277 100644 --- a/result/api/result.klib.api +++ b/result/api/result.klib.api @@ -6,17 +6,17 @@ // - Show declarations: true // Library unique name: -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitCustomerInfoResult(io.shortway.kobankat/CacheFetchPolicy =...): kotlin/Result // io.shortway.kobankat.result/awaitCustomerInfoResult|awaitCustomerInfoResult@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.CacheFetchPolicy){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitCustomerInfoResult(io.shortway.kobankat/CacheFetchPolicy = ...): kotlin/Result // io.shortway.kobankat.result/awaitCustomerInfoResult|awaitCustomerInfoResult@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.CacheFetchPolicy){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitGetProductsResult(kotlin.collections/List): kotlin/Result> // io.shortway.kobankat.result/awaitGetProductsResult|awaitGetProductsResult@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.collections.List){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitLogInResult(kotlin/String): kotlin/Result // io.shortway.kobankat.result/awaitLogInResult|awaitLogInResult@cocoapods.PurchasesHybridCommon.RCPurchases(kotlin.String){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitLogOutResult(): kotlin/Result // io.shortway.kobankat.result/awaitLogOutResult|awaitLogOutResult@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitOfferingsResult(): kotlin/Result // io.shortway.kobankat.result/awaitOfferingsResult|awaitOfferingsResult@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitPromotionalOfferResult(cocoapods.PurchasesHybridCommon/RCStoreProductDiscount, cocoapods.PurchasesHybridCommon/RCStoreProduct): kotlin/Result // io.shortway.kobankat.result/awaitPromotionalOfferResult|awaitPromotionalOfferResult@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProductDiscount;cocoapods.PurchasesHybridCommon.RCStoreProduct){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitPurchaseResult(cocoapods.PurchasesHybridCommon/RCPackage, cocoapods.PurchasesHybridCommon/RCPromotionalOffer): kotlin/Result // io.shortway.kobankat.result/awaitPurchaseResult|awaitPurchaseResult@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;cocoapods.PurchasesHybridCommon.RCPromotionalOffer){}[0] -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitPurchaseResult(cocoapods.PurchasesHybridCommon/RCPackage, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...): kotlin/Result // io.shortway.kobankat.result/awaitPurchaseResult|awaitPurchaseResult@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitPurchaseResult(cocoapods.PurchasesHybridCommon/RCPackage, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...): kotlin/Result // io.shortway.kobankat.result/awaitPurchaseResult|awaitPurchaseResult@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCPackage;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitPurchaseResult(cocoapods.PurchasesHybridCommon/RCStoreProduct, cocoapods.PurchasesHybridCommon/RCPromotionalOffer): kotlin/Result // io.shortway.kobankat.result/awaitPurchaseResult|awaitPurchaseResult@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;cocoapods.PurchasesHybridCommon.RCPromotionalOffer){}[0] -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitPurchaseResult(cocoapods.PurchasesHybridCommon/RCStoreProduct, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...): kotlin/Result // io.shortway.kobankat.result/awaitPurchaseResult|awaitPurchaseResult@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] -final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitPurchaseResult(io.shortway.kobankat.models/SubscriptionOption, kotlin/Boolean? =..., kotlin/String? =..., io.shortway.kobankat.models/GoogleReplacementMode =...): kotlin/Result // io.shortway.kobankat.result/awaitPurchaseResult|awaitPurchaseResult@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.models.SubscriptionOption;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitPurchaseResult(cocoapods.PurchasesHybridCommon/RCStoreProduct, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...): kotlin/Result // io.shortway.kobankat.result/awaitPurchaseResult|awaitPurchaseResult@cocoapods.PurchasesHybridCommon.RCPurchases(cocoapods.PurchasesHybridCommon.RCStoreProduct;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] +final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitPurchaseResult(io.shortway.kobankat.models/SubscriptionOption, kotlin/Boolean? = ..., kotlin/String? = ..., io.shortway.kobankat.models/GoogleReplacementMode = ...): kotlin/Result // io.shortway.kobankat.result/awaitPurchaseResult|awaitPurchaseResult@cocoapods.PurchasesHybridCommon.RCPurchases(io.shortway.kobankat.models.SubscriptionOption;kotlin.Boolean?;kotlin.String?;io.shortway.kobankat.models.GoogleReplacementMode){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitRestoreResult(): kotlin/Result // io.shortway.kobankat.result/awaitRestoreResult|awaitRestoreResult@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitSyncAttributesAndOfferingsIfNeededResult(): kotlin/Result // io.shortway.kobankat.result/awaitSyncAttributesAndOfferingsIfNeededResult|awaitSyncAttributesAndOfferingsIfNeededResult@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] final suspend fun (cocoapods.PurchasesHybridCommon/RCPurchases).io.shortway.kobankat.result/awaitSyncPurchasesResult(): kotlin/Result // io.shortway.kobankat.result/awaitSyncPurchasesResult|awaitSyncPurchasesResult@cocoapods.PurchasesHybridCommon.RCPurchases(){}[0] diff --git a/settings.gradle.kts b/settings.gradle.kts index 7c4fc4c3..488f54b5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,4 +23,5 @@ include(":core") include(":result") include(":either") include(":datetime") +include(":paywalls") include(":composeApp")