Skip to content
This repository has been archived by the owner on Dec 4, 2024. It is now read-only.

Commit

Permalink
feat: adds MAUI support
Browse files Browse the repository at this point in the history
  • Loading branch information
tanderson-ld committed Dec 18, 2023
1 parent ffc2e94 commit d01a865
Show file tree
Hide file tree
Showing 129 changed files with 1,480 additions and 12,056 deletions.
99 changes: 99 additions & 0 deletions .github/actions/ci/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: CI Action
inputs:
run_tests:
description: 'If true, run unit tests, otherwise skip them.'
required: false
default: 'true'

runs:
using: composite
steps:
- name: Download snk for signing assemblies
shell: bash
run: aws s3 cp s3://launchdarkly-releaser/dotnet/LaunchDarkly.ClientSdk.snk LaunchDarkly.ClientSdk.snk

- name: Setup dotnet build tools
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0

- name: Install MAUI Workload
shell: bash
run: dotnet workload install maui-android maui-ios maui-windows maui-maccatalyst --ignore-failed-sources

- name: Restore Dependencies
shell: bash
run: dotnet restore src/LaunchDarkly.ClientSdk/LaunchDarkly.ClientSdk.csproj

# - name: Build for NetStandard2.0
# shell: bash
# run: dotnet build /p:Configuration=release /p:TargetFramework=netstandard2.0 src/LaunchDarkly.ClientSdk/LaunchDarkly.ClientSdk.csproj

- name: Build for Net7
shell: bash
run: dotnet build /p:Configuration=release /p:TargetFramework=net7.0 src/LaunchDarkly.ClientSdk/LaunchDarkly.ClientSdk.csproj

- name: Build for Net7-android
shell: bash
run: dotnet build /p:Configuration=release /p:TargetFramework=net7.0-android src/LaunchDarkly.ClientSdk/LaunchDarkly.ClientSdk.csproj

- name: Build for Net7-ios
shell: bash
run: dotnet build /p:Configuration=release /p:TargetFramework=net7.0-ios src/LaunchDarkly.ClientSdk/LaunchDarkly.ClientSdk.csproj

- name: Build for Net7-windows
shell: bash
run: dotnet build /p:Configuration=release /p:TargetFramework=net7.0-maccatalyst src/LaunchDarkly.ClientSdk/LaunchDarkly.ClientSdk.csproj

- name: Build for Net7-maccatalyst
shell: bash
run: dotnet build /p:Configuration=release /p:TargetFramework=net7.0-windows src/LaunchDarkly.ClientSdk/LaunchDarkly.ClientSdk.csproj

- name: Run Unit Tests for Net7
run: |
dotnet restore tests/LaunchDarkly.ClientSdk.Tests
dotnet test -v=normal \
--logger:"junit;LogFilePath=/tmp/circle-reports/unit-tests.xml" \
tests/LaunchDarkly.ClientSdk.Tests/LaunchDarkly.ClientSdk.Tests.csproj
- name: Build Contract Tests
if: inputs.run_tests == 'true'
run: dotnet build contract-tests/TestService.csproj

- name: Run Contract Tests
if: inputs.run_tests == 'true'
run: |
dotnet contract-tests/bin/release/net7.0/ContractTestService.dll > test-service.log 2>&1 & disown
curl -s https://raw.githubusercontent.com/launchdarkly/sdk-test-harness/main/downloader/run.sh | VERSION=v2 PARAMS="-url http://localhost:8000 -debug -stop-service-at-end \
-junit /tmp/circle-reports/contract-tests-junit.xml" sh
- name: Build Test App
if: inputs.run_tests == 'true'
run: |
dotnet build /restore /p:Configuration=release \
tests/LaunchDarkly.ClientSdk.Device.Tests/LaunchDarkly.ClientSdk.Device.Tests.csproj
# - name: Set up JDK 17
# if: inputs.run_tests == 'true'
# uses: actions/setup-java@v3
# with:
# java-version: '17'
# distribution: 'temurin'

# - name: Setup Android Manager
# if: inputs.run_tests == 'true'
# uses: android-actions/setup-android@v3

# TODO: Tests are not auto executing, so this is commented out. For now this must be done manually.
# - name: Run Android Test App on Emulator
# if: inputs.run_tests == 'true'
# uses: reactivecircus/android-emulator-runner@v2
# with:
# api-level: 27
# script: |
# dotnet run --framework net7.0-android --project tests/LaunchDarkly.ClientSdk.Device.Tests/LaunchDarkly.ClientSdk.Device.Tests.csproj
# adb install tests/LaunchDarkly.ClientSdk.Device.Tests/bin/release/net7.0-android/com.LaunchDarkly.ClientSdk.Device.Tests-Signed.apk
# ( adb logcat DOTNET:D AndroidRuntime:D & ) | tee test-run.log | grep -q 'Tests run:'
# cat test-run.log | tr -s ' ' | cut -d ' ' -f 1,2,7-
# if grep '\[FAIL\]' test-run.log >/dev/null; then exit 1; fi

