diff --git a/CHANGELOG.md b/CHANGELOG.md index 73340aa..228fd21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,29 @@ The changelog for `Superwall`. Also see the [releases](https://github.com/superwall/Superwall-Flutter/releases) on GitHub. +## 1.1.9 + +### Enhancements + +- Upgrades Android SDK to 1.1.8. [View Android SDK release notes](https://github.com/superwall-me/Superwall-Android/releases/tag/1.1.8) + +### Fixes + +- Bumps minimum Kotlin version to 1.8.0 and Android Gradle Plugin to 8.1.0 to be able to be +compatible with the latest Android SDK. This was necessary for important bug fixes. +- SW-2868: Fixes transaction issues caused by hot restart. + ## 1.1.8 +### Enhancements + - Upgrades iOS SDK to 3.6.6. [View iOS SDK release notes](https://github.com/superwall-me/Superwall-iOS/releases/tag/3.6.6) - Upgrades Android SDK to 1.1.7. [View Android SDK release notes](https://github.com/superwall-me/Superwall-Android/releases/tag/1.1.7) ## 1.1.7 +### Enhancements + - Upgrades iOS SDK to 3.6.5. [View iOS SDK release notes](https://github.com/superwall-me/Superwall-iOS/releases/tag/3.6.5) ## 1.1.6 diff --git a/android/build.gradle b/android/build.gradle index 8d67814..14ebbc3 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,18 +1,6 @@ group 'com.superwall.superwallkit_flutter' version '1.0-SNAPSHOT' -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} allprojects { repositories { @@ -81,11 +69,11 @@ android { buildConfigField "boolean", "WAIT_FOR_DEBUGGER", waitForDebugger } } - +android.buildFeatures.buildConfig true dependencies { testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'org.mockito:mockito-core:5.0.0' - implementation "com.superwall.sdk:superwall-android:1.1.7" + implementation "com.superwall.sdk:superwall-android:1.1.8" implementation 'com.android.billingclient:billing:6.1.0' } \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index 3c9ea03..ce128e5 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1 +1,5 @@ +plugins { + id "com.android.application" version "8.1.0" apply false + id "org.jetbrains.kotlin.android" version "1.8.0" apply false +} rootProject.name = 'superwallkit_flutter' diff --git a/android/src/main/kotlin/com/superwall/superwallkit_flutter/bridges/SuperwallBridge.kt b/android/src/main/kotlin/com/superwall/superwallkit_flutter/bridges/SuperwallBridge.kt index c7da98f..4f37814 100644 --- a/android/src/main/kotlin/com/superwall/superwallkit_flutter/bridges/SuperwallBridge.kt +++ b/android/src/main/kotlin/com/superwall/superwallkit_flutter/bridges/SuperwallBridge.kt @@ -84,6 +84,10 @@ class SuperwallBridge( val isLoggedIn = Superwall.instance.isLoggedIn result.success(isLoggedIn) } + "getIsInitialized" -> { + val isInitialized = Superwall.initialized + result.success(isInitialized) + } "getPresentedViewController" -> { // TODO: Since UIViewController cannot be returned directly to Dart, handle appropriately result.notImplemented() diff --git a/android/src/main/kotlin/com/superwall/superwallkit_flutter/json/SuperwallEventInfo+Json.kt b/android/src/main/kotlin/com/superwall/superwallkit_flutter/json/SuperwallEventInfo+Json.kt index 8added8..a06aeb1 100644 --- a/android/src/main/kotlin/com/superwall/superwallkit_flutter/json/SuperwallEventInfo+Json.kt +++ b/android/src/main/kotlin/com/superwall/superwallkit_flutter/json/SuperwallEventInfo+Json.kt @@ -19,6 +19,7 @@ fun SuperwallEvent.toJson(): Map { is SuperwallEvent.IdentityAlias -> mapOf("event" to "identityAlias") is SuperwallEvent.AppInstall -> mapOf("event" to "appInstall") is SuperwallEvent.SessionStart -> mapOf("event" to "sessionStart") + is SuperwallEvent.Reset -> mapOf("event" to "reset") is SuperwallEvent.Restore.Start -> mapOf("event" to "restoreStart") is SuperwallEvent.Restore.Complete -> mapOf("event" to "restoreComplete") is SuperwallEvent.Restore.Fail -> { diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 0d29021..8b662c8 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -1,28 +1,36 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + avoid_redundant_argument_values: true + avoid_relative_lib_imports: true + avoid_types_as_parameter_names: true + avoid_unnecessary_containers: true + avoid_unused_constructor_parameters: true + avoid_void_async: true + deprecated_consistency: true + directives_ordering: true + omit_local_variable_types: true + prefer_const_constructors: true + prefer_const_constructors_in_immutables: true + prefer_const_declarations: true + prefer_expression_function_bodies: true + prefer_final_in_for_each: true + prefer_final_locals: true + prefer_is_not_empty: true + prefer_is_not_operator: true + prefer_iterable_whereType: true + prefer_relative_imports: true + prefer_single_quotes: true + prefer_spread_collections: true + provide_deprecation_message: true + require_trailing_commas: false + unnecessary_brace_in_string_interps: true + unnecessary_lambdas: true + unnecessary_null_aware_assignments: true + unnecessary_null_checks: true + unnecessary_parenthesis: true + unnecessary_statements: true + unnecessary_string_escapes: true + unnecessary_string_interpolations: true + unnecessary_this: true diff --git a/example/android/build.gradle b/example/android/build.gradle index 2a1217b..859ca48 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,15 +1,3 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 3c472b9..5d6560a 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 66db66d..5e615fc 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -23,7 +23,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false + id "com.android.application" version "8.1.0" apply false + id "org.jetbrains.kotlin.android" version "1.8.0" apply false } include ":app", ':superwallkit_flutter' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index f7b9e53..26475d9 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -2,15 +2,12 @@ PODS: - Flutter (1.0.0) - integration_test (0.0.1): - Flutter - - purchases_flutter (6.15.0): + - purchases_flutter (6.29.4): - Flutter - - PurchasesHybridCommon (= 8.10.1) - - PurchasesHybridCommon (8.10.1): - - RevenueCat (= 4.31.9) - - RevenueCatUI (= 4.31.9) - - RevenueCat (4.31.9) - - RevenueCatUI (4.31.9): - - RevenueCat (= 4.31.9) + - PurchasesHybridCommon (= 10.9.0) + - PurchasesHybridCommon (10.9.0): + - RevenueCat (= 4.43.0) + - RevenueCat (4.43.0) - SuperwallKit (3.6.6) - superwallkit_flutter (0.0.1): - Flutter @@ -26,7 +23,6 @@ SPEC REPOS: trunk: - PurchasesHybridCommon - RevenueCat - - RevenueCatUI - SuperwallKit EXTERNAL SOURCES: @@ -42,10 +38,9 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 integration_test: 13825b8a9334a850581300559b8839134b124670 - purchases_flutter: ce847b4848ccec17cadfb1a2e2a3ad1d3f681849 - PurchasesHybridCommon: 170de77f83fd9cb570457157e92ea04c7aef8196 - RevenueCat: aa3e439fb6282e7768609774a73c06ab87c3d9c3 - RevenueCatUI: ef3f12dddf49d333a66a13f1d32c73ef1075ae95 + purchases_flutter: a10ab0c4bb4effbc9b78be6513658ae659e67a20 + PurchasesHybridCommon: 0e157d11d04fdfdd434b5c6d05acb58fd1cc9db8 + RevenueCat: f7b90c52f4b7e322bc9e7adaf734030523534b06 SuperwallKit: e6db6c60f6dc95c3dc841525f84dc8504cfffb5a superwallkit_flutter: f0094805f4346cfa08efd8f11c9be64df8b8d0a5 diff --git a/example/lib/RCPurchaseController.dart b/example/lib/RCPurchaseController.dart index 8a04904..a82c9be 100644 --- a/example/lib/RCPurchaseController.dart +++ b/example/lib/RCPurchaseController.dart @@ -254,19 +254,30 @@ extension CustomerInfoAdditions on CustomerInfo { } DateTime? getLatestTransactionPurchaseDate() { - Map allPurchaseDates = this.allPurchaseDates; + Map allPurchaseDates = this.allPurchaseDates; + + // Return null if there are no purchase dates if (allPurchaseDates.entries.isEmpty) { return null; } + // Initialise the latestDate with the earliest possible date DateTime latestDate = DateTime.fromMillisecondsSinceEpoch(0); + + // Iterate over each entry in the map allPurchaseDates.forEach((key, value) { - DateTime date = DateTime.parse(value); - if (date.isAfter(latestDate)) { - latestDate = date; + // Check if the value is not null + if (value != null) { + // Parse the date from the string value + DateTime date = DateTime.parse(value); + // Update the latestDate if the current date is after the latestDate + if (date.isAfter(latestDate)) { + latestDate = date; + } } }); + // Return the latest date found return latestDate; } } diff --git a/example/lib/main.dart b/example/lib/main.dart index 7135f13..281b39a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -19,15 +19,12 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State implements SuperwallDelegate { - String _platformVersion = 'Unknown'; - @override void initState() { - bool useRevenueCat = true; + const useRevenueCat = true; super.initState(); configureSuperwall(useRevenueCat); - initPlatformState(); } // Configure Superwall @@ -35,18 +32,19 @@ class _MyAppState extends State implements SuperwallDelegate { try { // MARK: Step 1 - Create your Purchase Controller /// Create an `RCPurchaseController()` wherever Superwall and RevenueCat are being initialized. - RCPurchaseController purchaseController = RCPurchaseController(); + final purchaseController = RCPurchaseController(); // Get Superwall API Key String apiKey = Platform.isIOS ? "pk_5f6d9ae96b889bc2c36ca0f2368de2c4c3d5f6119aacd3d2" : "pk_d1f0959f70c761b1d55bb774a03e22b2b6ed290ce6561f85"; - Logging logging = Logging(); + final logging = Logging(); logging.level = LogLevel.warn; logging.scopes = {LogScope.all}; - SuperwallOptions options = SuperwallOptions(); + final options = SuperwallOptions(); + options.paywalls.shouldPreload = false; // options.logging = logging; // MARK: Step 2 - Configure Superwall @@ -54,7 +52,7 @@ class _MyAppState extends State implements SuperwallDelegate { Superwall.configure(apiKey, purchaseController: useRevenueCat ? purchaseController : null, options: options, completion: () { - print("Executing Superwall configure completion block"); + print('Executing Superwall configure completion block'); }); Superwall.shared.setDelegate(this); @@ -71,49 +69,26 @@ class _MyAppState extends State implements SuperwallDelegate { } } - // Platform messages are asynchronous, so we initialize in an async method. - Future initPlatformState() async { - String platformVersion; - // Platform messages may fail, so we use a try/catch PlatformException. - // We also handle the message potentially returning null. - try { - platformVersion = 'Unknown platform version'; - } on PlatformException { - platformVersion = 'Failed to get platform version.'; - } - - // If the widget was removed from the tree while the asynchronous platform - // message was in flight, we want to discard the reply rather than calling - // setState to update our non-existent appearance. - if (!mounted) return; - - setState(() { - _platformVersion = platformVersion; - }); - } - // Method to call when the button is tapped Future onRegisterTapped() async { try { - PaywallPresentationHandler handler = PaywallPresentationHandler(); - handler.onPresent((paywallInfo) async { - String name = await paywallInfo.name; - print("Handler (onPresent): $name"); - }); - handler.onDismiss((paywallInfo) async { - String name = await paywallInfo.name; - print("Handler (onDismiss): $name"); - }); - handler.onError((error) { - print("Handler (onError): ${error}"); - }); - handler.onSkip((skipReason) { - handleSkipReason(skipReason); - }); - - Superwall.shared.registerEvent('flutter', params: null, handler: handler, - feature: () { - print("Executing feature block"); + final handler = PaywallPresentationHandler(); + handler + ..onPresent((paywallInfo) async { + final name = await paywallInfo.name; + print('Handler (onPresent): $name'); + }) + ..onDismiss((paywallInfo) async { + final name = await paywallInfo.name; + print('Handler (onDismiss): $name'); + }) + ..onError((error) { + print('Handler (onError): $error'); + }) + ..onSkip(handleSkipReason); + + Superwall.shared.registerEvent('flutter', handler: handler, feature: () { + print('Executing feature block'); performFeatureBlockActions(); }); print('Register method called successfully.'); @@ -124,215 +99,204 @@ class _MyAppState extends State implements SuperwallDelegate { } Future performFeatureBlockActions() async { - PaywallInfo? paywallInfo = await Superwall.shared.getLatestPaywallInfo(); + final paywallInfo = await Superwall.shared.getLatestPaywallInfo(); if (paywallInfo != null) { - String? identifier = await paywallInfo.identifier; - print("Identifier: $identifier"); + final identifier = await paywallInfo.identifier; + print('Identifier: $identifier'); - Experiment? experiment = await paywallInfo.experiment; - print("Experiment: $experiment"); + final experiment = await paywallInfo.experiment; + print('Experiment: $experiment'); - String? triggerSessionId = await paywallInfo.triggerSessionId; - print("Trigger Session ID: $triggerSessionId"); + final triggerSessionId = await paywallInfo.triggerSessionId; + print('Trigger Session ID: $triggerSessionId'); - List products = await paywallInfo.products; - print("Products: $products"); + final products = await paywallInfo.products; + print('Products: $products'); - List productIds = await paywallInfo.productIds; - print("Product IDs: $productIds"); + final productIds = await paywallInfo.productIds; + print('Product IDs: $productIds'); - String name = await paywallInfo.name; - print("Name: $name"); + final name = await paywallInfo.name; + print('Name: $name'); - String url = await paywallInfo.url; - print("URL: $url"); + final url = await paywallInfo.url; + print('URL: $url'); - String? presentedByEventWithName = + final presentedByEventWithName = await paywallInfo.presentedByEventWithName; - print("Presented By Event With Name: $presentedByEventWithName"); + print('Presented By Event With Name: $presentedByEventWithName'); - String? presentedByEventWithId = await paywallInfo.presentedByEventWithId; - print("Presented By Event With Id: $presentedByEventWithId"); + final presentedByEventWithId = await paywallInfo.presentedByEventWithId; + print('Presented By Event With Id: $presentedByEventWithId'); - String? presentedByEventAt = await paywallInfo.presentedByEventAt; - print("Presented By Event At: $presentedByEventAt"); + final presentedByEventAt = await paywallInfo.presentedByEventAt; + print('Presented By Event At: $presentedByEventAt'); - String presentedBy = await paywallInfo.presentedBy; - print("Presented By: $presentedBy"); + final presentedBy = await paywallInfo.presentedBy; + print('Presented By: $presentedBy'); - String? presentationSourceType = await paywallInfo.presentationSourceType; - print("Presentation Source Type: $presentationSourceType"); + final presentationSourceType = await paywallInfo.presentationSourceType; + print('Presentation Source Type: $presentationSourceType'); - String? responseLoadStartTime = await paywallInfo.responseLoadStartTime; - print("Response Load Start Time: $responseLoadStartTime"); + final responseLoadStartTime = await paywallInfo.responseLoadStartTime; + print('Response Load Start Time: $responseLoadStartTime'); - String? responseLoadCompleteTime = + final responseLoadCompleteTime = await paywallInfo.responseLoadCompleteTime; - print("Response Load Complete Time: $responseLoadCompleteTime"); + print('Response Load Complete Time: $responseLoadCompleteTime'); - String? responseLoadFailTime = await paywallInfo.responseLoadFailTime; - print("Response Load Fail Time: $responseLoadFailTime"); + final responseLoadFailTime = await paywallInfo.responseLoadFailTime; + print('Response Load Fail Time: $responseLoadFailTime'); - double? responseLoadDuration = await paywallInfo.responseLoadDuration; - print("Response Load Duration: $responseLoadDuration"); + final responseLoadDuration = await paywallInfo.responseLoadDuration; + print('Response Load Duration: $responseLoadDuration'); - String? webViewLoadStartTime = await paywallInfo.webViewLoadStartTime; - print("Web View Load Start Time: $webViewLoadStartTime"); + final webViewLoadStartTime = await paywallInfo.webViewLoadStartTime; + print('Web View Load Start Time: $webViewLoadStartTime'); - String? webViewLoadCompleteTime = - await paywallInfo.webViewLoadCompleteTime; - print("Web View Load Complete Time: $webViewLoadCompleteTime"); + final webViewLoadCompleteTime = await paywallInfo.webViewLoadCompleteTime; + print('Web View Load Complete Time: $webViewLoadCompleteTime'); - String? webViewLoadFailTime = await paywallInfo.webViewLoadFailTime; - print("Web View Load Fail Time: $webViewLoadFailTime"); + final webViewLoadFailTime = await paywallInfo.webViewLoadFailTime; + print('Web View Load Fail Time: $webViewLoadFailTime'); - double? webViewLoadDuration = await paywallInfo.webViewLoadDuration; - print("Web View Load Duration: $webViewLoadDuration"); + final webViewLoadDuration = await paywallInfo.webViewLoadDuration; + print('Web View Load Duration: $webViewLoadDuration'); - String? productsLoadStartTime = await paywallInfo.productsLoadStartTime; - print("Products Load Start Time: $productsLoadStartTime"); + final productsLoadStartTime = await paywallInfo.productsLoadStartTime; + print('Products Load Start Time: $productsLoadStartTime'); - String? productsLoadCompleteTime = + final productsLoadCompleteTime = await paywallInfo.productsLoadCompleteTime; - print("Products Load Complete Time: $productsLoadCompleteTime"); + print('Products Load Complete Time: $productsLoadCompleteTime'); - String? productsLoadFailTime = await paywallInfo.productsLoadFailTime; - print("Products Load Fail Time: $productsLoadFailTime"); + final productsLoadFailTime = await paywallInfo.productsLoadFailTime; + print('Products Load Fail Time: $productsLoadFailTime'); - double? productsLoadDuration = await paywallInfo.productsLoadDuration; - print("Products Load Duration: $productsLoadDuration"); + final productsLoadDuration = await paywallInfo.productsLoadDuration; + print('Products Load Duration: $productsLoadDuration'); - String? paywalljsVersion = await paywallInfo.paywalljsVersion; - print("Paywall.js Version: $paywalljsVersion"); + final paywalljsVersion = await paywallInfo.paywalljsVersion; + print('Paywall.js Version: $paywalljsVersion'); - bool isFreeTrialAvailable = await paywallInfo.isFreeTrialAvailable; - print("Is Free Trial Available: $isFreeTrialAvailable"); + final isFreeTrialAvailable = await paywallInfo.isFreeTrialAvailable; + print('Is Free Trial Available: $isFreeTrialAvailable'); - FeatureGatingBehavior featureGatingBehavior = - await paywallInfo.featureGatingBehavior; - print("Feature Gating Behavior: $featureGatingBehavior"); + final featureGatingBehavior = await paywallInfo.featureGatingBehavior; + print('Feature Gating Behavior: $featureGatingBehavior'); - PaywallCloseReason closeReason = await paywallInfo.closeReason; - print("Close Reason: $closeReason"); + final closeReason = await paywallInfo.closeReason; + print('Close Reason: $closeReason'); - List localNotifications = - await paywallInfo.localNotifications; - print("Local Notifications: $localNotifications"); + final localNotifications = await paywallInfo.localNotifications; + print('Local Notifications: $localNotifications'); - List computedPropertyRequests = + final computedPropertyRequests = await paywallInfo.computedPropertyRequests; - print("Computed Property Requests: $computedPropertyRequests"); + print('Computed Property Requests: $computedPropertyRequests'); - List surveys = await paywallInfo.surveys; - print("Surveys: $surveys"); + final surveys = await paywallInfo.surveys; + print('Surveys: $surveys'); } else { - print("Paywall Info is null"); + print('Paywall Info is null'); } } Future performAction() async { try { - IdentityOptions options = - IdentityOptions(restorePaywallAssignments: true); - await Superwall.shared.identify("123456"); + await Superwall.shared.identify('123456'); - String userId = await Superwall.shared.getUserId(); + final userId = await Superwall.shared.getUserId(); print(userId); - await Superwall.shared.setUserAttributes({"someAttribute": "someValue"}); - Map attributes1 = - await Superwall.shared.getUserAttributes(); + await Superwall.shared.setUserAttributes({'someAttribute': 'someValue'}); + final attributes1 = await Superwall.shared.getUserAttributes(); print(attributes1); await Superwall.shared - .setUserAttributes({"jack": "lost", "kate": "antman"}); - Map attributes2 = - await Superwall.shared.getUserAttributes(); + .setUserAttributes({'jack': 'lost', 'kate': 'antman'}); + final attributes2 = await Superwall.shared.getUserAttributes(); print(attributes2); await Superwall.shared.setUserAttributes({ - "jack": "123", - "kate": {"tv": "series"} + 'jack': '123', + 'kate': {'tv': 'series'} }); - Map attributes3 = - await Superwall.shared.getUserAttributes(); + final attributes3 = await Superwall.shared.getUserAttributes(); print(attributes3); await Superwall.shared.reset(); - Map attributes4 = - await Superwall.shared.getUserAttributes(); + final attributes4 = await Superwall.shared.getUserAttributes(); print(attributes4); Superwall.shared.setLogLevel(LogLevel.error); - LogLevel logLevel = await Superwall.shared.getLogLevel(); - print("Log Level: $logLevel"); + final logLevel = await Superwall.shared.getLogLevel(); + print('Log Level: $logLevel'); } catch (e) { print('Failed perform action: $e'); } } - void handleSkipReason(PaywallSkippedReason skipReason) async { + Future handleSkipReason(PaywallSkippedReason skipReason) async { final description = await skipReason.description; if (skipReason is PaywallSkippedReasonHoldout) { final experiment = await skipReason.experiment; final experimentId = await experiment.id; - print("Holdout with experiment: ${experimentId}"); - print("Handler (onSkip): $description"); + print('Holdout with experiment: $experimentId'); + print('Handler (onSkip): $description'); } else if (skipReason is PaywallSkippedReasonNoRuleMatch) { - print("Handler (onSkip): $description"); + print('Handler (onSkip): $description'); } else if (skipReason is PaywallSkippedReasonEventNotFound) { - print("Handler (onSkip): $description"); + print('Handler (onSkip): $description'); } else if (skipReason is PaywallSkippedReasonUserIsSubscribed) { - print("Handler (onSkip): $description"); + print('Handler (onSkip): $description'); } else { - print("Handler (onSkip): Unknown skip reason"); + print('Handler (onSkip): Unknown skip reason'); } } @override - Widget build(BuildContext context) { - return MaterialApp( - debugShowCheckedModeBanner: false, - home: Scaffold( - appBar: AppBar( - title: const Text('Flutter superapp'), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('Running'), - ElevatedButton( - onPressed: onRegisterTapped, - child: const Text('Register event'), - ), - ElevatedButton( - onPressed: performAction, - child: const Text('Perform action'), - ), - ], + Widget build(BuildContext context) => MaterialApp( + debugShowCheckedModeBanner: false, + home: Scaffold( + appBar: AppBar( + title: const Text('Flutter superapp'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Running'), + ElevatedButton( + onPressed: onRegisterTapped, + child: const Text('Register event'), + ), + ElevatedButton( + onPressed: performAction, + child: const Text('Perform action'), + ), + ], + ), ), ), - ), - ); - } + ); @override void didDismissPaywall(PaywallInfo paywallInfo) { - print("didDismissPaywall: $paywallInfo"); + print('didDismissPaywall: $paywallInfo'); } @override void didPresentPaywall(PaywallInfo paywallInfo) { - print("didPresentPaywall: $paywallInfo"); + print('didPresentPaywall: $paywallInfo'); } @override void handleCustomPaywallAction(String name) { - print("handleCustomPaywallAction: $name"); + print('handleCustomPaywallAction: $name'); } @override @@ -346,23 +310,23 @@ class _MyAppState extends State implements SuperwallDelegate { // This delegate function is noisy. Uncomment to debug. return; - print("handleSuperwallEvent: $eventInfo"); + print('handleSuperwallEvent: $eventInfo'); switch (eventInfo.event.type) { case EventType.appOpen: - print("appOpen event"); + print('appOpen event'); case EventType.deviceAttributes: - print("deviceAttributes event: ${eventInfo.event.deviceAttributes} "); + print('deviceAttributes event: ${eventInfo.event.deviceAttributes} '); case EventType.paywallOpen: final paywallInfo = eventInfo.event.paywallInfo; - print("paywallOpen event: ${paywallInfo} "); + print('paywallOpen event: $paywallInfo '); if (paywallInfo != null) { final identifier = await paywallInfo.identifier; - print("paywallInfo.identifier: ${identifier} "); + print('paywallInfo.identifier: $identifier '); final productIds = await paywallInfo.productIds; - print("paywallInfo.productIds: ${productIds} "); + print('paywallInfo.productIds: $productIds '); } default: break; @@ -371,34 +335,34 @@ class _MyAppState extends State implements SuperwallDelegate { @override void paywallWillOpenDeepLink(Uri url) { - print("paywallWillOpenDeepLink: $url"); + print('paywallWillOpenDeepLink: $url'); } @override void paywallWillOpenURL(Uri url) { - print("paywallWillOpenURL: $url"); + print('paywallWillOpenURL: $url'); } @override void subscriptionStatusDidChange(SubscriptionStatus newValue) { - print("subscriptionStatusDidChange: $newValue"); + print('subscriptionStatusDidChange: $newValue'); } @override void willDismissPaywall(PaywallInfo paywallInfo) { - print("willDismissPaywall: $paywallInfo"); + print('willDismissPaywall: $paywallInfo'); } @override void willPresentPaywall(PaywallInfo paywallInfo) { printSubscriptionStatus(); - print("willPresentPaywall: $paywallInfo"); + print('willPresentPaywall: $paywallInfo'); } - void printSubscriptionStatus() async { + Future printSubscriptionStatus() async { final status = await Superwall.shared.getSubscriptionStatus(); final description = await status.description; - print("Status: $description"); + print('Status: $description'); } } diff --git a/example/pubspec.lock b/example/pubspec.lock index 8ace9bc..f27b627 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -222,10 +222,10 @@ packages: dependency: "direct main" description: name: purchases_flutter - sha256: "8a0131c917c55e520723ae267ca60bea7e5f980108320d9b6349b7f332941129" + sha256: b79b4ad7b180991877915f4c9bb151cabfec8ce5609b6789715db7f5ea8e8767 url: "https://pub.dev" source: hosted - version: "6.15.0" + version: "6.29.4" sky_engine: dependency: transitive description: flutter @@ -277,7 +277,7 @@ packages: path: ".." relative: true source: path - version: "1.1.3" + version: "1.1.8" sync_http: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 94058f3..24a7f00 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -30,7 +30,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - purchases_flutter: ^6.15.0 + purchases_flutter: ^6.29.4 dev_dependencies: integration_test: diff --git a/ios/Classes/Bridges/PurchaseControllerProxyBridge.swift b/ios/Classes/Bridges/PurchaseControllerProxyBridge.swift index d0bb4fa..6b05f79 100644 --- a/ios/Classes/Bridges/PurchaseControllerProxyBridge.swift +++ b/ios/Classes/Bridges/PurchaseControllerProxyBridge.swift @@ -26,7 +26,7 @@ public class PurchaseControllerProxyBridge: BridgeInstance, PurchaseController { print("Attempting to invoke restorePurchases method internally") guard let restorationResultBridgeId = await communicator.asyncInvokeMethodOnMain("restorePurchases") as? BridgeId else { print("WARNING: Failed to invoke restorePurchases") - return .failed(PurchaseControllerProxyPluginError()) + return .failed(PurchaseControllerProxyPluginError()); } guard let restorationResultBridge: RestorationResultBridge = restorationResultBridgeId.bridgeInstance() else { print("WARNING: Failed to get bridge instance") @@ -37,4 +37,4 @@ public class PurchaseControllerProxyBridge: BridgeInstance, PurchaseController { } } -struct PurchaseControllerProxyPluginError: Error {} +struct PurchaseControllerProxyPluginError: Error {} \ No newline at end of file diff --git a/ios/Classes/Bridges/SuperwallBridge.swift b/ios/Classes/Bridges/SuperwallBridge.swift index acce520..98a5443 100644 --- a/ios/Classes/Bridges/SuperwallBridge.swift +++ b/ios/Classes/Bridges/SuperwallBridge.swift @@ -56,6 +56,10 @@ public class SuperwallBridge: BridgeInstance { // Implement logic to check if the user is logged in to Superwall result(Superwall.shared.isLoggedIn) + case "getIsInitialized": + // Implement logic to check if Superwall is initialized + result(Superwall.isInitialized) + case "getPresentedViewController": // TODO: Implement logic to get the presented paywall view controller result(FlutterMethodNotImplemented) @@ -134,7 +138,6 @@ public class SuperwallBridge: BridgeInstance { result(call.badArgs) return } - let purchaseControllerProxyBridge: PurchaseControllerProxyBridge? = call.bridgeInstance(for: "purchaseControllerProxyBridgeId") let options: SuperwallOptions? = { diff --git a/ios/Classes/SuperwallkitFlutterPlugin.swift b/ios/Classes/SuperwallkitFlutterPlugin.swift index 6615f60..1dbd899 100644 --- a/ios/Classes/SuperwallkitFlutterPlugin.swift +++ b/ios/Classes/SuperwallkitFlutterPlugin.swift @@ -41,19 +41,18 @@ extension FlutterMethodCall { extension FlutterMethodChannel { func invokeMethodOnMain(_ method: String, arguments: Any? = nil) { - DispatchQueue.main.async { [weak self] in + Task { @MainActor [weak self] in guard let self else { return } invokeMethod(method, arguments: arguments) } } + @MainActor @discardableResult func asyncInvokeMethodOnMain(_ method: String, arguments: Any? = nil) async -> Any? { - return await withCheckedContinuation { continuation in - DispatchQueue.main.async { [weak self] in - guard let self else { return } - invokeMethod(method, arguments: arguments) { result in - continuation.resume(returning: result) - } + return await withCheckedContinuation { [weak self] continuation in + guard let self else { return } + invokeMethod(method, arguments: arguments) { result in + continuation.resume(returning: result) } } } diff --git a/lib/src/private/BridgingCreator.dart b/lib/src/private/BridgingCreator.dart index 7fb40c1..3f1be78 100644 --- a/lib/src/private/BridgingCreator.dart +++ b/lib/src/private/BridgingCreator.dart @@ -14,9 +14,11 @@ class BridgingCreator { // This will later be used when invoking creation to pass in initialization arguments static final Map> _metadataByBridgeId = {}; - static BridgeId _createBridgeId(BridgeClass bridgeClass, - [Map? initializationArgs]) { - BridgeId bridgeId = bridgeClass.generateBridgeId(); + static BridgeId _createBridgeId( + {String? givenId, + required BridgeClass bridgeClass, + Map? initializationArgs}) { + BridgeId bridgeId = givenId ?? bridgeClass.generateBridgeId(); _metadataByBridgeId[bridgeId] = {"args": initializationArgs}; return bridgeId; @@ -54,9 +56,15 @@ abstract class BridgeIdInstantiable { BridgeIdInstantiable( {required BridgeClass bridgeClass, BridgeId? bridgeId, + BridgeId? givenId, Map? initializationArgs}) : bridgeId = bridgeId ?? - BridgingCreator._createBridgeId(bridgeClass, initializationArgs) { + BridgingCreator._createBridgeId( + givenId: givenId, + bridgeClass: bridgeClass, + initializationArgs: initializationArgs) { + assert(this.bridgeId.endsWith("-bridgeId"), + "Make sure bridgeIds end with \"-bridgeId\""); this.bridgeId.associate(this); this.bridgeId.communicator.setMethodCallHandler(handleMethodCall); } diff --git a/lib/src/private/PurchaseControllerProxy.dart b/lib/src/private/PurchaseControllerProxy.dart index c959b1a..958437b 100644 --- a/lib/src/private/PurchaseControllerProxy.dart +++ b/lib/src/private/PurchaseControllerProxy.dart @@ -5,14 +5,10 @@ import 'package:superwallkit_flutter/src/public/RestorationResult.dart'; import '../public/PurchaseController.dart'; class PurchaseControllerProxy extends BridgeIdInstantiable { - void dispose() { - print("PurchaseControllerProxy didDispose. bridgeId: $bridgeId"); - } - static const BridgeClass bridgeClass = "PurchaseControllerProxyBridge"; PurchaseControllerProxy( - {required this.purchaseController, BridgeId? bridgeId}) - : super(bridgeClass: bridgeClass, bridgeId: bridgeId); + {required this.purchaseController, super.givenId, super.bridgeId}) + : super(bridgeClass: bridgeClass); PurchaseController purchaseController; diff --git a/lib/src/public/Superwall.dart b/lib/src/public/Superwall.dart index e7eebc2..8f44deb 100644 --- a/lib/src/public/Superwall.dart +++ b/lib/src/public/Superwall.dart @@ -119,6 +119,11 @@ class Superwall extends BridgeIdInstantiable { return await bridgeId.communicator.invokeBridgeMethod('getIsLoggedIn'); } + // Asynchronous method to check if Superwall is initialized + Future getIsInitialized() async { + return await bridgeId.communicator.invokeBridgeMethod('getIsInitialized'); + } + // Asynchronous method to get the presented paywall view controller // Future getPresentedViewController() async { // await _waitForBridgeInstanceCreation(); @@ -154,13 +159,10 @@ class Superwall extends BridgeIdInstantiable { // Asynchronous method to set the subscription status of the user Future setSubscriptionStatus(SubscriptionStatus status) async { - print("Calling subs status!!"); await _waitForBridgeInstanceCreation(); - print("Got bridheee"); final subscriptionStatusBridgeId = status.bridgeId; - print("Gona call"); var result = await bridgeId.communicator.invokeBridgeMethod( 'setSubscriptionStatus', {'subscriptionStatusBridgeId': subscriptionStatusBridgeId}); @@ -249,10 +251,12 @@ class Superwall extends BridgeIdInstantiable { SuperwallOptions? options, Function? completion}) async { PurchaseControllerProxy? purchaseControllerProxy; + if (purchaseController != null) { // Create a proxy bridge for the non-null purchaseController - purchaseControllerProxy = - PurchaseControllerProxy(purchaseController: purchaseController); + purchaseControllerProxy = PurchaseControllerProxy( + purchaseController: purchaseController, + givenId: "PurchaseControllerProxyBridge-bridgeId"); } CompletionBlockProxy? completionBlockProxy; diff --git a/pubspec.yaml b/pubspec.yaml index c6927e6..78daa62 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: superwallkit_flutter description: "Remotely configure every aspect of your paywall and double your revenue." -version: 1.1.8 +version: 1.1.9 homepage: "https://superwall.com" environment: