From 34c6a63ae52c1bbca61920ac69facc69aa407fce Mon Sep 17 00:00:00 2001 From: Mark Phelps <209477+markphelps@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:06:25 -0400 Subject: [PATCH] chore: Swift readme (#496) * chore: add swift readme; add release scripts for swift and c# * chore: update main README Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> --------- Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> --- README.md | 2 +- flipt-client-swift/README.md | 115 +++++++++++++++++++++++++++++++++++ release/release.py | 10 ++- release/sdks/__init__.py | 2 + release/sdks/csharp.py | 32 ++++++++++ release/sdks/swift.py | 29 +++++++++ 6 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 flipt-client-swift/README.md create mode 100644 release/sdks/csharp.py create mode 100644 release/sdks/swift.py diff --git a/README.md b/README.md index bb5cc083..9be7c9f9 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Currently, we support the following languages/platforms: | [React Web (Browser)](./flipt-client-react) | WASM | N/A | N/A | | [Flutter/Dart](./flipt-client-dart) | FFI | ✅ | ❌ | | [C#](./flipt-client-csharp) | FFI | ✅ | ❌ | +| [Swift](./flipt-client-swift) | FFI | N/A | N/A | Documentation for each client can be found in the README of that client's directory. @@ -78,7 +79,6 @@ Languages we are planning to support: Languages we would like to support but lack expertise in: 1. [Kotlin](https://github.com/flipt-io/flipt-client-sdks/issues/264) -1. [Swift](https://github.com/flipt-io/flipt-client-sdks/issues/263) 1. [React Native](https://github.com/flipt-io/flipt-client-sdks/issues/345) Want to see a client in a language we don't support? [Open an issue](https://github.com/flipt-io/flipt-client-sdks/issues/new?assignees=&labels=new-language&projects=&template=new_language.yml) and let us know! diff --git a/flipt-client-swift/README.md b/flipt-client-swift/README.md new file mode 100644 index 00000000..3995518e --- /dev/null +++ b/flipt-client-swift/README.md @@ -0,0 +1,115 @@ +# Flipt Client Swift + +[![GitHub Release](https://img.shields.io/github/v/release/flipt-io/flipt-client-sdks?filter=flipt-client-swift-*)](https://github.com/flipt-io/flipt-client-sdks/releases) + +The `flipt-client-swift` library contains the Swift source code for the Flipt [client-side evaluation](https://www.flipt.io/docs/integration/client) client. + +## Installation + +### Swift Package Manager + +Add the following to your `Package.swift` file: + +```swift +dependencies: [ + .package(url: "https://github.com/flipt-io/flipt-client-swift.git", from: "0.1.0") +] +``` + +Or add it directly through Xcode: +1. File > Add Package Dependencies +2. Enter package URL: `https://github.com/flipt-io/flipt-client-swift` + +## How Does It Work? + +The `flipt-client-swift` library is a wrapper around the [flipt-engine-ffi](https://github.com/flipt-io/flipt-client-sdks/tree/main/flipt-engine-ffi) library. + +All evaluation happens within the SDK, using the shared library built from the [flipt-engine-ffi](https://github.com/flipt-io/flipt-client-sdks/tree/main/flipt-engine-ffi) library. + +Because the evaluation happens within the SDK, the SDKs can be used in environments where the Flipt server is not available or reachable after the initial data is fetched. + +## Data Fetching + +Upon instantiation, the `flipt-client-swift` library will fetch the flag state from the Flipt server and store it in memory. This means that the first time you use the SDK, it will make a request to the Flipt server. + +### Polling (Default) + +By default, the SDK will poll the Flipt server for new flag state at a regular interval. This interval can be configured using the `updateInterval` option when constructing a client. The default interval is 120 seconds. + +### Streaming (Flipt Cloud Only) + +[Flipt Cloud](https://flipt.io/cloud) users can use the `streaming` fetch method to stream flag state changes from the Flipt server to the SDK. + +When in streaming mode, the SDK will connect to the Flipt server and open a persistent connection that will remain open until the client is closed. The SDK will then receive flag state changes in real-time. + +## Supported Platforms + +This SDK currently supports the following platforms/architectures: + +- iOS arm64 +- iOS Simulator arm64 +- macOS arm64 + +## Usage + +```swift +import FliptClient + +// Initialize the client +let client = try FliptClient( + namespace: "default", + url: "http://localhost:8080", + authentication: .clientToken("your-token"), + updateInterval: 120, + fetchMode: .polling +) + +// Evaluate a boolean flag +let boolResult = try client.evaluateBoolean( + flagKey: "my-flag", + entityId: "user-123", + context: ["key": "value"] +) +print("Flag enabled: \(boolResult.enabled)") + +// Evaluate a variant flag +let variantResult = try client.evaluateVariant( + flagKey: "my-variant-flag", + entityId: "user-123", + context: ["key": "value"] +) +print("Variant key: \(variantResult.variantKey)") + +// Don't forget to close the client when you're done +defer { + client.close() +} +``` + +### Authentication + +The `FliptClient` supports the following authentication strategies: + +- No Authentication (default) +- [Client Token Authentication](https://docs.flipt.io/authentication/using-tokens) +- [JWT Authentication](https://docs.flipt.io/authentication/using-jwts) + +## Memory Management + +The engine that is allocated on the Rust side to compute evaluations for flag state will not be properly deallocated unless you call the `close()` method on a `FliptClient` instance. + +**Please be sure to do this to avoid leaking memory!** + +```swift +defer { + client.close() +} +``` + +## Contributing + +Contributions are welcome! Please feel free to open an issue or submit a Pull Request. + +## License + +This project is licensed under the [MIT License](LICENSE). diff --git a/release/release.py b/release/release.py index 023285e3..e009dda0 100644 --- a/release/release.py +++ b/release/release.py @@ -2,7 +2,7 @@ from semver import VersionInfo from prompt_toolkit.shortcuts import checkboxlist_dialog, radiolist_dialog from colorama import init, Fore, Style -from sdks import GoSDK, JavaSDK, JavaScriptSDK, RubySDK, PythonSDK, DartSDK +from sdks import GoSDK, JavaSDK, JavaScriptSDK, RubySDK, PythonSDK, DartSDK, SwiftSDK, CSharpSDK from sdks.base import SDK, MuslSupportSDK # Initialize colorama @@ -19,6 +19,8 @@ def get_sdk(name: str, path: str) -> SDK: "flipt-client-dart": DartSDK, "flipt-client-python": PythonSDK, "flipt-client-ruby": RubySDK, + "flipt-client-swift": SwiftSDK, + "flipt-client-csharp": CSharpSDK, } return sdk_classes[name](name, path) @@ -43,6 +45,8 @@ def update_sdk_versions(bump_type="patch", sdks_to_update=None): "flipt-client-dart", "flipt-client-python", "flipt-client-ruby", + "flipt-client-swift", + "flipt-client-csharp", ] sdk_dirs = sdks_to_update if sdks_to_update else all_sdk_dirs @@ -97,6 +101,8 @@ def get_sdk_selection(all_sdk_dirs): "flipt-client-dart": "Dart", "flipt-client-python": "Python", "flipt-client-ruby": "Ruby", + "flipt-client-swift": "Swift", + "flipt-client-csharp": "C#", } selected_sdks = checkboxlist_dialog( @@ -149,6 +155,8 @@ def main(): "flipt-client-dart", "flipt-client-python", "flipt-client-ruby", + "flipt-client-swift", + "flipt-client-csharp", ] selected_sdks = get_sdk_selection(all_sdk_dirs) diff --git a/release/sdks/__init__.py b/release/sdks/__init__.py index 5357572e..440efdee 100644 --- a/release/sdks/__init__.py +++ b/release/sdks/__init__.py @@ -4,3 +4,5 @@ from .ruby import RubySDK from .python import PythonSDK from .dart import DartSDK +from .swift import SwiftSDK +from .csharp import CSharpSDK diff --git a/release/sdks/csharp.py b/release/sdks/csharp.py new file mode 100644 index 00000000..37fa33eb --- /dev/null +++ b/release/sdks/csharp.py @@ -0,0 +1,32 @@ +import os +import re +from .base import SDK + + +class CSharpSDK(SDK): + def get_current_version(self): + return self._get_version_from_file("src/FliptClient/FliptClient.csproj") + + def _get_version_from_file(self, filename): + with open(os.path.join(self.path, filename), "r") as f: + content = f.read() + # 0.0.1 + match = re.search(r"([\d.]+)", content) + if match: + return match.group(1) + raise ValueError(f"Version not found in {filename}") + + def update_version(self, new_version): + self._update_version_in_file("src/FliptClient/FliptClient.csproj", new_version) + + def _update_version_in_file(self, filename, new_version): + file_path = os.path.join(self.path, filename) + with open(file_path, "r") as f: + content = f.read() + + updated_content = re.sub( + r"([\d.]+)", f"{new_version}", content + ) + + with open(file_path, "w") as f: + f.write(updated_content) diff --git a/release/sdks/swift.py b/release/sdks/swift.py new file mode 100644 index 00000000..4e56f336 --- /dev/null +++ b/release/sdks/swift.py @@ -0,0 +1,29 @@ +import subprocess +from colorama import Fore, Style +from .base import SDK + + +class SwiftSDK(SDK): + def get_current_version(self) -> str: + return self._get_version_from_tag(f"{self.name}-v*") + + def _get_version_from_tag(self, tag_pattern: str) -> str: + try: + result = subprocess.run( + ["git", "describe", "--tags", "--abbrev=0", "--match", tag_pattern], + capture_output=True, + text=True, + check=True, + ) + latest_tag = result.stdout.strip() + return latest_tag.split("-v")[-1] + except subprocess.CalledProcessError: + print( + f"{Fore.YELLOW}Warning: No tags found for {tag_pattern}. Using 0.0.0 as the base version.{Style.RESET_ALL}" + ) + return "0.0.0" + + def update_version(self, new_version: str): + print( + f"{Fore.YELLOW}Note: Version for {self.name} is managed via Git tags. No files updated.{Style.RESET_ALL}" + ) \ No newline at end of file