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

Support on feature flags loaded #111

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ android {
}

dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.0.0'
// + Version 3.+ and the versions up to 4.0, not including 4.0 and higher
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

/** PosthogFlutterPlugin */
class PosthogFlutterPlugin : FlutterPlugin, MethodCallHandler {
Expand All @@ -29,6 +33,8 @@ class PosthogFlutterPlugin : FlutterPlugin, MethodCallHandler {
channel.setMethodCallHandler(this)
}

private val isLoaded = MutableStateFlow(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd not use coroutines but rather implement a dependency-free mechanism, there are a few days to do this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense


private fun initPlugin(applicationContext: Context) {
try {
// TODO: replace deprecated method API 33
Expand All @@ -53,6 +59,9 @@ class PosthogFlutterPlugin : FlutterPlugin, MethodCallHandler {
debug = enableDebug
sdkName = "posthog-flutter"
sdkVersion = postHogVersion
onFeatureFlags = PostHogOnFeatureFlags {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the callback is a good idea but it'd not work in some cases, for example
https://github.com/PostHog/posthog-android/blob/8013cccadfe52ad347eedcb492ce01690a1f7786/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt#L40-L45
in this case, the callback will never be called, we'd need to either change the PostHogOnFeatureFlags interface which is a breaking change or find a way to expose the loaded flags differently (on the iOS and Android SDK)

isLoaded.value = true;
}
}
PostHogAndroid.setup(applicationContext, config)

Expand All @@ -64,74 +73,25 @@ class PosthogFlutterPlugin : FlutterPlugin, MethodCallHandler {
override fun onMethodCall(call: MethodCall, result: Result) {

when (call.method) {

"identify" -> {
identify(call, result)
}

"capture" -> {
capture(call, result)
}

"screen" -> {
screen(call, result)
}

"alias" -> {
alias(call, result)
}

"distinctId" -> {
distinctId(result)
}

"reset" -> {
reset(result)
}

"disable" -> {
disable(result)
}

"enable" -> {
enable(result)
}

"isFeatureEnabled" -> {
isFeatureEnabled(call, result)
}

"reloadFeatureFlags" -> {
reloadFeatureFlags(result)
}

"group" -> {
group(call, result)
}

"getFeatureFlag" -> {
getFeatureFlag(call, result)
}

"getFeatureFlagPayload" -> {
getFeatureFlagPayload(call, result)
}

"register" -> {
register(call, result)
}
"unregister" -> {
unregister(call, result)
}
"debug" -> {
debug(call, result)
}
"flush" -> {
flush(result)
}
else -> {
result.notImplemented()
}
"identify" -> identify(call, result)
"capture" -> capture(call, result)
"screen" -> screen(call, result)
"alias" -> alias(call, result)
"distinctId" -> distinctId(result)
"reset" -> reset(result)
"disable" -> disable(result)
"enable" -> enable(result)
"isFeatureEnabled" -> isFeatureEnabled(call, result)
"reloadFeatureFlags" -> reloadFeatureFlags(result)
"group" -> group(call, result)
"getFeatureFlag" -> getFeatureFlag(call, result)
"getFeatureFlagPayload" -> getFeatureFlagPayload(call, result)
"register" -> register(call, result)
"unregister" -> unregister(call, result)
"debug" -> debug(call, result)
"flush" -> flush(result)
"awaitFeatureFlagsLoaded" -> awaitFeatureFlagsLoaded(result)
else -> result.notImplemented()
}

}
Expand Down Expand Up @@ -269,6 +229,20 @@ class PosthogFlutterPlugin : FlutterPlugin, MethodCallHandler {
}
}

private fun awaitFeatureFlagsLoaded(result: Result){
try {
GlobalScope.launch {
isLoaded.collect { isLoaded ->
if(isLoaded){
result.success()
}
}
}
} catch (error: Throwable){
result.error("PosthogFlutterException", e.localizedMessage, null)
}
}

private fun group(call: MethodCall, result: Result) {
try {
val groupType: String = call.argument("groupType")!!
Expand Down
21 changes: 21 additions & 0 deletions ios/Classes/PosthogFlutterPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import AppKit
#endif

public class PosthogFlutterPlugin: NSObject, FlutterPlugin {

let isLoadedSubject = CurrentValueSubject<Bool, Error>(false)

public static func register(with registrar: FlutterPluginRegistrar) {
#if os(iOS)
let channel = FlutterMethodChannel(name: "posthog_flutter", binaryMessenger: registrar.messenger())
Expand Down Expand Up @@ -44,6 +47,9 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin {
postHogSdkName = "posthog-flutter"
postHogVersion = postHogFlutterVersion

NotificationCenter.default.addObserver(forName: PostHogSDK.didReceiveFeatureFlags, object: nil, queue: nil) { (notification) in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to https://github.com/PostHog/posthog-flutter/pull/111/files#r1624601103
but in this case didReceiveFeatureFlags is called even if there's an error, we'd probably need something more so we know if feature flags are errored or not.

isLoadedSubject.send(true)
}
PostHogSDK.shared.setup(config)
//
}
Expand Down Expand Up @@ -84,6 +90,8 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin {
unregister(call, result: result)
case "flush":
flush(result)
case "awaitFeatureFlagsLoaded":
awaitFeatureFlagsLoaded(result)
default:
result(FlutterMethodNotImplemented)
}
Expand Down Expand Up @@ -242,6 +250,19 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin {
result(nil)
}

private func awaitFeatureFlagsLoaded(_ result: @escaping FlutterResult
) {
do {
isLoadedSubject.sink { completion in
if completion {
result(nil)
}
}
} catch let error {
result(FlutterError(code: "PosthogFlutterException", message: error.localizedDescription, details: nil))
}
}

private func group(
_ call: FlutterMethodCall,
result: @escaping FlutterResult
Expand Down
9 changes: 9 additions & 0 deletions lib/src/posthog_flutter_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface {
}
}

@override
Future<void> awaitFeatureFlagsLoaded() async {
try {
await _methodChannel.invokeMethod('awaitFeatureFlagsLoaded');
} on PlatformException catch (exception) {
_printIfDebug('Exception on awaitFeatureFlagsLoaded: $exception');
}
}

@override
Future<void> group({
required String groupType,
Expand Down
5 changes: 5 additions & 0 deletions lib/src/posthog_flutter_platform_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ abstract class PosthogFlutterPlatformInterface extends PlatformInterface {
throw UnimplementedError('reloadFeatureFlags() has not been implemented.');
}

/// Returns a Future, that completes only once the Feature Flags are loaded
Future<void> awaitFeatureFlagsLoaded() {
throw UnimplementedError('awaitFeatureFlagsLoaded() has not been implemented.');
}

Future<void> group({
required String groupType,
required String groupKey,
Expand Down
Loading