29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Run CI
on:
push:
branches: [main, 'feat/**']
paths-ignore:
- '**.md' # Do not need to run CI for markdown changes.
pull_request:
branches: [main, 'feat/**']
paths-ignore:
- '**.md'

jobs:
ci-build:
runs-on: macos-latest-large
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # If you only need the current version keep this.

- uses: launchdarkly/gh-actions/actions/[email protected]
name: Get secrets
with:
aws_assume_role: ${{ vars.AWS_ROLE_ARN }}
ssm_parameter_pairs: '/production/common/releasing/digicert/host = DIGICERT_HOST,/production/common/releasing/digicert/api_key = DIGICERT_API_KEY,/production/common/releasing/digicert/client_cert_file_b64 = DIGICERT_CLIENT_CERT_FILE_B64,/production/common/releasing/digicert/client_cert_password = DIGICERT_CLIENT_CERT_PASSWORD,/production/common/releasing/digicert/code_signing_cert_sha1_hash = DIGICERT_CODE_SIGNING_CERT_SHA1_HASH'

- uses: ./.github/actions/ci
12 changes: 12 additions & 0 deletions .github/workflows/lint-pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Lint PR title

on:
pull_request_target:
types:
- opened
- edited
- synchronize

jobs:
lint-pr-title:
uses: launchdarkly/gh-actions/.github/workflows/lint-pr-title.yml@main
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ We encourage pull requests and other contributions from the community. Before su

### Prerequisites

The .NET Standard target requires only the .NET Core 2.1 SDK or higher, while the iOS and Android targets require the corresponding Xamarin SDKs.
The .NET 7 target requires only the .NET Core 2.1 SDK or higher, while the iOS, Android, MacCatalys, and Windows targets require the corresponding MAUI SDKs.

### Building

