Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose userDefaults in Purchases.configure just like iOS repo #49

Closed
om-ha opened this issue May 16, 2020 · 3 comments
Closed

Expose userDefaults in Purchases.configure just like iOS repo #49

om-ha opened this issue May 16, 2020 · 3 comments
Assignees
Labels
enhancement New feature or request pr:next_release

Comments

@om-ha
Copy link

om-ha commented May 16, 2020

Problem

The original problem that requires this change, is that RevenueCat uses NSUserDefaults as a data layer. The actual problem is using the default instance and not a separate standalone instance specially instantiated for RevenueCat.

This means when NSUserDefaults is reset/reloaded/cleared, then RevenueCat will lose track of all persisted data.

This might happen automatically for some reason, abnormalities with NSUserDefaults shared instance that puts RevenueCat in an undefined state.

Fix

With this introduction for the problem, the solution is simply taking in an optional string ID as a parameter within Purchases.Configure that would create the standalone NSUserDefaults instance automatically for us. Like this solution in an issue from iOS repo. Or at least just make it so that RevenueCat uses a hardcoded NSUserDefaults domain ID.

@om-ha
Copy link
Author

om-ha commented May 16, 2020

Temporary Fix

For reference until this is patched, I actually made a platform channel to fix this. In case if anybody is interested:

Inside AppDelegate.swift -> didFinishLaunchingWithOptions -> create a new FlutterMethodChannel (for our example "myapp_platform_channel") and inside setMethodCallHandler callback parameter FlutterMethodCallHandler add the following:

// MARK: `setupRevenueCatPurchasesCupertino` method
if call.method == "setupRevenueCatPurchasesCupertino" {
    let argumentsDictionary = call.arguments as? Dictionary<String, Any?>
    let apiKey = argumentsDictionary?["apiKey"] as? String
    self.setupRevenueCatPurchasesCupertino(result: result, apiKey: apiKey)
    return
}

Then within AppDelegate class, create this private instance method:

// MARK:- RevenueCat Method Channel
private func setupRevenueCatPurchasesCupertino(result: FlutterResult, apiKey: String?)  {
    let userDefaultsDomain = "com.yourdomain.yourapp.revenuecat"
    let revenueCatAPIKey = apiKey ?? "hard_code_your_api_key_if_you_dare"
    
    // create UserDefaults instance/domain for RevenueCat so our app wouldn't overwrite it
    guard let revenueCatUserDefaults = UserDefaults.init(suiteName: userDefaultsDomain) else {
        let errorMessage = "RevenueCat Error: can't create user defaults for RevenueCat domain"
        print(errorMessage)
        result(false)
        return
    }
    
    // configure/setup/initialize RevenueCat Singleton Instance
    Purchases.configure(withAPIKey: revenueCatAPIKey,
                        appUserID: nil,
                        observerMode: false,
                        userDefaults: revenueCatUserDefaults)
    
    // finish execution
    result(true)
}

Create myapp_platform_channel.dart

import 'dart:io';

import 'package:flutter/services.dart';

class MyAppPlatformChannel {
  static const platform = const MethodChannel("myapp_platform_channel");

  Future<void> setupRevenueCatPurchasesCupertino(String apiKey) async {
    try {
      await platform.invokeMethod(
          "setupRevenueCatPurchasesCupertino", {"apiKey": apiKey});
    } catch (e) {
      print(e.message);
    }
  }
}

Finally, setup your RevenueCat instance like this:

// Setup
if (Platform.isIOS) {
  // iOS -- platform-specific setup to account for a bug where AppUserID is null (since library is using NSDefaults)
  // - https://github.com/RevenueCat/purchases-ios/issues/156
  // - https://github.com/RevenueCat/purchases-ios/issues/151
  // - https://github.com/RevenueCat/purchases-flutter/issues/49
  await MyAppPlatformChannel()
      .setupRevenueCatPurchasesCupertino(revenueCatSDKPublicKey);
} else if (Platform.isAndroid) {
  // Android -- normal setup
  await Purchases.setup(
    revenueCatSDKPublicKey,
  );
}

@aboedo aboedo self-assigned this May 18, 2020
@aboedo
Copy link
Member

aboedo commented May 18, 2020

Hi! Thanks for reporting and for posting a workaround! I'm sure that'll help folks until we get a fix out.
We'll make the parameter public on the next flutter release. I'll keep this open until then

@aboedo
Copy link
Member

aboedo commented Sep 21, 2020

forgot to close this one out, but this has been integrated into version 1.2.0

@aboedo aboedo closed this as completed Sep 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request pr:next_release
Projects
None yet
Development

No branches or pull requests

3 participants