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

Natively support Launch Darkly #117

Merged
merged 14 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: release
on:
push:
tags:
- '**'
- trunk

env:
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false"
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
.gradle
**/build/
local.properties
.kotlin
.kotlin
integrations/launch-darkly/exportedLaunchDarkly/
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ kotlin.code.style=official
kotlin.jvm.target=11

GROUP=io.github.kevincianfarini.monarch
VERSION_NAME=0.2.2
VERSION_NAME=0.3.0-SNAPSHOT
Copy link
Owner Author

Choose a reason for hiding this comment

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

Going to push a snapshot release and dogfood it to see if there's anything weird going on with static linking.


POM_INCEPTION_YEAR=2024

Expand All @@ -25,4 +25,4 @@ SONATYPE_AUTOMATIC_RELEASE=true
SONATYPE_HOST=S01
RELEASE_SIGNING_ENABLED=true

android.useAndroidX=true
android.useAndroidX=true
4 changes: 3 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ kotlinx-serialization = "1.7.3"
launchdarkly-android = "5.5.0"
molecule = "2.0.0"
publish = "0.30.0"
spmForKmp = "0.0.8"
turbine = "1.2.0"

[libraries]
Expand All @@ -31,4 +32,5 @@ dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
publish = { id = "com.vanniktech.maven.publish", version.ref = "publish" }
publish = { id = "com.vanniktech.maven.publish", version.ref = "publish" }
spmForKmp = { id = "io.github.frankois944.spmForKmp", version.ref = "spmForKmp" }
33 changes: 30 additions & 3 deletions integrations/launch-darkly/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import io.github.frankois944.spmForKmp.definition.SwiftDependency
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import java.net.URI

plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.dokka)
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.publish)
alias(libs.plugins.spmForKmp)
}

android {
Expand All @@ -20,9 +25,9 @@ kotlin {
explicitApi()
jvmToolchain(17)

iosArm64()
iosSimulatorArm64()
iosX64()
iosArm64 { configureSpmInterop() }
iosSimulatorArm64 { configureSpmInterop() }
iosX64 { configureSpmInterop() }
androidTarget {
publishLibraryVariants("release")
}
Expand All @@ -40,4 +45,26 @@ kotlin {
api(libs.launchdarkly.android)
}
}
}

private fun KotlinNativeTarget.configureSpmInterop() {
compilations {
val main by getting {
cinterops.create("swiftlaunchdarkly")
}
}
}

swiftPackageConfig {
create("swiftlaunchdarkly") {
dependency(
SwiftDependency.Package.Remote.Version(
url = URI("https://github.com/launchdarkly/ios-client-sdk.git"),
version = "9.12.0",
products = {
add("LaunchDarkly", exportToKotlin = true)
}
)
)
}
}
25 changes: 25 additions & 0 deletions integrations/launch-darkly/exportedSwiftlaunchdarkly/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
name: "exportedSwiftlaunchdarkly",
platforms: [.iOS("12.0"), .macOS("10.13"), .tvOS("12.0"), .watchOS("4.0")],
products: [
.library(
name: "exportedSwiftlaunchdarkly",
type: .static,
targets: ["exportedSwiftlaunchdarkly"])
],
dependencies: [
.package(url: "https://github.com/launchdarkly/ios-client-sdk.git", exact: "9.12.0")
],
targets: [
.target(
name: "exportedSwiftlaunchdarkly",
dependencies: [
.product(name: "LaunchDarkly", package: "ios-client-sdk")
],
path: "Sources")

]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import Foundation
4 changes: 3 additions & 1 deletion integrations/launch-darkly/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
POM_ARTIFACT_ID=launch-darkly-integration
POM_NAME=Monarch Launch Darkly Integration
POM_DESCRIPTION=Multiplatform integration with Launch Darkly feature flag SDKs
POM_DESCRIPTION=Multiplatform integration with Launch Darkly feature flag SDKs

kotlin.mpp.enableCInteropCommonization=true
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ private class FakeLDClient : LDClientInterface, MutableLDClientInterface {
}

override fun intVariation(p0: String, p1: Int): Int {
return (flagValues[p0] as? Int) ?: p1
return (flagValues[p0] as? Long)?.toInt() ?: p1
}