Expand Down
30 changes: 26 additions & 4 deletions LaunchDarkly.ClientSdk.sln
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ VisualStudioVersion = 15.0.26730.16
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaunchDarkly.ClientSdk", "src\LaunchDarkly.ClientSdk\LaunchDarkly.ClientSdk.csproj", "{7717A2B2-9905-40A7-989F-790139D69543}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaunchDarkly.ClientSdk.Tests", "tests\LaunchDarkly.ClientSdk.Tests\LaunchDarkly.ClientSdk.Tests.csproj", "{F6B71DFE-314C-4F27-A219-A14569C8CF48}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaunchDarkly.ClientSdk.Device.Tests", "tests\LaunchDarkly.ClientSdk.Device.Tests\LaunchDarkly.ClientSdk.Device.Tests.csproj", "{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaunchDarkly.ClientSdk.iOS.Tests", "tests\LaunchDarkly.ClientSdk.iOS.Tests\LaunchDarkly.ClientSdk.iOS.Tests.csproj", "{5EFF7561-35C1-4C62-B0BE-A76E37DCEB32}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaunchDarkly.ClientSdk.Android.Tests", "tests\LaunchDarkly.ClientSdk.Android.Tests\LaunchDarkly.ClientSdk.Android.Tests.csproj", "{2E7720E4-01A0-403B-863C-C6C596DF5926}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaunchDarkly.ClientSdk.Tests", "tests\LaunchDarkly.ClientSdk.Tests\LaunchDarkly.ClientSdk.Tests.csproj", "{36701E5A-EC04-4B47-8739-BACCCB673C77}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -68,6 +66,30 @@ Global
{2E7720E4-01A0-403B-863C-C6C596DF5926}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{2E7720E4-01A0-403B-863C-C6C596DF5926}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{2E7720E4-01A0-403B-863C-C6C596DF5926}.Debug|iPhone.Build.0 = Debug|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Release|Any CPU.Build.0 = Release|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Release|iPhone.ActiveCfg = Release|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Release|iPhone.Build.0 = Release|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{0D88C80E-8CD8-4064-AE99-9849C7CD6E35}.Debug|iPhone.Build.0 = Debug|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Debug|Any CPU.Build.0 = Debug|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Release|Any CPU.ActiveCfg = Release|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Release|Any CPU.Build.0 = Release|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Release|iPhone.ActiveCfg = Release|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Release|iPhone.Build.0 = Release|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{36701E5A-EC04-4B47-8739-BACCCB673C77}.Debug|iPhone.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

The LaunchDarkly Client-Side SDK for .NET is designed primarily for use by code that is deployed to an end user, such as in a desktop application or a smart device. It follows the client-side LaunchDarkly model for single-user contexts (much like our mobile or JavaScript SDKs). It is not intended for use in multi-user systems such as web servers and applications.

On supported mobile platforms (Android and iOS), the SDK uses the Xamarin framework which allows .NET code to run on those devices. For that reason, its name was previously "LaunchDarkly Xamarin SDK". However, Xamarin is not the only way to run .NET code in a client-side context (see "Supported platforms" below), so the SDK now has a more general name.
On platforms with MAUI support (Android, iOS, Mac, Windows), the SDK depends on the MAUI framework which allows .NET code to run on those devices. However, MAUI is not the only way to run .NET code in a client-side context (see "Supported platforms" below), so the SDK has a more general name.

For using LaunchDarkly in *server-side* .NET applications, refer to our [Server-Side .NET SDK](https://github.com/launchdarkly/dotnet-server-sdk).

Expand All @@ -20,11 +20,13 @@ For using LaunchDarkly in *server-side* .NET applications, refer to our [Server-

This version of the SDK is built for the following targets:

* Xamarin Android 8.1, for use with Android 8.1 (Android API 27) and higher.
* Xamarin iOS 10, for use with iOS 10 and higher.
* .NET Standard 2.0, for use with any runtime platform that supports .NET Standard 2.0, or in portable .NET Standard library code.
* .Net 7 Android, for use with Android 5.0 (Android API 21) and higher.
* .Net 7 iOS, for use with iOS 11 and higher.
* .Net 7 macOS (using Mac Catalyst), for use with macOS 10.15 and higher.
* .Net 7 Windows (using WinUI), for Windows 11 and Windows 10 version 1809 or higher.
* .NET 7, for use with any runtime platform that supports .NET Standard 2.1, or in portable .NET Standard library code.

The .NET Standard 2.0 target does not use any Xamarin packages and has no OS-specific code. This allows the SDK to be used in a desktop .NET Framework or .NET 5.0 application, or in a Xamarin MacOS application. However, due to the lack of OS-specific integration, SDK functionality will be limited in those environments: for instance, the SDK will not be able to detect whether networking is turned on or off.
The .NET 7 target does not use any MAUI packages and has no OS-specific code. This allows the SDK to be used in a desktop .NET Framework or .NET 7.0 application. However, due to the lack of OS-specific integration, SDK functionality will be limited in those environments: for instance, the SDK will not be able to detect whether networking is turned on or off.

The .NET build tools should automatically load the most appropriate build of the SDK for whatever platform your application or library is targeted to.

Expand Down
2 changes: 1 addition & 1 deletion contract-tests/TestService.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TestFramework Condition="'$(TESTFRAMEWORK)' == ''">net6.0</TestFramework>
<TestFramework Condition="'$(TESTFRAMEWORK)' == ''">net7.0</TestFramework>
<TargetFrameworks>$(TESTFRAMEWORK)</TargetFrameworks>
<DebugType>portable</DebugType>
<AssemblyName>ContractTestService</AssemblyName>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ public sealed class EventProcessorBuilder : IComponentConfigurer<IEventProcessor
/// undesirable in a mobile application as opposed to a desktop application.
/// </remarks>
public static readonly TimeSpan DefaultFlushInterval =
#if NETSTANDARD
TimeSpan.FromSeconds(5);
#else
#if (ANDROID || IOS)
TimeSpan.FromSeconds(30);
#else
TimeSpan.FromSeconds(5);
#endif

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public sealed class HttpConfigurationBuilder : IDiagnosticDescription
/// </para>
/// <para>
/// Not all .NET platforms support setting a connection timeout. It is supported in
/// .NET Core 2.1+, .NET 5+, and Xamarin Android, but not in Xamarin iOS. On platforms
/// .NET Core 2.1+, .NET 5+, and MAUI Android, but not in MAUI iOS. On platforms
/// where it is not supported, only <see cref="ResponseStartTimeout"/> will be used.
/// </para>
/// <para>
Expand Down Expand Up @@ -109,8 +109,7 @@ public HttpConfigurationBuilder CustomHeader(string name, string value)
/// <remarks>
/// This is mainly useful for testing, to cause the SDK to use custom logic instead of actual HTTP requests,
/// but can also be used to customize HTTP behavior on platforms where the default handler is not optimal.
/// The default is the usual native HTTP handler for the current platform, if any (for instance,
/// <c>Xamarin.Android.Net.AndroidClientHandler</c>), or else <see cref="System.Net.Http.HttpClientHandler"/>.
/// The default is the usual native HTTP handler for the current platform, else <see cref="System.Net.Http.HttpClientHandler"/>.
/// </remarks>
/// <param name="messageHandler">the message handler, or null to use the platform's default handler</param>
/// <returns>the builder</returns>
Expand Down Expand Up @@ -203,7 +202,7 @@ public HttpConfigurationBuilder ResponseStartTimeout(TimeSpan responseStartTimeo
/// </remarks>
/// <param name="useReport">true to enable the REPORT method</param>
/// <returns>the builder</returns>
#if !MONOANDROID
#if !ANDROID
public HttpConfigurationBuilder UseReport(bool useReport)
#else
internal HttpConfigurationBuilder UseReport(bool useReport)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal sealed class DefaultConnectivityStateManager : IConnectivityStateManage
internal DefaultConnectivityStateManager()
{
UpdateConnectedStatus();
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
PlatformConnectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
}

bool isConnected;
Expand All @@ -23,16 +23,16 @@ bool IConnectivityStateManager.IsConnected
isConnected = value;
}
}
void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)

void Connectivity_ConnectivityChanged(object sender, EventArgs e)
{
UpdateConnectedStatus();
ConnectionChanged?.Invoke(isConnected);
}

private void UpdateConnectedStatus()
{
isConnected = Connectivity.NetworkAccess == NetworkAccess.Internet;
isConnected = PlatformConnectivity.LdNetworkAccess == LdNetworkAccess.Internet;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal class ClientDiagnosticStore : DiagnosticStoreBase
protected override string SdkKeyOrMobileKey => _context.MobileKey;
protected override string SdkName => SdkPackage.Name;
protected override IEnumerable<LdValue> ConfigProperties => GetConfigProperties();
protected override string DotNetTargetFramework => GetDotNetTargetFramework();
protected override string DotNetTargetFramework => SdkPackage.DotNetTargetFramework;
protected override HttpProperties HttpProperties => _context.Http.HttpProperties;
protected override Type TypeOfLdClient => typeof(LdClient);

Expand Down Expand Up @@ -50,25 +50,5 @@ private IEnumerable<LdValue> GetConfigProperties()
private LdValue GetComponentDescription(object component) =>
component is IDiagnosticDescription dd ?
dd.DescribeConfiguration(_context) : LdValue.Null;

internal static string GetDotNetTargetFramework()
{
// Note that this is the _target framework_ that was selected at build time based on the application's
// compatibility requirements; it doesn't tell us anything about the actual OS version. We'll need to
// update this whenever we add or remove supported target frameworks in the .csproj file.
#if NETSTANDARD2_0
return "netstandard2.0";
#elif MONOANDROID71
return "monoandroid7.1";
#elif MONOANDROID80
return "monoandroid8.0";
#elif MONOANDROID81
return "monoandroid8.1";
#elif XAMARIN_IOS10
return "xamarinios1.0";
#else
return "unknown";
#endif
}
}
}
27 changes: 26 additions & 1 deletion src/LaunchDarkly.ClientSdk/Internal/SdkPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal static class SdkPackage
/// The prefix for the User-Agent header, omitting the version string. This may be different than the Name
/// due to historical reasons.
/// </summary>
private const string UserAgentPrefix = "XamarinClient";
private const string UserAgentPrefix = "DotnetClientSide";

/// <summary>
/// Version of the SDK.
Expand All @@ -29,5 +29,30 @@ internal static class SdkPackage
/// </summary>
internal static string UserAgent => $"{UserAgentPrefix}/{Version}";

/// <summary>
/// The target framework selected at build time.
/// </summary>
/// <remarks>
/// This is the _target framework_ that was selected at build time based
/// on the application's compatibility requirements; it doesn't tell
/// anything about the actual OS version.
/// </remarks>
internal static string DotNetTargetFramework =>
// We'll need to update this whenever we add or remove supported target frameworks in the .csproj file.
// Order of these conditonals matters. Specific frameworks come before net7.0 intentionally.
#if ANDROID
"net7.0-android";
#elif IOS
"net7.0-ios";
#elif MACCATALYST
"net7.0-maccatalyst";
#elif WINDOWS
"net7.0-windows";
#elif NET7_0
"net7.0";
#else
"unknown";
#endif

}
}
Loading

0 comments on commit d01a865

Please sign in to comment.