override fun doubleVariation(p0: String, p1: Double): Double {
Expand Down Expand Up @@ -67,7 +67,7 @@ private class FakeLDClient : LDClientInterface, MutableLDClientInterface {
listeners[flagKey]?.forEach { it.onFeatureFlagChange(flagKey) }
}

override fun setVariation(flagKey: String, value: Int) {
override fun setVariation(flagKey: String, value: Long) {
flagValues[flagKey] = value
listeners[flagKey]?.forEach { it.onFeatureFlagChange(flagKey) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ interface MutableLDClientInterface {
fun setVariation(flagKey: String, value: Boolean)
fun setVariation(flagKey: String, value: String)
fun setVariation(flagKey: String, value: Double)
fun setVariation(flagKey: String, value: Int)
fun setVariation(flagKey: String, value: Long)
}

Original file line number Diff line number Diff line change
@@ -1,47 +1,75 @@
package io.github.kevincianfarini.monarch.launchdarkly

import LaunchDarkly.LDClient
import kotlinx.cinterop.ExperimentalForeignApi

/**
* A temporary, experimental shim to allow iOS consumers of Monarch to wire their own LDClient
* as a data store using [LaunchDarklyClientShim.asFeatureFlagDataStore]. This interface will be
* removed in future versions of this library when future, first-party support of LaunchDarkly
* is available.
* An interface around [LDClient].
*/
public interface LaunchDarklyClientShim {
internal interface LaunchDarklyClientShim {

/**
* Return a [Boolean] value [forKey], or [default] if no value exists.
*/
public fun boolVariation(forKey: String, default: Boolean): Boolean
fun boolVariation(forKey: String, default: Boolean): Boolean

/**
* Return an [Int] value [forKey], or [default] if no value exists.
*/
public fun intVariation(forKey: String, default: Int): Int
fun longVariation(forKey: String, default: Long): Long

/**
* Return a [Double] value [forKey], or [default] if no value exists.
*/
public fun doubleVariation(forKey: String, default: Double): Double
fun doubleVariation(forKey: String, default: Double): Double

/**
* Return a [String] value [forKey], or [default] if no value exists.
*/
public fun stringVariation(forKey: String, default: String): String
fun stringVariation(forKey: String, default: String): String

/**
* Register a [handler] to be invoked when the value associated with [key] changes, scoped
* to [owner].
*/
public fun observe(key: String, owner: ObserverOwner, handler: () -> Unit)
fun observe(key: String, owner: ObserverOwner, handler: () -> Unit)

/**
* Unregister all observers scoped to [owner].
*/
public fun stopObserving(owner: ObserverOwner)
fun stopObserving(owner: ObserverOwner)
}

/**
* A marker object used in [LaunchDarklyClientShim.observe] and
* [LaunchDarklyClientShim.stopObserving].
*/
public class ObserverOwner internal constructor()
internal class ObserverOwner internal constructor()

@OptIn(ExperimentalForeignApi::class)
internal class RealLaunchDarklyShim(private val client: LDClient) : LaunchDarklyClientShim {

override fun boolVariation(forKey: String, default: Boolean): Boolean {
return client.boolVariationForKey(forKey, default)
}

override fun longVariation(forKey: String, default: Long): Long {
return client.integerVariationForKey(forKey, default)
}

override fun doubleVariation(forKey: String, default: Double): Double {
return client.doubleVariationForKey(forKey, default)
}

override fun stringVariation(forKey: String, default: String): String {
return client.stringVariationForKey(forKey, default)
}

override fun observe(key: String, owner: ObserverOwner, handler: () -> Unit) {
client.observe(key, owner) { handler() }
}

override fun stopObserving(owner: ObserverOwner) {
client.stopObservingForOwner(owner)
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
package io.github.kevincianfarini.monarch.launchdarkly

import LaunchDarkly.LDClient
import io.github.kevincianfarini.monarch.ObservableFeatureFlagDataStore
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.pin
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import LaunchDarkly.LDConfig
import LaunchDarkly.LDContext
import platform.Foundation.NSTimeInterval

/**
* Represent this [LaunchDarklyClientShim] as an [ObservableFeatureFlagDataStore].
*/
public fun LaunchDarklyClientShim.asFeatureFlagDataStore(): ObservableFeatureFlagDataStore {
return LaunchDarklyFeatureFlagDataStore(this)
@Suppress("FunctionName")
@OptIn(ExperimentalForeignApi::class)
public fun LaunchDarklyFeatureFlagDataStore(
config: LDConfig,
context: LDContext,
startWaitSeconds: NSTimeInterval = 0.0,
completion: ((didTimeOut: Boolean) -> Unit)? = null,
): ObservableFeatureFlagDataStore {
LDClient.startWithConfiguration(config, context, startWaitSeconds, completion)
return LaunchDarklyFeatureFlagDataStore(
RealLaunchDarklyShim(LDClient.get()!!)
)
}

private class LaunchDarklyFeatureFlagDataStore(
internal class LaunchDarklyFeatureFlagDataStore(
private val shim: LaunchDarklyClientShim
) : ObservableFeatureFlagDataStore {

Expand Down Expand Up @@ -70,7 +81,7 @@ private inline fun <reified T : Any> LaunchDarklyClientShim.getValue(key: String
Boolean::class -> boolVariation(key, default as Boolean) as T
String::class -> stringVariation(key, default as String) as T
Double::class -> doubleVariation(key, default as Double) as T
Long::class -> intVariation(key, (default as Long).toInt()).toLong() as T
Long::class -> longVariation(key, default as Long) as T
else -> throw IllegalArgumentException("Illegal type for getValue: $clazz")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import io.github.kevincianfarini.monarch.ObservableFeatureFlagDataStore

actual fun sut(): Pair<ObservableFeatureFlagDataStore, MutableLDClientInterface> {
val client = FakeLDShim()
return Pair(client.asFeatureFlagDataStore(), client)
return Pair(LaunchDarklyFeatureFlagDataStore(client), client)
}

private class FakeLDShim : LaunchDarklyClientShim, MutableLDClientInterface {
Expand All @@ -27,7 +27,7 @@ private class FakeLDShim : LaunchDarklyClientShim, MutableLDClientInterface {
listeners.filter { it.key == flagKey }.forEach { it.handler() }
}

override fun setVariation(flagKey: String, value: Int) {
override fun setVariation(flagKey: String, value: Long) {
flagValues[flagKey] = value
listeners.filter { it.key == flagKey }.forEach { it.handler() }
}
Expand All @@ -36,8 +36,8 @@ private class FakeLDShim : LaunchDarklyClientShim, MutableLDClientInterface {
return (flagValues[forKey] as? Boolean) ?: default
}

override fun intVariation(forKey: String, default: Int): Int {
return (flagValues[forKey] as? Int) ?: default
override fun longVariation(forKey: String, default: Long): Long {
return (flagValues[forKey] as? Long) ?: default
}

override fun doubleVariation(forKey: String, default: Double): Double {
Expand Down
Loading