From c976b5f8affcc5bf78579a48a1379c88e5f738a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Wed, 31 Aug 2022 12:09:05 -0300 Subject: [PATCH 01/25] Migrate to internal package (#44) --- .github/workflows/build.yml | 13 ++++---- .github/workflows/test.yml | 4 +-- .gitignore | 13 -------- splitio/.gitignore | 29 ++++++++++++++++++ .metadata => splitio/.metadata | 0 CHANGELOG.md => splitio/CHANGELOG.md | 0 .../CONTRIBUTORS-GUIDE.md | 0 LICENSE => splitio/LICENSE | 0 README.md => splitio/README.md | 4 +-- .../analysis_options.yaml | 0 {android => splitio/android}/.gitignore | 0 {android => splitio/android}/build.gradle | 0 {android => splitio/android}/settings.gradle | 0 .../android}/src/main/AndroidManifest.xml | 0 .../java/io/split/splitio/ArgumentParser.java | 0 .../io/split/splitio/ArgumentParserImpl.java | 0 .../io/split/splitio/AttributesWrapper.java | 0 .../main/java/io/split/splitio/Constants.java | 0 .../io/split/splitio/EvaluationWrapper.java | 0 .../main/java/io/split/splitio/Helper.java | 0 .../split/splitio/ImpressionListenerImp.java | 0 .../splitio/SplitClientConfigHelper.java | 0 .../split/splitio/SplitFactoryProvider.java | 0 .../splitio/SplitFactoryProviderImpl.java | 0 .../io/split/splitio/SplitMethodParser.java | 0 .../split/splitio/SplitMethodParserImpl.java | 0 .../io/split/splitio/SplitProviderHelper.java | 0 .../java/io/split/splitio/SplitWrapper.java | 0 .../io/split/splitio/SplitWrapperImpl.java | 0 .../java/io/split/splitio/SplitioPlugin.java | 0 .../split/splitio/ArgumentParserImplTest.java | 0 .../splitio/ImpressionListenerImpTest.java | 0 .../splitio/SplitClientConfigHelperTest.java | 0 .../splitio/SplitMethodParserImplTest.java | 0 .../splitio/SplitProviderHelperImplTest.java | 0 .../split/splitio/SplitWrapperImplTest.java | 0 {example => splitio/example}/.gitignore | 0 {example => splitio/example}/.metadata | 0 {example => splitio/example}/README.md | 0 .../example}/analysis_options.yaml | 0 .../example}/android/.gitignore | 0 .../example}/android/app/build.gradle | 0 .../android/app/src/debug/AndroidManifest.xml | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../split/splitio_example/MainActivity.java | 0 .../res/drawable-v21/launch_background.xml | 0 .../main/res/drawable/launch_background.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../app/src/main/res/values-night/styles.xml | 0 .../app/src/main/res/values/styles.xml | 0 .../app/src/profile/AndroidManifest.xml | 0 .../example}/android/build.gradle | 0 .../example}/android/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../example}/android/settings.gradle | 0 {example => splitio/example}/ios/.gitignore | 0 .../ios/Flutter/AppFrameworkInfo.plist | 0 .../example}/ios/Flutter/Debug.xcconfig | 0 .../example}/ios/Flutter/Release.xcconfig | 0 {example => splitio/example}/ios/Podfile | 0 {example => splitio/example}/ios/Podfile.lock | 0 .../ios/Runner.xcodeproj/project.pbxproj | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../example}/ios/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../Icon-App-1024x1024@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin .../Icon-App-83.5x83.5@2x.png | Bin .../LaunchImage.imageset/Contents.json | 0 .../LaunchImage.imageset/LaunchImage.png | Bin .../LaunchImage.imageset/LaunchImage@2x.png | Bin .../LaunchImage.imageset/LaunchImage@3x.png | Bin .../LaunchImage.imageset/README.md | 0 .../Runner/Base.lproj/LaunchScreen.storyboard | 0 .../ios/Runner/Base.lproj/Main.storyboard | 0 .../example}/ios/Runner/Info.plist | 0 .../ios/Runner/Runner-Bridging-Header.h | 0 .../ios/SplitTests/ArgumentParserTests.swift | 0 .../ios/SplitTests/ExtensionsTests.swift | 0 .../SplitClientConfigHelperTests.swift | 0 .../SplitTests/SplitMethodParserTests.swift | 0 .../SplitTests/SplitProviderHelperTests.swift | 0 .../example}/ios/SplitTests/SplitTests.swift | 0 {example => splitio/example}/lib/main.dart | 0 {example => splitio/example}/pubspec.lock | 0 {example => splitio/example}/pubspec.yaml | 0 {ios => splitio/ios}/.gitignore | 0 {ios => splitio/ios}/Assets/.gitkeep | 0 .../ios}/Classes/ArgumentParser.swift | 0 {ios => splitio/ios}/Classes/Constants.swift | 0 {ios => splitio/ios}/Classes/Extensions.swift | 0 .../Classes/SplitClientConfigHelper.swift | 0 .../ios}/Classes/SplitFactoryProvider.swift | 0 .../ios}/Classes/SplitMethodParser.swift | 0 .../ios}/Classes/SplitWrapper.swift | 0 {ios => splitio/ios}/Classes/SplitioPlugin.h | 0 {ios => splitio/ios}/Classes/SplitioPlugin.m | 0 .../ios}/Classes/SwiftSplitioPlugin.swift | 0 {ios => splitio/ios}/splitio.podspec | 0 .../lib}/channel/method_channel_manager.dart | 0 .../lib}/events/split_events_listener.dart | 0 .../events/split_method_call_handler.dart | 0 .../impressions_method_call_handler.dart | 0 .../lib}/impressions/split_impression.dart | 0 {lib => splitio/lib}/method_call_handler.dart | 0 {lib => splitio/lib}/split_client.dart | 0 {lib => splitio/lib}/split_configuration.dart | 0 {lib => splitio/lib}/split_result.dart | 0 {lib => splitio/lib}/split_sync_config.dart | 0 {lib => splitio/lib}/split_view.dart | 0 {lib => splitio/lib}/splitio.dart | 0 pubspec.yaml => splitio/pubspec.yaml | 2 +- .../impressions_method_call_handler_test.dart | 0 .../test}/method_channel_manager_test.dart | 0 .../test}/split_event_listener_test.dart | 0 .../test}/split_impression_test.dart | 0 {test => splitio/test}/split_view_test.dart | 0 .../test}/splitio_client_test.dart | 0 .../test}/splitio_configuration_test.dart | 0 {test => splitio/test}/splitio_test.dart | 0 142 files changed, 41 insertions(+), 24 deletions(-) create mode 100644 splitio/.gitignore rename .metadata => splitio/.metadata (100%) rename CHANGELOG.md => splitio/CHANGELOG.md (100%) rename CONTRIBUTORS-GUIDE.md => splitio/CONTRIBUTORS-GUIDE.md (100%) rename LICENSE => splitio/LICENSE (100%) rename README.md => splitio/README.md (96%) rename analysis_options.yaml => splitio/analysis_options.yaml (100%) rename {android => splitio/android}/.gitignore (100%) rename {android => splitio/android}/build.gradle (100%) rename {android => splitio/android}/settings.gradle (100%) rename {android => splitio/android}/src/main/AndroidManifest.xml (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/ArgumentParser.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/ArgumentParserImpl.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/AttributesWrapper.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/Constants.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/EvaluationWrapper.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/Helper.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/ImpressionListenerImp.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/SplitClientConfigHelper.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/SplitFactoryProvider.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/SplitFactoryProviderImpl.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/SplitMethodParser.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/SplitMethodParserImpl.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/SplitProviderHelper.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/SplitWrapper.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/SplitWrapperImpl.java (100%) rename {android => splitio/android}/src/main/java/io/split/splitio/SplitioPlugin.java (100%) rename {android => splitio/android}/src/test/java/io/split/splitio/ArgumentParserImplTest.java (100%) rename {android => splitio/android}/src/test/java/io/split/splitio/ImpressionListenerImpTest.java (100%) rename {android => splitio/android}/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java (100%) rename {android => splitio/android}/src/test/java/io/split/splitio/SplitMethodParserImplTest.java (100%) rename {android => splitio/android}/src/test/java/io/split/splitio/SplitProviderHelperImplTest.java (100%) rename {android => splitio/android}/src/test/java/io/split/splitio/SplitWrapperImplTest.java (100%) rename {example => splitio/example}/.gitignore (100%) rename {example => splitio/example}/.metadata (100%) rename {example => splitio/example}/README.md (100%) rename {example => splitio/example}/analysis_options.yaml (100%) rename {example => splitio/example}/android/.gitignore (100%) rename {example => splitio/example}/android/app/build.gradle (100%) rename {example => splitio/example}/android/app/src/debug/AndroidManifest.xml (100%) rename {example => splitio/example}/android/app/src/main/AndroidManifest.xml (100%) rename {example => splitio/example}/android/app/src/main/java/io/split/splitio_example/MainActivity.java (100%) rename {example => splitio/example}/android/app/src/main/res/drawable-v21/launch_background.xml (100%) rename {example => splitio/example}/android/app/src/main/res/drawable/launch_background.xml (100%) rename {example => splitio/example}/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {example => splitio/example}/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {example => splitio/example}/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {example => splitio/example}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {example => splitio/example}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {example => splitio/example}/android/app/src/main/res/values-night/styles.xml (100%) rename {example => splitio/example}/android/app/src/main/res/values/styles.xml (100%) rename {example => splitio/example}/android/app/src/profile/AndroidManifest.xml (100%) rename {example => splitio/example}/android/build.gradle (100%) rename {example => splitio/example}/android/gradle.properties (100%) rename {example => splitio/example}/android/gradle/wrapper/gradle-wrapper.properties (100%) rename {example => splitio/example}/android/settings.gradle (100%) rename {example => splitio/example}/ios/.gitignore (100%) rename {example => splitio/example}/ios/Flutter/AppFrameworkInfo.plist (100%) rename {example => splitio/example}/ios/Flutter/Debug.xcconfig (100%) rename {example => splitio/example}/ios/Flutter/Release.xcconfig (100%) rename {example => splitio/example}/ios/Podfile (100%) rename {example => splitio/example}/ios/Podfile.lock (100%) rename {example => splitio/example}/ios/Runner.xcodeproj/project.pbxproj (100%) rename {example => splitio/example}/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename {example => splitio/example}/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {example => splitio/example}/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {example => splitio/example}/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename {example => splitio/example}/ios/Runner.xcworkspace/contents.xcworkspacedata (100%) rename {example => splitio/example}/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {example => splitio/example}/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {example => splitio/example}/ios/Runner/AppDelegate.swift (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (100%) rename {example => splitio/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md (100%) rename {example => splitio/example}/ios/Runner/Base.lproj/LaunchScreen.storyboard (100%) rename {example => splitio/example}/ios/Runner/Base.lproj/Main.storyboard (100%) rename {example => splitio/example}/ios/Runner/Info.plist (100%) rename {example => splitio/example}/ios/Runner/Runner-Bridging-Header.h (100%) rename {example => splitio/example}/ios/SplitTests/ArgumentParserTests.swift (100%) rename {example => splitio/example}/ios/SplitTests/ExtensionsTests.swift (100%) rename {example => splitio/example}/ios/SplitTests/SplitClientConfigHelperTests.swift (100%) rename {example => splitio/example}/ios/SplitTests/SplitMethodParserTests.swift (100%) rename {example => splitio/example}/ios/SplitTests/SplitProviderHelperTests.swift (100%) rename {example => splitio/example}/ios/SplitTests/SplitTests.swift (100%) rename {example => splitio/example}/lib/main.dart (100%) rename {example => splitio/example}/pubspec.lock (100%) rename {example => splitio/example}/pubspec.yaml (100%) rename {ios => splitio/ios}/.gitignore (100%) rename {ios => splitio/ios}/Assets/.gitkeep (100%) rename {ios => splitio/ios}/Classes/ArgumentParser.swift (100%) rename {ios => splitio/ios}/Classes/Constants.swift (100%) rename {ios => splitio/ios}/Classes/Extensions.swift (100%) rename {ios => splitio/ios}/Classes/SplitClientConfigHelper.swift (100%) rename {ios => splitio/ios}/Classes/SplitFactoryProvider.swift (100%) rename {ios => splitio/ios}/Classes/SplitMethodParser.swift (100%) rename {ios => splitio/ios}/Classes/SplitWrapper.swift (100%) rename {ios => splitio/ios}/Classes/SplitioPlugin.h (100%) rename {ios => splitio/ios}/Classes/SplitioPlugin.m (100%) rename {ios => splitio/ios}/Classes/SwiftSplitioPlugin.swift (100%) rename {ios => splitio/ios}/splitio.podspec (100%) rename {lib => splitio/lib}/channel/method_channel_manager.dart (100%) rename {lib => splitio/lib}/events/split_events_listener.dart (100%) rename {lib => splitio/lib}/events/split_method_call_handler.dart (100%) rename {lib => splitio/lib}/impressions/impressions_method_call_handler.dart (100%) rename {lib => splitio/lib}/impressions/split_impression.dart (100%) rename {lib => splitio/lib}/method_call_handler.dart (100%) rename {lib => splitio/lib}/split_client.dart (100%) rename {lib => splitio/lib}/split_configuration.dart (100%) rename {lib => splitio/lib}/split_result.dart (100%) rename {lib => splitio/lib}/split_sync_config.dart (100%) rename {lib => splitio/lib}/split_view.dart (100%) rename {lib => splitio/lib}/splitio.dart (100%) rename pubspec.yaml => splitio/pubspec.yaml (89%) rename {test => splitio/test}/impressions_method_call_handler_test.dart (100%) rename {test => splitio/test}/method_channel_manager_test.dart (100%) rename {test => splitio/test}/split_event_listener_test.dart (100%) rename {test => splitio/test}/split_impression_test.dart (100%) rename {test => splitio/test}/split_view_test.dart (100%) rename {test => splitio/test}/splitio_client_test.dart (100%) rename {test => splitio/test}/splitio_configuration_test.dart (100%) rename {test => splitio/test}/splitio_test.dart (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 08b883c..adce0a6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,7 @@ on: branches: - main - development + - '**_baseline' jobs: build-android: @@ -19,7 +20,7 @@ jobs: with: channel: 'stable' - name: Build example app APK - run: cd example; flutter build apk + run: cd splitio/example; flutter build apk test-android: name: Test Android @@ -34,11 +35,11 @@ jobs: with: channel: 'stable' - name: Run Pub Get - run: flutter pub get + run: cd splitio/; flutter pub get - name: Build Android - run: flutter build + run: cd splitio/; flutter build - name: Run Android test - run: cd example/android/; gradle :splitio:testReleaseUnitTest; + run: cd splitio/example/android/; gradle :splitio:testReleaseUnitTest; test-ios: name: Test iOS @@ -50,7 +51,7 @@ jobs: with: channel: 'stable' - name: Run Build iOS - run: cd example/ios/; flutter build ios --no-codesign; + run: cd splitio/example/ios; flutter build ios --no-codesign; - name: Select Xcode uses: maxim-lobanov/setup-xcode@v1 @@ -58,4 +59,4 @@ jobs: xcode-version: 13.2.1 - name: ios - run: cd example/ios/; xcodebuild test -workspace "Runner.xcworkspace" -scheme "Runner" -destination "platform=iOS Simulator,name=iPhone 12,OS=latest" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ONLY_ACTIVE_ARCH=NO; + run: cd splitio/example/ios; xcodebuild test -workspace "Runner.xcworkspace" -scheme "Runner" -destination "platform=iOS Simulator,name=iPhone 12,OS=latest" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ONLY_ACTIVE_ARCH=NO; diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7f7198..a503a2a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,6 +18,6 @@ jobs: with: channel: 'stable' - name: Run Pub Get - run: flutter pub get + run: cd splitio/; flutter pub get - name: Run flutter test - run: flutter test + run: cd splitio/; flutter test diff --git a/.gitignore b/.gitignore index 9be145f..c95f7e3 100644 --- a/.gitignore +++ b/.gitignore @@ -14,16 +14,3 @@ *.ipr *.iws .idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -/pubspec.lock -**/doc/api/ -.dart_tool/ -.packages -build/ diff --git a/splitio/.gitignore b/splitio/.gitignore new file mode 100644 index 0000000..9be145f --- /dev/null +++ b/splitio/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/.metadata b/splitio/.metadata similarity index 100% rename from .metadata rename to splitio/.metadata diff --git a/CHANGELOG.md b/splitio/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to splitio/CHANGELOG.md diff --git a/CONTRIBUTORS-GUIDE.md b/splitio/CONTRIBUTORS-GUIDE.md similarity index 100% rename from CONTRIBUTORS-GUIDE.md rename to splitio/CONTRIBUTORS-GUIDE.md diff --git a/LICENSE b/splitio/LICENSE similarity index 100% rename from LICENSE rename to splitio/LICENSE diff --git a/README.md b/splitio/README.md similarity index 96% rename from README.md rename to splitio/README.md index fe2b2e1..7d64ff8 100644 --- a/README.md +++ b/splitio/README.md @@ -35,7 +35,7 @@ _split.client(onReady: (client) async { } ``` -For a more elaborate usage example see [here](https://github.com/splitio/flutter-sdk-plugin/blob/main/example/lib/main.dart). +For a more elaborate usage example see [here](https://github.com/splitio/flutter-sdk-plugin/blob/splitio/main/example/lib/main.dart). For additional information, refer to our docs page. @@ -44,7 +44,7 @@ For additional information, refer to our docs page. The Split team monitors all issues submitted to this [issue tracker](https://github.com/splitio/flutter-sdk-plugin/issues). We encourage you to use this issue tracker to submit any bug reports, feedback, and feature enhancements. We'll do our best to respond in a timely manner. ## Contributing -Please see [contributors guide](https://github.com/splitio/flutter-sdk-plugin/blob/main/CONTRIBUTORS-GUIDE.md) to find all you need to submit a Pull Request (PR). +Please see [contributors guide](https://github.com/splitio/flutter-sdk-plugin/blob/splitio/main/CONTRIBUTORS-GUIDE.md) to find all you need to submit a Pull Request (PR). ## License Licensed under the Apache License, Version 2.0. See: [Apache License](https://www.apache.org/licenses/). diff --git a/analysis_options.yaml b/splitio/analysis_options.yaml similarity index 100% rename from analysis_options.yaml rename to splitio/analysis_options.yaml diff --git a/android/.gitignore b/splitio/android/.gitignore similarity index 100% rename from android/.gitignore rename to splitio/android/.gitignore diff --git a/android/build.gradle b/splitio/android/build.gradle similarity index 100% rename from android/build.gradle rename to splitio/android/build.gradle diff --git a/android/settings.gradle b/splitio/android/settings.gradle similarity index 100% rename from android/settings.gradle rename to splitio/android/settings.gradle diff --git a/android/src/main/AndroidManifest.xml b/splitio/android/src/main/AndroidManifest.xml similarity index 100% rename from android/src/main/AndroidManifest.xml rename to splitio/android/src/main/AndroidManifest.xml diff --git a/android/src/main/java/io/split/splitio/ArgumentParser.java b/splitio/android/src/main/java/io/split/splitio/ArgumentParser.java similarity index 100% rename from android/src/main/java/io/split/splitio/ArgumentParser.java rename to splitio/android/src/main/java/io/split/splitio/ArgumentParser.java diff --git a/android/src/main/java/io/split/splitio/ArgumentParserImpl.java b/splitio/android/src/main/java/io/split/splitio/ArgumentParserImpl.java similarity index 100% rename from android/src/main/java/io/split/splitio/ArgumentParserImpl.java rename to splitio/android/src/main/java/io/split/splitio/ArgumentParserImpl.java diff --git a/android/src/main/java/io/split/splitio/AttributesWrapper.java b/splitio/android/src/main/java/io/split/splitio/AttributesWrapper.java similarity index 100% rename from android/src/main/java/io/split/splitio/AttributesWrapper.java rename to splitio/android/src/main/java/io/split/splitio/AttributesWrapper.java diff --git a/android/src/main/java/io/split/splitio/Constants.java b/splitio/android/src/main/java/io/split/splitio/Constants.java similarity index 100% rename from android/src/main/java/io/split/splitio/Constants.java rename to splitio/android/src/main/java/io/split/splitio/Constants.java diff --git a/android/src/main/java/io/split/splitio/EvaluationWrapper.java b/splitio/android/src/main/java/io/split/splitio/EvaluationWrapper.java similarity index 100% rename from android/src/main/java/io/split/splitio/EvaluationWrapper.java rename to splitio/android/src/main/java/io/split/splitio/EvaluationWrapper.java diff --git a/android/src/main/java/io/split/splitio/Helper.java b/splitio/android/src/main/java/io/split/splitio/Helper.java similarity index 100% rename from android/src/main/java/io/split/splitio/Helper.java rename to splitio/android/src/main/java/io/split/splitio/Helper.java diff --git a/android/src/main/java/io/split/splitio/ImpressionListenerImp.java b/splitio/android/src/main/java/io/split/splitio/ImpressionListenerImp.java similarity index 100% rename from android/src/main/java/io/split/splitio/ImpressionListenerImp.java rename to splitio/android/src/main/java/io/split/splitio/ImpressionListenerImp.java diff --git a/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java b/splitio/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java similarity index 100% rename from android/src/main/java/io/split/splitio/SplitClientConfigHelper.java rename to splitio/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java diff --git a/android/src/main/java/io/split/splitio/SplitFactoryProvider.java b/splitio/android/src/main/java/io/split/splitio/SplitFactoryProvider.java similarity index 100% rename from android/src/main/java/io/split/splitio/SplitFactoryProvider.java rename to splitio/android/src/main/java/io/split/splitio/SplitFactoryProvider.java diff --git a/android/src/main/java/io/split/splitio/SplitFactoryProviderImpl.java b/splitio/android/src/main/java/io/split/splitio/SplitFactoryProviderImpl.java similarity index 100% rename from android/src/main/java/io/split/splitio/SplitFactoryProviderImpl.java rename to splitio/android/src/main/java/io/split/splitio/SplitFactoryProviderImpl.java diff --git a/android/src/main/java/io/split/splitio/SplitMethodParser.java b/splitio/android/src/main/java/io/split/splitio/SplitMethodParser.java similarity index 100% rename from android/src/main/java/io/split/splitio/SplitMethodParser.java rename to splitio/android/src/main/java/io/split/splitio/SplitMethodParser.java diff --git a/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java b/splitio/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java similarity index 100% rename from android/src/main/java/io/split/splitio/SplitMethodParserImpl.java rename to splitio/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java diff --git a/android/src/main/java/io/split/splitio/SplitProviderHelper.java b/splitio/android/src/main/java/io/split/splitio/SplitProviderHelper.java similarity index 100% rename from android/src/main/java/io/split/splitio/SplitProviderHelper.java rename to splitio/android/src/main/java/io/split/splitio/SplitProviderHelper.java diff --git a/android/src/main/java/io/split/splitio/SplitWrapper.java b/splitio/android/src/main/java/io/split/splitio/SplitWrapper.java similarity index 100% rename from android/src/main/java/io/split/splitio/SplitWrapper.java rename to splitio/android/src/main/java/io/split/splitio/SplitWrapper.java diff --git a/android/src/main/java/io/split/splitio/SplitWrapperImpl.java b/splitio/android/src/main/java/io/split/splitio/SplitWrapperImpl.java similarity index 100% rename from android/src/main/java/io/split/splitio/SplitWrapperImpl.java rename to splitio/android/src/main/java/io/split/splitio/SplitWrapperImpl.java diff --git a/android/src/main/java/io/split/splitio/SplitioPlugin.java b/splitio/android/src/main/java/io/split/splitio/SplitioPlugin.java similarity index 100% rename from android/src/main/java/io/split/splitio/SplitioPlugin.java rename to splitio/android/src/main/java/io/split/splitio/SplitioPlugin.java diff --git a/android/src/test/java/io/split/splitio/ArgumentParserImplTest.java b/splitio/android/src/test/java/io/split/splitio/ArgumentParserImplTest.java similarity index 100% rename from android/src/test/java/io/split/splitio/ArgumentParserImplTest.java rename to splitio/android/src/test/java/io/split/splitio/ArgumentParserImplTest.java diff --git a/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java b/splitio/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java similarity index 100% rename from android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java rename to splitio/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java diff --git a/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java b/splitio/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java similarity index 100% rename from android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java rename to splitio/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java diff --git a/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java b/splitio/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java similarity index 100% rename from android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java rename to splitio/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java diff --git a/android/src/test/java/io/split/splitio/SplitProviderHelperImplTest.java b/splitio/android/src/test/java/io/split/splitio/SplitProviderHelperImplTest.java similarity index 100% rename from android/src/test/java/io/split/splitio/SplitProviderHelperImplTest.java rename to splitio/android/src/test/java/io/split/splitio/SplitProviderHelperImplTest.java diff --git a/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java b/splitio/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java similarity index 100% rename from android/src/test/java/io/split/splitio/SplitWrapperImplTest.java rename to splitio/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java diff --git a/example/.gitignore b/splitio/example/.gitignore similarity index 100% rename from example/.gitignore rename to splitio/example/.gitignore diff --git a/example/.metadata b/splitio/example/.metadata similarity index 100% rename from example/.metadata rename to splitio/example/.metadata diff --git a/example/README.md b/splitio/example/README.md similarity index 100% rename from example/README.md rename to splitio/example/README.md diff --git a/example/analysis_options.yaml b/splitio/example/analysis_options.yaml similarity index 100% rename from example/analysis_options.yaml rename to splitio/example/analysis_options.yaml diff --git a/example/android/.gitignore b/splitio/example/android/.gitignore similarity index 100% rename from example/android/.gitignore rename to splitio/example/android/.gitignore diff --git a/example/android/app/build.gradle b/splitio/example/android/app/build.gradle similarity index 100% rename from example/android/app/build.gradle rename to splitio/example/android/app/build.gradle diff --git a/example/android/app/src/debug/AndroidManifest.xml b/splitio/example/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from example/android/app/src/debug/AndroidManifest.xml rename to splitio/example/android/app/src/debug/AndroidManifest.xml diff --git a/example/android/app/src/main/AndroidManifest.xml b/splitio/example/android/app/src/main/AndroidManifest.xml similarity index 100% rename from example/android/app/src/main/AndroidManifest.xml rename to splitio/example/android/app/src/main/AndroidManifest.xml diff --git a/example/android/app/src/main/java/io/split/splitio_example/MainActivity.java b/splitio/example/android/app/src/main/java/io/split/splitio_example/MainActivity.java similarity index 100% rename from example/android/app/src/main/java/io/split/splitio_example/MainActivity.java rename to splitio/example/android/app/src/main/java/io/split/splitio_example/MainActivity.java diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/splitio/example/android/app/src/main/res/drawable-v21/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable-v21/launch_background.xml rename to splitio/example/android/app/src/main/res/drawable-v21/launch_background.xml diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/splitio/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable/launch_background.xml rename to splitio/example/android/app/src/main/res/drawable/launch_background.xml diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/splitio/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to splitio/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/splitio/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to splitio/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/splitio/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to splitio/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/splitio/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to splitio/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/splitio/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to splitio/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/values-night/styles.xml b/splitio/example/android/app/src/main/res/values-night/styles.xml similarity index 100% rename from example/android/app/src/main/res/values-night/styles.xml rename to splitio/example/android/app/src/main/res/values-night/styles.xml diff --git a/example/android/app/src/main/res/values/styles.xml b/splitio/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from example/android/app/src/main/res/values/styles.xml rename to splitio/example/android/app/src/main/res/values/styles.xml diff --git a/example/android/app/src/profile/AndroidManifest.xml b/splitio/example/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from example/android/app/src/profile/AndroidManifest.xml rename to splitio/example/android/app/src/profile/AndroidManifest.xml diff --git a/example/android/build.gradle b/splitio/example/android/build.gradle similarity index 100% rename from example/android/build.gradle rename to splitio/example/android/build.gradle diff --git a/example/android/gradle.properties b/splitio/example/android/gradle.properties similarity index 100% rename from example/android/gradle.properties rename to splitio/example/android/gradle.properties diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/splitio/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from example/android/gradle/wrapper/gradle-wrapper.properties rename to splitio/example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/example/android/settings.gradle b/splitio/example/android/settings.gradle similarity index 100% rename from example/android/settings.gradle rename to splitio/example/android/settings.gradle diff --git a/example/ios/.gitignore b/splitio/example/ios/.gitignore similarity index 100% rename from example/ios/.gitignore rename to splitio/example/ios/.gitignore diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/splitio/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from example/ios/Flutter/AppFrameworkInfo.plist rename to splitio/example/ios/Flutter/AppFrameworkInfo.plist diff --git a/example/ios/Flutter/Debug.xcconfig b/splitio/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from example/ios/Flutter/Debug.xcconfig rename to splitio/example/ios/Flutter/Debug.xcconfig diff --git a/example/ios/Flutter/Release.xcconfig b/splitio/example/ios/Flutter/Release.xcconfig similarity index 100% rename from example/ios/Flutter/Release.xcconfig rename to splitio/example/ios/Flutter/Release.xcconfig diff --git a/example/ios/Podfile b/splitio/example/ios/Podfile similarity index 100% rename from example/ios/Podfile rename to splitio/example/ios/Podfile diff --git a/example/ios/Podfile.lock b/splitio/example/ios/Podfile.lock similarity index 100% rename from example/ios/Podfile.lock rename to splitio/example/ios/Podfile.lock diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/splitio/example/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from example/ios/Runner.xcodeproj/project.pbxproj rename to splitio/example/ios/Runner.xcodeproj/project.pbxproj diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/splitio/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to splitio/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/splitio/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to splitio/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/splitio/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to splitio/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/splitio/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to splitio/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/splitio/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to splitio/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/splitio/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to splitio/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/splitio/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to splitio/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner/AppDelegate.swift b/splitio/example/ios/Runner/AppDelegate.swift similarity index 100% rename from example/ios/Runner/AppDelegate.swift rename to splitio/example/ios/Runner/AppDelegate.swift diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to splitio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/splitio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to splitio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/splitio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to splitio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/splitio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to splitio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/splitio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to splitio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/splitio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to splitio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/splitio/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to splitio/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/splitio/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/Main.storyboard rename to splitio/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/example/ios/Runner/Info.plist b/splitio/example/ios/Runner/Info.plist similarity index 100% rename from example/ios/Runner/Info.plist rename to splitio/example/ios/Runner/Info.plist diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/splitio/example/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from example/ios/Runner/Runner-Bridging-Header.h rename to splitio/example/ios/Runner/Runner-Bridging-Header.h diff --git a/example/ios/SplitTests/ArgumentParserTests.swift b/splitio/example/ios/SplitTests/ArgumentParserTests.swift similarity index 100% rename from example/ios/SplitTests/ArgumentParserTests.swift rename to splitio/example/ios/SplitTests/ArgumentParserTests.swift diff --git a/example/ios/SplitTests/ExtensionsTests.swift b/splitio/example/ios/SplitTests/ExtensionsTests.swift similarity index 100% rename from example/ios/SplitTests/ExtensionsTests.swift rename to splitio/example/ios/SplitTests/ExtensionsTests.swift diff --git a/example/ios/SplitTests/SplitClientConfigHelperTests.swift b/splitio/example/ios/SplitTests/SplitClientConfigHelperTests.swift similarity index 100% rename from example/ios/SplitTests/SplitClientConfigHelperTests.swift rename to splitio/example/ios/SplitTests/SplitClientConfigHelperTests.swift diff --git a/example/ios/SplitTests/SplitMethodParserTests.swift b/splitio/example/ios/SplitTests/SplitMethodParserTests.swift similarity index 100% rename from example/ios/SplitTests/SplitMethodParserTests.swift rename to splitio/example/ios/SplitTests/SplitMethodParserTests.swift diff --git a/example/ios/SplitTests/SplitProviderHelperTests.swift b/splitio/example/ios/SplitTests/SplitProviderHelperTests.swift similarity index 100% rename from example/ios/SplitTests/SplitProviderHelperTests.swift rename to splitio/example/ios/SplitTests/SplitProviderHelperTests.swift diff --git a/example/ios/SplitTests/SplitTests.swift b/splitio/example/ios/SplitTests/SplitTests.swift similarity index 100% rename from example/ios/SplitTests/SplitTests.swift rename to splitio/example/ios/SplitTests/SplitTests.swift diff --git a/example/lib/main.dart b/splitio/example/lib/main.dart similarity index 100% rename from example/lib/main.dart rename to splitio/example/lib/main.dart diff --git a/example/pubspec.lock b/splitio/example/pubspec.lock similarity index 100% rename from example/pubspec.lock rename to splitio/example/pubspec.lock diff --git a/example/pubspec.yaml b/splitio/example/pubspec.yaml similarity index 100% rename from example/pubspec.yaml rename to splitio/example/pubspec.yaml diff --git a/ios/.gitignore b/splitio/ios/.gitignore similarity index 100% rename from ios/.gitignore rename to splitio/ios/.gitignore diff --git a/ios/Assets/.gitkeep b/splitio/ios/Assets/.gitkeep similarity index 100% rename from ios/Assets/.gitkeep rename to splitio/ios/Assets/.gitkeep diff --git a/ios/Classes/ArgumentParser.swift b/splitio/ios/Classes/ArgumentParser.swift similarity index 100% rename from ios/Classes/ArgumentParser.swift rename to splitio/ios/Classes/ArgumentParser.swift diff --git a/ios/Classes/Constants.swift b/splitio/ios/Classes/Constants.swift similarity index 100% rename from ios/Classes/Constants.swift rename to splitio/ios/Classes/Constants.swift diff --git a/ios/Classes/Extensions.swift b/splitio/ios/Classes/Extensions.swift similarity index 100% rename from ios/Classes/Extensions.swift rename to splitio/ios/Classes/Extensions.swift diff --git a/ios/Classes/SplitClientConfigHelper.swift b/splitio/ios/Classes/SplitClientConfigHelper.swift similarity index 100% rename from ios/Classes/SplitClientConfigHelper.swift rename to splitio/ios/Classes/SplitClientConfigHelper.swift diff --git a/ios/Classes/SplitFactoryProvider.swift b/splitio/ios/Classes/SplitFactoryProvider.swift similarity index 100% rename from ios/Classes/SplitFactoryProvider.swift rename to splitio/ios/Classes/SplitFactoryProvider.swift diff --git a/ios/Classes/SplitMethodParser.swift b/splitio/ios/Classes/SplitMethodParser.swift similarity index 100% rename from ios/Classes/SplitMethodParser.swift rename to splitio/ios/Classes/SplitMethodParser.swift diff --git a/ios/Classes/SplitWrapper.swift b/splitio/ios/Classes/SplitWrapper.swift similarity index 100% rename from ios/Classes/SplitWrapper.swift rename to splitio/ios/Classes/SplitWrapper.swift diff --git a/ios/Classes/SplitioPlugin.h b/splitio/ios/Classes/SplitioPlugin.h similarity index 100% rename from ios/Classes/SplitioPlugin.h rename to splitio/ios/Classes/SplitioPlugin.h diff --git a/ios/Classes/SplitioPlugin.m b/splitio/ios/Classes/SplitioPlugin.m similarity index 100% rename from ios/Classes/SplitioPlugin.m rename to splitio/ios/Classes/SplitioPlugin.m diff --git a/ios/Classes/SwiftSplitioPlugin.swift b/splitio/ios/Classes/SwiftSplitioPlugin.swift similarity index 100% rename from ios/Classes/SwiftSplitioPlugin.swift rename to splitio/ios/Classes/SwiftSplitioPlugin.swift diff --git a/ios/splitio.podspec b/splitio/ios/splitio.podspec similarity index 100% rename from ios/splitio.podspec rename to splitio/ios/splitio.podspec diff --git a/lib/channel/method_channel_manager.dart b/splitio/lib/channel/method_channel_manager.dart similarity index 100% rename from lib/channel/method_channel_manager.dart rename to splitio/lib/channel/method_channel_manager.dart diff --git a/lib/events/split_events_listener.dart b/splitio/lib/events/split_events_listener.dart similarity index 100% rename from lib/events/split_events_listener.dart rename to splitio/lib/events/split_events_listener.dart diff --git a/lib/events/split_method_call_handler.dart b/splitio/lib/events/split_method_call_handler.dart similarity index 100% rename from lib/events/split_method_call_handler.dart rename to splitio/lib/events/split_method_call_handler.dart diff --git a/lib/impressions/impressions_method_call_handler.dart b/splitio/lib/impressions/impressions_method_call_handler.dart similarity index 100% rename from lib/impressions/impressions_method_call_handler.dart rename to splitio/lib/impressions/impressions_method_call_handler.dart diff --git a/lib/impressions/split_impression.dart b/splitio/lib/impressions/split_impression.dart similarity index 100% rename from lib/impressions/split_impression.dart rename to splitio/lib/impressions/split_impression.dart diff --git a/lib/method_call_handler.dart b/splitio/lib/method_call_handler.dart similarity index 100% rename from lib/method_call_handler.dart rename to splitio/lib/method_call_handler.dart diff --git a/lib/split_client.dart b/splitio/lib/split_client.dart similarity index 100% rename from lib/split_client.dart rename to splitio/lib/split_client.dart diff --git a/lib/split_configuration.dart b/splitio/lib/split_configuration.dart similarity index 100% rename from lib/split_configuration.dart rename to splitio/lib/split_configuration.dart diff --git a/lib/split_result.dart b/splitio/lib/split_result.dart similarity index 100% rename from lib/split_result.dart rename to splitio/lib/split_result.dart diff --git a/lib/split_sync_config.dart b/splitio/lib/split_sync_config.dart similarity index 100% rename from lib/split_sync_config.dart rename to splitio/lib/split_sync_config.dart diff --git a/lib/split_view.dart b/splitio/lib/split_view.dart similarity index 100% rename from lib/split_view.dart rename to splitio/lib/split_view.dart diff --git a/lib/splitio.dart b/splitio/lib/splitio.dart similarity index 100% rename from lib/splitio.dart rename to splitio/lib/splitio.dart diff --git a/pubspec.yaml b/splitio/pubspec.yaml similarity index 89% rename from pubspec.yaml rename to splitio/pubspec.yaml index 1069ef9..a4f85a3 100644 --- a/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -2,7 +2,7 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via a Split feature flag to manage your complete customer experience. version: 0.1.1 homepage: https://split.io/ -repository: https://github.com/splitio/flutter-sdk-plugin +repository: https://github.com/splitio/flutter-sdk-plugin/splitio/ environment: sdk: ">=2.16.2 <3.0.0" diff --git a/test/impressions_method_call_handler_test.dart b/splitio/test/impressions_method_call_handler_test.dart similarity index 100% rename from test/impressions_method_call_handler_test.dart rename to splitio/test/impressions_method_call_handler_test.dart diff --git a/test/method_channel_manager_test.dart b/splitio/test/method_channel_manager_test.dart similarity index 100% rename from test/method_channel_manager_test.dart rename to splitio/test/method_channel_manager_test.dart diff --git a/test/split_event_listener_test.dart b/splitio/test/split_event_listener_test.dart similarity index 100% rename from test/split_event_listener_test.dart rename to splitio/test/split_event_listener_test.dart diff --git a/test/split_impression_test.dart b/splitio/test/split_impression_test.dart similarity index 100% rename from test/split_impression_test.dart rename to splitio/test/split_impression_test.dart diff --git a/test/split_view_test.dart b/splitio/test/split_view_test.dart similarity index 100% rename from test/split_view_test.dart rename to splitio/test/split_view_test.dart diff --git a/test/splitio_client_test.dart b/splitio/test/splitio_client_test.dart similarity index 100% rename from test/splitio_client_test.dart rename to splitio/test/splitio_client_test.dart diff --git a/test/splitio_configuration_test.dart b/splitio/test/splitio_configuration_test.dart similarity index 100% rename from test/splitio_configuration_test.dart rename to splitio/test/splitio_configuration_test.dart diff --git a/test/splitio_test.dart b/splitio/test/splitio_test.dart similarity index 100% rename from test/splitio_test.dart rename to splitio/test/splitio_test.dart From ec0e7168217452e30ba418d18e993173313a3931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Tue, 6 Sep 2022 18:09:38 -0300 Subject: [PATCH 02/25] Platform interface (#45) --- .../lib/channel/method_channel_manager.dart | 38 ---- splitio/lib/events/split_events_listener.dart | 53 ----- .../lib/events/split_method_call_handler.dart | 91 -------- splitio/test/method_channel_manager_test.dart | 99 --------- splitio_platform_interface/.gitignore | 29 +++ splitio_platform_interface/CHANGELOG.md | 3 + splitio_platform_interface/LICENSE | 201 ++++++++++++++++++ splitio_platform_interface/README.md | 0 .../lib/events/split_method_call_handler.dart | 79 +++++++ .../impressions_method_call_handler.dart | 11 +- .../lib/method_call_handler.dart | 4 +- .../lib/split_configuration.dart | 0 .../lib}/split_impression.dart | 0 .../lib/split_result.dart | 0 .../lib/split_sync_config.dart | 0 .../lib/split_view.dart | 0 .../lib/splitio_platform_interface.dart | 183 ++++++++++++++++ splitio_platform_interface/pubspec.yaml | 23 ++ .../impressions_method_call_handler_test.dart | 0 .../test/split_event_listener_test.dart | 0 .../test/split_impression_test.dart | 0 .../test/split_view_test.dart | 0 .../test/splitio_configuration_test.dart | 0 23 files changed, 524 insertions(+), 290 deletions(-) delete mode 100644 splitio/lib/channel/method_channel_manager.dart delete mode 100644 splitio/lib/events/split_events_listener.dart delete mode 100644 splitio/lib/events/split_method_call_handler.dart delete mode 100644 splitio/test/method_channel_manager_test.dart create mode 100644 splitio_platform_interface/.gitignore create mode 100644 splitio_platform_interface/CHANGELOG.md create mode 100644 splitio_platform_interface/LICENSE create mode 100644 splitio_platform_interface/README.md create mode 100644 splitio_platform_interface/lib/events/split_method_call_handler.dart rename {splitio => splitio_platform_interface}/lib/impressions/impressions_method_call_handler.dart (59%) rename {splitio => splitio_platform_interface}/lib/method_call_handler.dart (61%) rename {splitio => splitio_platform_interface}/lib/split_configuration.dart (100%) rename {splitio/lib/impressions => splitio_platform_interface/lib}/split_impression.dart (100%) rename {splitio => splitio_platform_interface}/lib/split_result.dart (100%) rename {splitio => splitio_platform_interface}/lib/split_sync_config.dart (100%) rename {splitio => splitio_platform_interface}/lib/split_view.dart (100%) create mode 100644 splitio_platform_interface/lib/splitio_platform_interface.dart create mode 100644 splitio_platform_interface/pubspec.yaml rename {splitio => splitio_platform_interface}/test/impressions_method_call_handler_test.dart (100%) rename {splitio => splitio_platform_interface}/test/split_event_listener_test.dart (100%) rename {splitio => splitio_platform_interface}/test/split_impression_test.dart (100%) rename {splitio => splitio_platform_interface}/test/split_view_test.dart (100%) rename {splitio => splitio_platform_interface}/test/splitio_configuration_test.dart (100%) diff --git a/splitio/lib/channel/method_channel_manager.dart b/splitio/lib/channel/method_channel_manager.dart deleted file mode 100644 index 89ec677..0000000 --- a/splitio/lib/channel/method_channel_manager.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:splitio/method_call_handler.dart'; - -class MethodChannelManager { - final MethodChannel _channel; - - final Set _handlers = {}; - - MethodChannelManager(this._channel) { - _channel.setMethodCallHandler((call) => handle(call)); - } - - void addHandler(MethodCallHandler handler) { - _handlers.add(handler); - } - - void removeHandler(MethodCallHandler handler) { - _handlers.remove(handler); - } - - Future handle(MethodCall call) async { - for (MethodCallHandler handler in _handlers) { - handler.handle(call); - } - } - - Future invokeMethod(String method, [dynamic arguments]) { - return _channel.invokeMethod(method, arguments); - } - - Future?> invokeMapMethod(String method, [dynamic arguments]) { - return _channel.invokeMapMethod(method, arguments); - } - - Future?> invokeListMethod(String method, [dynamic arguments]) { - return _channel.invokeListMethod(method, arguments); - } -} diff --git a/splitio/lib/events/split_events_listener.dart b/splitio/lib/events/split_events_listener.dart deleted file mode 100644 index be9c9f6..0000000 --- a/splitio/lib/events/split_events_listener.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'dart:async'; - -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/events/split_method_call_handler.dart'; -import 'package:splitio/split_client.dart'; - -abstract class SplitEventsListener { - Future onReady(); - - Future onReadyFromCache(); - - Stream onUpdated(); - - Future onTimeout(); - - void destroy(); -} - -class DefaultEventsListener implements SplitEventsListener { - final MethodChannelManager _methodChannelManager; - - final SplitEventMethodCallHandler _methodCallHandler; - - DefaultEventsListener(this._methodChannelManager, this._methodCallHandler) { - _methodChannelManager.addHandler(_methodCallHandler); - } - - @override - Future onReady() { - return _methodCallHandler.onReady(); - } - - @override - Future onReadyFromCache() { - return _methodCallHandler.onReadyFromCache(); - } - - @override - Stream onUpdated() { - return _methodCallHandler.onUpdated(); - } - - @override - Future onTimeout() { - return _methodCallHandler.onTimeout(); - } - - @override - void destroy() { - _methodCallHandler.destroy(); - _methodChannelManager.removeHandler(_methodCallHandler); - } -} diff --git a/splitio/lib/events/split_method_call_handler.dart b/splitio/lib/events/split_method_call_handler.dart deleted file mode 100644 index 862017a..0000000 --- a/splitio/lib/events/split_method_call_handler.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/services.dart'; -import 'package:splitio/events/split_events_listener.dart'; -import 'package:splitio/method_call_handler.dart'; -import 'package:splitio/split_client.dart'; - -class SplitEventMethodCallHandler - implements MethodCallHandler, SplitEventsListener { - static const String _eventClientReady = 'clientReady'; - static const String _eventClientReadyFromCache = 'clientReadyFromCache'; - static const String _eventClientTimeout = 'clientTimeout'; - static const String _eventClientUpdated = 'clientUpdated'; - - final String _matchingKey; - final String? _bucketingKey; - final SplitClient _splitClient; - final StreamController _updateStreamCompleter = - StreamController(); - - final Map> _clientEventCallbacks = { - _eventClientReady: Completer(), - _eventClientReadyFromCache: Completer(), - _eventClientTimeout: Completer(), - }; - - final Map _triggeredClientEvents = { - _eventClientReady: false, - _eventClientReadyFromCache: false, - _eventClientTimeout: false, - }; - - SplitEventMethodCallHandler( - this._matchingKey, this._bucketingKey, this._splitClient); - - @override - Future handle(MethodCall call) async { - if (_clientEventCallbacks.containsKey(call.method)) { - if (_matchingKey == call.arguments['matchingKey'] && - _bucketingKey == call.arguments['bucketingKey']) { - var clientEventCallback = _clientEventCallbacks[call.method]; - if (clientEventCallback != null && !clientEventCallback.isCompleted) { - clientEventCallback.complete(_splitClient); - } - - if (_triggeredClientEvents.containsKey(call.method)) { - _triggeredClientEvents[call.method] = true; - } - } - } else if (call.method == _eventClientUpdated && - _updateStreamCompleter.hasListener && - !_updateStreamCompleter.isPaused && - !_updateStreamCompleter.isClosed) { - _updateStreamCompleter.add(_splitClient); - } - } - - @override - Future onReady() { - return _onEvent(_eventClientReady); - } - - @override - Future onReadyFromCache() { - return _onEvent(_eventClientReadyFromCache); - } - - @override - Stream onUpdated() { - return _updateStreamCompleter.stream; - } - - @override - Future onTimeout() { - return _onEvent(_eventClientTimeout); - } - - @override - void destroy() { - _updateStreamCompleter.close(); - } - - Future _onEvent(String sdkEvent) { - if (_triggeredClientEvents.containsKey(sdkEvent) && - _triggeredClientEvents[sdkEvent] == true) { - return Future.value(_splitClient); - } - - return (_clientEventCallbacks[sdkEvent])?.future ?? Future.error(Object()); - } -} diff --git a/splitio/test/method_channel_manager_test.dart b/splitio/test/method_channel_manager_test.dart deleted file mode 100644 index 0c98d25..0000000 --- a/splitio/test/method_channel_manager_test.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/method_call_handler.dart'; - -void main() { - late MethodChannelStub _channel; - late MethodChannelManager _methodChannelManager; - - setUp(() { - _channel = MethodChannelStub('mock'); - TestWidgetsFlutterBinding.ensureInitialized(); - _methodChannelManager = MethodChannelManager(_channel); - }); - - test('handle calls are delegated to each handler', () { - var mockMethodHandler1 = MethodCallHandlerStub(); - var mockMethodHandler2 = MethodCallHandlerStub(); - var mockMethodHandler3 = MethodCallHandlerStub(); - _methodChannelManager.addHandler(mockMethodHandler1); - _methodChannelManager.addHandler(mockMethodHandler2); - _methodChannelManager.addHandler(mockMethodHandler3); - - _methodChannelManager.handle(const MethodCall('test-method')); - - expect(mockMethodHandler1.handledCalls, {'test-method'}); - expect(mockMethodHandler2.handledCalls, {'test-method'}); - expect(mockMethodHandler3.handledCalls, {'test-method'}); - }); - - test('invokeMethod calls are delegated to channel', () { - _methodChannelManager.invokeMethod('any-method'); - - expect(_channel.calledMethods, {'invokeMethod'}); - }); - - test('invokeMapMethod calls are delegated to channel', () { - _methodChannelManager.invokeMapMethod('any-method'); - - expect(_channel.calledMethods, {'invokeMapMethod'}); - }); - - test('invokeListMethod calls are delegated to channel', () { - _methodChannelManager.invokeListMethod('any-method'); - - expect(_channel.calledMethods, {'invokeListMethod'}); - }); - - test('removed call handlers do not handle methods', () { - final MethodCallHandlerStub handler1 = MethodCallHandlerStub(); - final MethodCallHandlerStub handler2 = MethodCallHandlerStub(); - final MethodCallHandlerStub handler3 = MethodCallHandlerStub(); - _methodChannelManager.addHandler(handler1); - _methodChannelManager.addHandler(handler2); - _methodChannelManager.addHandler(handler3); - - _methodChannelManager.removeHandler(handler2); - - _methodChannelManager.handle(const MethodCall('test-method')); - - expect(handler1.handledCalls, {'test-method'}); - expect(handler2.handledCalls, isEmpty); - expect(handler3.handledCalls, {'test-method'}); - }); -} - -class MethodCallHandlerStub extends MethodCallHandler { - final Set handledCalls = {}; - - @override - Future handle(MethodCall call) async { - handledCalls.add(call.method); - } -} - -class MethodChannelStub extends MethodChannel { - MethodChannelStub(String name) : super(name); - Set calledMethods = {}; - - @override - Future invokeMethod(String method, [dynamic arguments]) async { - calledMethods.add('invokeMethod'); - return Future.value(null); - } - - @override - Future?> invokeMapMethod(String method, - [dynamic arguments]) async { - calledMethods.add('invokeMapMethod'); - return Future.value(null); - } - - @override - Future?> invokeListMethod(String method, - [dynamic arguments]) async { - calledMethods.add('invokeListMethod'); - return Future.value(null); - } -} diff --git a/splitio_platform_interface/.gitignore b/splitio_platform_interface/.gitignore new file mode 100644 index 0000000..9be145f --- /dev/null +++ b/splitio_platform_interface/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/splitio_platform_interface/CHANGELOG.md b/splitio_platform_interface/CHANGELOG.md new file mode 100644 index 0000000..2dd7b16 --- /dev/null +++ b/splitio_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +# 1.0.0 + +Initial release. diff --git a/splitio_platform_interface/LICENSE b/splitio_platform_interface/LICENSE new file mode 100644 index 0000000..20e4bd8 --- /dev/null +++ b/splitio_platform_interface/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/splitio_platform_interface/README.md b/splitio_platform_interface/README.md new file mode 100644 index 0000000..e69de29 diff --git a/splitio_platform_interface/lib/events/split_method_call_handler.dart b/splitio_platform_interface/lib/events/split_method_call_handler.dart new file mode 100644 index 0000000..6a0db02 --- /dev/null +++ b/splitio_platform_interface/lib/events/split_method_call_handler.dart @@ -0,0 +1,79 @@ +import 'dart:async'; + +import 'package:splitio_platform_interface/method_call_handler.dart'; + +class SplitEventMethodCallHandler implements MethodCallHandler { + static const String _eventClientReady = 'clientReady'; + static const String _eventClientReadyFromCache = 'clientReadyFromCache'; + static const String _eventClientTimeout = 'clientTimeout'; + static const String _eventClientUpdated = 'clientUpdated'; + + final String _matchingKey; + final String? _bucketingKey; + final StreamController _updateStreamCompleter = StreamController(); + + final Map> _clientEventCallbacks = { + _eventClientReady: Completer(), + _eventClientReadyFromCache: Completer(), + _eventClientTimeout: Completer(), + }; + + final Map _triggeredClientEvents = { + _eventClientReady: false, + _eventClientReadyFromCache: false, + _eventClientTimeout: false, + }; + + SplitEventMethodCallHandler(this._matchingKey, this._bucketingKey); + + @override + Future handle(String methodName, dynamic methodArguments) async { + if (_clientEventCallbacks.containsKey(methodName)) { + if (_matchingKey == methodArguments['matchingKey'] && + _bucketingKey == methodArguments['bucketingKey']) { + var clientEventCallback = _clientEventCallbacks[methodName]; + if (clientEventCallback != null && !clientEventCallback.isCompleted) { + clientEventCallback.complete(); + } + + if (_triggeredClientEvents.containsKey(methodName)) { + _triggeredClientEvents[methodName] = true; + } + } + } else if (methodName == _eventClientUpdated && + _updateStreamCompleter.hasListener && + !_updateStreamCompleter.isPaused && + !_updateStreamCompleter.isClosed) { + _updateStreamCompleter.add(null); + } + } + + Future onReady() { + return _onEvent(_eventClientReady); + } + + Future onReadyFromCache() { + return _onEvent(_eventClientReadyFromCache); + } + + Stream onUpdated() { + return _updateStreamCompleter.stream; + } + + Future onTimeout() { + return _onEvent(_eventClientTimeout); + } + + void destroy() { + _updateStreamCompleter.close(); + } + + Future _onEvent(String sdkEvent) { + if (_triggeredClientEvents.containsKey(sdkEvent) && + _triggeredClientEvents[sdkEvent] == true) { + return Future.value(); + } + + return (_clientEventCallbacks[sdkEvent])?.future ?? Future.error(Object()); + } +} diff --git a/splitio/lib/impressions/impressions_method_call_handler.dart b/splitio_platform_interface/lib/impressions/impressions_method_call_handler.dart similarity index 59% rename from splitio/lib/impressions/impressions_method_call_handler.dart rename to splitio_platform_interface/lib/impressions/impressions_method_call_handler.dart index d09527a..ea4e4a4 100644 --- a/splitio/lib/impressions/impressions_method_call_handler.dart +++ b/splitio_platform_interface/lib/impressions/impressions_method_call_handler.dart @@ -1,20 +1,19 @@ import 'dart:async'; -import 'package:flutter/services.dart'; -import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/method_call_handler.dart'; +import 'package:splitio_platform_interface/method_call_handler.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; class ImpressionsMethodCallHandler extends StreamMethodCallHandler { final _streamController = StreamController(); @override - Future handle(MethodCall call) async { - if (call.method == 'impressionLog') { + Future handle(String methodName, dynamic methodArguments) async { + if (methodName == 'impressionLog') { if (_streamController.hasListener && !_streamController.isPaused && !_streamController.isClosed) { _streamController.add(Impression.fromMap( - Map.from(call.arguments ?? {}))); + Map.from(methodArguments ?? {}))); } } } diff --git a/splitio/lib/method_call_handler.dart b/splitio_platform_interface/lib/method_call_handler.dart similarity index 61% rename from splitio/lib/method_call_handler.dart rename to splitio_platform_interface/lib/method_call_handler.dart index 42c7c4f..6ef88ac 100644 --- a/splitio/lib/method_call_handler.dart +++ b/splitio_platform_interface/lib/method_call_handler.dart @@ -1,7 +1,5 @@ -import 'package:flutter/services.dart'; - abstract class MethodCallHandler { - Future handle(MethodCall call); + Future handle(String methodName, dynamic methodArguments); } abstract class StreamMethodCallHandler extends MethodCallHandler { diff --git a/splitio/lib/split_configuration.dart b/splitio_platform_interface/lib/split_configuration.dart similarity index 100% rename from splitio/lib/split_configuration.dart rename to splitio_platform_interface/lib/split_configuration.dart diff --git a/splitio/lib/impressions/split_impression.dart b/splitio_platform_interface/lib/split_impression.dart similarity index 100% rename from splitio/lib/impressions/split_impression.dart rename to splitio_platform_interface/lib/split_impression.dart diff --git a/splitio/lib/split_result.dart b/splitio_platform_interface/lib/split_result.dart similarity index 100% rename from splitio/lib/split_result.dart rename to splitio_platform_interface/lib/split_result.dart diff --git a/splitio/lib/split_sync_config.dart b/splitio_platform_interface/lib/split_sync_config.dart similarity index 100% rename from splitio/lib/split_sync_config.dart rename to splitio_platform_interface/lib/split_sync_config.dart diff --git a/splitio/lib/split_view.dart b/splitio_platform_interface/lib/split_view.dart similarity index 100% rename from splitio/lib/split_view.dart rename to splitio_platform_interface/lib/split_view.dart diff --git a/splitio_platform_interface/lib/splitio_platform_interface.dart b/splitio_platform_interface/lib/splitio_platform_interface.dart new file mode 100644 index 0000000..e368763 --- /dev/null +++ b/splitio_platform_interface/lib/splitio_platform_interface.dart @@ -0,0 +1,183 @@ +import 'dart:async'; + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:splitio_platform_interface/method_channel_platform.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; +import 'package:splitio_platform_interface/split_result.dart'; +import 'package:splitio_platform_interface/split_view.dart'; + +abstract class _FactoryPlatform { + Future getClient( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future init({ + required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration, + }) { + throw UnimplementedError(); + } + + Future split( + {required String matchingKey, + required String? bucketingKey, + required String splitName}) { + throw UnimplementedError(); + } + + Future> splitNames( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future> splits( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Stream impressionsStream() { + throw UnimplementedError(); + } +} + +abstract class _ClientPlatform { + Future getTreatment( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + throw UnimplementedError(); + } + + Future getTreatmentWithConfig( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + throw UnimplementedError(); + } + + Future> getTreatments( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + throw UnimplementedError(); + } + + Future> getTreatmentsWithConfig( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + throw UnimplementedError(); + } + + Future> getAllAttributes( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future setAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName, + required dynamic value}) { + throw UnimplementedError(); + } + + Future setAttributes( + {required String matchingKey, + required String? bucketingKey, + required Map attributes}) { + throw UnimplementedError(); + } + + Future getAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + throw UnimplementedError(); + } + + Future removeAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + throw UnimplementedError(); + } + + Future clearAttributes( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future flush( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future destroy( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future track( + {required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) { + throw UnimplementedError(); + } + + Future? onReady( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future? onReadyFromCache( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Stream? onUpdated( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } + + Future? onTimeout( + {required String matchingKey, required String? bucketingKey}) { + throw UnimplementedError(); + } +} + +/// The interface that implementations of splitio must implement. +/// +/// Platform implementations should extend this class rather than implement it as `splitio` +/// does not consider newly added methods to be breaking changes. Extending this class +/// (using `extends`) ensures that the subclass will get the default implementation, while +/// platform implementations that `implements` this interface will be broken by newly added +/// [SplitioPlatform] methods. +abstract class SplitioPlatform extends PlatformInterface + with _FactoryPlatform, _ClientPlatform { + SplitioPlatform() : super(token: _token); + + static SplitioPlatform _instance = MethodChannelPlatform(); + + static final Object _token = Object(); + + static SplitioPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [SplitioPlatform] when they register themselves. + static set instance(SplitioPlatform instance) { + PlatformInterface.verify(instance, _token); + _instance = instance; + } +} diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml new file mode 100644 index 0000000..0d43036 --- /dev/null +++ b/splitio_platform_interface/pubspec.yaml @@ -0,0 +1,23 @@ +name: splitio_platform_interface +description: Common platform interface for the splitio plugin. +# NOTE: We strongly prefer non-breaking changes, even at the expense of a +# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes +version: 1.0.0 +homepage: https://split.io/ +repository: https://github.com/splitio/flutter-sdk-plugin/splitio/splitio_platform_interface + +publish_to: none + +environment: + sdk: ">=2.16.2 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.1.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.0 diff --git a/splitio/test/impressions_method_call_handler_test.dart b/splitio_platform_interface/test/impressions_method_call_handler_test.dart similarity index 100% rename from splitio/test/impressions_method_call_handler_test.dart rename to splitio_platform_interface/test/impressions_method_call_handler_test.dart diff --git a/splitio/test/split_event_listener_test.dart b/splitio_platform_interface/test/split_event_listener_test.dart similarity index 100% rename from splitio/test/split_event_listener_test.dart rename to splitio_platform_interface/test/split_event_listener_test.dart diff --git a/splitio/test/split_impression_test.dart b/splitio_platform_interface/test/split_impression_test.dart similarity index 100% rename from splitio/test/split_impression_test.dart rename to splitio_platform_interface/test/split_impression_test.dart diff --git a/splitio/test/split_view_test.dart b/splitio_platform_interface/test/split_view_test.dart similarity index 100% rename from splitio/test/split_view_test.dart rename to splitio_platform_interface/test/split_view_test.dart diff --git a/splitio/test/splitio_configuration_test.dart b/splitio_platform_interface/test/splitio_configuration_test.dart similarity index 100% rename from splitio/test/splitio_configuration_test.dart rename to splitio_platform_interface/test/splitio_configuration_test.dart From 40b75e9580bab1cab54dc64700cbf2a628b7dda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Tue, 6 Sep 2022 18:31:31 -0300 Subject: [PATCH 03/25] Use SplitioPlatform interface (#46) --- splitio/example/pubspec.lock | 14 + splitio/lib/split_client.dart | 198 ++++------- splitio/lib/splitio.dart | 116 +++--- splitio/pubspec.yaml | 2 + splitio/test/splitio_client_test.dart | 2 - .../lib/method_channel_platform.dart | 332 ++++++++++++++++++ .../lib/split_configuration.dart | 2 +- .../impressions_method_call_handler_test.dart | 12 +- .../test/split_event_listener_test.dart | 6 +- 9 files changed, 477 insertions(+), 207 deletions(-) create mode 100644 splitio_platform_interface/lib/method_channel_platform.dart diff --git a/splitio/example/pubspec.lock b/splitio/example/pubspec.lock index 3d5ec72..1de670f 100644 --- a/splitio/example/pubspec.lock +++ b/splitio/example/pubspec.lock @@ -109,6 +109,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" sky_engine: dependency: transitive description: flutter @@ -128,6 +135,13 @@ packages: relative: true source: path version: "0.1.1" + splitio_platform_interface: + dependency: transitive + description: + path: "../../splitio_platform_interface" + relative: true + source: path + version: "1.0.0" stack_trace: dependency: transitive description: diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index fb72ce0..ec333b6 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,8 +1,6 @@ import 'package:flutter/foundation.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/events/split_events_listener.dart'; -import 'package:splitio/events/split_method_call_handler.dart'; -import 'package:splitio/split_result.dart'; +import 'package:splitio_platform_interface/split_result.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; abstract class SplitClient { /// Performs an evaluation for the [splitName] feature. @@ -142,79 +140,55 @@ abstract class SplitClient { } class DefaultSplitClient implements SplitClient { - static const String _controlTreatment = 'control'; - static const SplitResult _controlResult = - SplitResult(_controlTreatment, null); - - final MethodChannelManager _methodChannelManager; - late final SplitEventMethodCallHandler _methodCallHandler; + final SplitioPlatform _platform; final String _matchingKey; final String? _bucketingKey; - late final SplitEventsListener _splitEventsListener; - - DefaultSplitClient( - this._methodChannelManager, this._matchingKey, this._bucketingKey) { - _methodCallHandler = - SplitEventMethodCallHandler(_matchingKey, _bucketingKey, this); - _splitEventsListener = - DefaultEventsListener(_methodChannelManager, _methodCallHandler); - } + DefaultSplitClient(this._platform, this._matchingKey, this._bucketingKey); @visibleForTesting - DefaultSplitClient.withEventListener(this._methodChannelManager, - this._matchingKey, this._bucketingKey, this._splitEventsListener); + DefaultSplitClient.withEventListener( + this._platform, this._matchingKey, this._bucketingKey); @override Future getTreatment(String splitName, [Map attributes = const {}]) async { - return await _methodChannelManager.invokeMethod( - 'getTreatment', - _buildParameters( - {'splitName': splitName, 'attributes': attributes})) ?? - _controlTreatment; + return _platform.getTreatment( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitName: splitName, + attributes: attributes); } @override Future getTreatmentWithConfig(String splitName, [Map attributes = const {}]) async { - Map? treatment = (await _methodChannelManager.invokeMapMethod( - 'getTreatmentWithConfig', - _buildParameters( - {'splitName': splitName, 'attributes': attributes}))) - ?.entries - .first - .value; - if (treatment == null) { - return _controlResult; - } - - return SplitResult(treatment['treatment'], treatment['config']); + return _platform.getTreatmentWithConfig( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitName: splitName, + attributes: attributes); } @override Future> getTreatments(List splitNames, [Map attributes = const {}]) async { - Map? treatments = await _methodChannelManager.invokeMapMethod( - 'getTreatments', - _buildParameters({'splitName': splitNames, 'attributes': attributes})); - - return treatments - ?.map((key, value) => MapEntry(key, value)) ?? - {for (var item in splitNames) item: _controlTreatment}; + return _platform.getTreatments( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitNames: splitNames, + attributes: attributes); } @override Future> getTreatmentsWithConfig( List splitNames, [Map attributes = const {}]) async { - Map? treatments = await _methodChannelManager.invokeMapMethod( - 'getTreatmentsWithConfig', - _buildParameters({'splitName': splitNames, 'attributes': attributes})); - - return treatments?.map((key, value) => - MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? - {for (var item in splitNames) item: _controlResult}; + return _platform.getTreatmentsWithConfig( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + splitNames: splitNames, + attributes: attributes); } @override @@ -222,121 +196,101 @@ class DefaultSplitClient implements SplitClient { {String? trafficType, double? value, Map properties = const {}}) async { - var parameters = _buildParameters({'eventType': eventType}); - - if (trafficType != null) { - parameters['trafficType'] = trafficType; - } - - if (value != null) { - parameters['value'] = value; - } - - try { - return await _methodChannelManager.invokeMethod("track", parameters) - as bool; - } on Exception catch (_) { - return false; - } + return _platform.track( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + eventType: eventType, + trafficType: trafficType, + value: value, + properties: properties); } @override Future setAttribute(String attributeName, dynamic value) async { - var result = await _methodChannelManager.invokeMethod('setAttribute', - _buildParameters({'attributeName': attributeName, 'value': value})); - - if (result is bool) { - return result; - } - - return false; + return _platform.setAttribute( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributeName: attributeName, + value: value); } @override Future getAttribute(String attributeName) async { - return _methodChannelManager.invokeMethod( - 'getAttribute', _buildParameters({'attributeName': attributeName})); + return _platform.getAttribute( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributeName: attributeName); } @override Future setAttributes(Map attributes) async { - var result = await _methodChannelManager.invokeMethod( - 'setAttributes', _buildParameters({'attributes': attributes})); - - if (result is bool) { - return result; - } - - return false; + return _platform.setAttributes( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributes: attributes); } @override Future> getAttributes() async { - return (await _methodChannelManager.invokeMapMethod( - 'getAllAttributes', _buildParameters())) - ?.map((key, value) => MapEntry(key, value)) ?? - {}; + return _platform.getAllAttributes( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future removeAttribute(String attributeName) async { - return await _methodChannelManager.invokeMethod( - 'removeAttribute', _buildParameters({'attributeName': attributeName})); + return _platform.removeAttribute( + matchingKey: _matchingKey, + bucketingKey: _bucketingKey, + attributeName: attributeName); } @override Future clearAttributes() async { - return await _methodChannelManager.invokeMethod( - 'clearAttributes', _buildParameters()); + return _platform.clearAttributes( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future flush() async { - return _methodChannelManager.invokeMethod('flush', _buildParameters()); + return _platform.flush( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override Future destroy() async { - _splitEventsListener.destroy(); - return _methodChannelManager.invokeMethod('destroy', _buildParameters()); + return _platform.destroy( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); } @override - Future whenReady() { - return _splitEventsListener.onReady(); + Future whenReady() async { + await _platform.onReady( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); + + return Future.value(this); } @override - Future whenReadyFromCache() { - return _splitEventsListener.onReadyFromCache(); + Future whenReadyFromCache() async { + await _platform.onReadyFromCache( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); + + return Future.value(this); } @override Stream whenUpdated() { - return _splitEventsListener.onUpdated(); + return _platform + .onUpdated(matchingKey: _matchingKey, bucketingKey: _bucketingKey) + ?.map((event) => this) ?? + const Stream.empty(); } @override - Future whenTimeout() { - return _splitEventsListener.onTimeout(); - } - - Map _getKeysMap() { - Map result = {'matchingKey': _matchingKey}; - - if (_bucketingKey != null) { - result.addAll({'bucketingKey': _bucketingKey!}); - } - - return result; - } - - Map _buildParameters( - [Map parameters = const {}]) { - Map result = {}; - result.addAll(parameters); - result.addAll(_getKeysMap()); + Future whenTimeout() async { + await _platform.onTimeout( + matchingKey: _matchingKey, bucketingKey: _bucketingKey); - return result; + return Future.value(this); } } diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 254a486..203153e 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -1,31 +1,28 @@ import 'dart:async'; -import 'package:flutter/services.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/impressions/impressions_method_call_handler.dart'; -import 'package:splitio/impressions/split_impression.dart'; -import 'package:splitio/method_call_handler.dart'; import 'package:splitio/split_client.dart'; -import 'package:splitio/split_configuration.dart'; -import 'package:splitio/split_view.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; +import 'package:splitio_platform_interface/split_view.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; -export 'package:splitio/impressions/split_impression.dart'; export 'package:splitio/split_client.dart'; -export 'package:splitio/split_configuration.dart'; -export 'package:splitio/split_result.dart'; -export 'package:splitio/split_sync_config.dart'; -export 'package:splitio/split_view.dart'; +export 'package:splitio_platform_interface/split_configuration.dart'; +export 'package:splitio_platform_interface/split_impression.dart'; +export 'package:splitio_platform_interface/split_view.dart'; typedef ClientReadinessCallback = void Function(SplitClient splitClient); class Splitio { final String _apiKey; + final String _defaultMatchingKey; + late final String? _defaultBucketingKey; + late final SplitConfiguration? _splitConfiguration; - late final StreamMethodCallHandler _impressionsMethodCallHandler; - final MethodChannelManager _methodChannelManager = - MethodChannelManager(const MethodChannel('splitio')); + + final SplitioPlatform _platform = SplitioPlatform.instance; /// SDK instance constructor. /// @@ -41,8 +38,6 @@ class Splitio { {String? bucketingKey, SplitConfiguration? configuration}) { _defaultBucketingKey = bucketingKey; _splitConfiguration = configuration; - _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); - _methodChannelManager.addHandler(_impressionsMethodCallHandler); _init(); } @@ -77,97 +72,66 @@ class Splitio { ClientReadinessCallback? onUpdated, ClientReadinessCallback? onTimeout}) { String? key = matchingKey ?? _defaultMatchingKey; + _platform.getClient(matchingKey: key, bucketingKey: bucketingKey); - var client = DefaultSplitClient(_methodChannelManager, key, bucketingKey); + var client = DefaultSplitClient(_platform, key, bucketingKey); if (onReady != null) { - client.whenReady().then((client) => onReady.call(client)); + _platform + .onReady(matchingKey: key, bucketingKey: bucketingKey) + ?.then((val) => onReady.call(client)); } if (onReadyFromCache != null) { - client - .whenReadyFromCache() - .then((client) => onReadyFromCache.call(client)); + _platform + .onReadyFromCache(matchingKey: key, bucketingKey: bucketingKey) + ?.then((val) => onReadyFromCache.call(client)); } if (onTimeout != null) { - client.whenTimeout().then((client) => onTimeout.call(client)); + _platform + .onTimeout(matchingKey: key, bucketingKey: bucketingKey) + ?.then((val) => onTimeout.call(client)); } if (onUpdated != null) { - client.whenUpdated().listen((client) => onUpdated.call(client)); + _platform + .onUpdated(matchingKey: key, bucketingKey: bucketingKey) + ?.listen((event) => onUpdated.call(client)); } - _methodChannelManager.invokeMethod( - 'getClient', _buildGetClientArguments(key, bucketingKey)); - return client; } Future> splitNames() async { - List splitNames = - await _methodChannelManager.invokeListMethod('splitNames') ?? - []; + List splitNames = await _platform.splitNames( + matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); return splitNames; } Future> splits() async { - List callResult = (await _methodChannelManager - .invokeListMethod>('splits') ?? - []); - - List splits = []; - for (var element in callResult) { - SplitView? splitView = SplitView.fromEntry(element); - if (splitView != null) { - splits.add(splitView); - } - } - - return Future.value(splits); + return _platform.splits( + matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); } /// If the impressionListener configuration has been enabled, /// generated impressions will be streamed here. Stream impressionsStream() { - return _impressionsMethodCallHandler.stream(); + return _platform.impressionsStream(); } Future split(String splitName) async { - Map? mapResult = await _methodChannelManager - .invokeMapMethod('split', {'splitName': splitName}); - - if (mapResult == null) { - return null; - } - - return SplitView.fromEntry(mapResult); + return _platform.split( + matchingKey: _defaultMatchingKey, + bucketingKey: _defaultBucketingKey, + splitName: splitName); } Future _init() { - Map arguments = { - 'apiKey': _apiKey, - 'matchingKey': _defaultMatchingKey, - 'sdkConfiguration': _splitConfiguration?.configurationMap ?? {}, - }; - - if (_defaultBucketingKey != null) { - arguments.addAll({'bucketingKey': _defaultBucketingKey}); - } - - return _methodChannelManager.invokeMethod('init', arguments); - } - - Map _buildGetClientArguments( - String key, String? bucketingKey) { - var arguments = { - 'matchingKey': key, - }; - - if (bucketingKey != null) { - arguments.addAll({'bucketingKey': bucketingKey}); - } - - return arguments; + return _platform.init( + apiKey: _apiKey, + matchingKey: _defaultMatchingKey, + bucketingKey: _defaultBucketingKey, + sdkConfiguration: _splitConfiguration); } } diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index a4f85a3..ddc7878 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -11,6 +11,8 @@ environment: dependencies: flutter: sdk: flutter + splitio_platform_interface: # ^1.0.0 + path: ../splitio_platform_interface dev_dependencies: flutter_test: diff --git a/splitio/test/splitio_client_test.dart b/splitio/test/splitio_client_test.dart index 52e7a44..0babeb2 100644 --- a/splitio/test/splitio_client_test.dart +++ b/splitio/test/splitio_client_test.dart @@ -1,7 +1,5 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/split_client.dart'; void main() { diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart new file mode 100644 index 0000000..95ff186 --- /dev/null +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -0,0 +1,332 @@ + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:splitio_platform_interface/events/split_method_call_handler.dart'; +import 'package:splitio_platform_interface/impressions/impressions_method_call_handler.dart'; +import 'package:splitio_platform_interface/method_call_handler.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; +import 'package:splitio_platform_interface/split_result.dart'; +import 'package:splitio_platform_interface/split_view.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; + +const String _controlTreatment = 'control'; +const SplitResult _controlResult = SplitResult(_controlTreatment, null); + +class MethodChannelPlatform extends SplitioPlatform { + + final MethodChannel _methodChannel = const MethodChannel('splitio'); + + final Map _handlers = {}; + + final ImpressionsMethodCallHandler _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); + + MethodChannelPlatform() { + _methodChannel.setMethodCallHandler((call) => handle(call)); + } + + @visibleForTesting + Future handle(MethodCall call) async { + _impressionsMethodCallHandler.handle(call.method, call.arguments); + for (MethodCallHandler handler in _handlers.values) { + handler.handle(call.method, call.arguments); + } + } + + @override + Future init({required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { + Map arguments = { + 'apiKey': apiKey, + 'matchingKey': matchingKey, + 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, + }; + + if (bucketingKey != null) { + arguments.addAll({'bucketingKey': bucketingKey}); + } + + return _methodChannel.invokeMethod('init', arguments); + } + + @override + Future getClient( + {required String matchingKey, required String? bucketingKey}) { + _handlers.addAll( + { + _buildMapKey(matchingKey, bucketingKey): SplitEventMethodCallHandler( + matchingKey, bucketingKey) + }); + + return _methodChannel.invokeMethod( + 'getClient', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future clearAttributes( + {required String matchingKey, required String? bucketingKey}) async { + return await _methodChannel.invokeMethod( + 'clearAttributes', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future destroy( + {required String matchingKey, required String? bucketingKey}) async { + var handlerKey = _buildMapKey(matchingKey, bucketingKey); + _handlers[handlerKey]?.destroy(); + _handlers.remove(handlerKey); + + return await _methodChannel.invokeMethod( + 'destroy', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future flush( + {required String matchingKey, required String? bucketingKey}) async { + return await _methodChannel.invokeMethod( + 'flush', _buildParameters(matchingKey, bucketingKey)); + } + + @override + Future> getAllAttributes( + {required String matchingKey, required String? bucketingKey}) async { + return (await _methodChannel.invokeMapMethod( + 'getAllAttributes', _buildParameters(matchingKey, bucketingKey))) + ?.map((key, value) => MapEntry(key, value)) ?? + {}; + } + + @override + Future getAttribute({required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + return _methodChannel.invokeMethod( + 'getAttribute', _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); + } + + @override + Future getTreatment({required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) async { + return await _methodChannel.invokeMethod( + 'getTreatment', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitName, 'attributes': attributes})) ?? + _controlTreatment; + } + + @override + Future getTreatmentWithConfig({required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) async { + Map? treatment = (await _methodChannel.invokeMapMethod( + 'getTreatmentWithConfig', + _buildParameters(matchingKey, bucketingKey + {'splitName': splitName, 'attributes': attributes}))) + ?.entries + .first + .value; + if (treatment == null) { + return _controlResult; + } + + return SplitResult(treatment['treatment'], treatment['config']); + } + + @override + Future> getTreatments({required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) async { + Map? treatments = await _methodChannel.invokeMapMethod( + 'getTreatments', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitNames, 'attributes': attributes})); + + return treatments + ?.map((key, value) => MapEntry(key, value)) ?? + {for (var item in splitNames) item: _controlTreatment}; + } + + @override + Future> getTreatmentsWithConfig( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) async { + Map? treatments = await _methodChannel.invokeMapMethod( + 'getTreatmentsWithConfig', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitNames, 'attributes': attributes})); + + return treatments?.map((key, value) => + MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? + {for (var item in splitNames) item: _controlResult}; + } + + @override + Future removeAttribute({required String matchingKey, + required String? bucketingKey, + required String attributeName}) async { + return await _methodChannel.invokeMethod( + 'removeAttribute', _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); + } + + @override + Future setAttribute({required String matchingKey, + required String? bucketingKey, + required String attributeName, + required value}) async { + var result = await _methodChannel.invokeMethod('setAttribute', + _buildParameters(matchingKey, bucketingKey, + {'attributeName': attributeName, 'value': value})); + + if (result is bool) { + return result; + } + + return false; + } + + @override + Future setAttributes({required String matchingKey, + required String? bucketingKey, + required Map attributes}) async { + var result = await _methodChannel.invokeMethod( + 'setAttributes', _buildParameters( + matchingKey, bucketingKey, {'attributes': attributes})); + + if (result is bool) { + return result; + } + + return false; + } + + @override + Future split({required String matchingKey, + required String? bucketingKey, + required String splitName}) async { + Map? mapResult = + await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); + + if (mapResult == null) { + return null; + } + + return SplitView.fromEntry(mapResult); + } + + @override + Future> splitNames( + {required String matchingKey, required String? bucketingKey}) async { + List splitNames = + await _methodChannel.invokeListMethod('splitNames') ?? []; + + return splitNames; + } + + @override + Future> splits( + {required String matchingKey, required String? bucketingKey}) async { + List callResult = (await _methodChannel + .invokeListMethod>('splits') ?? + []); + + List splits = []; + for (var element in callResult) { + SplitView? splitView = SplitView.fromEntry(element); + if (splitView != null) { + splits.add(splitView); + } + } + + return Future.value(splits); + } + + @override + Future track({required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) async { + var parameters = _buildParameters( + matchingKey, bucketingKey, {'eventType': eventType}); + + if (trafficType != null) { + parameters['trafficType'] = trafficType; + } + + if (value != null) { + parameters['value'] = value; + } + + try { + return await _methodChannel.invokeMethod("track", parameters) + as bool; + } on Exception catch (_) { + return false; + } + } + + Map _buildParameters(String matchingKey, + String? bucketingKey, + [Map parameters = const {}]) { + Map result = {}; + result.addAll(parameters); + result.addAll(_getKeysMap(matchingKey, bucketingKey)); + + return result; + } + + Map _getKeysMap(String matchingKey, String? bucketingKey) { + Map result = {'matchingKey': matchingKey}; + + if (bucketingKey != null) { + result.addAll({'bucketingKey': bucketingKey}); + } + + return result; + } + + @override + Future? onReady( + {required String matchingKey, required String? bucketingKey}) { + return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onReady(); + } + + @override + Future? onReadyFromCache( + {required String matchingKey, required String? bucketingKey}) { + return _handlers[_buildMapKey(matchingKey, bucketingKey)] + ?.onReadyFromCache(); + } + + @override + Stream? onUpdated( + {required String matchingKey, required String? bucketingKey}) { + return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onUpdated(); + } + + @override + Future? onTimeout( + {required String matchingKey, required String? bucketingKey}) { + return _handlers[_buildMapKey(matchingKey, bucketingKey)]?.onTimeout(); + } + + @override + Stream impressionsStream() { + return _impressionsMethodCallHandler.stream(); + } + + String _buildMapKey(String matchingKey, String? bucketingKey) { + return '${matchingKey}_$bucketingKey'; + } +} diff --git a/splitio_platform_interface/lib/split_configuration.dart b/splitio_platform_interface/lib/split_configuration.dart index 5e93df9..80c55fb 100644 --- a/splitio_platform_interface/lib/split_configuration.dart +++ b/splitio_platform_interface/lib/split_configuration.dart @@ -1,4 +1,4 @@ -import 'package:splitio/split_sync_config.dart'; +import 'package:splitio_platform_interface/split_sync_config.dart'; class SplitConfiguration { final Map configurationMap = {}; diff --git a/splitio_platform_interface/test/impressions_method_call_handler_test.dart b/splitio_platform_interface/test/impressions_method_call_handler_test.dart index f919f9a..6ae0cbb 100644 --- a/splitio_platform_interface/test/impressions_method_call_handler_test.dart +++ b/splitio_platform_interface/test/impressions_method_call_handler_test.dart @@ -1,8 +1,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/impressions/impressions_method_call_handler.dart'; import 'package:splitio/impressions/split_impression.dart'; +import '../lib/impressions/impressions_method_call_handler.dart'; + void main() { test('correct impressionLog method call emits value on stream', () async { const Map sourceMap = { @@ -34,8 +35,9 @@ void main() { }), ); - impressionsMethodCallHandler - .handle(const MethodCall('impressionLog', sourceMap)); + const methodCall = MethodCall('impressionLog', sourceMap); + impressionsMethodCallHandler.handle( + methodCall.method, methodCall.arguments); }); test('other method names are ignored', () async { @@ -47,6 +49,8 @@ void main() { expect(event, null); }, count: 0), ); - impressionsMethodCallHandler.handle(const MethodCall('clientReady')); + const methodCall = MethodCall('clientReady'); + impressionsMethodCallHandler.handle( + methodCall.method, methodCall.arguments); }); } diff --git a/splitio_platform_interface/test/split_event_listener_test.dart b/splitio_platform_interface/test/split_event_listener_test.dart index 3099e8f..f06c463 100644 --- a/splitio_platform_interface/test/split_event_listener_test.dart +++ b/splitio_platform_interface/test/split_event_listener_test.dart @@ -3,9 +3,10 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/channel/method_channel_manager.dart'; import 'package:splitio/events/split_events_listener.dart'; import 'package:splitio/events/split_method_call_handler.dart'; -import 'package:splitio/split_client.dart'; import 'package:splitio/split_result.dart'; +import '../lib/split_client.dart'; + void main() { const MethodChannel _channel = MethodChannel('splitio'); @@ -25,7 +26,8 @@ void main() { setUp(() { _channel.setMockMethodCallHandler((MethodCall methodCall) { - splitEventMethodCallHandler.handle(methodCall); + splitEventMethodCallHandler.handle( + methodCall.method, methodCall.arguments); }); }); From 37d316721fc868fde2c33679246a31774f0a0e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Tue, 6 Sep 2022 18:54:49 -0300 Subject: [PATCH 04/25] Tests for platform interface (#47) --- splitio/lib/split_client.dart | 5 - splitio/lib/splitio.dart | 15 +- splitio/test/splitio_client_test.dart | 255 +++------ splitio/test/splitio_platform_stub.dart | 346 ++++++++++++ splitio/test/splitio_test.dart | 59 +- .../lib/method_channel_platform.dart | 167 +++--- .../lib/splitio_platform_interface.dart | 10 +- .../impressions_method_call_handler_test.dart | 4 +- .../test/method_channel_platform_test.dart | 517 ++++++++++++++++++ .../test/split_event_listener_test.dart | 239 -------- .../split_event_method_call_handler_test.dart | 97 ++++ .../test/split_impression_test.dart | 2 +- .../test/split_view_test.dart | 2 +- .../test/splitio_configuration_test.dart | 11 +- 14 files changed, 1167 insertions(+), 562 deletions(-) create mode 100644 splitio/test/splitio_platform_stub.dart create mode 100644 splitio_platform_interface/test/method_channel_platform_test.dart delete mode 100644 splitio_platform_interface/test/split_event_listener_test.dart create mode 100644 splitio_platform_interface/test/split_event_method_call_handler_test.dart diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index ec333b6..673c978 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:splitio_platform_interface/split_result.dart'; import 'package:splitio_platform_interface/splitio_platform_interface.dart'; @@ -146,10 +145,6 @@ class DefaultSplitClient implements SplitClient { DefaultSplitClient(this._platform, this._matchingKey, this._bucketingKey); - @visibleForTesting - DefaultSplitClient.withEventListener( - this._platform, this._matchingKey, this._bucketingKey); - @override Future getTreatment(String splitName, [Map attributes = const {}]) async { diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 203153e..d5620cb 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -18,9 +18,9 @@ class Splitio { final String _defaultMatchingKey; - late final String? _defaultBucketingKey; + late String? _defaultBucketingKey; - late final SplitConfiguration? _splitConfiguration; + late SplitConfiguration? _splitConfiguration; final SplitioPlatform _platform = SplitioPlatform.instance; @@ -103,15 +103,13 @@ class Splitio { } Future> splitNames() async { - List splitNames = await _platform.splitNames( - matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); + List splitNames = await _platform.splitNames(); return splitNames; } Future> splits() async { - return _platform.splits( - matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); + return _platform.splits(); } /// If the impressionListener configuration has been enabled, @@ -121,10 +119,7 @@ class Splitio { } Future split(String splitName) async { - return _platform.split( - matchingKey: _defaultMatchingKey, - bucketingKey: _defaultBucketingKey, - splitName: splitName); + return _platform.split(splitName: splitName); } Future _init() { diff --git a/splitio/test/splitio_client_test.dart b/splitio/test/splitio_client_test.dart index 0babeb2..25e638f 100644 --- a/splitio/test/splitio_client_test.dart +++ b/splitio/test/splitio_client_test.dart @@ -1,59 +1,24 @@ -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/split_client.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; -void main() { - const MethodChannel _channel = MethodChannel('splitio'); - - String methodName = ''; - dynamic methodArguments; +import 'splitio_platform_stub.dart'; +void main() { TestWidgetsFlutterBinding.ensureInitialized(); - MethodChannelManager _methodChannelWrapper = MethodChannelManager(_channel); + SplitioPlatformStub _platform = SplitioPlatformStub(); - SplitClient _getClient([SplitEventsListener? splitEventsListener]) { - if (splitEventsListener != null) { - return DefaultSplitClient.withEventListener(_methodChannelWrapper, - 'matching-key', 'bucketing-key', splitEventsListener); + SplitClient _getClient([SplitioPlatform? platform]) { + if (platform != null) { + return DefaultSplitClient(platform, 'matching-key', 'bucketing-key'); } - return DefaultSplitClient( - _methodChannelWrapper, 'matching-key', 'bucketing-key'); + return DefaultSplitClient(_platform, 'matching-key', 'bucketing-key'); } setUp(() { - _channel.setMockMethodCallHandler((MethodCall methodCall) async { - methodName = methodCall.method; - methodArguments = methodCall.arguments; - - switch (methodCall.method) { - case 'getTreatment': - return ''; - case 'getTreatments': - return {'split1': 'on', 'split2': 'off'}; - case 'getTreatmentsWithConfig': - return { - 'split1': {'treatment': 'on', 'config': null}, - 'split2': {'treatment': 'off', 'config': null} - }; - case 'track': - return true; - case 'getAttribute': - return true; - case 'getAllAttributes': - return { - 'attr_1': true, - 'attr_2': ['list-element'], - 'attr_3': 28.20 - }; - case 'setAttribute': - case 'setAttributes': - case 'removeAttribute': - case 'clearAttributes': - return true; - } - }); + _platform = SplitioPlatformStub(); }); group('evaluation', () { @@ -62,8 +27,8 @@ void main() { client.getTreatment('split'); - expect(methodName, 'getTreatment'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatment'); + expect(_platform.methodArguments, { 'splitName': 'split', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -76,8 +41,8 @@ void main() { client.getTreatment('split', {'attr1': true}); - expect(methodName, 'getTreatment'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatment'); + expect(_platform.methodArguments, { 'splitName': 'split', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -90,8 +55,8 @@ void main() { client.getTreatments(['split1', 'split2']); - expect(methodName, 'getTreatments'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatments'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -104,8 +69,8 @@ void main() { client.getTreatments(['split1', 'split2'], {'attr1': true}); - expect(methodName, 'getTreatments'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatments'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -118,8 +83,8 @@ void main() { client.getTreatmentWithConfig('split1', {'attr1': true}); - expect(methodName, 'getTreatmentWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentWithConfig'); + expect(_platform.methodArguments, { 'splitName': 'split1', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -132,8 +97,8 @@ void main() { client.getTreatmentWithConfig('split1'); - expect(methodName, 'getTreatmentWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentWithConfig'); + expect(_platform.methodArguments, { 'splitName': 'split1', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -146,8 +111,8 @@ void main() { client.getTreatmentsWithConfig(['split1', 'split2']); - expect(methodName, 'getTreatmentsWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentsWithConfig'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -160,8 +125,8 @@ void main() { client.getTreatmentsWithConfig(['split1', 'split2'], {'attr1': true}); - expect(methodName, 'getTreatmentsWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentsWithConfig'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -175,8 +140,8 @@ void main() { SplitClient client = _getClient(); client.track('my_event', trafficType: 'my_traffic_type', value: 25.10); - expect(methodName, 'track'); - expect(methodArguments, { + expect(_platform.methodName, 'track'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'eventType': 'my_event', @@ -189,8 +154,8 @@ void main() { SplitClient client = _getClient(); client.track('my_event', value: 25.10); - expect(methodName, 'track'); - expect(methodArguments, { + expect(_platform.methodName, 'track'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'eventType': 'my_event', @@ -202,8 +167,8 @@ void main() { SplitClient client = _getClient(); client.track('my_event', trafficType: 'my_traffic_type'); - expect(methodName, 'track'); - expect(methodArguments, { + expect(_platform.methodName, 'track'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'eventType': 'my_event', @@ -217,8 +182,8 @@ void main() { SplitClient client = _getClient(); client.getAttribute('attribute-name'); - expect(methodName, 'getAttribute'); - expect(methodArguments, { + expect(_platform.methodName, 'getAttribute'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributeName': 'attribute-name', @@ -229,8 +194,8 @@ void main() { SplitClient client = _getClient(); client.getAttributes(); - expect(methodName, 'getAllAttributes'); - expect(methodArguments, { + expect(_platform.methodName, 'getAllAttributes'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', }); @@ -240,8 +205,8 @@ void main() { SplitClient client = _getClient(); client.setAttribute('my_attr', 'attr_value'); - expect(methodName, 'setAttribute'); - expect(methodArguments, { + expect(_platform.methodName, 'setAttribute'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributeName': 'my_attr', @@ -258,8 +223,8 @@ void main() { 'string_attr': 'attr-value', 'list_attr': ['one', 'two'], }); - expect(methodName, 'setAttributes'); - expect(methodArguments, { + expect(_platform.methodName, 'setAttributes'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributes': { @@ -275,8 +240,8 @@ void main() { SplitClient client = _getClient(); client.removeAttribute('attr-name'); - expect(methodName, 'removeAttribute'); - expect(methodArguments, { + expect(_platform.methodName, 'removeAttribute'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributeName': 'attr-name', @@ -287,8 +252,8 @@ void main() { SplitClient client = _getClient(); client.clearAttributes(); - expect(methodName, 'clearAttributes'); - expect(methodArguments, { + expect(_platform.methodName, 'clearAttributes'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', }); @@ -298,122 +263,58 @@ void main() { SplitClient client = _getClient(); client.flush(); - expect(methodName, 'flush'); - expect(methodArguments, { + expect(_platform.methodName, 'flush'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', }); }); - test('destroy', () async { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); + test('destroy', () { + _platform.destroy( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - client.destroy(); - - expect(splitEventsListenerStub.calledMethods['destroy'], 1); - - client.destroy(); - expect(methodName, 'destroy'); - expect(methodArguments, { - 'matchingKey': 'matching-key', - 'bucketingKey': 'bucketing-key', - }); + expect(_platform.methodName, 'destroy'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); }); }); group('events', () { - test('onReady is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = client.whenReady().then((value) => client == value); - expect(splitEventsListenerStub.calledMethods['onReady'], 1); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], null); - expect(splitEventsListenerStub.calledMethods['onTimeout'], null); - expect(splitEventsListenerStub.calledMethods['onUpdated'], null); - expect(future, completion(equals(true))); - }); + test('onReady', () { + _platform.onReady( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - test('onReadyFromCache is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = client.whenReadyFromCache().then((value) => client == value); - expect(splitEventsListenerStub.calledMethods['onReady'], null); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], 1); - expect(splitEventsListenerStub.calledMethods['onTimeout'], null); - expect(splitEventsListenerStub.calledMethods['onUpdated'], null); - expect(future, completion(equals(true))); + expect(_platform.methodName, 'onReady'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); }); - test('onTimeout is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = client.whenTimeout().then((value) => client == value); - expect(splitEventsListenerStub.calledMethods['onReady'], null); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], null); - expect(splitEventsListenerStub.calledMethods['onTimeout'], 1); - expect(splitEventsListenerStub.calledMethods['onUpdated'], null); - expect(future, completion(equals(true))); - }); + test('onReadyFromCache', () { + _platform.onReadyFromCache( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - test('onUpdated is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = - client.whenUpdated().first.then(((value) => client == value)); - expect(splitEventsListenerStub.calledMethods['onReady'], null); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], null); - expect(splitEventsListenerStub.calledMethods['onTimeout'], null); - expect(splitEventsListenerStub.calledMethods['onUpdated'], 1); - expect(future, completion(equals(true))); + expect(_platform.methodName, 'onReadyFromCache'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); }); - }); -} -class SplitEventsListenerStub extends SplitEventsListener { - Map calledMethods = {}; + test('onTimeout', () { + _platform.onTimeout( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - late final Future _clientFuture; - - void attachClient(SplitClient splitClient) { - _clientFuture = Future.value(splitClient); - } - - @override - Future onReady() { - calledMethods.update('onReady', (value) => value + 1, ifAbsent: () => 1); - return _clientFuture; - } - - @override - Future onReadyFromCache() { - calledMethods.update('onReadyFromCache', (value) => value + 1, - ifAbsent: () => 1); - return _clientFuture; - } - - @override - Future onTimeout() { - calledMethods.update('onTimeout', (value) => value + 1, ifAbsent: () => 1); - return _clientFuture; - } + expect(_platform.methodName, 'onTimeout'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); + }); - @override - Stream onUpdated() { - calledMethods.update('onUpdated', (value) => value + 1, ifAbsent: () => 1); - return Stream.fromFuture(_clientFuture); - } + test('onUpdated', () { + _platform.onUpdated( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - @override - void destroy() { - calledMethods.update('destroy', (value) => value + 1, ifAbsent: () => 1); - } + expect(_platform.methodName, 'onUpdated'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); + }); + }); } diff --git a/splitio/test/splitio_platform_stub.dart b/splitio/test/splitio_platform_stub.dart new file mode 100644 index 0000000..13e27f7 --- /dev/null +++ b/splitio/test/splitio_platform_stub.dart @@ -0,0 +1,346 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; +import 'package:splitio_platform_interface/split_result.dart'; +import 'package:splitio_platform_interface/split_view.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; + +class SplitioPlatformStub + with MockPlatformInterfaceMixin + implements SplitioPlatform { + String methodName = ''; + Map methodArguments = {}; + + @override + Future clearAttributes( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'clearAttributes'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + return Future.value(true); + } + + @override + Future destroy( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'destroy'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Future flush( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'flush'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + return Future.value(); + } + + @override + Future> getAllAttributes( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'getAllAttributes'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value({}); + } + + @override + Future getAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + methodName = 'getAttribute'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributeName': attributeName + }; + + return Future.value(); + } + + @override + Future getClient( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'getClient'; + + methodArguments = { + 'matchingKey': matchingKey, + }; + + if (bucketingKey != null) { + methodArguments['bucketingKey'] = bucketingKey; + } + + return Future.value(); + } + + @override + Future getTreatment( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + methodName = 'getTreatment'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitName, + 'attributes': attributes + }; + + return Future.value(''); + } + + @override + Future getTreatmentWithConfig( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + methodName = 'getTreatmentWithConfig'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitName, + 'attributes': attributes, + }; + + return Future.value(const SplitResult('on', null)); + } + + @override + Future> getTreatments( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + methodName = 'getTreatments'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitNames, + 'attributes': attributes, + }; + + return Future.value({}); + } + + @override + Future> getTreatmentsWithConfig( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + methodName = 'getTreatmentsWithConfig'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitNames, + 'attributes': attributes, + }; + + return Future.value({}); + } + + @override + Stream impressionsStream() { + methodName = 'impressionsStream'; + + return const Stream.empty(); + } + + @override + Future init( + {required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { + methodName = 'init'; + + methodArguments = { + 'matchingKey': matchingKey, + 'apiKey': apiKey, + 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, + }; + + if (bucketingKey != null) { + methodArguments['bucketingKey'] = bucketingKey; + } + + return Future.value(); + } + + @override + Future? onReady( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onReady'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Future? onReadyFromCache( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onReadyFromCache'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Future? onTimeout( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onTimeout'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Stream? onUpdated( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onUpdated'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return const Stream.empty(); + } + + @override + Future removeAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + methodName = 'removeAttribute'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributeName': attributeName + }; + + return Future.value(true); + } + + @override + Future setAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName, + required value}) { + methodName = 'setAttribute'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributeName': attributeName, + 'value': value, + }; + + return Future.value(true); + } + + @override + Future setAttributes( + {required String matchingKey, + required String? bucketingKey, + required Map attributes}) { + methodName = 'setAttributes'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributes': attributes + }; + + return Future.value(true); + } + + @override + Future split({required String splitName}) { + methodName = 'split'; + + methodArguments = {'splitName': splitName}; + + return Future.value(null); + } + + @override + Future> splitNames() { + methodName = 'splitNames'; + + return Future.value([]); + } + + @override + Future> splits() { + methodName = 'splits'; + + return Future.value([]); + } + + @override + Future track( + {required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) { + methodName = 'track'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'eventType': eventType + }; + + if (properties.isNotEmpty) { + methodArguments['properties'] = properties; + } + + if (trafficType != null) { + methodArguments['trafficType'] = trafficType; + } + + if (value != null) { + methodArguments['value'] = value; + } + + return Future.value(true); + } +} diff --git a/splitio/test/splitio_test.dart b/splitio/test/splitio_test.dart index 6d46e08..37cd1c5 100644 --- a/splitio/test/splitio_test.dart +++ b/splitio/test/splitio_test.dart @@ -1,37 +1,20 @@ -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/splitio.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; -void main() { - const MethodChannel channel = MethodChannel('splitio'); - - var methodArguments; - var methodName; +import 'splitio_platform_stub.dart'; +void main() { TestWidgetsFlutterBinding.ensureInitialized(); - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - methodName = methodCall.method; - methodArguments = methodCall.arguments; - - if (methodCall.method == 'splitNames') { - List emptyList = []; - return Future.value(emptyList); - } - }); - }); - - tearDown(() async { - methodName = null; - methodArguments = null; - }); + SplitioPlatformStub _platform = SplitioPlatformStub(); + SplitioPlatform.instance = _platform; group('initialization', () { test('init with matching key only', () { Splitio('api-key', 'matching-key'); - expect(methodName, 'init'); - expect(methodArguments, { + expect(_platform.methodName, 'init'); + expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'sdkConfiguration': {} @@ -40,8 +23,8 @@ void main() { test('init with bucketing key', () { Splitio('api-key', 'matching-key', bucketingKey: 'bucketing-key'); - expect(methodName, 'init'); - expect(methodArguments, { + expect(_platform.methodName, 'init'); + expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -54,8 +37,8 @@ void main() { bucketingKey: 'bucketing-key', configuration: SplitConfiguration(enableDebug: true, streamingEnabled: false)); - expect(methodName, 'init'); - expect(methodArguments, { + expect(_platform.methodName, 'init'); + expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -70,8 +53,8 @@ void main() { splitio.client(); - expect(methodName, 'getClient'); - expect(methodArguments, {'matchingKey': 'matching-key'}); + expect(_platform.methodName, 'getClient'); + expect(_platform.methodArguments, {'matchingKey': 'matching-key'}); }); test('get client with new matching key', () { @@ -79,8 +62,8 @@ void main() { splitio.client(matchingKey: 'new-matching-key'); - expect(methodName, 'getClient'); - expect(methodArguments, {'matchingKey': 'new-matching-key'}); + expect(_platform.methodName, 'getClient'); + expect(_platform.methodArguments, {'matchingKey': 'new-matching-key'}); }); test('get client with new matching key and bucketing key', () { @@ -89,8 +72,8 @@ void main() { splitio.client( matchingKey: 'new-matching-key', bucketingKey: 'bucketing-key'); - expect(methodName, 'getClient'); - expect(methodArguments, + expect(_platform.methodName, 'getClient'); + expect(_platform.methodArguments, {'matchingKey': 'new-matching-key', 'bucketingKey': 'bucketing-key'}); }); }); @@ -100,22 +83,22 @@ void main() { var splitio = Splitio('api-key', 'matching-key'); splitio.splitNames(); - expect(methodName, 'splitNames'); + expect(_platform.methodName, 'splitNames'); }); test('get splits', () { var splitio = Splitio('api-key', 'matching-key'); splitio.splits(); - expect(methodName, 'splits'); + expect(_platform.methodName, 'splits'); }); test('get split', () { var splitio = Splitio('api-key', 'matching-key'); splitio.split('my_split'); - expect(methodName, 'split'); - expect(methodArguments, {'splitName': 'my_split'}); + expect(_platform.methodName, 'split'); + expect(_platform.methodArguments, {'splitName': 'my_split'}); }); }); } diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart index 95ff186..b9a0513 100644 --- a/splitio_platform_interface/lib/method_channel_platform.dart +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -1,4 +1,3 @@ - import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:splitio_platform_interface/events/split_method_call_handler.dart'; @@ -14,17 +13,20 @@ const String _controlTreatment = 'control'; const SplitResult _controlResult = SplitResult(_controlTreatment, null); class MethodChannelPlatform extends SplitioPlatform { - final MethodChannel _methodChannel = const MethodChannel('splitio'); final Map _handlers = {}; - final ImpressionsMethodCallHandler _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); + final ImpressionsMethodCallHandler _impressionsMethodCallHandler = + ImpressionsMethodCallHandler(); MethodChannelPlatform() { _methodChannel.setMethodCallHandler((call) => handle(call)); } + @visibleForTesting + MethodChannelPlatform.withoutHandler(); + @visibleForTesting Future handle(MethodCall call) async { _impressionsMethodCallHandler.handle(call.method, call.arguments); @@ -34,10 +36,11 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future init({required String apiKey, - required String matchingKey, - required String? bucketingKey, - SplitConfiguration? sdkConfiguration}) { + Future init( + {required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { Map arguments = { 'apiKey': apiKey, 'matchingKey': matchingKey, @@ -54,11 +57,10 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future getClient( {required String matchingKey, required String? bucketingKey}) { - _handlers.addAll( - { - _buildMapKey(matchingKey, bucketingKey): SplitEventMethodCallHandler( - matchingKey, bucketingKey) - }); + _handlers.addAll({ + _buildMapKey(matchingKey, bucketingKey): + SplitEventMethodCallHandler(matchingKey, bucketingKey) + }); return _methodChannel.invokeMethod( 'getClient', _buildParameters(matchingKey, bucketingKey)); @@ -92,42 +94,46 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future> getAllAttributes( {required String matchingKey, required String? bucketingKey}) async { - return (await _methodChannel.invokeMapMethod( - 'getAllAttributes', _buildParameters(matchingKey, bucketingKey))) - ?.map((key, value) => MapEntry(key, value)) ?? + return (await _methodChannel.invokeMapMethod('getAllAttributes', + _buildParameters(matchingKey, bucketingKey))) + ?.map((key, value) => MapEntry(key, value)) ?? {}; } @override - Future getAttribute({required String matchingKey, - required String? bucketingKey, - required String attributeName}) { + Future getAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { return _methodChannel.invokeMethod( - 'getAttribute', _buildParameters( - matchingKey, bucketingKey, {'attributeName': attributeName})); + 'getAttribute', + _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); } @override - Future getTreatment({required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) async { + Future getTreatment( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) async { return await _methodChannel.invokeMethod( - 'getTreatment', - _buildParameters(matchingKey, bucketingKey, - {'splitName': splitName, 'attributes': attributes})) ?? + 'getTreatment', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitName, 'attributes': attributes})) ?? _controlTreatment; } @override - Future getTreatmentWithConfig({required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) async { + Future getTreatmentWithConfig( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) async { Map? treatment = (await _methodChannel.invokeMapMethod( - 'getTreatmentWithConfig', - _buildParameters(matchingKey, bucketingKey - {'splitName': splitName, 'attributes': attributes}))) + 'getTreatmentWithConfig', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitName, 'attributes': attributes}))) ?.entries .first .value; @@ -139,51 +145,56 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future> getTreatments({required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) async { + Future> getTreatments( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) async { Map? treatments = await _methodChannel.invokeMapMethod( 'getTreatments', _buildParameters(matchingKey, bucketingKey, {'splitName': splitNames, 'attributes': attributes})); return treatments - ?.map((key, value) => MapEntry(key, value)) ?? + ?.map((key, value) => MapEntry(key, value)) ?? {for (var item in splitNames) item: _controlTreatment}; } @override Future> getTreatmentsWithConfig( {required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) async { + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) async { Map? treatments = await _methodChannel.invokeMapMethod( 'getTreatmentsWithConfig', _buildParameters(matchingKey, bucketingKey, {'splitName': splitNames, 'attributes': attributes})); return treatments?.map((key, value) => - MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? + MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? {for (var item in splitNames) item: _controlResult}; } @override - Future removeAttribute({required String matchingKey, - required String? bucketingKey, - required String attributeName}) async { + Future removeAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) async { return await _methodChannel.invokeMethod( - 'removeAttribute', _buildParameters( - matchingKey, bucketingKey, {'attributeName': attributeName})); + 'removeAttribute', + _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); } @override - Future setAttribute({required String matchingKey, - required String? bucketingKey, - required String attributeName, - required value}) async { - var result = await _methodChannel.invokeMethod('setAttribute', + Future setAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName, + required value}) async { + var result = await _methodChannel.invokeMethod( + 'setAttribute', _buildParameters(matchingKey, bucketingKey, {'attributeName': attributeName, 'value': value})); @@ -195,12 +206,14 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future setAttributes({required String matchingKey, - required String? bucketingKey, - required Map attributes}) async { + Future setAttributes( + {required String matchingKey, + required String? bucketingKey, + required Map attributes}) async { var result = await _methodChannel.invokeMethod( - 'setAttributes', _buildParameters( - matchingKey, bucketingKey, {'attributes': attributes})); + 'setAttributes', + _buildParameters( + matchingKey, bucketingKey, {'attributes': attributes})); if (result is bool) { return result; @@ -210,11 +223,9 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future split({required String matchingKey, - required String? bucketingKey, - required String splitName}) async { + Future split({required String splitName}) async { Map? mapResult = - await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); + await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); if (mapResult == null) { return null; @@ -224,8 +235,7 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future> splitNames( - {required String matchingKey, required String? bucketingKey}) async { + Future> splitNames() async { List splitNames = await _methodChannel.invokeListMethod('splitNames') ?? []; @@ -233,10 +243,9 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future> splits( - {required String matchingKey, required String? bucketingKey}) async { + Future> splits() async { List callResult = (await _methodChannel - .invokeListMethod>('splits') ?? + .invokeListMethod>('splits') ?? []); List splits = []; @@ -251,14 +260,15 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future track({required String matchingKey, - required String? bucketingKey, - required String eventType, - String? trafficType, - double? value, - Map properties = const {}}) async { - var parameters = _buildParameters( - matchingKey, bucketingKey, {'eventType': eventType}); + Future track( + {required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) async { + var parameters = + _buildParameters(matchingKey, bucketingKey, {'eventType': eventType}); if (trafficType != null) { parameters['trafficType'] = trafficType; @@ -269,15 +279,14 @@ class MethodChannelPlatform extends SplitioPlatform { } try { - return await _methodChannel.invokeMethod("track", parameters) - as bool; + return await _methodChannel.invokeMethod('track', parameters) as bool; } on Exception catch (_) { return false; } } - Map _buildParameters(String matchingKey, - String? bucketingKey, + Map _buildParameters( + String matchingKey, String? bucketingKey, [Map parameters = const {}]) { Map result = {}; result.addAll(parameters); diff --git a/splitio_platform_interface/lib/splitio_platform_interface.dart b/splitio_platform_interface/lib/splitio_platform_interface.dart index e368763..71c97ea 100644 --- a/splitio_platform_interface/lib/splitio_platform_interface.dart +++ b/splitio_platform_interface/lib/splitio_platform_interface.dart @@ -22,20 +22,16 @@ abstract class _FactoryPlatform { throw UnimplementedError(); } - Future split( - {required String matchingKey, - required String? bucketingKey, + Future split({ required String splitName}) { throw UnimplementedError(); } - Future> splitNames( - {required String matchingKey, required String? bucketingKey}) { + Future> splitNames() { throw UnimplementedError(); } - Future> splits( - {required String matchingKey, required String? bucketingKey}) { + Future> splits() { throw UnimplementedError(); } diff --git a/splitio_platform_interface/test/impressions_method_call_handler_test.dart b/splitio_platform_interface/test/impressions_method_call_handler_test.dart index 6ae0cbb..8222156 100644 --- a/splitio_platform_interface/test/impressions_method_call_handler_test.dart +++ b/splitio_platform_interface/test/impressions_method_call_handler_test.dart @@ -1,8 +1,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/impressions/split_impression.dart'; - -import '../lib/impressions/impressions_method_call_handler.dart'; +import 'package:splitio_platform_interface/impressions/impressions_method_call_handler.dart'; void main() { test('correct impressionLog method call emits value on stream', () async { diff --git a/splitio_platform_interface/test/method_channel_platform_test.dart b/splitio_platform_interface/test/method_channel_platform_test.dart new file mode 100644 index 0000000..ee51457 --- /dev/null +++ b/splitio_platform_interface/test/method_channel_platform_test.dart @@ -0,0 +1,517 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio_platform_interface/method_channel_platform.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; + +void main() { + const MethodChannel _channel = MethodChannel('splitio'); + + String methodName = ''; + dynamic methodArguments; + + TestWidgetsFlutterBinding.ensureInitialized(); + + MethodChannelPlatform _platform = MethodChannelPlatform.withoutHandler(); + + void _simulateMethodInvocation(String methodName, + {String key = 'key', + String? bucketingKey, + Map? arguments}) { + if (arguments == null) { + arguments = {}; + } + arguments.addAll({'matchingKey': key, 'bucketingKey': bucketingKey}); + _channel.invokeMethod(methodName, arguments); + } + + setUp(() { + _channel.setMockMethodCallHandler((MethodCall methodCall) async { + methodName = methodCall.method; + methodArguments = methodCall.arguments; + + _platform.handle(methodCall); + switch (methodCall.method) { + case 'getTreatment': + return ''; + case 'getTreatments': + return {'split1': 'on', 'split2': 'off'}; + case 'getTreatmentsWithConfig': + return { + 'split1': {'treatment': 'on', 'config': null}, + 'split2': {'treatment': 'off', 'config': null} + }; + case 'track': + return true; + case 'getAttribute': + return true; + case 'getAllAttributes': + return { + 'attr_1': true, + 'attr_2': ['list-element'], + 'attr_3': 28.20 + }; + case 'setAttribute': + case 'setAttributes': + case 'removeAttribute': + case 'clearAttributes': + return true; + } + }); + }); + + group('evaluation', () { + test('getTreatment without attributes', () async { + _platform.getTreatment( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split'); + + expect(methodName, 'getTreatment'); + expect(methodArguments, { + 'splitName': 'split', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatment with attributes', () async { + _platform.getTreatment( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split', + attributes: {'attr1': true}); + + expect(methodName, 'getTreatment'); + expect(methodArguments, { + 'splitName': 'split', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatments without attributes', () async { + _platform.getTreatments( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2']); + + expect(methodName, 'getTreatments'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatments with attributes', () async { + _platform.getTreatments( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2'], + attributes: {'attr1': true}); + + expect(methodName, 'getTreatments'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatmentWithConfig with attributes', () async { + _platform.getTreatmentWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split1', + attributes: {'attr1': true}); + + expect(methodName, 'getTreatmentWithConfig'); + expect(methodArguments, { + 'splitName': 'split1', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatmentWithConfig without attributes', () async { + _platform.getTreatmentWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split1'); + + expect(methodName, 'getTreatmentWithConfig'); + expect(methodArguments, { + 'splitName': 'split1', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatmentsWithConfig without attributes', () async { + _platform.getTreatmentsWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2']); + + expect(methodName, 'getTreatmentsWithConfig'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatmentsWithConfig with attributes', () async { + _platform.getTreatmentsWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2'], + attributes: {'attr1': true}); + + expect(methodName, 'getTreatmentsWithConfig'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + }); + + group('track', () { + test('track with traffic type & value', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type', + value: 25.10); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'trafficType': 'my_traffic_type', + 'value': 25.10 + }); + }); + + test('track with value', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + value: 25.10); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'value': 25.10 + }); + }); + + test('track with traffic type', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type'); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'trafficType': 'my_traffic_type', + }); + }); + }); + + group('attributes', () { + test('get single attribute', () async { + _platform.getAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'attribute-name'); + expect(methodName, 'getAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'attribute-name', + }); + }); + + test('get all attributes', () async { + _platform.getAllAttributes( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'getAllAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('set attribute', () async { + _platform.setAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'my_attr', + value: 'attr_value'); + expect(methodName, 'setAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'my_attr', + 'value': 'attr_value', + }); + }); + + test('set multiple attributes', () async { + _platform.setAttributes( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributes: { + 'bool_attr': true, + 'number_attr': 25.56, + 'string_attr': 'attr-value', + 'list_attr': ['one', 'two'], + }); + expect(methodName, 'setAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': { + 'bool_attr': true, + 'number_attr': 25.56, + 'string_attr': 'attr-value', + 'list_attr': ['one', 'two'], + } + }); + }); + + test('remove attribute', () async { + _platform.removeAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'attr-name'); + expect(methodName, 'removeAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'attr-name', + }); + }); + + test('clear attributes', () async { + _platform.clearAttributes( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'clearAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('flush', () async { + _platform.flush( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'flush'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('destroy', () async { + _platform.destroy( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'destroy'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + }); + + group('initialization', () { + test('init with matching key only', () { + _platform.init( + apiKey: 'api-key', matchingKey: 'matching-key', bucketingKey: null); + + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'sdkConfiguration': {} + }); + }); + + test('init with bucketing key', () { + _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key'); + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'sdkConfiguration': {} + }); + }); + + test('init with config', () { + _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + sdkConfiguration: + SplitConfiguration(enableDebug: true, streamingEnabled: false)); + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'sdkConfiguration': {'enableDebug': true, 'streamingEnabled': false}, + }); + }); + }); + + group('client', () { + test('get client with no keys', () { + _platform.getClient(matchingKey: 'matching-key', bucketingKey: null); + + expect(methodName, 'getClient'); + expect(methodArguments, {'matchingKey': 'matching-key'}); + }); + + test('get client with new matching key', () { + _platform.getClient(matchingKey: 'new-matching-key', bucketingKey: null); + + expect(methodName, 'getClient'); + expect(methodArguments, {'matchingKey': 'new-matching-key'}); + }); + + test('get client with new matching key and bucketing key', () { + _platform.getClient( + matchingKey: 'new-matching-key', bucketingKey: 'bucketing-key'); + + expect(methodName, 'getClient'); + expect(methodArguments, + {'matchingKey': 'new-matching-key', 'bucketingKey': 'bucketing-key'}); + }); + }); + + group('manager', () { + test('get split names', () { + _platform.splitNames(); + + expect(methodName, 'splitNames'); + }); + + test('get splits', () { + _platform.splits(); + + expect(methodName, 'splits'); + }); + + test('get split', () { + _platform.split(splitName: 'my_split'); + + expect(methodName, 'split'); + expect(methodArguments, {'splitName': 'my_split'}); + }); + }); + + group('events', () { + test('onReady', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onReady = _platform + .onReady(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientReady', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onReady, completion(equals(true))); + }); + + test('onReadyFromCache', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onReadyFromCache = _platform + .onReadyFromCache( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientReadyFromCache', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onReadyFromCache, completion(equals(true))); + }); + + test('onTimeout', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onTimeout = _platform + .onTimeout(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientTimeout', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onTimeout, completion(equals(true))); + }); + + test('onUpdated', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onUpdated = _platform + .onUpdated(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.first + .then((value) => true); + + _simulateMethodInvocation('clientUpdated', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onUpdated, completion(equals(true))); + }); + }); + + test('impressions', () { + _platform.impressionsStream().listen( + expectAsync1((impression) { + expect(impression.key, 'key'); + expect(impression.bucketingKey, null); + expect(impression.split, 'split'); + expect(impression.treatment, 'treatment'); + expect(impression.time, 3000); + expect(impression.appliedRule, 'appliedRule'); + expect(impression.changeNumber, 200); + expect(impression.attributes, {}); + }), + ); + _simulateMethodInvocation('impressionLog', key: 'matching-key', arguments: { + 'key': 'key', + 'bucketingKey': 'bucketingKey', + 'split': 'split', + 'treatment': 'treatment', + 'time': 3000, + 'appliedRule': 'appliedRule', + 'changeNumber': 200, + 'attributes': {} + }); + }); +} diff --git a/splitio_platform_interface/test/split_event_listener_test.dart b/splitio_platform_interface/test/split_event_listener_test.dart deleted file mode 100644 index f06c463..0000000 --- a/splitio_platform_interface/test/split_event_listener_test.dart +++ /dev/null @@ -1,239 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/events/split_events_listener.dart'; -import 'package:splitio/events/split_method_call_handler.dart'; -import 'package:splitio/split_result.dart'; - -import '../lib/split_client.dart'; - -void main() { - const MethodChannel _channel = MethodChannel('splitio'); - - TestWidgetsFlutterBinding.ensureInitialized(); - - SplitClientMock splitClientMock = SplitClientMock(); - SplitEventMethodCallHandler splitEventMethodCallHandler = - SplitEventMethodCallHandler('key', 'bucketing', splitClientMock); - late final MethodChannelManager _methodChannelWrapper = - MethodChannelManager(_channel); - - void _simulateMethodInvocation(String methodName, - {String key = 'key', String bucketingKey = 'bucketing'}) { - _channel.invokeMethod( - methodName, {'matchingKey': key, 'bucketingKey': bucketingKey}); - } - - setUp(() { - _channel.setMockMethodCallHandler((MethodCall methodCall) { - splitEventMethodCallHandler.handle( - methodCall.method, methodCall.arguments); - }); - }); - - group('client events', () { - test('test client ready', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); - _simulateMethodInvocation('clientReady'); - - expect(future, completion(equals(true))); - }); - - test('test multiple client ready', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); - _simulateMethodInvocation('clientReady'); - - expect(future, completion(equals(true))); - - _simulateMethodInvocation('clientReady'); - expect(future, completion(equals(true))); - }); - - test('test client ready from cache', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = - eventListener.onReadyFromCache().then((value) => true); - _simulateMethodInvocation('clientReadyFromCache'); - - expect(future, completion(equals(true))); - }); - - test('test multiple client ready from cache', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); - _simulateMethodInvocation('clientReadyFromCache'); - - expect(future, completion(equals(true))); - - _simulateMethodInvocation('clientReadyFromCache'); - expect(future, completion(equals(true))); - }); - - test('test client timeout', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onTimeout().then((value) => true); - _simulateMethodInvocation('clientTimeout'); - - expect(future, completion(equals(true))); - }); - - test('test multiple client timeout', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); - _simulateMethodInvocation('clientTimeout'); - - expect(future, completion(equals(true))); - - _simulateMethodInvocation('clientTimeout'); - expect(future, completion(equals(true))); - }); - - test('test client updated', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - var count = 0; - eventListener - .onUpdated() - .map((event) => ++count) - .take(3) - .toList() - .then((value) => expect(value, [1, 2])); - - _simulateMethodInvocation('clientUpdated'); - _simulateMethodInvocation('clientUpdated'); - }); - }); -} - -class SplitClientMock extends SplitClient { - Map calledMethods = {}; - - @override - Future clearAttributes() { - calledMethods.update('clearAttributes', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future destroy() { - calledMethods.update('destroy', (value) => value + 1, ifAbsent: () => 1); - return Future.value(null); - } - - @override - Future flush() { - calledMethods.update('flush', (value) => value + 1, ifAbsent: () => 1); - return Future.value(null); - } - - @override - Future getAttribute(String attributeName) { - calledMethods.update('getAttribute', (value) => value + 1, - ifAbsent: () => 1); - return Future.value('value'); - } - - @override - Future> getAttributes() { - calledMethods.update('getAttributes', (value) => value + 1, - ifAbsent: () => 1); - return Future.value({'attr1': 'value1'}); - } - - @override - Future getTreatment(String splitName, - [Map attributes = const {}]) { - calledMethods.update('getTreatment', (value) => value + 1, - ifAbsent: () => 1); - return Future.value('treatment'); - } - - @override - Future getTreatmentWithConfig(String splitName, - [Map attributes = const {}]) { - calledMethods.update('getTreatmentWithConfig', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(const SplitResult('treatment', null)); - } - - @override - Future> getTreatments(List splitNames, - [Map attributes = const {}]) { - calledMethods.update('getTreatments', (value) => value + 1, - ifAbsent: () => 1); - return Future.value({'split1': 'treatment'}); - } - - @override - Future> getTreatmentsWithConfig( - List splitNames, - [Map attributes = const {}]) { - calledMethods.update('getTreatmentsWithConfig', (value) => value + 1, - ifAbsent: () => 1); - return Future.value({'split1': const SplitResult('treatment', null)}); - } - - @override - Future whenReady() { - calledMethods.update('onReady', (value) => value + 1, ifAbsent: () => 1); - return Future.value(this); - } - - @override - Future whenReadyFromCache() { - calledMethods.update('onReadyFromCache', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(this); - } - - @override - Future whenTimeout() { - calledMethods.update('onTimeout', (value) => value + 1, ifAbsent: () => 1); - return Future.value(this); - } - - @override - Stream whenUpdated() { - calledMethods.update('onUpdated', (value) => value + 1, ifAbsent: () => 1); - return Stream.value(this); - } - - @override - Future removeAttribute(String attributeName) { - calledMethods.update('removeAttribute', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future setAttribute(String attributeName, value) { - calledMethods.update('setAttribute', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future setAttributes(Map attributes) { - calledMethods.update('setAttributes', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future track(String eventType, - {String? trafficType, - double? value, - Map properties = const {}}) { - calledMethods.update('track', (value) => value + 1, ifAbsent: () => 1); - return Future.value(true); - } -} diff --git a/splitio_platform_interface/test/split_event_method_call_handler_test.dart b/splitio_platform_interface/test/split_event_method_call_handler_test.dart new file mode 100644 index 0000000..2388fe3 --- /dev/null +++ b/splitio_platform_interface/test/split_event_method_call_handler_test.dart @@ -0,0 +1,97 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio_platform_interface/events/split_method_call_handler.dart'; + +void main() { + const MethodChannel _channel = MethodChannel('splitio'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + SplitEventMethodCallHandler splitEventMethodCallHandler = + SplitEventMethodCallHandler('key', 'bucketing'); + + void _simulateMethodInvocation(String methodName, + {String key = 'key', String bucketingKey = 'bucketing'}) { + _channel.invokeMethod( + methodName, {'matchingKey': key, 'bucketingKey': bucketingKey}); + } + + setUp(() { + _channel.setMockMethodCallHandler((MethodCall methodCall) { + splitEventMethodCallHandler.handle( + methodCall.method, methodCall.arguments); + }); + }); + + group('client events', () { + test('test client ready', () async { + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); + _simulateMethodInvocation('clientReady'); + + expect(future, completion(equals(true))); + }); + + test('test multiple client ready', () async { + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); + _simulateMethodInvocation('clientReady'); + + expect(future, completion(equals(true))); + + _simulateMethodInvocation('clientReady'); + expect(future, completion(equals(true))); + }); + + test('test client ready from cache', () async { + Future future = + splitEventMethodCallHandler.onReadyFromCache().then((value) => true); + _simulateMethodInvocation('clientReadyFromCache'); + + expect(future, completion(equals(true))); + }); + + test('test multiple client ready from cache', () async { + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); + _simulateMethodInvocation('clientReadyFromCache'); + + expect(future, completion(equals(true))); + + _simulateMethodInvocation('clientReadyFromCache'); + expect(future, completion(equals(true))); + }); + + test('test client timeout', () async { + Future future = + splitEventMethodCallHandler.onTimeout().then((value) => true); + _simulateMethodInvocation('clientTimeout'); + + expect(future, completion(equals(true))); + }); + + test('test multiple client timeout', () async { + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); + _simulateMethodInvocation('clientTimeout'); + + expect(future, completion(equals(true))); + + _simulateMethodInvocation('clientTimeout'); + expect(future, completion(equals(true))); + }); + + test('test client updated', () async { + var count = 0; + splitEventMethodCallHandler + .onUpdated() + .map((event) => ++count) + .take(3) + .toList() + .then((value) => expect(value, [1, 2])); + + _simulateMethodInvocation('clientUpdated'); + _simulateMethodInvocation('clientUpdated'); + }); + }); +} diff --git a/splitio_platform_interface/test/split_impression_test.dart b/splitio_platform_interface/test/split_impression_test.dart index e792441..b993d2a 100644 --- a/splitio_platform_interface/test/split_impression_test.dart +++ b/splitio_platform_interface/test/split_impression_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/impressions/split_impression.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; void main() { test('from map creates correct Impression', () { diff --git a/splitio_platform_interface/test/split_view_test.dart b/splitio_platform_interface/test/split_view_test.dart index af0578a..0268b42 100644 --- a/splitio_platform_interface/test/split_view_test.dart +++ b/splitio_platform_interface/test/split_view_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/split_view.dart'; +import 'package:splitio_platform_interface/split_view.dart'; void main() { test('split view from empty map results in null object', () { diff --git a/splitio_platform_interface/test/splitio_configuration_test.dart b/splitio_platform_interface/test/splitio_configuration_test.dart index c04ed72..d1c44f5 100644 --- a/splitio_platform_interface/test/splitio_configuration_test.dart +++ b/splitio_platform_interface/test/splitio_configuration_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/split_configuration.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_sync_config.dart'; void main() { test('valuesAreMappedCorrectly', () async { @@ -21,7 +22,9 @@ void main() { eventsEndpoint: 'eventsEndpoint.split.io', authServiceEndpoint: 'authServiceEndpoint.split.io', streamingServiceEndpoint: 'streamingServiceEndpoint.split.io', - telemetryServiceEndpoint: 'telemetryServiceEndpoint.split.io'); + telemetryServiceEndpoint: 'telemetryServiceEndpoint.split.io', + syncConfig: + SyncConfig(names: ['one', 'two', 'three'], prefixes: ['pre1'])); expect(config.configurationMap['eventFlushInterval'], 2000); expect(config.configurationMap['eventsPerPush'], 300); @@ -44,6 +47,10 @@ void main() { expect(config.configurationMap['telemetryServiceEndpoint'], 'telemetryServiceEndpoint.split.io'); expect(config.configurationMap['impressionListener'], true); + expect(config.configurationMap['syncConfig']['syncConfigNames'], + ['one', 'two', 'three']); + expect( + config.configurationMap['syncConfig']['syncConfigPrefixes'], ['pre1']); }); test('noSpecialValuesLeavesMapEmpty', () async { From 7d50f3138a61fc0f520394556858bb1dd87128f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Wed, 7 Sep 2022 15:03:30 -0300 Subject: [PATCH 05/25] splitio_android package (#48) --- splitio/example/pubspec.lock | 9 +- splitio/example/pubspec.yaml | 54 -- splitio/pubspec.yaml | 21 +- splitio_android/.gitignore | 29 + splitio_android/CHANGELOG.md | 3 + splitio_android/LICENSE | 201 +++++++ splitio_android/README.md | 11 + splitio_android/analysis_options.yaml | 4 + .../android/.gitignore | 0 .../android/build.gradle | 0 .../android/settings.gradle | 0 .../android/src/main/AndroidManifest.xml | 0 .../java/io/split/splitio/ArgumentParser.java | 0 .../io/split/splitio/ArgumentParserImpl.java | 0 .../io/split/splitio/AttributesWrapper.java | 0 .../main/java/io/split/splitio/Constants.java | 0 .../io/split/splitio/EvaluationWrapper.java | 0 .../main/java/io/split/splitio/Helper.java | 0 .../split/splitio/ImpressionListenerImp.java | 0 .../splitio/SplitClientConfigHelper.java | 0 .../split/splitio/SplitFactoryProvider.java | 0 .../splitio/SplitFactoryProviderImpl.java | 0 .../io/split/splitio/SplitMethodParser.java | 0 .../split/splitio/SplitMethodParserImpl.java | 0 .../io/split/splitio/SplitProviderHelper.java | 0 .../java/io/split/splitio/SplitWrapper.java | 0 .../io/split/splitio/SplitWrapperImpl.java | 0 .../java/io/split/splitio/SplitioPlugin.java | 2 +- .../split/splitio/ArgumentParserImplTest.java | 0 .../splitio/ImpressionListenerImpTest.java | 0 .../splitio/SplitClientConfigHelperTest.java | 0 .../splitio/SplitMethodParserImplTest.java | 0 .../splitio/SplitProviderHelperImplTest.java | 0 .../split/splitio/SplitWrapperImplTest.java | 0 splitio_android/lib/splitio_android.dart | 16 + splitio_android/pubspec.yaml | 29 + .../test/splitio_android_test.dart | 515 ++++++++++++++++++ .../analysis_options.yaml | 4 + .../lib/method_channel_platform.dart | 55 +- .../lib/split_sync_config.dart | 13 +- .../lib/splitio_platform_interface.dart | 13 +- splitio_platform_interface/pubspec.yaml | 2 +- .../impressions_method_call_handler_test.dart | 1 + .../test/method_channel_platform_test.dart | 6 +- 44 files changed, 879 insertions(+), 109 deletions(-) create mode 100644 splitio_android/.gitignore create mode 100644 splitio_android/CHANGELOG.md create mode 100644 splitio_android/LICENSE create mode 100644 splitio_android/README.md create mode 100644 splitio_android/analysis_options.yaml rename {splitio => splitio_android}/android/.gitignore (100%) rename {splitio => splitio_android}/android/build.gradle (100%) rename {splitio => splitio_android}/android/settings.gradle (100%) rename {splitio => splitio_android}/android/src/main/AndroidManifest.xml (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/ArgumentParser.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/ArgumentParserImpl.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/AttributesWrapper.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/Constants.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/EvaluationWrapper.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/Helper.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/ImpressionListenerImp.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/SplitFactoryProvider.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/SplitFactoryProviderImpl.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/SplitMethodParser.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/SplitProviderHelper.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/SplitWrapper.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/SplitWrapperImpl.java (100%) rename {splitio => splitio_android}/android/src/main/java/io/split/splitio/SplitioPlugin.java (97%) rename {splitio => splitio_android}/android/src/test/java/io/split/splitio/ArgumentParserImplTest.java (100%) rename {splitio => splitio_android}/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java (100%) rename {splitio => splitio_android}/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java (100%) rename {splitio => splitio_android}/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java (100%) rename {splitio => splitio_android}/android/src/test/java/io/split/splitio/SplitProviderHelperImplTest.java (100%) rename {splitio => splitio_android}/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java (100%) create mode 100644 splitio_android/lib/splitio_android.dart create mode 100644 splitio_android/pubspec.yaml create mode 100644 splitio_android/test/splitio_android_test.dart create mode 100644 splitio_platform_interface/analysis_options.yaml diff --git a/splitio/example/pubspec.lock b/splitio/example/pubspec.lock index 1de670f..a216ab7 100644 --- a/splitio/example/pubspec.lock +++ b/splitio/example/pubspec.lock @@ -134,7 +134,14 @@ packages: path: ".." relative: true source: path - version: "0.1.1" + version: "0.1.2" + splitio_android: + dependency: transitive + description: + path: "../../splitio_android" + relative: true + source: path + version: "0.1.2" splitio_platform_interface: dependency: transitive description: diff --git a/splitio/example/pubspec.yaml b/splitio/example/pubspec.yaml index 35cec97..49d15a3 100644 --- a/splitio/example/pubspec.yaml +++ b/splitio/example/pubspec.yaml @@ -8,12 +8,6 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: sdk: ">=2.16.2 <3.0.0" -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter @@ -25,60 +19,12 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. flutter_lints: ^1.0.0 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index ddc7878..87f2500 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,6 +1,6 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via a Split feature flag to manage your complete customer experience. -version: 0.1.1 +version: 0.1.2 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/splitio/ @@ -8,22 +8,23 @@ environment: sdk: ">=2.16.2 <3.0.0" flutter: ">=2.5.0" +flutter: + plugin: + platforms: + android: + default_package: splitio_android + ios: + pluginClass: SplitioPlugin + dependencies: flutter: sdk: flutter splitio_platform_interface: # ^1.0.0 path: ../splitio_platform_interface + splitio_android: # ^1.0.0 + path: ../splitio_android dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^1.0.0 - -flutter: - plugin: - platforms: - android: - package: io.split.splitio - pluginClass: SplitioPlugin - ios: - pluginClass: SplitioPlugin diff --git a/splitio_android/.gitignore b/splitio_android/.gitignore new file mode 100644 index 0000000..9be145f --- /dev/null +++ b/splitio_android/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/splitio_android/CHANGELOG.md b/splitio_android/CHANGELOG.md new file mode 100644 index 0000000..cd0d685 --- /dev/null +++ b/splitio_android/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.2 + +Splits from `splitio` as federated implementation. diff --git a/splitio_android/LICENSE b/splitio_android/LICENSE new file mode 100644 index 0000000..20e4bd8 --- /dev/null +++ b/splitio_android/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/splitio_android/README.md b/splitio_android/README.md new file mode 100644 index 0000000..54614af --- /dev/null +++ b/splitio_android/README.md @@ -0,0 +1,11 @@ +# splitio\_android + +The Android implementation of [`splitio`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `splitio` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/splitio +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/splitio_android/analysis_options.yaml b/splitio_android/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/splitio_android/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/splitio/android/.gitignore b/splitio_android/android/.gitignore similarity index 100% rename from splitio/android/.gitignore rename to splitio_android/android/.gitignore diff --git a/splitio/android/build.gradle b/splitio_android/android/build.gradle similarity index 100% rename from splitio/android/build.gradle rename to splitio_android/android/build.gradle diff --git a/splitio/android/settings.gradle b/splitio_android/android/settings.gradle similarity index 100% rename from splitio/android/settings.gradle rename to splitio_android/android/settings.gradle diff --git a/splitio/android/src/main/AndroidManifest.xml b/splitio_android/android/src/main/AndroidManifest.xml similarity index 100% rename from splitio/android/src/main/AndroidManifest.xml rename to splitio_android/android/src/main/AndroidManifest.xml diff --git a/splitio/android/src/main/java/io/split/splitio/ArgumentParser.java b/splitio_android/android/src/main/java/io/split/splitio/ArgumentParser.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/ArgumentParser.java rename to splitio_android/android/src/main/java/io/split/splitio/ArgumentParser.java diff --git a/splitio/android/src/main/java/io/split/splitio/ArgumentParserImpl.java b/splitio_android/android/src/main/java/io/split/splitio/ArgumentParserImpl.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/ArgumentParserImpl.java rename to splitio_android/android/src/main/java/io/split/splitio/ArgumentParserImpl.java diff --git a/splitio/android/src/main/java/io/split/splitio/AttributesWrapper.java b/splitio_android/android/src/main/java/io/split/splitio/AttributesWrapper.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/AttributesWrapper.java rename to splitio_android/android/src/main/java/io/split/splitio/AttributesWrapper.java diff --git a/splitio/android/src/main/java/io/split/splitio/Constants.java b/splitio_android/android/src/main/java/io/split/splitio/Constants.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/Constants.java rename to splitio_android/android/src/main/java/io/split/splitio/Constants.java diff --git a/splitio/android/src/main/java/io/split/splitio/EvaluationWrapper.java b/splitio_android/android/src/main/java/io/split/splitio/EvaluationWrapper.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/EvaluationWrapper.java rename to splitio_android/android/src/main/java/io/split/splitio/EvaluationWrapper.java diff --git a/splitio/android/src/main/java/io/split/splitio/Helper.java b/splitio_android/android/src/main/java/io/split/splitio/Helper.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/Helper.java rename to splitio_android/android/src/main/java/io/split/splitio/Helper.java diff --git a/splitio/android/src/main/java/io/split/splitio/ImpressionListenerImp.java b/splitio_android/android/src/main/java/io/split/splitio/ImpressionListenerImp.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/ImpressionListenerImp.java rename to splitio_android/android/src/main/java/io/split/splitio/ImpressionListenerImp.java diff --git a/splitio/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java b/splitio_android/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java rename to splitio_android/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java diff --git a/splitio/android/src/main/java/io/split/splitio/SplitFactoryProvider.java b/splitio_android/android/src/main/java/io/split/splitio/SplitFactoryProvider.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/SplitFactoryProvider.java rename to splitio_android/android/src/main/java/io/split/splitio/SplitFactoryProvider.java diff --git a/splitio/android/src/main/java/io/split/splitio/SplitFactoryProviderImpl.java b/splitio_android/android/src/main/java/io/split/splitio/SplitFactoryProviderImpl.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/SplitFactoryProviderImpl.java rename to splitio_android/android/src/main/java/io/split/splitio/SplitFactoryProviderImpl.java diff --git a/splitio/android/src/main/java/io/split/splitio/SplitMethodParser.java b/splitio_android/android/src/main/java/io/split/splitio/SplitMethodParser.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/SplitMethodParser.java rename to splitio_android/android/src/main/java/io/split/splitio/SplitMethodParser.java diff --git a/splitio/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java b/splitio_android/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java rename to splitio_android/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java diff --git a/splitio/android/src/main/java/io/split/splitio/SplitProviderHelper.java b/splitio_android/android/src/main/java/io/split/splitio/SplitProviderHelper.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/SplitProviderHelper.java rename to splitio_android/android/src/main/java/io/split/splitio/SplitProviderHelper.java diff --git a/splitio/android/src/main/java/io/split/splitio/SplitWrapper.java b/splitio_android/android/src/main/java/io/split/splitio/SplitWrapper.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/SplitWrapper.java rename to splitio_android/android/src/main/java/io/split/splitio/SplitWrapper.java diff --git a/splitio/android/src/main/java/io/split/splitio/SplitWrapperImpl.java b/splitio_android/android/src/main/java/io/split/splitio/SplitWrapperImpl.java similarity index 100% rename from splitio/android/src/main/java/io/split/splitio/SplitWrapperImpl.java rename to splitio_android/android/src/main/java/io/split/splitio/SplitWrapperImpl.java diff --git a/splitio/android/src/main/java/io/split/splitio/SplitioPlugin.java b/splitio_android/android/src/main/java/io/split/splitio/SplitioPlugin.java similarity index 97% rename from splitio/android/src/main/java/io/split/splitio/SplitioPlugin.java rename to splitio_android/android/src/main/java/io/split/splitio/SplitioPlugin.java index a2645d0..6cc5c2c 100644 --- a/splitio/android/src/main/java/io/split/splitio/SplitioPlugin.java +++ b/splitio_android/android/src/main/java/io/split/splitio/SplitioPlugin.java @@ -23,7 +23,7 @@ public class SplitioPlugin implements FlutterPlugin, MethodCallHandler { @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "splitio"); + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "splitio.io/splitio_android"); channel.setMethodCallHandler(this); SplitFactoryProvider provider = getSplitFactoryProvider(flutterPluginBinding.getApplicationContext()); methodParser = new SplitMethodParserImpl(flutterPluginBinding.getApplicationContext(), channel, provider); diff --git a/splitio/android/src/test/java/io/split/splitio/ArgumentParserImplTest.java b/splitio_android/android/src/test/java/io/split/splitio/ArgumentParserImplTest.java similarity index 100% rename from splitio/android/src/test/java/io/split/splitio/ArgumentParserImplTest.java rename to splitio_android/android/src/test/java/io/split/splitio/ArgumentParserImplTest.java diff --git a/splitio/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java b/splitio_android/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java similarity index 100% rename from splitio/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java rename to splitio_android/android/src/test/java/io/split/splitio/ImpressionListenerImpTest.java diff --git a/splitio/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java b/splitio_android/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java similarity index 100% rename from splitio/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java rename to splitio_android/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java diff --git a/splitio/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java b/splitio_android/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java similarity index 100% rename from splitio/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java rename to splitio_android/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java diff --git a/splitio/android/src/test/java/io/split/splitio/SplitProviderHelperImplTest.java b/splitio_android/android/src/test/java/io/split/splitio/SplitProviderHelperImplTest.java similarity index 100% rename from splitio/android/src/test/java/io/split/splitio/SplitProviderHelperImplTest.java rename to splitio_android/android/src/test/java/io/split/splitio/SplitProviderHelperImplTest.java diff --git a/splitio/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java b/splitio_android/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java similarity index 100% rename from splitio/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java rename to splitio_android/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java diff --git a/splitio_android/lib/splitio_android.dart b/splitio_android/lib/splitio_android.dart new file mode 100644 index 0000000..78cd29f --- /dev/null +++ b/splitio_android/lib/splitio_android.dart @@ -0,0 +1,16 @@ +import 'package:flutter/services.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; + +const MethodChannel _methodChannel = + MethodChannel('splitio.io/splitio_android'); + +/// Implementation for Android of [SplitioPlatform]. +class SplitioAndroid extends MethodChannelPlatform { + /// Registers this class as the default platform implementation. + static void registerWith() { + SplitioPlatform.instance = SplitioAndroid(); + } + + @override + MethodChannel get methodChannel => _methodChannel; +} diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml new file mode 100644 index 0000000..366ef28 --- /dev/null +++ b/splitio_android/pubspec.yaml @@ -0,0 +1,29 @@ +name: splitio_android +description: The official Android implementation of splitio Flutter plugin. +repository: https://github.com/splitio/flutter-sdk-plugin/splitio/splitio_android +version: 0.1.2 +publish_to: 'none' + +environment: + sdk: ">=2.16.2 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: splitio + platforms: + android: + package: io.split.splitio + pluginClass: SplitioPlugin + dartPluginClass: SplitioAndroid + +dependencies: + flutter: + sdk: flutter + splitio_platform_interface: # ^1.0.0 + path: ../splitio_platform_interface + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.0 diff --git a/splitio_android/test/splitio_android_test.dart b/splitio_android/test/splitio_android_test.dart new file mode 100644 index 0000000..e33332e --- /dev/null +++ b/splitio_android/test/splitio_android_test.dart @@ -0,0 +1,515 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio_android/splitio_android.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; + +void main() { + const MethodChannel _channel = MethodChannel('splitio.io/splitio_android'); + + String methodName = ''; + dynamic methodArguments; + + TestWidgetsFlutterBinding.ensureInitialized(); + + SplitioAndroid _platform = SplitioAndroid(); + + void _simulateMethodInvocation(String methodName, + {String key = 'key', + String? bucketingKey, + Map? arguments}) { + arguments ??= {}; + arguments.addAll({'matchingKey': key, 'bucketingKey': bucketingKey}); + _channel.invokeMethod(methodName, arguments); + } + + setUp(() { + _channel.setMockMethodCallHandler((MethodCall methodCall) async { + methodName = methodCall.method; + methodArguments = methodCall.arguments; + + _platform.handle(methodCall); + switch (methodCall.method) { + case 'getTreatment': + return ''; + case 'getTreatments': + return {'split1': 'on', 'split2': 'off'}; + case 'getTreatmentsWithConfig': + return { + 'split1': {'treatment': 'on', 'config': null}, + 'split2': {'treatment': 'off', 'config': null} + }; + case 'track': + return true; + case 'getAttribute': + return true; + case 'getAllAttributes': + return { + 'attr_1': true, + 'attr_2': ['list-element'], + 'attr_3': 28.20 + }; + case 'setAttribute': + case 'setAttributes': + case 'removeAttribute': + case 'clearAttributes': + return true; + } + }); + }); + + group('evaluation', () { + test('getTreatment without attributes', () async { + _platform.getTreatment( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split'); + + expect(methodName, 'getTreatment'); + expect(methodArguments, { + 'splitName': 'split', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatment with attributes', () async { + _platform.getTreatment( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split', + attributes: {'attr1': true}); + + expect(methodName, 'getTreatment'); + expect(methodArguments, { + 'splitName': 'split', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatments without attributes', () async { + _platform.getTreatments( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2']); + + expect(methodName, 'getTreatments'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatments with attributes', () async { + _platform.getTreatments( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2'], + attributes: {'attr1': true}); + + expect(methodName, 'getTreatments'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatmentWithConfig with attributes', () async { + _platform.getTreatmentWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split1', + attributes: {'attr1': true}); + + expect(methodName, 'getTreatmentWithConfig'); + expect(methodArguments, { + 'splitName': 'split1', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatmentWithConfig without attributes', () async { + _platform.getTreatmentWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split1'); + + expect(methodName, 'getTreatmentWithConfig'); + expect(methodArguments, { + 'splitName': 'split1', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatmentsWithConfig without attributes', () async { + _platform.getTreatmentsWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2']); + + expect(methodName, 'getTreatmentsWithConfig'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatmentsWithConfig with attributes', () async { + _platform.getTreatmentsWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2'], + attributes: {'attr1': true}); + + expect(methodName, 'getTreatmentsWithConfig'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + }); + + group('track', () { + test('track with traffic type & value', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type', + value: 25.10); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'trafficType': 'my_traffic_type', + 'value': 25.10 + }); + }); + + test('track with value', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + value: 25.10); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'value': 25.10 + }); + }); + + test('track with traffic type', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type'); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'trafficType': 'my_traffic_type', + }); + }); + }); + + group('attributes', () { + test('get single attribute', () async { + _platform.getAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'attribute-name'); + expect(methodName, 'getAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'attribute-name', + }); + }); + + test('get all attributes', () async { + _platform.getAllAttributes( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'getAllAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('set attribute', () async { + _platform.setAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'my_attr', + value: 'attr_value'); + expect(methodName, 'setAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'my_attr', + 'value': 'attr_value', + }); + }); + + test('set multiple attributes', () async { + _platform.setAttributes( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributes: { + 'bool_attr': true, + 'number_attr': 25.56, + 'string_attr': 'attr-value', + 'list_attr': ['one', 'two'], + }); + expect(methodName, 'setAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': { + 'bool_attr': true, + 'number_attr': 25.56, + 'string_attr': 'attr-value', + 'list_attr': ['one', 'two'], + } + }); + }); + + test('remove attribute', () async { + _platform.removeAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'attr-name'); + expect(methodName, 'removeAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'attr-name', + }); + }); + + test('clear attributes', () async { + _platform.clearAttributes( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'clearAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('flush', () async { + _platform.flush( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'flush'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('destroy', () async { + _platform.destroy( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'destroy'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + }); + + group('initialization', () { + test('init with matching key only', () { + _platform.init( + apiKey: 'api-key', matchingKey: 'matching-key', bucketingKey: null); + + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'sdkConfiguration': {} + }); + }); + + test('init with bucketing key', () { + _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key'); + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'sdkConfiguration': {} + }); + }); + + test('init with config', () { + _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + sdkConfiguration: + SplitConfiguration(enableDebug: true, streamingEnabled: false)); + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'sdkConfiguration': {'enableDebug': true, 'streamingEnabled': false}, + }); + }); + }); + + group('client', () { + test('get client with no keys', () { + _platform.getClient(matchingKey: 'matching-key', bucketingKey: null); + + expect(methodName, 'getClient'); + expect(methodArguments, {'matchingKey': 'matching-key'}); + }); + + test('get client with new matching key', () { + _platform.getClient(matchingKey: 'new-matching-key', bucketingKey: null); + + expect(methodName, 'getClient'); + expect(methodArguments, {'matchingKey': 'new-matching-key'}); + }); + + test('get client with new matching key and bucketing key', () { + _platform.getClient( + matchingKey: 'new-matching-key', bucketingKey: 'bucketing-key'); + + expect(methodName, 'getClient'); + expect(methodArguments, + {'matchingKey': 'new-matching-key', 'bucketingKey': 'bucketing-key'}); + }); + }); + + group('manager', () { + test('get split names', () { + _platform.splitNames(); + + expect(methodName, 'splitNames'); + }); + + test('get splits', () { + _platform.splits(); + + expect(methodName, 'splits'); + }); + + test('get split', () { + _platform.split(splitName: 'my_split'); + + expect(methodName, 'split'); + expect(methodArguments, {'splitName': 'my_split'}); + }); + }); + + group('events', () { + test('onReady', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onReady = _platform + .onReady(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientReady', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onReady, completion(equals(true))); + }); + + test('onReadyFromCache', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onReadyFromCache = _platform + .onReadyFromCache( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientReadyFromCache', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onReadyFromCache, completion(equals(true))); + }); + + test('onTimeout', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onTimeout = _platform + .onTimeout(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientTimeout', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onTimeout, completion(equals(true))); + }); + + test('onUpdated', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onUpdated = _platform + .onUpdated(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.first + .then((value) => true); + + _simulateMethodInvocation('clientUpdated', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onUpdated, completion(equals(true))); + }); + }); + + test('impressions', () { + _platform.impressionsStream().listen( + expectAsync1((impression) { + expect(impression.key, 'key'); + expect(impression.bucketingKey, null); + expect(impression.split, 'split'); + expect(impression.treatment, 'treatment'); + expect(impression.time, 3000); + expect(impression.appliedRule, 'appliedRule'); + expect(impression.changeNumber, 200); + expect(impression.attributes, {}); + }), + ); + _simulateMethodInvocation('impressionLog', key: 'matching-key', arguments: { + 'key': 'key', + 'bucketingKey': 'bucketingKey', + 'split': 'split', + 'treatment': 'treatment', + 'time': 3000, + 'appliedRule': 'appliedRule', + 'changeNumber': 200, + 'attributes': {} + }); + }); +} diff --git a/splitio_platform_interface/analysis_options.yaml b/splitio_platform_interface/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/splitio_platform_interface/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart index b9a0513..b499305 100644 --- a/splitio_platform_interface/lib/method_channel_platform.dart +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -1,32 +1,19 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:splitio_platform_interface/events/split_method_call_handler.dart'; -import 'package:splitio_platform_interface/impressions/impressions_method_call_handler.dart'; -import 'package:splitio_platform_interface/method_call_handler.dart'; -import 'package:splitio_platform_interface/split_configuration.dart'; -import 'package:splitio_platform_interface/split_impression.dart'; -import 'package:splitio_platform_interface/split_result.dart'; -import 'package:splitio_platform_interface/split_view.dart'; import 'package:splitio_platform_interface/splitio_platform_interface.dart'; const String _controlTreatment = 'control'; const SplitResult _controlResult = SplitResult(_controlTreatment, null); +const MethodChannel _methodChannel = MethodChannel('splitio'); class MethodChannelPlatform extends SplitioPlatform { - final MethodChannel _methodChannel = const MethodChannel('splitio'); + MethodChannel get methodChannel => _methodChannel; final Map _handlers = {}; final ImpressionsMethodCallHandler _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); - MethodChannelPlatform() { - _methodChannel.setMethodCallHandler((call) => handle(call)); - } - - @visibleForTesting - MethodChannelPlatform.withoutHandler(); - @visibleForTesting Future handle(MethodCall call) async { _impressionsMethodCallHandler.handle(call.method, call.arguments); @@ -41,6 +28,8 @@ class MethodChannelPlatform extends SplitioPlatform { required String matchingKey, required String? bucketingKey, SplitConfiguration? sdkConfiguration}) { + methodChannel.setMethodCallHandler((call) => handle(call)); + Map arguments = { 'apiKey': apiKey, 'matchingKey': matchingKey, @@ -51,7 +40,7 @@ class MethodChannelPlatform extends SplitioPlatform { arguments.addAll({'bucketingKey': bucketingKey}); } - return _methodChannel.invokeMethod('init', arguments); + return methodChannel.invokeMethod('init', arguments); } @override @@ -62,14 +51,14 @@ class MethodChannelPlatform extends SplitioPlatform { SplitEventMethodCallHandler(matchingKey, bucketingKey) }); - return _methodChannel.invokeMethod( + return methodChannel.invokeMethod( 'getClient', _buildParameters(matchingKey, bucketingKey)); } @override Future clearAttributes( {required String matchingKey, required String? bucketingKey}) async { - return await _methodChannel.invokeMethod( + return await methodChannel.invokeMethod( 'clearAttributes', _buildParameters(matchingKey, bucketingKey)); } @@ -80,21 +69,21 @@ class MethodChannelPlatform extends SplitioPlatform { _handlers[handlerKey]?.destroy(); _handlers.remove(handlerKey); - return await _methodChannel.invokeMethod( + return await methodChannel.invokeMethod( 'destroy', _buildParameters(matchingKey, bucketingKey)); } @override Future flush( {required String matchingKey, required String? bucketingKey}) async { - return await _methodChannel.invokeMethod( + return await methodChannel.invokeMethod( 'flush', _buildParameters(matchingKey, bucketingKey)); } @override Future> getAllAttributes( {required String matchingKey, required String? bucketingKey}) async { - return (await _methodChannel.invokeMapMethod('getAllAttributes', + return (await methodChannel.invokeMapMethod('getAllAttributes', _buildParameters(matchingKey, bucketingKey))) ?.map((key, value) => MapEntry(key, value)) ?? {}; @@ -105,7 +94,7 @@ class MethodChannelPlatform extends SplitioPlatform { {required String matchingKey, required String? bucketingKey, required String attributeName}) { - return _methodChannel.invokeMethod( + return methodChannel.invokeMethod( 'getAttribute', _buildParameters( matchingKey, bucketingKey, {'attributeName': attributeName})); @@ -117,7 +106,7 @@ class MethodChannelPlatform extends SplitioPlatform { required String? bucketingKey, required String splitName, Map attributes = const {}}) async { - return await _methodChannel.invokeMethod( + return await methodChannel.invokeMethod( 'getTreatment', _buildParameters(matchingKey, bucketingKey, {'splitName': splitName, 'attributes': attributes})) ?? @@ -130,7 +119,7 @@ class MethodChannelPlatform extends SplitioPlatform { required String? bucketingKey, required String splitName, Map attributes = const {}}) async { - Map? treatment = (await _methodChannel.invokeMapMethod( + Map? treatment = (await methodChannel.invokeMapMethod( 'getTreatmentWithConfig', _buildParameters(matchingKey, bucketingKey, {'splitName': splitName, 'attributes': attributes}))) @@ -150,7 +139,7 @@ class MethodChannelPlatform extends SplitioPlatform { required String? bucketingKey, required List splitNames, Map attributes = const {}}) async { - Map? treatments = await _methodChannel.invokeMapMethod( + Map? treatments = await methodChannel.invokeMapMethod( 'getTreatments', _buildParameters(matchingKey, bucketingKey, {'splitName': splitNames, 'attributes': attributes})); @@ -166,7 +155,7 @@ class MethodChannelPlatform extends SplitioPlatform { required String? bucketingKey, required List splitNames, Map attributes = const {}}) async { - Map? treatments = await _methodChannel.invokeMapMethod( + Map? treatments = await methodChannel.invokeMapMethod( 'getTreatmentsWithConfig', _buildParameters(matchingKey, bucketingKey, {'splitName': splitNames, 'attributes': attributes})); @@ -181,7 +170,7 @@ class MethodChannelPlatform extends SplitioPlatform { {required String matchingKey, required String? bucketingKey, required String attributeName}) async { - return await _methodChannel.invokeMethod( + return await methodChannel.invokeMethod( 'removeAttribute', _buildParameters( matchingKey, bucketingKey, {'attributeName': attributeName})); @@ -193,7 +182,7 @@ class MethodChannelPlatform extends SplitioPlatform { required String? bucketingKey, required String attributeName, required value}) async { - var result = await _methodChannel.invokeMethod( + var result = await methodChannel.invokeMethod( 'setAttribute', _buildParameters(matchingKey, bucketingKey, {'attributeName': attributeName, 'value': value})); @@ -210,7 +199,7 @@ class MethodChannelPlatform extends SplitioPlatform { {required String matchingKey, required String? bucketingKey, required Map attributes}) async { - var result = await _methodChannel.invokeMethod( + var result = await methodChannel.invokeMethod( 'setAttributes', _buildParameters( matchingKey, bucketingKey, {'attributes': attributes})); @@ -225,7 +214,7 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future split({required String splitName}) async { Map? mapResult = - await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); + await methodChannel.invokeMapMethod('split', {'splitName': splitName}); if (mapResult == null) { return null; @@ -237,14 +226,14 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future> splitNames() async { List splitNames = - await _methodChannel.invokeListMethod('splitNames') ?? []; + await methodChannel.invokeListMethod('splitNames') ?? []; return splitNames; } @override Future> splits() async { - List callResult = (await _methodChannel + List callResult = (await methodChannel .invokeListMethod>('splits') ?? []); @@ -279,7 +268,7 @@ class MethodChannelPlatform extends SplitioPlatform { } try { - return await _methodChannel.invokeMethod('track', parameters) as bool; + return await methodChannel.invokeMethod('track', parameters) as bool; } on Exception catch (_) { return false; } diff --git a/splitio_platform_interface/lib/split_sync_config.dart b/splitio_platform_interface/lib/split_sync_config.dart index 8e59b20..6eec23d 100644 --- a/splitio_platform_interface/lib/split_sync_config.dart +++ b/splitio_platform_interface/lib/split_sync_config.dart @@ -1,12 +1,19 @@ class SyncConfig { - late final Set _names; - late final Set _prefixes; + late Set _names; + late Set _prefixes; Set get names => _names; Set get prefixes => _prefixes; - SyncConfig({Set names = const {}, Set prefixes = const {}}) { + SyncConfig( + {List names = const [], List prefixes = const []}) { + _names = names.toSet(); + _prefixes = prefixes.toSet(); + } + + SyncConfig.fromSet( + {Set names = const {}, Set prefixes = const {}}) { _names = names; _prefixes = prefixes; } diff --git a/splitio_platform_interface/lib/splitio_platform_interface.dart b/splitio_platform_interface/lib/splitio_platform_interface.dart index 71c97ea..729e001 100644 --- a/splitio_platform_interface/lib/splitio_platform_interface.dart +++ b/splitio_platform_interface/lib/splitio_platform_interface.dart @@ -7,6 +7,16 @@ import 'package:splitio_platform_interface/split_impression.dart'; import 'package:splitio_platform_interface/split_result.dart'; import 'package:splitio_platform_interface/split_view.dart'; +export 'package:splitio_platform_interface/events/split_method_call_handler.dart'; +export 'package:splitio_platform_interface/impressions/impressions_method_call_handler.dart'; +export 'package:splitio_platform_interface/method_call_handler.dart'; +export 'package:splitio_platform_interface/method_channel_platform.dart'; +export 'package:splitio_platform_interface/split_configuration.dart'; +export 'package:splitio_platform_interface/split_impression.dart'; +export 'package:splitio_platform_interface/split_result.dart'; +export 'package:splitio_platform_interface/split_view.dart'; +export 'package:splitio_platform_interface/splitio_platform_interface.dart'; + abstract class _FactoryPlatform { Future getClient( {required String matchingKey, required String? bucketingKey}) { @@ -22,8 +32,7 @@ abstract class _FactoryPlatform { throw UnimplementedError(); } - Future split({ - required String splitName}) { + Future split({required String splitName}) { throw UnimplementedError(); } diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml index 0d43036..4331e21 100644 --- a/splitio_platform_interface/pubspec.yaml +++ b/splitio_platform_interface/pubspec.yaml @@ -6,7 +6,7 @@ version: 1.0.0 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/splitio/splitio_platform_interface -publish_to: none +publish_to: 'none' environment: sdk: ">=2.16.2 <3.0.0" diff --git a/splitio_platform_interface/test/impressions_method_call_handler_test.dart b/splitio_platform_interface/test/impressions_method_call_handler_test.dart index 8222156..f6836bd 100644 --- a/splitio_platform_interface/test/impressions_method_call_handler_test.dart +++ b/splitio_platform_interface/test/impressions_method_call_handler_test.dart @@ -1,6 +1,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:splitio_platform_interface/impressions/impressions_method_call_handler.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; void main() { test('correct impressionLog method call emits value on stream', () async { diff --git a/splitio_platform_interface/test/method_channel_platform_test.dart b/splitio_platform_interface/test/method_channel_platform_test.dart index ee51457..9f5ee07 100644 --- a/splitio_platform_interface/test/method_channel_platform_test.dart +++ b/splitio_platform_interface/test/method_channel_platform_test.dart @@ -11,15 +11,13 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); - MethodChannelPlatform _platform = MethodChannelPlatform.withoutHandler(); + MethodChannelPlatform _platform = MethodChannelPlatform(); void _simulateMethodInvocation(String methodName, {String key = 'key', String? bucketingKey, Map? arguments}) { - if (arguments == null) { - arguments = {}; - } + arguments ??= {}; arguments.addAll({'matchingKey': key, 'bucketingKey': bucketingKey}); _channel.invokeMethod(methodName, arguments); } From 3865588ae8e4924aa7ccdbe3fbe98a4bf786f663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Wed, 7 Sep 2022 18:30:12 -0300 Subject: [PATCH 06/25] splitio_ios package (#49) --- ...RIBUTORS-GUIDE.md => CONTRIBUTORS-GUIDE.md | 0 splitio/example/ios/Podfile.lock | 10 +- .../ios/Runner.xcodeproj/project.pbxproj | 46 +- .../ios/SplitTests/ArgumentParserTests.swift | 2 +- .../SplitClientConfigHelperTests.swift | 2 +- .../SplitTests/SplitMethodParserTests.swift | 2 +- .../SplitTests/SplitProviderHelperTests.swift | 2 +- .../example/ios/SplitTests/SplitTests.swift | 2 +- splitio/example/pubspec.lock | 7 + splitio/pubspec.yaml | 6 +- .../java/io/split/splitio/SplitioPlugin.java | 2 +- splitio_android/lib/splitio_android.dart | 3 +- .../test/splitio_android_test.dart | 2 +- splitio_ios/.gitignore | 29 + splitio_ios/CHANGELOG.md | 3 + splitio_ios/LICENSE | 201 +++++++ splitio_ios/README.md | 11 + splitio_ios/analysis_options.yaml | 4 + {splitio => splitio_ios}/ios/.gitignore | 0 {splitio => splitio_ios}/ios/Assets/.gitkeep | 0 .../ios/Classes/ArgumentParser.swift | 0 .../ios/Classes/Constants.swift | 0 .../ios/Classes/Extensions.swift | 0 .../ios/Classes/SplitClientConfigHelper.swift | 0 .../ios/Classes/SplitFactoryProvider.swift | 0 .../ios/Classes/SplitMethodParser.swift | 0 .../ios/Classes/SplitWrapper.swift | 0 .../ios/Classes/SplitioPlugin.h | 0 .../ios/Classes/SplitioPlugin.m | 6 +- .../ios/Classes/SwiftSplitioPlugin.swift | 2 +- .../ios/splitio_ios.podspec | 4 +- splitio_ios/lib/splitio_ios.dart | 15 + splitio_ios/pubspec.yaml | 28 + splitio_ios/test/splitio_ios_test.dart | 515 ++++++++++++++++++ 34 files changed, 859 insertions(+), 45 deletions(-) rename splitio/CONTRIBUTORS-GUIDE.md => CONTRIBUTORS-GUIDE.md (100%) create mode 100644 splitio_ios/.gitignore create mode 100644 splitio_ios/CHANGELOG.md create mode 100644 splitio_ios/LICENSE create mode 100644 splitio_ios/README.md create mode 100644 splitio_ios/analysis_options.yaml rename {splitio => splitio_ios}/ios/.gitignore (100%) rename {splitio => splitio_ios}/ios/Assets/.gitkeep (100%) rename {splitio => splitio_ios}/ios/Classes/ArgumentParser.swift (100%) rename {splitio => splitio_ios}/ios/Classes/Constants.swift (100%) rename {splitio => splitio_ios}/ios/Classes/Extensions.swift (100%) rename {splitio => splitio_ios}/ios/Classes/SplitClientConfigHelper.swift (100%) rename {splitio => splitio_ios}/ios/Classes/SplitFactoryProvider.swift (100%) rename {splitio => splitio_ios}/ios/Classes/SplitMethodParser.swift (100%) rename {splitio => splitio_ios}/ios/Classes/SplitWrapper.swift (100%) rename {splitio => splitio_ios}/ios/Classes/SplitioPlugin.h (100%) rename {splitio => splitio_ios}/ios/Classes/SplitioPlugin.m (77%) rename {splitio => splitio_ios}/ios/Classes/SwiftSplitioPlugin.swift (87%) rename splitio/ios/splitio.podspec => splitio_ios/ios/splitio_ios.podspec (87%) create mode 100644 splitio_ios/lib/splitio_ios.dart create mode 100644 splitio_ios/pubspec.yaml create mode 100644 splitio_ios/test/splitio_ios_test.dart diff --git a/splitio/CONTRIBUTORS-GUIDE.md b/CONTRIBUTORS-GUIDE.md similarity index 100% rename from splitio/CONTRIBUTORS-GUIDE.md rename to CONTRIBUTORS-GUIDE.md diff --git a/splitio/example/ios/Podfile.lock b/splitio/example/ios/Podfile.lock index bd44637..38ef52f 100644 --- a/splitio/example/ios/Podfile.lock +++ b/splitio/example/ios/Podfile.lock @@ -1,13 +1,13 @@ PODS: - Flutter (1.0.0) - Split (2.15.0) - - splitio (0.1.0): + - splitio_ios (0.1.0): - Flutter - Split (~> 2.15.0) DEPENDENCIES: - Flutter (from `Flutter`) - - splitio (from `.symlinks/plugins/splitio/ios`) + - splitio_ios (from `.symlinks/plugins/splitio_ios/ios`) SPEC REPOS: trunk: @@ -16,13 +16,13 @@ SPEC REPOS: EXTERNAL SOURCES: Flutter: :path: Flutter - splitio: - :path: ".symlinks/plugins/splitio/ios" + splitio_ios: + :path: ".symlinks/plugins/splitio_ios/ios" SPEC CHECKSUMS: Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a Split: d103c6afa47b5d1eac21e066c6bc09c879b21798 - splitio: 6ef65e65e042489e9d30fe1f56abe9340bf69039 + splitio_ios: 665651004a4984a415386b1eed4ae315b50bc069 PODFILE CHECKSUM: 6ab177d3659abbf5f15e864674366127c98cb8c0 diff --git a/splitio/example/ios/Runner.xcodeproj/project.pbxproj b/splitio/example/ios/Runner.xcodeproj/project.pbxproj index ca1a98b..7f3b9bb 100644 --- a/splitio/example/ios/Runner.xcodeproj/project.pbxproj +++ b/splitio/example/ios/Runner.xcodeproj/project.pbxproj @@ -193,7 +193,7 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 030A5D79BA57E380D8657A73 /* [CP] Embed Pods Frameworks */, + 83B902C28BA5AB1DD426315E /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -212,7 +212,7 @@ C512775C2847F3D800B7D1A2 /* Sources */, C512775D2847F3D800B7D1A2 /* Frameworks */, C512775E2847F3D800B7D1A2 /* Resources */, - 8639C5B3E80E5EE46CFFE931 /* [CP] Embed Pods Frameworks */, + 456FD0F25BD4B27148539A17 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -285,23 +285,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 030A5D79BA57E380D8657A73 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 06FEC5AC68198B8F6612EC6F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -338,6 +321,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 456FD0F25BD4B27148539A17 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 6EB34F8858EB9AEFDA66439B /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -360,21 +360,21 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 8639C5B3E80E5EE46CFFE931 /* [CP] Embed Pods Frameworks */ = { + 83B902C28BA5AB1DD426315E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 9740EEB61CF901F6004384FC /* Run Script */ = { diff --git a/splitio/example/ios/SplitTests/ArgumentParserTests.swift b/splitio/example/ios/SplitTests/ArgumentParserTests.swift index 9b67744..dcdf716 100644 --- a/splitio/example/ios/SplitTests/ArgumentParserTests.swift +++ b/splitio/example/ios/SplitTests/ArgumentParserTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import splitio +@testable import splitio_ios class ArgumentParserTests: XCTestCase { diff --git a/splitio/example/ios/SplitTests/SplitClientConfigHelperTests.swift b/splitio/example/ios/SplitTests/SplitClientConfigHelperTests.swift index 6534c9f..69b43a5 100644 --- a/splitio/example/ios/SplitTests/SplitClientConfigHelperTests.swift +++ b/splitio/example/ios/SplitTests/SplitClientConfigHelperTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import splitio +@testable import splitio_ios class SplitClientConfigHelperTests: XCTestCase { diff --git a/splitio/example/ios/SplitTests/SplitMethodParserTests.swift b/splitio/example/ios/SplitTests/SplitMethodParserTests.swift index 7e65312..12fa488 100644 --- a/splitio/example/ios/SplitTests/SplitMethodParserTests.swift +++ b/splitio/example/ios/SplitTests/SplitMethodParserTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import splitio +@testable import splitio_ios @testable import Split class SplitMethodParserTests: XCTestCase { diff --git a/splitio/example/ios/SplitTests/SplitProviderHelperTests.swift b/splitio/example/ios/SplitTests/SplitProviderHelperTests.swift index e9269fd..4de13ea 100644 --- a/splitio/example/ios/SplitTests/SplitProviderHelperTests.swift +++ b/splitio/example/ios/SplitTests/SplitProviderHelperTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import splitio +@testable import splitio_ios @testable import Split class SplitProviderHelperTests: XCTestCase { diff --git a/splitio/example/ios/SplitTests/SplitTests.swift b/splitio/example/ios/SplitTests/SplitTests.swift index 217e792..d94c831 100644 --- a/splitio/example/ios/SplitTests/SplitTests.swift +++ b/splitio/example/ios/SplitTests/SplitTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import splitio +@testable import splitio_ios @testable import Split class SplitTests: XCTestCase { diff --git a/splitio/example/pubspec.lock b/splitio/example/pubspec.lock index a216ab7..9c2f871 100644 --- a/splitio/example/pubspec.lock +++ b/splitio/example/pubspec.lock @@ -142,6 +142,13 @@ packages: relative: true source: path version: "0.1.2" + splitio_ios: + dependency: transitive + description: + path: "../../splitio_ios" + relative: true + source: path + version: "0.1.2" splitio_platform_interface: dependency: transitive description: diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 87f2500..bd23fb1 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -14,15 +14,17 @@ flutter: android: default_package: splitio_android ios: - pluginClass: SplitioPlugin + default_package: splitio_ios dependencies: flutter: sdk: flutter splitio_platform_interface: # ^1.0.0 path: ../splitio_platform_interface - splitio_android: # ^1.0.0 + splitio_android: # ^0.1.2 path: ../splitio_android + splitio_ios: # ^0.1.2 + path: ../splitio_ios dev_dependencies: flutter_test: diff --git a/splitio_android/android/src/main/java/io/split/splitio/SplitioPlugin.java b/splitio_android/android/src/main/java/io/split/splitio/SplitioPlugin.java index 6cc5c2c..372c399 100644 --- a/splitio_android/android/src/main/java/io/split/splitio/SplitioPlugin.java +++ b/splitio_android/android/src/main/java/io/split/splitio/SplitioPlugin.java @@ -23,7 +23,7 @@ public class SplitioPlugin implements FlutterPlugin, MethodCallHandler { @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "splitio.io/splitio_android"); + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "split.io/splitio_android"); channel.setMethodCallHandler(this); SplitFactoryProvider provider = getSplitFactoryProvider(flutterPluginBinding.getApplicationContext()); methodParser = new SplitMethodParserImpl(flutterPluginBinding.getApplicationContext(), channel, provider); diff --git a/splitio_android/lib/splitio_android.dart b/splitio_android/lib/splitio_android.dart index 78cd29f..bdffa45 100644 --- a/splitio_android/lib/splitio_android.dart +++ b/splitio_android/lib/splitio_android.dart @@ -1,8 +1,7 @@ import 'package:flutter/services.dart'; import 'package:splitio_platform_interface/splitio_platform_interface.dart'; -const MethodChannel _methodChannel = - MethodChannel('splitio.io/splitio_android'); +const MethodChannel _methodChannel = MethodChannel('split.io/splitio_android'); /// Implementation for Android of [SplitioPlatform]. class SplitioAndroid extends MethodChannelPlatform { diff --git a/splitio_android/test/splitio_android_test.dart b/splitio_android/test/splitio_android_test.dart index e33332e..308d056 100644 --- a/splitio_android/test/splitio_android_test.dart +++ b/splitio_android/test/splitio_android_test.dart @@ -4,7 +4,7 @@ import 'package:splitio_android/splitio_android.dart'; import 'package:splitio_platform_interface/split_configuration.dart'; void main() { - const MethodChannel _channel = MethodChannel('splitio.io/splitio_android'); + const MethodChannel _channel = MethodChannel('split.io/splitio_android'); String methodName = ''; dynamic methodArguments; diff --git a/splitio_ios/.gitignore b/splitio_ios/.gitignore new file mode 100644 index 0000000..9be145f --- /dev/null +++ b/splitio_ios/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/splitio_ios/CHANGELOG.md b/splitio_ios/CHANGELOG.md new file mode 100644 index 0000000..cd0d685 --- /dev/null +++ b/splitio_ios/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.2 + +Splits from `splitio` as federated implementation. diff --git a/splitio_ios/LICENSE b/splitio_ios/LICENSE new file mode 100644 index 0000000..20e4bd8 --- /dev/null +++ b/splitio_ios/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/splitio_ios/README.md b/splitio_ios/README.md new file mode 100644 index 0000000..20af48e --- /dev/null +++ b/splitio_ios/README.md @@ -0,0 +1,11 @@ +# splitio\_android + +The iOS implementation of [`splitio`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `splitio` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/splitio +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/splitio_ios/analysis_options.yaml b/splitio_ios/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/splitio_ios/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/splitio/ios/.gitignore b/splitio_ios/ios/.gitignore similarity index 100% rename from splitio/ios/.gitignore rename to splitio_ios/ios/.gitignore diff --git a/splitio/ios/Assets/.gitkeep b/splitio_ios/ios/Assets/.gitkeep similarity index 100% rename from splitio/ios/Assets/.gitkeep rename to splitio_ios/ios/Assets/.gitkeep diff --git a/splitio/ios/Classes/ArgumentParser.swift b/splitio_ios/ios/Classes/ArgumentParser.swift similarity index 100% rename from splitio/ios/Classes/ArgumentParser.swift rename to splitio_ios/ios/Classes/ArgumentParser.swift diff --git a/splitio/ios/Classes/Constants.swift b/splitio_ios/ios/Classes/Constants.swift similarity index 100% rename from splitio/ios/Classes/Constants.swift rename to splitio_ios/ios/Classes/Constants.swift diff --git a/splitio/ios/Classes/Extensions.swift b/splitio_ios/ios/Classes/Extensions.swift similarity index 100% rename from splitio/ios/Classes/Extensions.swift rename to splitio_ios/ios/Classes/Extensions.swift diff --git a/splitio/ios/Classes/SplitClientConfigHelper.swift b/splitio_ios/ios/Classes/SplitClientConfigHelper.swift similarity index 100% rename from splitio/ios/Classes/SplitClientConfigHelper.swift rename to splitio_ios/ios/Classes/SplitClientConfigHelper.swift diff --git a/splitio/ios/Classes/SplitFactoryProvider.swift b/splitio_ios/ios/Classes/SplitFactoryProvider.swift similarity index 100% rename from splitio/ios/Classes/SplitFactoryProvider.swift rename to splitio_ios/ios/Classes/SplitFactoryProvider.swift diff --git a/splitio/ios/Classes/SplitMethodParser.swift b/splitio_ios/ios/Classes/SplitMethodParser.swift similarity index 100% rename from splitio/ios/Classes/SplitMethodParser.swift rename to splitio_ios/ios/Classes/SplitMethodParser.swift diff --git a/splitio/ios/Classes/SplitWrapper.swift b/splitio_ios/ios/Classes/SplitWrapper.swift similarity index 100% rename from splitio/ios/Classes/SplitWrapper.swift rename to splitio_ios/ios/Classes/SplitWrapper.swift diff --git a/splitio/ios/Classes/SplitioPlugin.h b/splitio_ios/ios/Classes/SplitioPlugin.h similarity index 100% rename from splitio/ios/Classes/SplitioPlugin.h rename to splitio_ios/ios/Classes/SplitioPlugin.h diff --git a/splitio/ios/Classes/SplitioPlugin.m b/splitio_ios/ios/Classes/SplitioPlugin.m similarity index 77% rename from splitio/ios/Classes/SplitioPlugin.m rename to splitio_ios/ios/Classes/SplitioPlugin.m index 68beac7..0b18eab 100644 --- a/splitio/ios/Classes/SplitioPlugin.m +++ b/splitio_ios/ios/Classes/SplitioPlugin.m @@ -1,11 +1,11 @@ #import "SplitioPlugin.h" -#if __has_include() -#import +#if __has_include() +#import #else // Support project import fallback if the generated compatibility header // is not copied when this plugin is created as a library. // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 -#import "splitio-Swift.h" +#import "splitio_ios-Swift.h" #endif @implementation SplitioPlugin diff --git a/splitio/ios/Classes/SwiftSplitioPlugin.swift b/splitio_ios/ios/Classes/SwiftSplitioPlugin.swift similarity index 87% rename from splitio/ios/Classes/SwiftSplitioPlugin.swift rename to splitio_ios/ios/Classes/SwiftSplitioPlugin.swift index 659b2d3..30df9c7 100644 --- a/splitio/ios/Classes/SwiftSplitioPlugin.swift +++ b/splitio_ios/ios/Classes/SwiftSplitioPlugin.swift @@ -6,7 +6,7 @@ public class SwiftSplitioPlugin: NSObject, FlutterPlugin { private var methodParser: SplitMethodParser? public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "splitio", binaryMessenger: registrar.messenger()) + let channel = FlutterMethodChannel(name: "split.io/splitio_ios", binaryMessenger: registrar.messenger()) let instance = SwiftSplitioPlugin() let externalProvider: SplitFactoryProvider? = UIApplication.shared.delegate as? SplitFactoryProvider instance.methodParser = DefaultSplitMethodParser(methodChannel: channel, splitFactoryProvider: externalProvider) diff --git a/splitio/ios/splitio.podspec b/splitio_ios/ios/splitio_ios.podspec similarity index 87% rename from splitio/ios/splitio.podspec rename to splitio_ios/ios/splitio_ios.podspec index d501394..6999409 100644 --- a/splitio/ios/splitio.podspec +++ b/splitio_ios/ios/splitio_ios.podspec @@ -1,9 +1,9 @@ # # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint splitio.podspec` to validate before publishing. +# Run `pod lib lint splitio_ios.podspec` to validate before publishing. # Pod::Spec.new do |s| - s.name = 'splitio' + s.name = 'splitio_ios' s.version = '0.1.0' s.summary = 'split.io official Flutter plugin.' s.description = <<-DESC diff --git a/splitio_ios/lib/splitio_ios.dart b/splitio_ios/lib/splitio_ios.dart new file mode 100644 index 0000000..3d4c812 --- /dev/null +++ b/splitio_ios/lib/splitio_ios.dart @@ -0,0 +1,15 @@ +import 'package:flutter/services.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; + +const MethodChannel _methodChannel = MethodChannel('split.io/splitio_ios'); + +/// Implementation for iOS of [SplitioPlatform]. +class SplitioIOS extends MethodChannelPlatform { + /// Registers this class as the default platform implementation. + static void registerWith() { + SplitioPlatform.instance = SplitioIOS(); + } + + @override + MethodChannel get methodChannel => _methodChannel; +} diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml new file mode 100644 index 0000000..9cbd442 --- /dev/null +++ b/splitio_ios/pubspec.yaml @@ -0,0 +1,28 @@ +name: splitio_ios +description: The official Android implementation of splitio Flutter plugin. +repository: https://github.com/splitio/flutter-sdk-plugin/splitio/splitio_ios +version: 0.1.2 +publish_to: 'none' + +environment: + sdk: ">=2.16.2 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: splitio + platforms: + ios: + pluginClass: SwiftSplitioPlugin + dartPluginClass: SplitioIOS + +dependencies: + flutter: + sdk: flutter + splitio_platform_interface: # ^1.0.0 + path: ../splitio_platform_interface + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.0 diff --git a/splitio_ios/test/splitio_ios_test.dart b/splitio_ios/test/splitio_ios_test.dart new file mode 100644 index 0000000..a68a007 --- /dev/null +++ b/splitio_ios/test/splitio_ios_test.dart @@ -0,0 +1,515 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio_ios/splitio_ios.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; + +void main() { + const MethodChannel _channel = MethodChannel('split.io/splitio_ios'); + + String methodName = ''; + dynamic methodArguments; + + TestWidgetsFlutterBinding.ensureInitialized(); + + SplitioIOS _platform = SplitioIOS(); + + void _simulateMethodInvocation(String methodName, + {String key = 'key', + String? bucketingKey, + Map? arguments}) { + arguments ??= {}; + arguments.addAll({'matchingKey': key, 'bucketingKey': bucketingKey}); + _channel.invokeMethod(methodName, arguments); + } + + setUp(() { + _channel.setMockMethodCallHandler((MethodCall methodCall) async { + methodName = methodCall.method; + methodArguments = methodCall.arguments; + + _platform.handle(methodCall); + switch (methodCall.method) { + case 'getTreatment': + return ''; + case 'getTreatments': + return {'split1': 'on', 'split2': 'off'}; + case 'getTreatmentsWithConfig': + return { + 'split1': {'treatment': 'on', 'config': null}, + 'split2': {'treatment': 'off', 'config': null} + }; + case 'track': + return true; + case 'getAttribute': + return true; + case 'getAllAttributes': + return { + 'attr_1': true, + 'attr_2': ['list-element'], + 'attr_3': 28.20 + }; + case 'setAttribute': + case 'setAttributes': + case 'removeAttribute': + case 'clearAttributes': + return true; + } + }); + }); + + group('evaluation', () { + test('getTreatment without attributes', () async { + _platform.getTreatment( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split'); + + expect(methodName, 'getTreatment'); + expect(methodArguments, { + 'splitName': 'split', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatment with attributes', () async { + _platform.getTreatment( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split', + attributes: {'attr1': true}); + + expect(methodName, 'getTreatment'); + expect(methodArguments, { + 'splitName': 'split', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatments without attributes', () async { + _platform.getTreatments( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2']); + + expect(methodName, 'getTreatments'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatments with attributes', () async { + _platform.getTreatments( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2'], + attributes: {'attr1': true}); + + expect(methodName, 'getTreatments'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatmentWithConfig with attributes', () async { + _platform.getTreatmentWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split1', + attributes: {'attr1': true}); + + expect(methodName, 'getTreatmentWithConfig'); + expect(methodArguments, { + 'splitName': 'split1', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatmentWithConfig without attributes', () async { + _platform.getTreatmentWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split1'); + + expect(methodName, 'getTreatmentWithConfig'); + expect(methodArguments, { + 'splitName': 'split1', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatmentsWithConfig without attributes', () async { + _platform.getTreatmentsWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2']); + + expect(methodName, 'getTreatmentsWithConfig'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatmentsWithConfig with attributes', () async { + _platform.getTreatmentsWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2'], + attributes: {'attr1': true}); + + expect(methodName, 'getTreatmentsWithConfig'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + }); + + group('track', () { + test('track with traffic type & value', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type', + value: 25.10); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'trafficType': 'my_traffic_type', + 'value': 25.10 + }); + }); + + test('track with value', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + value: 25.10); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'value': 25.10 + }); + }); + + test('track with traffic type', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type'); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'trafficType': 'my_traffic_type', + }); + }); + }); + + group('attributes', () { + test('get single attribute', () async { + _platform.getAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'attribute-name'); + expect(methodName, 'getAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'attribute-name', + }); + }); + + test('get all attributes', () async { + _platform.getAllAttributes( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'getAllAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('set attribute', () async { + _platform.setAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'my_attr', + value: 'attr_value'); + expect(methodName, 'setAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'my_attr', + 'value': 'attr_value', + }); + }); + + test('set multiple attributes', () async { + _platform.setAttributes( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributes: { + 'bool_attr': true, + 'number_attr': 25.56, + 'string_attr': 'attr-value', + 'list_attr': ['one', 'two'], + }); + expect(methodName, 'setAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': { + 'bool_attr': true, + 'number_attr': 25.56, + 'string_attr': 'attr-value', + 'list_attr': ['one', 'two'], + } + }); + }); + + test('remove attribute', () async { + _platform.removeAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'attr-name'); + expect(methodName, 'removeAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'attr-name', + }); + }); + + test('clear attributes', () async { + _platform.clearAttributes( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'clearAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('flush', () async { + _platform.flush( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'flush'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('destroy', () async { + _platform.destroy( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'destroy'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + }); + + group('initialization', () { + test('init with matching key only', () { + _platform.init( + apiKey: 'api-key', matchingKey: 'matching-key', bucketingKey: null); + + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'sdkConfiguration': {} + }); + }); + + test('init with bucketing key', () { + _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key'); + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'sdkConfiguration': {} + }); + }); + + test('init with config', () { + _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + sdkConfiguration: + SplitConfiguration(enableDebug: true, streamingEnabled: false)); + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'sdkConfiguration': {'enableDebug': true, 'streamingEnabled': false}, + }); + }); + }); + + group('client', () { + test('get client with no keys', () { + _platform.getClient(matchingKey: 'matching-key', bucketingKey: null); + + expect(methodName, 'getClient'); + expect(methodArguments, {'matchingKey': 'matching-key'}); + }); + + test('get client with new matching key', () { + _platform.getClient(matchingKey: 'new-matching-key', bucketingKey: null); + + expect(methodName, 'getClient'); + expect(methodArguments, {'matchingKey': 'new-matching-key'}); + }); + + test('get client with new matching key and bucketing key', () { + _platform.getClient( + matchingKey: 'new-matching-key', bucketingKey: 'bucketing-key'); + + expect(methodName, 'getClient'); + expect(methodArguments, + {'matchingKey': 'new-matching-key', 'bucketingKey': 'bucketing-key'}); + }); + }); + + group('manager', () { + test('get split names', () { + _platform.splitNames(); + + expect(methodName, 'splitNames'); + }); + + test('get splits', () { + _platform.splits(); + + expect(methodName, 'splits'); + }); + + test('get split', () { + _platform.split(splitName: 'my_split'); + + expect(methodName, 'split'); + expect(methodArguments, {'splitName': 'my_split'}); + }); + }); + + group('events', () { + test('onReady', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onReady = _platform + .onReady(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientReady', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onReady, completion(equals(true))); + }); + + test('onReadyFromCache', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onReadyFromCache = _platform + .onReadyFromCache( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientReadyFromCache', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onReadyFromCache, completion(equals(true))); + }); + + test('onTimeout', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onTimeout = _platform + .onTimeout(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientTimeout', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onTimeout, completion(equals(true))); + }); + + test('onUpdated', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onUpdated = _platform + .onUpdated(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.first + .then((value) => true); + + _simulateMethodInvocation('clientUpdated', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onUpdated, completion(equals(true))); + }); + }); + + test('impressions', () { + _platform.impressionsStream().listen( + expectAsync1((impression) { + expect(impression.key, 'key'); + expect(impression.bucketingKey, null); + expect(impression.split, 'split'); + expect(impression.treatment, 'treatment'); + expect(impression.time, 3000); + expect(impression.appliedRule, 'appliedRule'); + expect(impression.changeNumber, 200); + expect(impression.attributes, {}); + }), + ); + _simulateMethodInvocation('impressionLog', key: 'matching-key', arguments: { + 'key': 'key', + 'bucketingKey': 'bucketingKey', + 'split': 'split', + 'treatment': 'treatment', + 'time': 3000, + 'appliedRule': 'appliedRule', + 'changeNumber': 200, + 'attributes': {} + }); + }); +} From 665f455e7385310725bfd8d1c382f6b040fe570b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Fri, 9 Sep 2022 10:23:01 -0300 Subject: [PATCH 07/25] Move native tests (#50) --- .github/workflows/build.yml | 16 +- splitio/example/ios/Podfile | 7 - splitio/example/ios/Podfile.lock | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 248 ------ splitio_ios/example/.gitignore | 46 + splitio_ios/example/ios/.gitignore | 34 + .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + splitio_ios/example/ios/Podfile | 48 ++ splitio_ios/example/ios/Podfile.lock | 29 + .../ios/Runner.xcodeproj/project.pbxproj | 805 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 97 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 +++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + splitio_ios/example/ios/Runner/Info.plist | 47 + .../ios/Runner/Runner-Bridging-Header.h | 1 + .../ios/SplitTests/ArgumentParserTests.swift | 0 .../ios/SplitTests/ExtensionsTests.swift | 0 .../SplitClientConfigHelperTests.swift | 0 .../SplitTests/SplitMethodParserTests.swift | 0 .../SplitTests/SplitProviderHelperTests.swift | 0 .../example/ios/SplitTests/SplitTests.swift | 0 splitio_ios/example/lib/main.dart | 101 +++ splitio_ios/example/pubspec.lock | 196 +++++ splitio_ios/example/pubspec.yaml | 30 + 54 files changed, 1754 insertions(+), 258 deletions(-) create mode 100644 splitio_ios/example/.gitignore create mode 100644 splitio_ios/example/ios/.gitignore create mode 100644 splitio_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 splitio_ios/example/ios/Flutter/Debug.xcconfig create mode 100644 splitio_ios/example/ios/Flutter/Release.xcconfig create mode 100644 splitio_ios/example/ios/Podfile create mode 100644 splitio_ios/example/ios/Podfile.lock create mode 100644 splitio_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 splitio_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 splitio_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 splitio_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 splitio_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 splitio_ios/example/ios/Runner/AppDelegate.swift create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 splitio_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 splitio_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 splitio_ios/example/ios/Runner/Info.plist create mode 100644 splitio_ios/example/ios/Runner/Runner-Bridging-Header.h rename {splitio => splitio_ios}/example/ios/SplitTests/ArgumentParserTests.swift (100%) rename {splitio => splitio_ios}/example/ios/SplitTests/ExtensionsTests.swift (100%) rename {splitio => splitio_ios}/example/ios/SplitTests/SplitClientConfigHelperTests.swift (100%) rename {splitio => splitio_ios}/example/ios/SplitTests/SplitMethodParserTests.swift (100%) rename {splitio => splitio_ios}/example/ios/SplitTests/SplitProviderHelperTests.swift (100%) rename {splitio => splitio_ios}/example/ios/SplitTests/SplitTests.swift (100%) create mode 100644 splitio_ios/example/lib/main.dart create mode 100644 splitio_ios/example/pubspec.lock create mode 100644 splitio_ios/example/pubspec.yaml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index adce0a6..f3add4d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,6 +41,18 @@ jobs: - name: Run Android test run: cd splitio/example/android/; gradle :splitio:testReleaseUnitTest; + build-ios: + name: Build iOS + runs-on: [ macos-latest ] + + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - name: Run Build iOS + run: cd splitio/example/ios; flutter build ios --no-codesign; + test-ios: name: Test iOS runs-on: [ macos-latest ] @@ -51,7 +63,7 @@ jobs: with: channel: 'stable' - name: Run Build iOS - run: cd splitio/example/ios; flutter build ios --no-codesign; + run: cd splitio_ios/example/; flutter build ios --no-codesign; - name: Select Xcode uses: maxim-lobanov/setup-xcode@v1 @@ -59,4 +71,4 @@ jobs: xcode-version: 13.2.1 - name: ios - run: cd splitio/example/ios; xcodebuild test -workspace "Runner.xcworkspace" -scheme "Runner" -destination "platform=iOS Simulator,name=iPhone 12,OS=latest" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ONLY_ACTIVE_ARCH=NO; + run: cd splitio_ios/example/ios; xcodebuild test -workspace "Runner.xcworkspace" -scheme "Runner" -destination "platform=iOS Simulator,name=iPhone 12,OS=latest" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ONLY_ACTIVE_ARCH=NO; diff --git a/splitio/example/ios/Podfile b/splitio/example/ios/Podfile index 727dd77..252d9ec 100644 --- a/splitio/example/ios/Podfile +++ b/splitio/example/ios/Podfile @@ -34,13 +34,6 @@ target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end -target 'SplitTests' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) diff --git a/splitio/example/ios/Podfile.lock b/splitio/example/ios/Podfile.lock index 38ef52f..21a2888 100644 --- a/splitio/example/ios/Podfile.lock +++ b/splitio/example/ios/Podfile.lock @@ -24,6 +24,6 @@ SPEC CHECKSUMS: Split: d103c6afa47b5d1eac21e066c6bc09c879b21798 splitio_ios: 665651004a4984a415386b1eed4ae315b50bc069 -PODFILE CHECKSUM: 6ab177d3659abbf5f15e864674366127c98cb8c0 +PODFILE CHECKSUM: a75497545d4391e2d394c3668e20cfb1c2bbd4aa COCOAPODS: 1.11.3 diff --git a/splitio/example/ios/Runner.xcodeproj/project.pbxproj b/splitio/example/ios/Runner.xcodeproj/project.pbxproj index 7f3b9bb..f52245a 100644 --- a/splitio/example/ios/Runner.xcodeproj/project.pbxproj +++ b/splitio/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,31 +8,14 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 37E51EEC3848ECE9E84DB5AC /* Pods_SplitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDE81357140D4D18CD672621 /* Pods_SplitTests.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 64E9F6FE44F7FCB12EB87449 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C36271CBA13C112842B4DCF /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - C51277632847F3D800B7D1A2 /* SplitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C51277622847F3D800B7D1A2 /* SplitTests.swift */; }; - C5538DE328A5483400EE141E /* ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5538DE228A5483400EE141E /* ExtensionsTests.swift */; }; - C56515F82857B47E007D9112 /* SplitClientConfigHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C56515F72857B47E007D9112 /* SplitClientConfigHelperTests.swift */; }; - C5E9331728BE5433007A9A93 /* SplitProviderHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5E9331628BE5433007A9A93 /* SplitProviderHelperTests.swift */; }; - C5EAE748284FCC610025614D /* ArgumentParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5EAE747284FCC610025614D /* ArgumentParserTests.swift */; }; - C5EAE74C2853A38C0025614D /* SplitMethodParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5EAE74B2853A38C0025614D /* SplitMethodParserTests.swift */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - C51277642847F3D800B7D1A2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -66,13 +49,6 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C51277602847F3D800B7D1A2 /* SplitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SplitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - C51277622847F3D800B7D1A2 /* SplitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitTests.swift; sourceTree = ""; }; - C5538DE228A5483400EE141E /* ExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionsTests.swift; sourceTree = ""; }; - C56515F72857B47E007D9112 /* SplitClientConfigHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitClientConfigHelperTests.swift; sourceTree = ""; }; - C5E9331628BE5433007A9A93 /* SplitProviderHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitProviderHelperTests.swift; sourceTree = ""; }; - C5EAE747284FCC610025614D /* ArgumentParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArgumentParserTests.swift; sourceTree = ""; }; - C5EAE74B2853A38C0025614D /* SplitMethodParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitMethodParserTests.swift; sourceTree = ""; }; CDE81357140D4D18CD672621 /* Pods_SplitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SplitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F5DF347A782571EC3639305B /* Pods-SplitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SplitTests.debug.xcconfig"; path = "Target Support Files/Pods-SplitTests/Pods-SplitTests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -86,14 +62,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C512775D2847F3D800B7D1A2 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 37E51EEC3848ECE9E84DB5AC /* Pods_SplitTests.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -135,7 +103,6 @@ children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - C51277612847F3D800B7D1A2 /* SplitTests */, 97C146EF1CF9000F007C117D /* Products */, 7153CEFEC2FBAAA4068E994C /* Pods */, 28069006813B6DC2FE9CB1B1 /* Frameworks */, @@ -146,7 +113,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - C51277602847F3D800B7D1A2 /* SplitTests.xctest */, ); name = Products; sourceTree = ""; @@ -166,19 +132,6 @@ path = Runner; sourceTree = ""; }; - C51277612847F3D800B7D1A2 /* SplitTests */ = { - isa = PBXGroup; - children = ( - C5E9331628BE5433007A9A93 /* SplitProviderHelperTests.swift */, - C5538DE228A5483400EE141E /* ExtensionsTests.swift */, - C56515F72857B47E007D9112 /* SplitClientConfigHelperTests.swift */, - C5EAE74B2853A38C0025614D /* SplitMethodParserTests.swift */, - C5EAE747284FCC610025614D /* ArgumentParserTests.swift */, - C51277622847F3D800B7D1A2 /* SplitTests.swift */, - ); - path = SplitTests; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -204,26 +157,6 @@ productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; }; - C512775F2847F3D800B7D1A2 /* SplitTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = C51277692847F3D800B7D1A2 /* Build configuration list for PBXNativeTarget "SplitTests" */; - buildPhases = ( - 6EB34F8858EB9AEFDA66439B /* [CP] Check Pods Manifest.lock */, - C512775C2847F3D800B7D1A2 /* Sources */, - C512775D2847F3D800B7D1A2 /* Frameworks */, - C512775E2847F3D800B7D1A2 /* Resources */, - 456FD0F25BD4B27148539A17 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - C51277652847F3D800B7D1A2 /* PBXTargetDependency */, - ); - name = SplitTests; - productName = SplitTests; - productReference = C51277602847F3D800B7D1A2 /* SplitTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -238,10 +171,6 @@ CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; }; - C512775F2847F3D800B7D1A2 = { - CreatedOnToolsVersion = 13.0; - TestTargetID = 97C146ED1CF9000F007C117D; - }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; @@ -258,7 +187,6 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - C512775F2847F3D800B7D1A2 /* SplitTests */, ); }; /* End PBXProject section */ @@ -275,13 +203,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C512775E2847F3D800B7D1A2 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -321,45 +242,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 456FD0F25BD4B27148539A17 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 6EB34F8858EB9AEFDA66439B /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SplitTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 83B902C28BA5AB1DD426315E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -403,29 +285,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C512775C2847F3D800B7D1A2 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C5538DE328A5483400EE141E /* ExtensionsTests.swift in Sources */, - C5E9331728BE5433007A9A93 /* SplitProviderHelperTests.swift in Sources */, - C5EAE74C2853A38C0025614D /* SplitMethodParserTests.swift in Sources */, - C56515F82857B47E007D9112 /* SplitClientConfigHelperTests.swift in Sources */, - C51277632847F3D800B7D1A2 /* SplitTests.swift in Sources */, - C5EAE748284FCC610025614D /* ArgumentParserTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - C51277652847F3D800B7D1A2 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = C51277642847F3D800B7D1A2 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -667,105 +528,6 @@ }; name = Release; }; - C51277662847F3D800B7D1A2 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = F5DF347A782571EC3639305B /* Pods-SplitTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GCC_C_LANGUAGE_STANDARD = gnu11; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = io.split.splitio.SplitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - C51277672847F3D800B7D1A2 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 8C1CA74A3F7715873887E477 /* Pods-SplitTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GCC_C_LANGUAGE_STANDARD = gnu11; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = io.split.splitio.SplitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; - C51277682847F3D800B7D1A2 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 96E6F25AD1A4E2E5BD01F74C /* Pods-SplitTests.profile.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GCC_C_LANGUAGE_STANDARD = gnu11; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = io.split.splitio.SplitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Profile; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -789,16 +551,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - C51277692847F3D800B7D1A2 /* Build configuration list for PBXNativeTarget "SplitTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C51277662847F3D800B7D1A2 /* Debug */, - C51277672847F3D800B7D1A2 /* Release */, - C51277682847F3D800B7D1A2 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; diff --git a/splitio_ios/example/.gitignore b/splitio_ios/example/.gitignore new file mode 100644 index 0000000..0fa6b67 --- /dev/null +++ b/splitio_ios/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/splitio_ios/example/ios/.gitignore b/splitio_ios/example/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/splitio_ios/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/splitio_ios/example/ios/Flutter/AppFrameworkInfo.plist b/splitio_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..8d4492f --- /dev/null +++ b/splitio_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/splitio_ios/example/ios/Flutter/Debug.xcconfig b/splitio_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/splitio_ios/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/splitio_ios/example/ios/Flutter/Release.xcconfig b/splitio_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/splitio_ios/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/splitio_ios/example/ios/Podfile b/splitio_ios/example/ios/Podfile new file mode 100644 index 0000000..727dd77 --- /dev/null +++ b/splitio_ios/example/ios/Podfile @@ -0,0 +1,48 @@ +# Uncomment this line to define a global platform for your project +platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +target 'SplitTests' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/splitio_ios/example/ios/Podfile.lock b/splitio_ios/example/ios/Podfile.lock new file mode 100644 index 0000000..38ef52f --- /dev/null +++ b/splitio_ios/example/ios/Podfile.lock @@ -0,0 +1,29 @@ +PODS: + - Flutter (1.0.0) + - Split (2.15.0) + - splitio_ios (0.1.0): + - Flutter + - Split (~> 2.15.0) + +DEPENDENCIES: + - Flutter (from `Flutter`) + - splitio_ios (from `.symlinks/plugins/splitio_ios/ios`) + +SPEC REPOS: + trunk: + - Split + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + splitio_ios: + :path: ".symlinks/plugins/splitio_ios/ios" + +SPEC CHECKSUMS: + Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + Split: d103c6afa47b5d1eac21e066c6bc09c879b21798 + splitio_ios: 665651004a4984a415386b1eed4ae315b50bc069 + +PODFILE CHECKSUM: 6ab177d3659abbf5f15e864674366127c98cb8c0 + +COCOAPODS: 1.11.3 diff --git a/splitio_ios/example/ios/Runner.xcodeproj/project.pbxproj b/splitio_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7f3b9bb --- /dev/null +++ b/splitio_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,805 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 37E51EEC3848ECE9E84DB5AC /* Pods_SplitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDE81357140D4D18CD672621 /* Pods_SplitTests.framework */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 64E9F6FE44F7FCB12EB87449 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C36271CBA13C112842B4DCF /* Pods_Runner.framework */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + C51277632847F3D800B7D1A2 /* SplitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C51277622847F3D800B7D1A2 /* SplitTests.swift */; }; + C5538DE328A5483400EE141E /* ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5538DE228A5483400EE141E /* ExtensionsTests.swift */; }; + C56515F82857B47E007D9112 /* SplitClientConfigHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C56515F72857B47E007D9112 /* SplitClientConfigHelperTests.swift */; }; + C5E9331728BE5433007A9A93 /* SplitProviderHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5E9331628BE5433007A9A93 /* SplitProviderHelperTests.swift */; }; + C5EAE748284FCC610025614D /* ArgumentParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5EAE747284FCC610025614D /* ArgumentParserTests.swift */; }; + C5EAE74C2853A38C0025614D /* SplitMethodParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5EAE74B2853A38C0025614D /* SplitMethodParserTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + C51277642847F3D800B7D1A2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1368C3F6AADFE7B866247EA7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 383F16084D3941F6D2383A90 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 5F80958AAA59EFFD9A29135E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 8C1CA74A3F7715873887E477 /* Pods-SplitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SplitTests.release.xcconfig"; path = "Target Support Files/Pods-SplitTests/Pods-SplitTests.release.xcconfig"; sourceTree = ""; }; + 8C36271CBA13C112842B4DCF /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 96E6F25AD1A4E2E5BD01F74C /* Pods-SplitTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SplitTests.profile.xcconfig"; path = "Target Support Files/Pods-SplitTests/Pods-SplitTests.profile.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C51277602847F3D800B7D1A2 /* SplitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SplitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + C51277622847F3D800B7D1A2 /* SplitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitTests.swift; sourceTree = ""; }; + C5538DE228A5483400EE141E /* ExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionsTests.swift; sourceTree = ""; }; + C56515F72857B47E007D9112 /* SplitClientConfigHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitClientConfigHelperTests.swift; sourceTree = ""; }; + C5E9331628BE5433007A9A93 /* SplitProviderHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitProviderHelperTests.swift; sourceTree = ""; }; + C5EAE747284FCC610025614D /* ArgumentParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArgumentParserTests.swift; sourceTree = ""; }; + C5EAE74B2853A38C0025614D /* SplitMethodParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitMethodParserTests.swift; sourceTree = ""; }; + CDE81357140D4D18CD672621 /* Pods_SplitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SplitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F5DF347A782571EC3639305B /* Pods-SplitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SplitTests.debug.xcconfig"; path = "Target Support Files/Pods-SplitTests/Pods-SplitTests.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 64E9F6FE44F7FCB12EB87449 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C512775D2847F3D800B7D1A2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 37E51EEC3848ECE9E84DB5AC /* Pods_SplitTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 28069006813B6DC2FE9CB1B1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 8C36271CBA13C112842B4DCF /* Pods_Runner.framework */, + CDE81357140D4D18CD672621 /* Pods_SplitTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 7153CEFEC2FBAAA4068E994C /* Pods */ = { + isa = PBXGroup; + children = ( + 1368C3F6AADFE7B866247EA7 /* Pods-Runner.debug.xcconfig */, + 5F80958AAA59EFFD9A29135E /* Pods-Runner.release.xcconfig */, + 383F16084D3941F6D2383A90 /* Pods-Runner.profile.xcconfig */, + F5DF347A782571EC3639305B /* Pods-SplitTests.debug.xcconfig */, + 8C1CA74A3F7715873887E477 /* Pods-SplitTests.release.xcconfig */, + 96E6F25AD1A4E2E5BD01F74C /* Pods-SplitTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + C51277612847F3D800B7D1A2 /* SplitTests */, + 97C146EF1CF9000F007C117D /* Products */, + 7153CEFEC2FBAAA4068E994C /* Pods */, + 28069006813B6DC2FE9CB1B1 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + C51277602847F3D800B7D1A2 /* SplitTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + C51277612847F3D800B7D1A2 /* SplitTests */ = { + isa = PBXGroup; + children = ( + C5E9331628BE5433007A9A93 /* SplitProviderHelperTests.swift */, + C5538DE228A5483400EE141E /* ExtensionsTests.swift */, + C56515F72857B47E007D9112 /* SplitClientConfigHelperTests.swift */, + C5EAE74B2853A38C0025614D /* SplitMethodParserTests.swift */, + C5EAE747284FCC610025614D /* ArgumentParserTests.swift */, + C51277622847F3D800B7D1A2 /* SplitTests.swift */, + ); + path = SplitTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 06FEC5AC68198B8F6612EC6F /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 83B902C28BA5AB1DD426315E /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; + C512775F2847F3D800B7D1A2 /* SplitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C51277692847F3D800B7D1A2 /* Build configuration list for PBXNativeTarget "SplitTests" */; + buildPhases = ( + 6EB34F8858EB9AEFDA66439B /* [CP] Check Pods Manifest.lock */, + C512775C2847F3D800B7D1A2 /* Sources */, + C512775D2847F3D800B7D1A2 /* Frameworks */, + C512775E2847F3D800B7D1A2 /* Resources */, + 456FD0F25BD4B27148539A17 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + C51277652847F3D800B7D1A2 /* PBXTargetDependency */, + ); + name = SplitTests; + productName = SplitTests; + productReference = C51277602847F3D800B7D1A2 /* SplitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1300; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + C512775F2847F3D800B7D1A2 = { + CreatedOnToolsVersion = 13.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + C512775F2847F3D800B7D1A2 /* SplitTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C512775E2847F3D800B7D1A2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 06FEC5AC68198B8F6612EC6F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 456FD0F25BD4B27148539A17 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SplitTests/Pods-SplitTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 6EB34F8858EB9AEFDA66439B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-SplitTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 83B902C28BA5AB1DD426315E /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C512775C2847F3D800B7D1A2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C5538DE328A5483400EE141E /* ExtensionsTests.swift in Sources */, + C5E9331728BE5433007A9A93 /* SplitProviderHelperTests.swift in Sources */, + C5EAE74C2853A38C0025614D /* SplitMethodParserTests.swift in Sources */, + C56515F82857B47E007D9112 /* SplitClientConfigHelperTests.swift in Sources */, + C51277632847F3D800B7D1A2 /* SplitTests.swift in Sources */, + C5EAE748284FCC610025614D /* ArgumentParserTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + C51277652847F3D800B7D1A2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = C51277642847F3D800B7D1A2 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.split.splitioExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.split.splitioExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.split.splitioExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + C51277662847F3D800B7D1A2 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F5DF347A782571EC3639305B /* Pods-SplitTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = io.split.splitio.SplitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + C51277672847F3D800B7D1A2 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8C1CA74A3F7715873887E477 /* Pods-SplitTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = io.split.splitio.SplitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + C51277682847F3D800B7D1A2 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 96E6F25AD1A4E2E5BD01F74C /* Pods-SplitTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = io.split.splitio.SplitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Profile; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C51277692847F3D800B7D1A2 /* Build configuration list for PBXNativeTarget "SplitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C51277662847F3D800B7D1A2 /* Debug */, + C51277672847F3D800B7D1A2 /* Release */, + C51277682847F3D800B7D1A2 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/splitio_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/splitio_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/splitio_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..087c2dd --- /dev/null +++ b/splitio_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/splitio_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/splitio_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/splitio_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/splitio_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/splitio_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/splitio_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/splitio_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/splitio_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/splitio_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/splitio_ios/example/ios/Runner/AppDelegate.swift b/splitio_ios/example/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/splitio_ios/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/splitio_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/splitio_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/splitio_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/splitio_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/splitio_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/splitio_ios/example/ios/Runner/Base.lproj/Main.storyboard b/splitio_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/splitio_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/splitio_ios/example/ios/Runner/Info.plist b/splitio_ios/example/ios/Runner/Info.plist new file mode 100644 index 0000000..db9625e --- /dev/null +++ b/splitio_ios/example/ios/Runner/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Splitio + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + splitio_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/splitio_ios/example/ios/Runner/Runner-Bridging-Header.h b/splitio_ios/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/splitio_ios/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/splitio/example/ios/SplitTests/ArgumentParserTests.swift b/splitio_ios/example/ios/SplitTests/ArgumentParserTests.swift similarity index 100% rename from splitio/example/ios/SplitTests/ArgumentParserTests.swift rename to splitio_ios/example/ios/SplitTests/ArgumentParserTests.swift diff --git a/splitio/example/ios/SplitTests/ExtensionsTests.swift b/splitio_ios/example/ios/SplitTests/ExtensionsTests.swift similarity index 100% rename from splitio/example/ios/SplitTests/ExtensionsTests.swift rename to splitio_ios/example/ios/SplitTests/ExtensionsTests.swift diff --git a/splitio/example/ios/SplitTests/SplitClientConfigHelperTests.swift b/splitio_ios/example/ios/SplitTests/SplitClientConfigHelperTests.swift similarity index 100% rename from splitio/example/ios/SplitTests/SplitClientConfigHelperTests.swift rename to splitio_ios/example/ios/SplitTests/SplitClientConfigHelperTests.swift diff --git a/splitio/example/ios/SplitTests/SplitMethodParserTests.swift b/splitio_ios/example/ios/SplitTests/SplitMethodParserTests.swift similarity index 100% rename from splitio/example/ios/SplitTests/SplitMethodParserTests.swift rename to splitio_ios/example/ios/SplitTests/SplitMethodParserTests.swift diff --git a/splitio/example/ios/SplitTests/SplitProviderHelperTests.swift b/splitio_ios/example/ios/SplitTests/SplitProviderHelperTests.swift similarity index 100% rename from splitio/example/ios/SplitTests/SplitProviderHelperTests.swift rename to splitio_ios/example/ios/SplitTests/SplitProviderHelperTests.swift diff --git a/splitio/example/ios/SplitTests/SplitTests.swift b/splitio_ios/example/ios/SplitTests/SplitTests.swift similarity index 100% rename from splitio/example/ios/SplitTests/SplitTests.swift rename to splitio_ios/example/ios/SplitTests/SplitTests.swift diff --git a/splitio_ios/example/lib/main.dart b/splitio_ios/example/lib/main.dart new file mode 100644 index 0000000..20461ef --- /dev/null +++ b/splitio_ios/example/lib/main.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'package:splitio_ios/splitio_ios.dart'; + +/// Replace these with valid values +const String _apiKey = 'api-key'; +const String _matchingKey = 'user-id'; + +void main() { + runApp(const SplitioExampleApp()); +} + +/// Splitio example home widget +class SplitioExampleApp extends StatefulWidget { + /// Default Constructor + const SplitioExampleApp({Key? key}) : super(key: key); + + @override + State createState() { + return _SplitioExampleAppState(); + } +} + +class _SplitioExampleAppState extends State { + bool _sdkReady = false; + bool _sdkReadyFromCache = false; + + final _split = SplitioIOS(); + + @override + void initState() { + super.initState(); + _initClients(); + } + + void _initClients() { + _split.init(apiKey: _apiKey, matchingKey: _matchingKey, bucketingKey: null); + + _split.getClient(matchingKey: _matchingKey, bucketingKey: null); + + _split + .onReady(matchingKey: _matchingKey, bucketingKey: null) + ?.then((value) { + setState(() { + _sdkReady = true; + }); + }); + + _split + .onReadyFromCache(matchingKey: _matchingKey, bucketingKey: null) + ?.then((value) { + setState(() { + _sdkReadyFromCache = true; + }); + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('split.io example app'), + ), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(8), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'SDK ready: $_sdkReady', + style: + const TextStyle(fontWeight: FontWeight.bold, fontSize: 24), + ), + Text( + 'SDK ready from cache: $_sdkReadyFromCache', + style: + const TextStyle(fontWeight: FontWeight.bold, fontSize: 24), + ), + Padding( + padding: const EdgeInsets.fromLTRB(32, 8, 32, 8), + child: TextField( + decoration: + const InputDecoration(hintText: 'Enter split name'), + onChanged: (text) { + setState(() {}); + }, + ), + ), + Visibility( + visible: !(_sdkReady || _sdkReadyFromCache), + child: const CircularProgressIndicator()) + ], + )), + )), + ), + ); + } +} diff --git a/splitio_ios/example/pubspec.lock b/splitio_ios/example/pubspec.lock new file mode 100644 index 0000000..ead8f9f --- /dev/null +++ b/splitio_ios/example/pubspec.lock @@ -0,0 +1,196 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.2" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + splitio_ios: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.1.2" + splitio_platform_interface: + dependency: transitive + description: + path: "../../splitio_platform_interface" + relative: true + source: path + version: "1.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.8" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" +sdks: + dart: ">=2.16.2 <3.0.0" + flutter: ">=2.5.0" diff --git a/splitio_ios/example/pubspec.yaml b/splitio_ios/example/pubspec.yaml new file mode 100644 index 0000000..52656f8 --- /dev/null +++ b/splitio_ios/example/pubspec.yaml @@ -0,0 +1,30 @@ +name: splitio_example +description: Demonstrates how to use the splitio_ios plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ">=2.16.2 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + splitio_ios: + # When depending on this package from a real application you should use: + # splitio: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.0 + +flutter: + uses-material-design: true From b1097a1e312cd21cf3897b4be8ff30b6fd45447d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Fri, 9 Sep 2022 14:15:27 -0300 Subject: [PATCH 08/25] Pre release (#51) --- splitio/CHANGELOG.md | 9 + splitio/LICENSE | 208 ++---------------------- splitio/pubspec.yaml | 11 +- splitio_android/LICENSE | 208 ++---------------------- splitio_android/pubspec.yaml | 6 +- splitio_ios/LICENSE | 208 ++---------------------- splitio_ios/README.md | 2 +- splitio_ios/pubspec.yaml | 8 +- splitio_platform_interface/LICENSE | 208 ++---------------------- splitio_platform_interface/README.md | 26 +++ splitio_platform_interface/pubspec.yaml | 7 +- 11 files changed, 87 insertions(+), 814 deletions(-) diff --git a/splitio/CHANGELOG.md b/splitio/CHANGELOG.md index 80fba2b..cb185c2 100644 --- a/splitio/CHANGELOG.md +++ b/splitio/CHANGELOG.md @@ -1,3 +1,12 @@ +# 0.1.2 + +* Migrated to federated structure. +* Added support for Impression Listener. +* Added support for Sync Configuration. +* Added support for SDK event listeners. +* Added support for manager methods. +* Added support for linking native factory. + # 0.1.1 Minor fixes. diff --git a/splitio/LICENSE b/splitio/LICENSE index 20e4bd8..051b5fd 100644 --- a/splitio/LICENSE +++ b/splitio/LICENSE @@ -1,201 +1,13 @@ - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ +Copyright © 2022 Split Software, Inc. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - 1. Definitions. + http://www.apache.org/licenses/LICENSE-2.0 - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index bd23fb1..aa60b97 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -2,7 +2,7 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via a Split feature flag to manage your complete customer experience. version: 0.1.2 homepage: https://split.io/ -repository: https://github.com/splitio/flutter-sdk-plugin/splitio/ +repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ environment: sdk: ">=2.16.2 <3.0.0" @@ -19,12 +19,9 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: # ^1.0.0 - path: ../splitio_platform_interface - splitio_android: # ^0.1.2 - path: ../splitio_android - splitio_ios: # ^0.1.2 - path: ../splitio_ios + splitio_android: ^0.1.2 + splitio_ios: ^0.1.2 + splitio_platform_interface: ^1.0.0 dev_dependencies: flutter_test: diff --git a/splitio_android/LICENSE b/splitio_android/LICENSE index 20e4bd8..051b5fd 100644 --- a/splitio_android/LICENSE +++ b/splitio_android/LICENSE @@ -1,201 +1,13 @@ - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ +Copyright © 2022 Split Software, Inc. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - 1. Definitions. + http://www.apache.org/licenses/LICENSE-2.0 - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index 366ef28..2ee9cbd 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -1,8 +1,7 @@ name: splitio_android description: The official Android implementation of splitio Flutter plugin. -repository: https://github.com/splitio/flutter-sdk-plugin/splitio/splitio_android +repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_android version: 0.1.2 -publish_to: 'none' environment: sdk: ">=2.16.2 <3.0.0" @@ -20,8 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: # ^1.0.0 - path: ../splitio_platform_interface + splitio_platform_interface: ^1.0.0 dev_dependencies: flutter_test: diff --git a/splitio_ios/LICENSE b/splitio_ios/LICENSE index 20e4bd8..051b5fd 100644 --- a/splitio_ios/LICENSE +++ b/splitio_ios/LICENSE @@ -1,201 +1,13 @@ - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ +Copyright © 2022 Split Software, Inc. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - 1. Definitions. + http://www.apache.org/licenses/LICENSE-2.0 - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/splitio_ios/README.md b/splitio_ios/README.md index 20af48e..d25f02e 100644 --- a/splitio_ios/README.md +++ b/splitio_ios/README.md @@ -1,4 +1,4 @@ -# splitio\_android +# splitio\_ios The iOS implementation of [`splitio`][1]. diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index 9cbd442..7523cf8 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -1,8 +1,7 @@ name: splitio_ios -description: The official Android implementation of splitio Flutter plugin. -repository: https://github.com/splitio/flutter-sdk-plugin/splitio/splitio_ios +description: The official iOS implementation of splitio Flutter plugin. +repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_ios version: 0.1.2 -publish_to: 'none' environment: sdk: ">=2.16.2 <3.0.0" @@ -19,8 +18,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: # ^1.0.0 - path: ../splitio_platform_interface + splitio_platform_interface: ^1.0.0 dev_dependencies: flutter_test: diff --git a/splitio_platform_interface/LICENSE b/splitio_platform_interface/LICENSE index 20e4bd8..051b5fd 100644 --- a/splitio_platform_interface/LICENSE +++ b/splitio_platform_interface/LICENSE @@ -1,201 +1,13 @@ - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ +Copyright © 2022 Split Software, Inc. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - 1. Definitions. + http://www.apache.org/licenses/LICENSE-2.0 - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/splitio_platform_interface/README.md b/splitio_platform_interface/README.md index e69de29..5492428 100644 --- a/splitio_platform_interface/README.md +++ b/splitio_platform_interface/README.md @@ -0,0 +1,26 @@ +# splitio_platform_interface + +A common platform interface for the [`splitio`][1] plugin. + +This interface allows platform-specific implementations of the `splitio` +plugin, as well as the plugin itself, to ensure they are supporting the +same interface. + +# Usage + +To implement a new platform-specific implementation of `splitio`, extend +[`SplitioPlatform`][2] with an implementation that performs the +platform-specific behavior, and when you register your plugin, set the default +`SplitioPlatform` by calling +`SplitioPlatform.instance = MySplitioPlatform()`. + +# Note on breaking changes + +Strongly prefer non-breaking changes (such as adding a method to the interface) +over breaking changes for this package. + +See https://flutter.dev/go/platform-interface-breaking-changes for a discussion +on why a less-clean interface is preferable to a breaking change. + +[1]: ../splitio +[2]: lib/splitio_platform_interface.dart diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml index 4331e21..5527973 100644 --- a/splitio_platform_interface/pubspec.yaml +++ b/splitio_platform_interface/pubspec.yaml @@ -1,12 +1,9 @@ name: splitio_platform_interface -description: Common platform interface for the splitio plugin. +description: A common platform interface for the splitio plugin. # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes version: 1.0.0 -homepage: https://split.io/ -repository: https://github.com/splitio/flutter-sdk-plugin/splitio/splitio_platform_interface - -publish_to: 'none' +repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_platform_interface environment: sdk: ">=2.16.2 <3.0.0" From 79971171ca90253b82435991c3158a9303c9f7a9 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Fri, 9 Sep 2022 15:16:03 -0300 Subject: [PATCH 09/25] Fix pub warning --- splitio/lib/splitio.dart | 3 --- splitio/pubspec.yaml | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index d5620cb..bf52ff6 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -1,9 +1,6 @@ import 'dart:async'; import 'package:splitio/split_client.dart'; -import 'package:splitio_platform_interface/split_configuration.dart'; -import 'package:splitio_platform_interface/split_impression.dart'; -import 'package:splitio_platform_interface/split_view.dart'; import 'package:splitio_platform_interface/splitio_platform_interface.dart'; export 'package:splitio/split_client.dart'; diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index aa60b97..8e6fe0c 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -27,3 +27,4 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^1.0.0 + plugin_platform_interface: ^2.1.2 From 1136835c33f6c5c61027797f6bb27b0275357231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Wed, 14 Sep 2022 12:53:54 -0300 Subject: [PATCH 10/25] 0.1.2+1 (#54) --- splitio/CHANGELOG.md | 8 ++++++-- splitio/lib/split_client.dart | 1 - splitio/lib/splitio.dart | 2 ++ splitio/pubspec.yaml | 2 +- splitio/test/splitio_platform_stub.dart | 4 ---- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/splitio/CHANGELOG.md b/splitio/CHANGELOG.md index cb185c2..0e96b58 100644 --- a/splitio/CHANGELOG.md +++ b/splitio/CHANGELOG.md @@ -1,4 +1,8 @@ -# 0.1.2 +## 0.1.2+1 + +* Added exports for models. + +## 0.1.2 * Migrated to federated structure. * Added support for Impression Listener. @@ -7,7 +11,7 @@ * Added support for manager methods. * Added support for linking native factory. -# 0.1.1 +## 0.1.1 Minor fixes. diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index 673c978..01b87e6 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,4 +1,3 @@ -import 'package:splitio_platform_interface/split_result.dart'; import 'package:splitio_platform_interface/splitio_platform_interface.dart'; abstract class SplitClient { diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index bf52ff6..a63f9f2 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -6,6 +6,8 @@ import 'package:splitio_platform_interface/splitio_platform_interface.dart'; export 'package:splitio/split_client.dart'; export 'package:splitio_platform_interface/split_configuration.dart'; export 'package:splitio_platform_interface/split_impression.dart'; +export 'package:splitio_platform_interface/split_result.dart'; +export 'package:splitio_platform_interface/split_sync_config.dart'; export 'package:splitio_platform_interface/split_view.dart'; typedef ClientReadinessCallback = void Function(SplitClient splitClient); diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 8e6fe0c..594f5d4 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,6 +1,6 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via a Split feature flag to manage your complete customer experience. -version: 0.1.2 +version: 0.1.2+1 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ diff --git a/splitio/test/splitio_platform_stub.dart b/splitio/test/splitio_platform_stub.dart index 13e27f7..29f1526 100644 --- a/splitio/test/splitio_platform_stub.dart +++ b/splitio/test/splitio_platform_stub.dart @@ -1,8 +1,4 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:splitio_platform_interface/split_configuration.dart'; -import 'package:splitio_platform_interface/split_impression.dart'; -import 'package:splitio_platform_interface/split_result.dart'; -import 'package:splitio_platform_interface/split_view.dart'; import 'package:splitio_platform_interface/splitio_platform_interface.dart'; class SplitioPlatformStub From b856b31050e4b2e00c654c7cb089ce430d6346e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Wed, 7 Dec 2022 10:42:38 -0300 Subject: [PATCH 11/25] 0.1.2+2, Consumer ProGuard rules (#56) --- splitio/CHANGELOG.md | 14 +++++++++----- splitio/pubspec.yaml | 4 ++-- splitio_android/CHANGELOG.md | 6 +++++- splitio_android/android/build.gradle | 2 ++ splitio_android/android/split-proguard-rules.pro | 15 +++++++++++++++ splitio_android/pubspec.yaml | 2 +- splitio_ios/CHANGELOG.md | 2 +- splitio_platform_interface/CHANGELOG.md | 2 +- 8 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 splitio_android/android/split-proguard-rules.pro diff --git a/splitio/CHANGELOG.md b/splitio/CHANGELOG.md index 0e96b58..ff269cb 100644 --- a/splitio/CHANGELOG.md +++ b/splitio/CHANGELOG.md @@ -1,8 +1,12 @@ -## 0.1.2+1 +## 0.1.2+2 (Dec 7, 2022) -* Added exports for models. +Added consumer ProGuard rules for Android. -## 0.1.2 +## 0.1.2+1 (Sep 14, 2022) + +Added exports for models. + +## 0.1.2 (Sep 13, 2022) * Migrated to federated structure. * Added support for Impression Listener. @@ -11,11 +15,11 @@ * Added support for manager methods. * Added support for linking native factory. -## 0.1.1 +## 0.1.1 (Aug 19, 2022) Minor fixes. -## 0.1.0 +## 0.1.0 (Aug 3, 2022) Initial release. diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 594f5d4..ae25ce4 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,6 +1,6 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via a Split feature flag to manage your complete customer experience. -version: 0.1.2+1 +version: 0.1.2+2 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_android: ^0.1.2 + splitio_android: ^0.1.2+1 splitio_ios: ^0.1.2 splitio_platform_interface: ^1.0.0 diff --git a/splitio_android/CHANGELOG.md b/splitio_android/CHANGELOG.md index cd0d685..4af79b3 100644 --- a/splitio_android/CHANGELOG.md +++ b/splitio_android/CHANGELOG.md @@ -1,3 +1,7 @@ -# 0.1.2 +## 0.1.2+1 (Dec 7, 2022) + +Added Consumer ProGuard rules. + +## 0.1.2 (Aug 13, 2022) Splits from `splitio` as federated implementation. diff --git a/splitio_android/android/build.gradle b/splitio_android/android/build.gradle index 5916d7c..62cfb6c 100644 --- a/splitio_android/android/build.gradle +++ b/splitio_android/android/build.gradle @@ -31,6 +31,8 @@ android { defaultConfig { minSdkVersion 16 + + consumerProguardFiles 'split-proguard-rules.pro' } dependencies { diff --git a/splitio_android/android/split-proguard-rules.pro b/splitio_android/android/split-proguard-rules.pro new file mode 100644 index 0000000..95ce5f5 --- /dev/null +++ b/splitio_android/android/split-proguard-rules.pro @@ -0,0 +1,15 @@ +# Please include these rules in your project +# in order to make Split code work properly when +# using proguard +-keep class io.split.android.client.dtos.* { *; } +-keep class io.split.android.client.storage.db.** { *; } +-keep public class io.split.android.client.service.sseclient.SseJwtToken.** { *; } +-keep public class io.split.android.client.service.sseclient.SseAuthToken.** { *; } +-keep public class io.split.android.client.service.sseclient.SseAuthenticationResponse.** { *; } +-keep class io.split.android.client.service.sseclient.notifications.** { *; } +-keepattributes Signature +-keep class com.google.gson.reflect.TypeToken { *; } +-keep class * extends com.google.gson.reflect.TypeToken +-keepclassmembers,allowobfuscation class * { + @com.google.gson.annotations.SerializedName ; +} diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index 2ee9cbd..689f942 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_android description: The official Android implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_android -version: 0.1.2 +version: 0.1.2+1 environment: sdk: ">=2.16.2 <3.0.0" diff --git a/splitio_ios/CHANGELOG.md b/splitio_ios/CHANGELOG.md index cd0d685..2096aba 100644 --- a/splitio_ios/CHANGELOG.md +++ b/splitio_ios/CHANGELOG.md @@ -1,3 +1,3 @@ -# 0.1.2 +# 0.1.2 (Aug 13, 2022) Splits from `splitio` as federated implementation. diff --git a/splitio_platform_interface/CHANGELOG.md b/splitio_platform_interface/CHANGELOG.md index 2dd7b16..520057b 100644 --- a/splitio_platform_interface/CHANGELOG.md +++ b/splitio_platform_interface/CHANGELOG.md @@ -1,3 +1,3 @@ -# 1.0.0 +# 1.0.0 (Aug 13, 2022) Initial release. From 82d9fd4ed1f68e84d8d0fe0d10185b5c0ad70d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Fri, 3 Mar 2023 10:10:21 -0300 Subject: [PATCH 12/25] Update license (#57) --- splitio/LICENSE | 2 +- splitio_android/LICENSE | 2 +- splitio_ios/LICENSE | 2 +- splitio_platform_interface/LICENSE | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/splitio/LICENSE b/splitio/LICENSE index 051b5fd..65f5999 100644 --- a/splitio/LICENSE +++ b/splitio/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2022 Split Software, Inc. +Copyright © 2023 Split Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/splitio_android/LICENSE b/splitio_android/LICENSE index 051b5fd..65f5999 100644 --- a/splitio_android/LICENSE +++ b/splitio_android/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2022 Split Software, Inc. +Copyright © 2023 Split Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/splitio_ios/LICENSE b/splitio_ios/LICENSE index 051b5fd..65f5999 100644 --- a/splitio_ios/LICENSE +++ b/splitio_ios/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2022 Split Software, Inc. +Copyright © 2023 Split Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/splitio_platform_interface/LICENSE b/splitio_platform_interface/LICENSE index 051b5fd..65f5999 100644 --- a/splitio_platform_interface/LICENSE +++ b/splitio_platform_interface/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2022 Split Software, Inc. +Copyright © 2023 Split Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 5bbdaaff7458970e79f4382cfcc1b29baa3b71e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Thu, 18 May 2023 13:38:57 -0300 Subject: [PATCH 13/25] DW changes (#67) --- .github/workflows/build.yml | 24 +-- splitio/LICENSE | 198 +++++++++++++++++- splitio/README.md | 6 +- splitio/example/android/.gitignore | 3 - .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- splitio/example/android/gradlew | 160 ++++++++++++++ splitio/example/android/gradlew.bat | 90 ++++++++ splitio/example/lib/main.dart | 21 +- splitio/lib/split_client.dart | 62 +++--- splitio/lib/splitio.dart | 10 +- splitio/pubspec.yaml | 2 +- splitio_android/LICENSE | 198 +++++++++++++++++- splitio_ios/LICENSE | 198 +++++++++++++++++- splitio_platform_interface/LICENSE | 198 +++++++++++++++++- .../lib/split_configuration.dart | 4 +- 16 files changed, 1067 insertions(+), 109 deletions(-) create mode 100755 splitio/example/android/gradle/wrapper/gradle-wrapper.jar create mode 100755 splitio/example/android/gradlew create mode 100755 splitio/example/android/gradlew.bat diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3add4d..fa2869b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,12 +11,12 @@ jobs: name: Build Android runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v2 + - uses: actions/checkout@v3.5.2 + - uses: actions/setup-java@v3.11.0 with: distribution: 'temurin' java-version: '11' - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@v2.10.0 with: channel: 'stable' - name: Build example app APK @@ -26,28 +26,28 @@ jobs: name: Test Android runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v2 + - uses: actions/checkout@v3.5.2 + - uses: actions/setup-java@v3.11.0 with: distribution: 'temurin' java-version: '11' - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@v2.10.0 with: channel: 'stable' - name: Run Pub Get run: cd splitio/; flutter pub get - name: Build Android - run: cd splitio/; flutter build + run: cd splitio/example/android/; flutter build apk - name: Run Android test - run: cd splitio/example/android/; gradle :splitio:testReleaseUnitTest; + run: cd splitio/example/android/; ./gradlew :splitio:testReleaseUnitTest; build-ios: name: Build iOS runs-on: [ macos-latest ] steps: - - uses: actions/checkout@v3 - - uses: subosito/flutter-action@v2 + - uses: actions/checkout@v3.5.2 + - uses: subosito/flutter-action@v2.10.0 with: channel: 'stable' - name: Run Build iOS @@ -58,8 +58,8 @@ jobs: runs-on: [ macos-latest ] steps: - - uses: actions/checkout@v3 - - uses: subosito/flutter-action@v2 + - uses: actions/checkout@v3.5.2 + - uses: subosito/flutter-action@v2.10.0 with: channel: 'stable' - name: Run Build iOS diff --git a/splitio/LICENSE b/splitio/LICENSE index 65f5999..54326b0 100644 --- a/splitio/LICENSE +++ b/splitio/LICENSE @@ -1,13 +1,191 @@ -Copyright © 2023 Split Software, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright © 2023 Split Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/splitio/README.md b/splitio/README.md index 7d64ff8..7d2b918 100644 --- a/splitio/README.md +++ b/splitio/README.md @@ -1,7 +1,7 @@ # Split Flutter plugin ## Overview -This is the official Flutter plugin designed to work with Split, the platform for controlled rollouts, which serves features to your users via a Split feature flag to manage your complete customer experience. +This is the official Flutter plugin designed to work with Split, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. [![Twitter Follow](https://img.shields.io/twitter/follow/splitsoftware.svg?style=social&label=Follow&maxAge=1529000)](https://twitter.com/intent/follow?screen_name=splitsoftware) @@ -19,11 +19,11 @@ Here is a small example of how to perform an evaluation. ```dart import 'package:splitio/splitio.dart'; -final Splitio _split = Splitio('YOUR_API_KEY', 'KEY'); +final Splitio _split = Splitio('YOUR_SDK_KEY', 'USER_KEY'); /// Get treatment _split.client(onReady: (client) async { - final String treatment = await client.getTreatment('SPLIT_NAME'); + final String treatment = await client.getTreatment('FEATURE_FLAG_NAME'); if (treatment == 'on') { /// Insert code here to show on treatment diff --git a/splitio/example/android/.gitignore b/splitio/example/android/.gitignore index 6f56801..7760dbb 100644 --- a/splitio/example/android/.gitignore +++ b/splitio/example/android/.gitignore @@ -1,8 +1,5 @@ -gradle-wrapper.jar /.gradle /captures/ -/gradlew -/gradlew.bat /local.properties GeneratedPluginRegistrant.java diff --git a/splitio/example/android/gradle/wrapper/gradle-wrapper.jar b/splitio/example/android/gradle/wrapper/gradle-wrapper.jar new file mode 100755 index 0000000000000000000000000000000000000000..13372aef5e24af05341d49695ee84e5f9b594659 GIT binary patch literal 53636 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ⅅ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 H-+%o-(+5aQ literal 0 HcmV?d00001 diff --git a/splitio/example/android/gradle/wrapper/gradle-wrapper.properties b/splitio/example/android/gradle/wrapper/gradle-wrapper.properties index 562c5e4..bfd9890 100644 --- a/splitio/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/splitio/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip diff --git a/splitio/example/android/gradlew b/splitio/example/android/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/splitio/example/android/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/splitio/example/android/gradlew.bat b/splitio/example/android/gradlew.bat new file mode 100755 index 0000000..aec9973 --- /dev/null +++ b/splitio/example/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/splitio/example/lib/main.dart b/splitio/example/lib/main.dart index 4e7fe9a..998a90b 100644 --- a/splitio/example/lib/main.dart +++ b/splitio/example/lib/main.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:splitio/splitio.dart'; /// Replace these with valid values -const String _apiKey = 'api-key'; +const String _sdkKey = 'sdk-key'; const String _matchingKey = 'user-id'; void main() { @@ -21,12 +21,12 @@ class SplitioExampleApp extends StatefulWidget { } class _SplitioExampleAppState extends State { - String _splitName = ''; + String _featureFlagName = ''; bool _sdkReady = false; bool _sdkReadyFromCache = false; late SplitClient _client; - final Splitio _split = Splitio(_apiKey, _matchingKey, + final Splitio _split = Splitio(_sdkKey, _matchingKey, configuration: SplitConfiguration( trafficType: "user", )); @@ -87,20 +87,20 @@ class _SplitioExampleAppState extends State { Padding( padding: const EdgeInsets.fromLTRB(32, 8, 32, 8), child: TextField( - decoration: - const InputDecoration(hintText: 'Enter split name'), + decoration: const InputDecoration( + hintText: 'Enter feature flag name'), onChanged: (text) { setState(() { - _splitName = text; + _featureFlagName = text; }); }, ), ), Visibility( - visible: _splitName != '', + visible: _featureFlagName != '', child: ElevatedButton( onPressed: performEvaluation, - child: Text('Evaluate: $_splitName')), + child: Text('Evaluate: $_featureFlagName')), ), Visibility( visible: _sdkReady || _sdkReadyFromCache, @@ -131,9 +131,8 @@ class _SplitioExampleAppState extends State { } void performEvaluation() async { - _client - .getTreatment(_splitName) - .then((value) => {print('Evaluation value for $_splitName is $value')}); + _client.getTreatment(_featureFlagName).then( + (value) => {print('Evaluation value for $_featureFlagName is $value')}); } void track() { diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index 01b87e6..7252206 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,66 +1,66 @@ import 'package:splitio_platform_interface/splitio_platform_interface.dart'; abstract class SplitClient { - /// Performs an evaluation for the [splitName] feature. + /// Performs an evaluation for the [featureFlagName] feature flag. /// /// This method returns the string 'control' if: there was an exception in - /// evaluating the feature, the SDK does not know of the existence of this - /// feature, and/or the feature was deleted through the web console. + /// evaluating the feature flag, the SDK does not know of the existence of this + /// feature flag, and/or the feature flag was deleted through the Split user interface. /// - /// The sdk returns the default treatment of this feature if: The feature was - /// killed, or the key did not match any of the conditions in the feature + /// The SDK returns the default treatment of this feature flag if: The feature flag was + /// killed, or the key did not match any of the conditions in the feature flag /// roll-out plan. /// - /// [splitName] is the feature we want to evaluate. + /// [featureFlagName] is the feature flag we want to evaluate. /// /// Optionally, a [Map] can be specified with the [attributes] parameter to /// take into account when evaluating. /// - /// Returns the evaluated treatment, the default treatment of this feature, or 'control'. - Future getTreatment(String splitName, + /// Returns the evaluated treatment, the default treatment of this feature flag, or 'control'. + Future getTreatment(String featureFlagName, [Map attributes = const {}]); /// Performs and evaluation and returns a [SplitResult] object for the - /// [splitName] feature. This object contains the treatment alongside the - /// split's configuration, if any. + /// [featureFlagName] feature flag. This object contains the treatment alongside the + /// feature flag's configuration, if any. /// /// This method returns 'control' if: there was an exception in /// evaluating the treatment, the SDK does not know of the existence of this - /// feature, and/or the feature was deleted through the web console. + /// feature flag, and/or the feature flag was deleted through the Split user interface. /// - /// The sdk returns the default treatment of this feature if: The feature was - /// killed, or the key did not match any of the conditions in the feature + /// The SDK returns the default treatment of this feature flag if: The feature flag was + /// killed, or the key did not match any of the conditions in the feature flag /// roll-out plan. /// - /// [splitName] is the feature we want to evaluate. + /// [featureFlagName] is the feature flag we want to evaluate. /// /// Optionally, a [Map] can be specified with the [attributes] parameter to /// take into account when evaluating. - Future getTreatmentWithConfig(String splitName, + Future getTreatmentWithConfig(String featureFlagName, [Map attributes = const {}]); /// Convenience method to perform multiple evaluations. Returns a [Map] in - /// which the keys are split names and the values are treatments. + /// which the keys are feature flag names and the values are treatments. /// - /// A list of splits need to be specified in [splitNames]. + /// A list of feature flag names need to be specified in [featureFlagNames]. /// /// Optionally, a [Map] can be specified with the [attributes] parameter to /// take into account when evaluating. - Future> getTreatments(List splitNames, + Future> getTreatments(List featureFlagNames, [Map attributes = const {}]); /// Convenience method to perform multiple evaluations. Returns a [Map] in - /// which the keys are split names and the values are [SplitResult] objects. + /// which the keys are feature flag names and the values are [SplitResult] objects. /// - /// A list of splits need to be specified in [splitNames]. + /// A list of feature flag names need to be specified in [featureFlagNames]. /// /// Optionally, a [Map] can be specified with the [attributes] parameter to /// take into account when evaluating. Future> getTreatmentsWithConfig( - List splitNames, + List featureFlagNames, [Map attributes = const {}]); - /// Enqueue a new event to be sent to split data collection services. + /// Enqueue a new event to be sent to Split data collection services. /// /// [eventType] is a [String] representing the event type. /// @@ -130,7 +130,7 @@ abstract class SplitClient { Future whenReadyFromCache(); /// Returns Future that is completed when changes have been made, such as creating - /// new splits or modifying segments. + /// new feature flags or modifying segments. Stream whenUpdated(); /// Returns Future that is completed if the SDK has not been able to get ready in time. @@ -145,43 +145,43 @@ class DefaultSplitClient implements SplitClient { DefaultSplitClient(this._platform, this._matchingKey, this._bucketingKey); @override - Future getTreatment(String splitName, + Future getTreatment(String featureFlagName, [Map attributes = const {}]) async { return _platform.getTreatment( matchingKey: _matchingKey, bucketingKey: _bucketingKey, - splitName: splitName, + splitName: featureFlagName, attributes: attributes); } @override - Future getTreatmentWithConfig(String splitName, + Future getTreatmentWithConfig(String featureFlagName, [Map attributes = const {}]) async { return _platform.getTreatmentWithConfig( matchingKey: _matchingKey, bucketingKey: _bucketingKey, - splitName: splitName, + splitName: featureFlagName, attributes: attributes); } @override - Future> getTreatments(List splitNames, + Future> getTreatments(List featureFlagNames, [Map attributes = const {}]) async { return _platform.getTreatments( matchingKey: _matchingKey, bucketingKey: _bucketingKey, - splitNames: splitNames, + splitNames: featureFlagNames, attributes: attributes); } @override Future> getTreatmentsWithConfig( - List splitNames, + List featureFlagNames, [Map attributes = const {}]) async { return _platform.getTreatmentsWithConfig( matchingKey: _matchingKey, bucketingKey: _bucketingKey, - splitNames: splitNames, + splitNames: featureFlagNames, attributes: attributes); } diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index a63f9f2..3db1151 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -13,7 +13,7 @@ export 'package:splitio_platform_interface/split_view.dart'; typedef ClientReadinessCallback = void Function(SplitClient splitClient); class Splitio { - final String _apiKey; + final String _sdkKey; final String _defaultMatchingKey; @@ -25,7 +25,7 @@ class Splitio { /// SDK instance constructor. /// - /// Use [_apiKey] to specify your Split API key. + /// Use [_sdkKey] to specify your Split SDK key. /// /// Use [_defaultMatchingKey] to specify a default matching key. The default /// client will be associated with it. @@ -33,7 +33,7 @@ class Splitio { /// An optional [bucketingKey] can also be specified. /// /// Use the optional [configuration] parameter to fine tune configuration options. - Splitio(this._apiKey, this._defaultMatchingKey, + Splitio(this._sdkKey, this._defaultMatchingKey, {String? bucketingKey, SplitConfiguration? configuration}) { _defaultBucketingKey = bucketingKey; _splitConfiguration = configuration; @@ -60,7 +60,7 @@ class Splitio { /// up-to-date, but all the functionality will be available. /// /// [onUpdated] is executed when changes have been made, such as creating - /// new splits or modifying segments. + /// new feature flags or modifying segments. /// /// [onTimeout] is executed if the SDK has not been able to get ready in time. SplitClient client( @@ -123,7 +123,7 @@ class Splitio { Future _init() { return _platform.init( - apiKey: _apiKey, + apiKey: _sdkKey, matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey, sdkConfiguration: _splitConfiguration); diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index ae25ce4..091916f 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,5 +1,5 @@ name: splitio -description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via a Split feature flag to manage your complete customer experience. +description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. version: 0.1.2+2 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ diff --git a/splitio_android/LICENSE b/splitio_android/LICENSE index 65f5999..54326b0 100644 --- a/splitio_android/LICENSE +++ b/splitio_android/LICENSE @@ -1,13 +1,191 @@ -Copyright © 2023 Split Software, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright © 2023 Split Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/splitio_ios/LICENSE b/splitio_ios/LICENSE index 65f5999..54326b0 100644 --- a/splitio_ios/LICENSE +++ b/splitio_ios/LICENSE @@ -1,13 +1,191 @@ -Copyright © 2023 Split Software, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright © 2023 Split Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/splitio_platform_interface/LICENSE b/splitio_platform_interface/LICENSE index 65f5999..54326b0 100644 --- a/splitio_platform_interface/LICENSE +++ b/splitio_platform_interface/LICENSE @@ -1,13 +1,191 @@ -Copyright © 2023 Split Software, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright © 2023 Split Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/splitio_platform_interface/lib/split_configuration.dart b/splitio_platform_interface/lib/split_configuration.dart index 80c55fb..8c19f9b 100644 --- a/splitio_platform_interface/lib/split_configuration.dart +++ b/splitio_platform_interface/lib/split_configuration.dart @@ -5,7 +5,7 @@ class SplitConfiguration { /// Initializes the Split configuration. /// - /// [featuresRefreshRate] the SDK polls Split servers for changes to feature splits at this rate (in seconds). + /// [featuresRefreshRate] the SDK polls Split servers for changes to feature flags at this rate (in seconds). /// /// [segmentsRefreshRate] The SDK polls Split servers for changes to segments at this rate (in seconds). /// @@ -31,7 +31,7 @@ class SplitConfiguration { /// /// [impressionListener] Enables impression listener. If true, generated impressions will be streamed in the impressionsStream() method of Splitio. /// - /// [syncConfig] Use it to filter specific splits to be synced and evaluated by the SDK. If not set, all splits will be downloaded. + /// [syncConfig] Use it to filter specific feature flags to be synced and evaluated by the SDK. If not set, all feature flags will be downloaded. SplitConfiguration({ int? featuresRefreshRate, int? segmentsRefreshRate, From 6514d45815a694c534409f256e0b927d56df335e Mon Sep 17 00:00:00 2001 From: Mauro Sanz <51236193+sanzmauro@users.noreply.github.com> Date: Thu, 18 May 2023 14:37:22 -0300 Subject: [PATCH 14/25] Create CODEOWNERS --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..9e31981 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @splitio/sdk From 511b83fba6070d2dfe52735bfeeda0f59fd42984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Thu, 18 May 2023 16:05:13 -0300 Subject: [PATCH 15/25] SDK version updates & new config parameters (#68) --- splitio/example/android/build.gradle | 4 +- splitio_android/android/build.gradle | 3 +- .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59821 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + splitio_android/android/gradlew | 234 ++++++++++++++++++ splitio_android/android/gradlew.bat | 89 +++++++ .../splitio/SplitClientConfigHelper.java | 54 +++- .../splitio/SplitClientConfigHelperTest.java | 86 ++++++- splitio_ios/example/ios/Podfile.lock | 10 +- .../SplitClientConfigHelperTests.swift | 85 +++++-- .../example/ios/SplitTests/SplitTests.swift | 11 + .../ios/Classes/SplitClientConfigHelper.swift | 51 +++- splitio_ios/ios/splitio_ios.podspec | 4 +- .../lib/split_configuration.dart | 53 +++- .../test/splitio_configuration_test.dart | 13 +- 15 files changed, 665 insertions(+), 37 deletions(-) create mode 100644 splitio_android/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 splitio_android/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 splitio_android/android/gradlew create mode 100644 splitio_android/android/gradlew.bat diff --git a/splitio/example/android/build.gradle b/splitio/example/android/build.gradle index 5160cdd..0822484 100644 --- a/splitio/example/android/build.gradle +++ b/splitio/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' + classpath 'com.android.tools.build:gradle:7.4.2' } } @@ -24,6 +24,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/splitio_android/android/build.gradle b/splitio_android/android/build.gradle index 62cfb6c..20feee8 100644 --- a/splitio_android/android/build.gradle +++ b/splitio_android/android/build.gradle @@ -36,8 +36,7 @@ android { } dependencies { - implementation 'io.split.client:android-client:2.12.0' - implementation 'androidx.work:work-runtime:2.7.1' + implementation 'io.split.client:android-client:3.2.1' implementation 'com.squareup.okhttp3:okhttp:3.12.13' testImplementation 'junit:junit:4.13.2' diff --git a/splitio_android/android/gradle/wrapper/gradle-wrapper.jar b/splitio_android/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..41d9927a4d4fb3f96a785543079b8df6723c946b GIT binary patch literal 59821 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 literal 0 HcmV?d00001 diff --git a/splitio_android/android/gradle/wrapper/gradle-wrapper.properties b/splitio_android/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..8049c68 --- /dev/null +++ b/splitio_android/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/splitio_android/android/gradlew b/splitio_android/android/gradlew new file mode 100755 index 0000000..1b6c787 --- /dev/null +++ b/splitio_android/android/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/splitio_android/android/gradlew.bat b/splitio_android/android/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/splitio_android/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/splitio_android/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java b/splitio_android/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java index 962c5bc..825550e 100644 --- a/splitio_android/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java +++ b/splitio_android/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java @@ -12,6 +12,8 @@ import io.split.android.client.SplitFilter; import io.split.android.client.SyncConfig; import io.split.android.client.impressions.ImpressionListener; +import io.split.android.client.shared.UserConsent; +import io.split.android.client.utils.logger.SplitLogLevel; class SplitClientConfigHelper { @@ -36,11 +38,16 @@ class SplitClientConfigHelper { private static final String SYNC_CONFIG = "syncConfig"; private static final String SYNC_CONFIG_NAMES = "syncConfigNames"; private static final String SYNC_CONFIG_PREFIXES = "syncConfigPrefixes"; + private static final String IMPRESSIONS_MODE = "impressionsMode"; + private static final String SYNC_ENABLED = "syncEnabled"; + private static final String USER_CONSENT = "userConsent"; + private static final String ENCRYPTION_ENABLED = "encryptionEnabled"; + private static final String LOG_LEVEL = "logLevel"; /** * Creates a {@link SplitClientConfig} object from a map. * - * @param configurationMap Map of config values. + * @param configurationMap Map of config values. * @param impressionListener Optional ImpressionListener. * @return {@link SplitClientConfig} object. */ @@ -94,7 +101,7 @@ static SplitClientConfig fromMap(@NonNull Map configurationMap, Boolean enableDebug = getBoolean(configurationMap, ENABLE_DEBUG); if (enableDebug != null && enableDebug) { - builder.enableDebug(); + builder.logLevel(SplitLogLevel.DEBUG); } Boolean streamingEnabled = getBoolean(configurationMap, STREAMING_ENABLED); @@ -156,6 +163,49 @@ static SplitClientConfig fromMap(@NonNull Map configurationMap, builder.syncConfig(syncConfigBuilder.build()); } + String impressionsMode = getString(configurationMap, IMPRESSIONS_MODE); + if (impressionsMode != null) { + builder.impressionsMode(impressionsMode); + } + + Boolean syncEnabled = getBoolean(configurationMap, SYNC_ENABLED); + if (syncEnabled != null) { + builder.syncEnabled(syncEnabled); + } + + String userConsent = getString(configurationMap, USER_CONSENT); + if (userConsent != null) { + if ("unknown".equalsIgnoreCase(userConsent)) { + builder.userConsent(UserConsent.UNKNOWN); + } else if ("declined".equalsIgnoreCase(userConsent)) { + builder.userConsent(UserConsent.DECLINED); + } else { + builder.userConsent(UserConsent.GRANTED); + } + } + + Boolean encryptionEnabled = getBoolean(configurationMap, ENCRYPTION_ENABLED); + if (encryptionEnabled != null) { + builder.encryptionEnabled(encryptionEnabled); + } + + String logLevel = getString(configurationMap, LOG_LEVEL); + if (logLevel != null) { + if ("verbose".equalsIgnoreCase(logLevel)) { + builder.logLevel(SplitLogLevel.VERBOSE); + } else if ("debug".equalsIgnoreCase(logLevel)) { + builder.logLevel(SplitLogLevel.DEBUG); + } else if ("info".equalsIgnoreCase(logLevel)) { + builder.logLevel(SplitLogLevel.INFO); + } else if ("warning".equalsIgnoreCase(logLevel)) { + builder.logLevel(SplitLogLevel.WARNING); + } else if ("error".equalsIgnoreCase(logLevel)) { + builder.logLevel(SplitLogLevel.ERROR); + } else { + builder.logLevel(SplitLogLevel.NONE); + } + } + return builder.serviceEndpoints(serviceEndpointsBuilder.build()).build(); } diff --git a/splitio_android/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java b/splitio_android/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java index 7add0b9..aa9b7f4 100644 --- a/splitio_android/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java +++ b/splitio_android/android/src/test/java/io/split/splitio/SplitClientConfigHelperTest.java @@ -1,6 +1,7 @@ package io.split.splitio; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -16,6 +17,9 @@ import io.split.android.client.SplitClientConfig; import io.split.android.client.SplitFilter; import io.split.android.client.impressions.ImpressionListener; +import io.split.android.client.service.impressions.ImpressionsMode; +import io.split.android.client.shared.UserConsent; +import io.split.android.client.utils.logger.SplitLogLevel; public class SplitClientConfigHelperTest { @@ -44,8 +48,14 @@ public void configValuesAreMappedCorrectly() { syncConfigMap.put("syncConfigNames", Arrays.asList("split1", "split2")); syncConfigMap.put("syncConfigPrefixes", Arrays.asList("split_", "my_split_")); configValues.put("syncConfig", syncConfigMap); + configValues.put("impressionsMode", "none"); + configValues.put("syncEnabled", false); + configValues.put("userConsent", "declined"); + configValues.put("encryptionEnabled", true); + configValues.put("logLevel", "verbose"); - SplitClientConfig splitClientConfig = SplitClientConfigHelper.fromMap(configValues, mock(ImpressionListener.class)); + SplitClientConfig splitClientConfig = SplitClientConfigHelper + .fromMap(configValues, mock(ImpressionListener.class)); assertEquals(80000, splitClientConfig.featuresRefreshRate()); assertEquals(70000, splitClientConfig.segmentsRefreshRate()); @@ -56,7 +66,6 @@ public void configValuesAreMappedCorrectly() { assertEquals(40000, splitClientConfig.eventFlushInterval()); assertEquals(5000, splitClientConfig.eventsPerPush()); assertEquals("none", splitClientConfig.trafficType()); - assertTrue(splitClientConfig.debugEnabled()); assertTrue(splitClientConfig.streamingEnabled()); assertTrue(splitClientConfig.persistentAttributesEnabled()); assertEquals("apiEndpoint.split.io", splitClientConfig.endpoint()); @@ -68,5 +77,78 @@ public void configValuesAreMappedCorrectly() { assertEquals(SplitFilter.Type.BY_NAME, splitClientConfig.syncConfig().getFilters().get(0).getType()); assertEquals(Arrays.asList("split_", "my_split_"), splitClientConfig.syncConfig().getFilters().get(1).getValues()); assertEquals(SplitFilter.Type.BY_PREFIX, splitClientConfig.syncConfig().getFilters().get(1).getType()); + assertEquals(ImpressionsMode.NONE, splitClientConfig.impressionsMode()); + assertFalse(splitClientConfig.syncEnabled()); + assertEquals(UserConsent.DECLINED, splitClientConfig.userConsent()); + assertTrue(splitClientConfig.encryptionEnabled()); + assertEquals(SplitLogLevel.VERBOSE, splitClientConfig.logLevel()); + } + + @Test + public void enableDebugLogLevelIsMappedCorrectly() { + Map configValues = new HashMap<>(); + + configValues.put("enableDebug", true); + + SplitClientConfig splitClientConfig = SplitClientConfigHelper + .fromMap(configValues, mock(ImpressionListener.class)); + + assertEquals(SplitLogLevel.DEBUG, splitClientConfig.logLevel()); + } + + @Test + public void logLevelsAreMappedCorrectly() { + Map verboseConfigValues = Collections.singletonMap("logLevel", "verbose"); + Map debugConfigValues = Collections.singletonMap("logLevel", "debug"); + Map infoConfigValues = Collections.singletonMap("logLevel", "info"); + Map warnConfigValues = Collections.singletonMap("logLevel", "warning"); + Map errorConfigValues = Collections.singletonMap("logLevel", "error"); + Map noneConfigValues = Collections.singletonMap("logLevel", "none"); + + SplitClientConfig verboseConfig = SplitClientConfigHelper + .fromMap(verboseConfigValues, mock(ImpressionListener.class)); + SplitClientConfig debugConfig = SplitClientConfigHelper.fromMap(debugConfigValues, mock(ImpressionListener.class)); + SplitClientConfig infoConfig = SplitClientConfigHelper.fromMap(infoConfigValues, mock(ImpressionListener.class)); + SplitClientConfig warnConfig = SplitClientConfigHelper.fromMap(warnConfigValues, mock(ImpressionListener.class)); + SplitClientConfig errorConfig = SplitClientConfigHelper.fromMap(errorConfigValues, mock(ImpressionListener.class)); + SplitClientConfig noneConfig = SplitClientConfigHelper.fromMap(noneConfigValues, mock(ImpressionListener.class)); + + assertEquals(SplitLogLevel.VERBOSE, verboseConfig.logLevel()); + assertEquals(SplitLogLevel.DEBUG, debugConfig.logLevel()); + assertEquals(SplitLogLevel.INFO, infoConfig.logLevel()); + assertEquals(SplitLogLevel.WARNING, warnConfig.logLevel()); + assertEquals(SplitLogLevel.ERROR, errorConfig.logLevel()); + assertEquals(SplitLogLevel.NONE, noneConfig.logLevel()); + } + + @Test + public void userConsentIsMappedCorrectly() { + SplitClientConfig grantedConfig = SplitClientConfigHelper + .fromMap(Collections.singletonMap("userConsent", "granted"), mock(ImpressionListener.class)); + SplitClientConfig unknownConfig = SplitClientConfigHelper + .fromMap(Collections.singletonMap("userConsent", "unknown"), mock(ImpressionListener.class)); + SplitClientConfig declinedConfig = SplitClientConfigHelper + .fromMap(Collections.singletonMap("userConsent", "declined"), mock(ImpressionListener.class)); + SplitClientConfig anyConfig = SplitClientConfigHelper + .fromMap(Collections.singletonMap("userConsent", "any"), mock(ImpressionListener.class)); + + assertEquals(UserConsent.GRANTED, grantedConfig.userConsent()); + assertEquals(UserConsent.UNKNOWN, unknownConfig.userConsent()); + assertEquals(UserConsent.DECLINED, declinedConfig.userConsent()); + assertEquals(UserConsent.GRANTED, anyConfig.userConsent()); + } + + @Test + public void impressionsModeValuesAreMappedCorrectly() { + SplitClientConfig debugConfig = SplitClientConfigHelper + .fromMap(Collections.singletonMap("impressionsMode", "debug"), mock(ImpressionListener.class)); + SplitClientConfig noneConfig = SplitClientConfigHelper + .fromMap(Collections.singletonMap("impressionsMode", "none"), mock(ImpressionListener.class)); + SplitClientConfig optimizedConfig = SplitClientConfigHelper + .fromMap(Collections.singletonMap("impressionsMode", "optimized"), mock(ImpressionListener.class)); + + assertEquals(ImpressionsMode.DEBUG, debugConfig.impressionsMode()); + assertEquals(ImpressionsMode.NONE, noneConfig.impressionsMode()); + assertEquals(ImpressionsMode.OPTIMIZED, optimizedConfig.impressionsMode()); } } diff --git a/splitio_ios/example/ios/Podfile.lock b/splitio_ios/example/ios/Podfile.lock index 38ef52f..8ec7ad5 100644 --- a/splitio_ios/example/ios/Podfile.lock +++ b/splitio_ios/example/ios/Podfile.lock @@ -1,9 +1,9 @@ PODS: - Flutter (1.0.0) - - Split (2.15.0) - - splitio_ios (0.1.0): + - Split (2.20.1) + - splitio_ios (0.2.0): - Flutter - - Split (~> 2.15.0) + - Split (~> 2.20.1) DEPENDENCIES: - Flutter (from `Flutter`) @@ -21,8 +21,8 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - Split: d103c6afa47b5d1eac21e066c6bc09c879b21798 - splitio_ios: 665651004a4984a415386b1eed4ae315b50bc069 + Split: afbdfda76fe2c90bd449564eed2d83de00c0ff69 + splitio_ios: e6d3dfe108ba894129a6b249d8914e3c44f0ffc4 PODFILE CHECKSUM: 6ab177d3659abbf5f15e864674366127c98cb8c0 diff --git a/splitio_ios/example/ios/SplitTests/SplitClientConfigHelperTests.swift b/splitio_ios/example/ios/SplitTests/SplitClientConfigHelperTests.swift index 69b43a5..beaa317 100644 --- a/splitio_ios/example/ios/SplitTests/SplitClientConfigHelperTests.swift +++ b/splitio_ios/example/ios/SplitTests/SplitClientConfigHelperTests.swift @@ -1,27 +1,33 @@ import XCTest @testable import splitio_ios +@testable import Split class SplitClientConfigHelperTests: XCTestCase { func testConfigValuesAreMappedCorrectly() throws { let configValues: [String: Any?] = ["featuresRefreshRate": 80000, - "segmentsRefreshRate": 70000, - "impressionsRefreshRate": 60000, - "telemetryRefreshRate": 2000, - "eventsQueueSize": 3999, - "impressionsQueueSize": 2999, - "eventFlushInterval": 40000, - "eventsPerPush": 5000, - "trafficType": "none", - "connectionTimeOut": 10, - "readTimeout": 25, - "disableLabels": true, - "enableDebug": true, - "proxyHost": "https://proxy", - "ready": 25, - "streamingEnabled": true, - "persistentAttributesEnabled": true, - "syncConfig": ["syncConfigNames": ["split1", "split2"], "syncConfigPrefixes": ["split_", "my_split_"]]] + "segmentsRefreshRate": 70000, + "impressionsRefreshRate": 60000, + "telemetryRefreshRate": 2000, + "eventsQueueSize": 3999, + "impressionsQueueSize": 2999, + "eventFlushInterval": 40000, + "eventsPerPush": 5000, + "trafficType": "none", + "connectionTimeOut": 10, + "readTimeout": 25, + "disableLabels": true, + "proxyHost": "https://proxy", + "ready": 25, + "streamingEnabled": true, + "persistentAttributesEnabled": true, + "syncConfig": ["syncConfigNames": ["split1", "split2"], "syncConfigPrefixes": ["split_", "my_split_"]], + "impressionsMode": "none", + "syncEnabled": false, + "userConsent": "declined", + "encryptionEnabled": true, + "logLevel": "verbose" + ] let splitClientConfig = SplitClientConfigHelper.fromMap(configurationMap: configValues, impressionListener: nil) @@ -34,8 +40,51 @@ class SplitClientConfigHelperTests: XCTestCase { XCTAssert(40000 == splitClientConfig.eventsPushRate) XCTAssert(5000 == splitClientConfig.eventsPerPush) XCTAssert("none" == splitClientConfig.trafficType) - XCTAssert(splitClientConfig.isDebugModeEnabled) XCTAssert(splitClientConfig.streamingEnabled) XCTAssert(splitClientConfig.persistentAttributesEnabled) + XCTAssertEqual("NONE", splitClientConfig.impressionsMode) + XCTAssertFalse(splitClientConfig.syncEnabled) + XCTAssertEqual(.declined, splitClientConfig.userConsent) + XCTAssertTrue(splitClientConfig.encryptionEnabled) + XCTAssertEqual(.verbose, splitClientConfig.logLevel) + } + + func testEnableDebugLogLevelIsMappedCorrectly() { + let configValues: [String: Any] = ["enableDebug": true] + let splitClientConfig = SplitClientConfigHelper.fromMap(configurationMap: configValues, impressionListener: nil) + XCTAssertEqual(SplitLogLevel.debug, splitClientConfig.logLevel) + } + + func testLogLevelsAreMappedCorrectly() { + let logLevels = ["verbose", "debug", "info", "warning", "error", "none"] + let expectedLogLevels: [SplitLogLevel] = [.verbose, .debug, .info, .warning, .error, .none] + + for (index, logLevel) in logLevels.enumerated() { + let configValues: [String: Any] = ["logLevel": logLevel] + let config = SplitClientConfigHelper.fromMap(configurationMap: configValues, impressionListener: nil) + XCTAssertEqual(expectedLogLevels[index], config.logLevel) + } + } + + func testUserConsentIsMappedCorrectly() { + let userConsents = ["granted", "unknown", "declined", "any"] + let expectedUserConsents: [UserConsent] = [.granted, .unknown, .declined, .granted] + + for (index, userConsent) in userConsents.enumerated() { + let configValues: [String: Any] = ["userConsent": userConsent] + let config = SplitClientConfigHelper.fromMap(configurationMap: configValues, impressionListener: nil) + XCTAssertEqual(expectedUserConsents[index], config.userConsent) + } + } + + func testImpressionsModeValuesAreMappedCorrectly() { + let impressionsModes = ["debug", "none", "optimized"] + let expectedImpressionsModes: [ImpressionsMode] = [.debug, .none, .optimized] + + for (index, impressionsMode) in impressionsModes.enumerated() { + let configValues: [String: Any] = ["impressionsMode": impressionsMode] + let config = SplitClientConfigHelper.fromMap(configurationMap: configValues, impressionListener: nil) + XCTAssertEqual(expectedImpressionsModes[index].rawValue, config.impressionsMode) + } } } diff --git a/splitio_ios/example/ios/SplitTests/SplitTests.swift b/splitio_ios/example/ios/SplitTests/SplitTests.swift index d94c831..7853301 100644 --- a/splitio_ios/example/ios/SplitTests/SplitTests.swift +++ b/splitio_ios/example/ios/SplitTests/SplitTests.swift @@ -235,12 +235,15 @@ class SplitFactoryStub: SplitFactory { var apiKey: String + var userConsent: UserConsent + init(apiKey: String, client: SplitClient) { self.apiKey = apiKey self.client = client self.nilBucketingKeyClient = SplitClientStub() manager = SplitManagerStub() version = "0.0.0-stub" + userConsent = .unknown } convenience init(apiKey: String) { @@ -267,6 +270,14 @@ class SplitFactoryStub: SplitFactory { func client(matchingKey: String, bucketingKey: String?) -> SplitClient { return client } + + func setUserConsent(enabled: Bool) { + if (enabled) { + self.userConsent = .granted + } else { + self.userConsent = .declined + } + } } class SplitClientStub: SplitClient { diff --git a/splitio_ios/ios/Classes/SplitClientConfigHelper.swift b/splitio_ios/ios/Classes/SplitClientConfigHelper.swift index f6aad57..6d03b98 100644 --- a/splitio_ios/ios/Classes/SplitClientConfigHelper.swift +++ b/splitio_ios/ios/Classes/SplitClientConfigHelper.swift @@ -24,6 +24,11 @@ class SplitClientConfigHelper { static private let SYNC_CONFIG = "syncConfig" static private let SYNC_CONFIG_NAMES = "syncConfigNames" static private let SYNC_CONFIG_PREFIXES = "syncConfigPrefixes" + static private let IMPRESSIONS_MODE = "impressionsMode" + static private let SYNC_ENABLED = "syncEnabled" + static private let USER_CONSENT = "userConsent" + static private let ENCRYPTION_ENABLED = "encryptionEnabled" + static private let LOG_LEVEL = "logLevel" static func fromMap(configurationMap: [String: Any?], impressionListener: SplitImpressionListener?) -> SplitClientConfig { let config = SplitClientConfig() @@ -84,7 +89,11 @@ class SplitClientConfigHelper { if configurationMap[ENABLE_DEBUG] != nil { if let enableDebug = configurationMap[ENABLE_DEBUG] as? Bool { - config.isDebugModeEnabled = enableDebug + if (enableDebug) { + config.logLevel = .debug + } else { + config.logLevel = .none + } } } @@ -150,6 +159,46 @@ class SplitClientConfigHelper { config.sync = syncConfigBuilder.build() } } + + if let impressionsMode = configurationMap[IMPRESSIONS_MODE] as? String { + config.impressionsMode = impressionsMode.uppercased() + } + + if let syncEnabled = configurationMap[SYNC_ENABLED] as? Bool { + config.syncEnabled = syncEnabled + } + + if let userConsent = configurationMap[USER_CONSENT] as? String { + switch userConsent.lowercased() { + case "unknown": + config.userConsent = .unknown + case "declined": + config.userConsent = .declined + default: + config.userConsent = .granted + } + } + + if let encryptionEnabled = configurationMap[ENCRYPTION_ENABLED] as? Bool { + config.encryptionEnabled = encryptionEnabled + } + + if let logLevel = configurationMap[LOG_LEVEL] as? String { + switch logLevel.lowercased() { + case "verbose": + config.logLevel = .verbose + case "debug": + config.logLevel = .debug + case "info": + config.logLevel = .info + case "warning": + config.logLevel = .warning + case "error": + config.logLevel = .error + default: + config.logLevel = .none + } + } config.serviceEndpoints = serviceEndpointsBuilder.build() diff --git a/splitio_ios/ios/splitio_ios.podspec b/splitio_ios/ios/splitio_ios.podspec index 6999409..8cb33f7 100644 --- a/splitio_ios/ios/splitio_ios.podspec +++ b/splitio_ios/ios/splitio_ios.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'splitio_ios' - s.version = '0.1.0' + s.version = '0.2.0' s.summary = 'split.io official Flutter plugin.' s.description = <<-DESC split.io official Flutter plugin. @@ -15,7 +15,7 @@ split.io official Flutter plugin. s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.dependency 'Split', '~> 2.15.0' + s.dependency 'Split', '~> 2.20.1' s.platform = :ios, '9.0' # Flutter.framework does not contain a i386 slice. diff --git a/splitio_platform_interface/lib/split_configuration.dart b/splitio_platform_interface/lib/split_configuration.dart index 8c19f9b..9adb7d6 100644 --- a/splitio_platform_interface/lib/split_configuration.dart +++ b/splitio_platform_interface/lib/split_configuration.dart @@ -23,7 +23,7 @@ class SplitConfiguration { /// /// [trafficType] The default traffic type for events tracked using the track method. If not specified, every track call should specify a traffic type. /// - /// [enableDebug] If true, the SDK will log debug messages to the console. + /// [enableDebug] (Deprecated; use logLevel instead) If true, the SDK will log debug messages to the console. /// /// [streamingEnabled] Boolean flag to enable the streaming service as default synchronization mechanism when in foreground. In the event of an issue with streaming, the SDK will fallback to the polling mechanism. If false, the SDK will poll for changes as usual without attempting to use streaming. /// @@ -32,6 +32,16 @@ class SplitConfiguration { /// [impressionListener] Enables impression listener. If true, generated impressions will be streamed in the impressionsStream() method of Splitio. /// /// [syncConfig] Use it to filter specific feature flags to be synced and evaluated by the SDK. If not set, all feature flags will be downloaded. + /// + /// [impressionsMode] This configuration defines how impressions (decisioning events) are queued. Supported modes are [ImpressionsMode.optimized], [ImpressionsMode.none], and [ImpressionsMode.debug]. In [ImpressionsMode.optimized] mode, only unique impressions are queued and posted to Split; this is the recommended mode for experimentation use cases. In [ImpressionsMode.none] mode, no impression is tracked in Split and only minimum viable data to support usage stats is, so never use this mode if you are experimenting with that instance impressions. Use [ImpressionsMode.none] when you want to optimize for feature flagging only use cases and reduce impressions network and storage load. In [ImpressionsMode.debug] mode, ALL impressions are queued and sent to Split; this is useful for validations. This mode doesn't impact the impression listener which receives all generated impressions locally. + /// + /// [syncEnabled] Controls the SDK continuous synchronization flags. When true, a running SDK processes rollout plan updates performed in the Split user interface (default). When false, it fetches all data upon init, which ensures a consistent experience during a user session and optimizes resources when these updates are not consumed by the app. + /// + /// [UserConsent] User consent status used to control the tracking of events and impressions. Possible values are [UserConsent.granted], [UserConsent.declined], and [UserConsent.unknown]. + /// + /// [encryptionEnabled] If set to true, the local database contents is encrypted. Defaults to false. + /// + /// [logLevel] Enables logging according to the level specified. Options are [SplitLogLevel.verbose], [SplitLogLevel.none], [SplitLogLevel.debug], [SplitLogLevel.info], [SplitLogLevel.warning], and [SplitLogLevel.error]. SplitConfiguration({ int? featuresRefreshRate, int? segmentsRefreshRate, @@ -42,7 +52,7 @@ class SplitConfiguration { int? eventFlushInterval, int? eventsPerPush, String? trafficType, - bool? enableDebug, + @Deprecated('Use logLevel instead') bool? enableDebug, bool? streamingEnabled, bool? persistentAttributesEnabled, bool? impressionListener, @@ -52,6 +62,11 @@ class SplitConfiguration { String? streamingServiceEndpoint, String? telemetryServiceEndpoint, SyncConfig? syncConfig, + ImpressionsMode? impressionsMode, + bool? syncEnabled, + UserConsent? userConsent, + bool? encryptionEnabled, + SplitLogLevel? logLevel, }) { if (featuresRefreshRate != null) { configurationMap['featuresRefreshRate'] = featuresRefreshRate; @@ -132,5 +147,39 @@ class SplitConfiguration { 'syncConfigPrefixes': syncConfig.prefixes.toList(growable: false) }; } + + if (impressionsMode != null) { + configurationMap['impressionsMode'] = impressionsMode.name; + } + + if (syncEnabled != null) { + configurationMap['syncEnabled'] = syncEnabled; + } + + if (userConsent != null) { + configurationMap['userConsent'] = userConsent.name; + } + + if (encryptionEnabled != null) { + configurationMap['encryptionEnabled'] = encryptionEnabled; + } + + if (logLevel != null) { + configurationMap['logLevel'] = logLevel.name; + } } } + +enum ImpressionsMode { + debug, + optimized, + none, +} + +enum UserConsent { + granted, + declined, + unknown, +} + +enum SplitLogLevel { verbose, debug, info, warning, error, none } diff --git a/splitio_platform_interface/test/splitio_configuration_test.dart b/splitio_platform_interface/test/splitio_configuration_test.dart index d1c44f5..72c563f 100644 --- a/splitio_platform_interface/test/splitio_configuration_test.dart +++ b/splitio_platform_interface/test/splitio_configuration_test.dart @@ -24,7 +24,13 @@ void main() { streamingServiceEndpoint: 'streamingServiceEndpoint.split.io', telemetryServiceEndpoint: 'telemetryServiceEndpoint.split.io', syncConfig: - SyncConfig(names: ['one', 'two', 'three'], prefixes: ['pre1'])); + SyncConfig(names: ['one', 'two', 'three'], prefixes: ['pre1']), + impressionsMode: ImpressionsMode.none, + syncEnabled: false, + userConsent: UserConsent.declined, + encryptionEnabled: true, + logLevel: SplitLogLevel.debug, + ); expect(config.configurationMap['eventFlushInterval'], 2000); expect(config.configurationMap['eventsPerPush'], 300); @@ -51,6 +57,11 @@ void main() { ['one', 'two', 'three']); expect( config.configurationMap['syncConfig']['syncConfigPrefixes'], ['pre1']); + expect(config.configurationMap['impressionsMode'], 'none'); + expect(config.configurationMap['syncEnabled'], false); + expect(config.configurationMap['userConsent'], 'declined'); + expect(config.configurationMap['encryptionEnabled'], true); + expect(config.configurationMap['logLevel'], 'debug'); }); test('noSpecialValuesLeavesMapEmpty', () async { From 9867ff6c77effc1a6b566b92ce38a58958cbd3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Thu, 18 May 2023 17:24:06 -0300 Subject: [PATCH 16/25] 0.1.3 rc.1 (#70) --- splitio/CHANGELOG.md | 12 +++++++----- splitio/example/ios/Flutter/AppFrameworkInfo.plist | 2 +- splitio/example/ios/Podfile | 2 +- splitio/example/ios/Podfile.lock | 14 +++++++------- .../example/ios/Runner.xcodeproj/project.pbxproj | 11 +++++++---- splitio/example/ios/Runner/Info.plist | 4 ++++ splitio/pubspec.yaml | 12 ++++++------ splitio/test/splitio_test.dart | 4 ++-- splitio_android/CHANGELOG.md | 6 ++++-- splitio_android/pubspec.yaml | 6 +++--- splitio_ios/CHANGELOG.md | 2 ++ splitio_ios/pubspec.yaml | 6 +++--- splitio_platform_interface/CHANGELOG.md | 2 ++ splitio_platform_interface/pubspec.yaml | 6 +++--- 14 files changed, 52 insertions(+), 37 deletions(-) diff --git a/splitio/CHANGELOG.md b/splitio/CHANGELOG.md index ff269cb..43e580e 100644 --- a/splitio/CHANGELOG.md +++ b/splitio/CHANGELOG.md @@ -1,12 +1,14 @@ -## 0.1.2+2 (Dec 7, 2022) +# 0.1.3-rc.1 (May 18, 2023) + +# 0.1.2+2 (Dec 7, 2022) Added consumer ProGuard rules for Android. -## 0.1.2+1 (Sep 14, 2022) +# 0.1.2+1 (Sep 14, 2022) Added exports for models. -## 0.1.2 (Sep 13, 2022) +# 0.1.2 (Sep 13, 2022) * Migrated to federated structure. * Added support for Impression Listener. @@ -15,11 +17,11 @@ Added exports for models. * Added support for manager methods. * Added support for linking native factory. -## 0.1.1 (Aug 19, 2022) +# 0.1.1 (Aug 19, 2022) Minor fixes. -## 0.1.0 (Aug 3, 2022) +# 0.1.0 (Aug 3, 2022) Initial release. diff --git a/splitio/example/ios/Flutter/AppFrameworkInfo.plist b/splitio/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f..9625e10 100644 --- a/splitio/example/ios/Flutter/AppFrameworkInfo.plist +++ b/splitio/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/splitio/example/ios/Podfile b/splitio/example/ios/Podfile index 252d9ec..313ea4a 100644 --- a/splitio/example/ios/Podfile +++ b/splitio/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '9.0' +platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/splitio/example/ios/Podfile.lock b/splitio/example/ios/Podfile.lock index 21a2888..f9b0e95 100644 --- a/splitio/example/ios/Podfile.lock +++ b/splitio/example/ios/Podfile.lock @@ -1,9 +1,9 @@ PODS: - Flutter (1.0.0) - - Split (2.15.0) - - splitio_ios (0.1.0): + - Split (2.20.1) + - splitio_ios (0.2.0): - Flutter - - Split (~> 2.15.0) + - Split (~> 2.20.1) DEPENDENCIES: - Flutter (from `Flutter`) @@ -20,10 +20,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/splitio_ios/ios" SPEC CHECKSUMS: - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - Split: d103c6afa47b5d1eac21e066c6bc09c879b21798 - splitio_ios: 665651004a4984a415386b1eed4ae315b50bc069 + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + Split: afbdfda76fe2c90bd449564eed2d83de00c0ff69 + splitio_ios: e6d3dfe108ba894129a6b249d8914e3c44f0ffc4 -PODFILE CHECKSUM: a75497545d4391e2d394c3668e20cfb1c2bbd4aa +PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d COCOAPODS: 1.11.3 diff --git a/splitio/example/ios/Runner.xcodeproj/project.pbxproj b/splitio/example/ios/Runner.xcodeproj/project.pbxproj index f52245a..a69a7aa 100644 --- a/splitio/example/ios/Runner.xcodeproj/project.pbxproj +++ b/splitio/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -230,10 +230,12 @@ }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -261,6 +263,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -348,7 +351,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -425,7 +428,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -474,7 +477,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/splitio/example/ios/Runner/Info.plist b/splitio/example/ios/Runner/Info.plist index db9625e..8fe8f6a 100644 --- a/splitio/example/ios/Runner/Info.plist +++ b/splitio/example/ios/Runner/Info.plist @@ -43,5 +43,9 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 091916f..1fb383b 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,11 +1,11 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. -version: 0.1.2+2 +version: 0.1.3-rc.1 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ environment: - sdk: ">=2.16.2 <3.0.0" + sdk: ">=2.16.2 <4.0.0" flutter: ">=2.5.0" flutter: @@ -19,12 +19,12 @@ flutter: dependencies: flutter: sdk: flutter - splitio_android: ^0.1.2+1 - splitio_ios: ^0.1.2 - splitio_platform_interface: ^1.0.0 + splitio_android: ^0.1.3-rc.1 + splitio_ios: ^0.1.3-rc.1 + splitio_platform_interface: ^1.1.0-rc.1 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^1.0.0 - plugin_platform_interface: ^2.1.2 + plugin_platform_interface: ^2.1.4 diff --git a/splitio/test/splitio_test.dart b/splitio/test/splitio_test.dart index 37cd1c5..ac227f2 100644 --- a/splitio/test/splitio_test.dart +++ b/splitio/test/splitio_test.dart @@ -36,13 +36,13 @@ void main() { Splitio('api-key', 'matching-key', bucketingKey: 'bucketing-key', configuration: - SplitConfiguration(enableDebug: true, streamingEnabled: false)); + SplitConfiguration(logLevel: SplitLogLevel.debug, streamingEnabled: false)); expect(_platform.methodName, 'init'); expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', - 'sdkConfiguration': {'enableDebug': true, 'streamingEnabled': false}, + 'sdkConfiguration': {'logLevel': 'debug', 'streamingEnabled': false}, }); }); }); diff --git a/splitio_android/CHANGELOG.md b/splitio_android/CHANGELOG.md index 4af79b3..e292dd9 100644 --- a/splitio_android/CHANGELOG.md +++ b/splitio_android/CHANGELOG.md @@ -1,7 +1,9 @@ -## 0.1.2+1 (Dec 7, 2022) +# 0.1.3-rc.1 (May 18, 2023) + +# 0.1.2+1 (Dec 7, 2022) Added Consumer ProGuard rules. -## 0.1.2 (Aug 13, 2022) +# 0.1.2 (Aug 13, 2022) Splits from `splitio` as federated implementation. diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index 689f942..d1282ec 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -1,10 +1,10 @@ name: splitio_android description: The official Android implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_android -version: 0.1.2+1 +version: 0.1.3-rc.1 environment: - sdk: ">=2.16.2 <3.0.0" + sdk: ">=2.16.2 <4.0.0" flutter: ">=2.5.0" flutter: @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.0.0 + splitio_platform_interface: ^1.1.0-rc.1 dev_dependencies: flutter_test: diff --git a/splitio_ios/CHANGELOG.md b/splitio_ios/CHANGELOG.md index 2096aba..301f867 100644 --- a/splitio_ios/CHANGELOG.md +++ b/splitio_ios/CHANGELOG.md @@ -1,3 +1,5 @@ +# 0.1.3-rc.1 (May 18, 2023) + # 0.1.2 (Aug 13, 2022) Splits from `splitio` as federated implementation. diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index 7523cf8..14110d7 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -1,10 +1,10 @@ name: splitio_ios description: The official iOS implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_ios -version: 0.1.2 +version: 0.1.3-rc.1 environment: - sdk: ">=2.16.2 <3.0.0" + sdk: ">=2.16.2 <4.0.0" flutter: ">=2.5.0" flutter: @@ -18,7 +18,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.0.0 + splitio_platform_interface: ^1.1.0-rc.1 dev_dependencies: flutter_test: diff --git a/splitio_platform_interface/CHANGELOG.md b/splitio_platform_interface/CHANGELOG.md index 520057b..74a02f8 100644 --- a/splitio_platform_interface/CHANGELOG.md +++ b/splitio_platform_interface/CHANGELOG.md @@ -1,3 +1,5 @@ +# 1.1.0-rc.1 (May 18, 2023) + # 1.0.0 (Aug 13, 2022) Initial release. diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml index 5527973..e20f38b 100644 --- a/splitio_platform_interface/pubspec.yaml +++ b/splitio_platform_interface/pubspec.yaml @@ -2,17 +2,17 @@ name: splitio_platform_interface description: A common platform interface for the splitio plugin. # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.0.0 +version: 1.1.0-rc.1 repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_platform_interface environment: - sdk: ">=2.16.2 <3.0.0" + sdk: ">=2.16.2 <4.0.0" flutter: ">=2.5.0" dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.1.2 + plugin_platform_interface: ^2.1.4 dev_dependencies: flutter_test: From 29c574b6718434c4e22ff8e313ff913af6b44e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Thu, 18 May 2023 18:59:07 -0300 Subject: [PATCH 17/25] 0.1.3 (#71) --- splitio/CHANGELOG.md | 11 +++++++++++ splitio/pubspec.yaml | 8 ++++---- splitio_android/CHANGELOG.md | 9 +++++++++ splitio_android/pubspec.yaml | 4 ++-- splitio_android/test/splitio_android_test.dart | 8 +++++--- splitio_ios/CHANGELOG.md | 9 +++++++++ splitio_ios/pubspec.yaml | 4 ++-- splitio_ios/test/splitio_ios_test.dart | 8 +++++--- splitio_platform_interface/CHANGELOG.md | 9 +++++++++ .../lib/split_configuration.dart | 2 +- splitio_platform_interface/pubspec.yaml | 2 +- .../test/method_channel_platform_test.dart | 8 +++++--- .../test/split_event_method_call_handler_test.dart | 4 +++- .../test/splitio_configuration_test.dart | 1 - 14 files changed, 66 insertions(+), 21 deletions(-) diff --git a/splitio/CHANGELOG.md b/splitio/CHANGELOG.md index 43e580e..84e0b1f 100644 --- a/splitio/CHANGELOG.md +++ b/splitio/CHANGELOG.md @@ -1,3 +1,14 @@ +# 0.1.3 (May 18, 2023) + +* Updated iOS SDK to `2.20.1` & Android SDK to `3.2.1` +* Added support for new configuration options: + * `impressionsMode` + * `syncEnabled` + * `logLevel` + * `userConsent` + * `encryptionEnabled` +* Deprecated `enableDebug` configuration in favor of `logLevel`. + # 0.1.3-rc.1 (May 18, 2023) # 0.1.2+2 (Dec 7, 2022) diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 1fb383b..650fcbc 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,6 +1,6 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. -version: 0.1.3-rc.1 +version: 0.1.3 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ @@ -19,9 +19,9 @@ flutter: dependencies: flutter: sdk: flutter - splitio_android: ^0.1.3-rc.1 - splitio_ios: ^0.1.3-rc.1 - splitio_platform_interface: ^1.1.0-rc.1 + splitio_android: ^0.1.3 + splitio_ios: ^0.1.3 + splitio_platform_interface: ^1.1.0 dev_dependencies: flutter_test: diff --git a/splitio_android/CHANGELOG.md b/splitio_android/CHANGELOG.md index e292dd9..09abe36 100644 --- a/splitio_android/CHANGELOG.md +++ b/splitio_android/CHANGELOG.md @@ -1,3 +1,12 @@ +# 0.1.3 (May 18, 2023) +* Updated Android SDK to `3.2.1` +* Added support for new configuration options: + * `impressionsMode` + * `syncEnabled` + * `logLevel` + * `userConsent` + * `encryptionEnabled` + # 0.1.3-rc.1 (May 18, 2023) # 0.1.2+1 (Dec 7, 2022) diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index d1282ec..33d267c 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_android description: The official Android implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_android -version: 0.1.3-rc.1 +version: 0.1.3 environment: sdk: ">=2.16.2 <4.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.1.0-rc.1 + splitio_platform_interface: ^1.1.0 dev_dependencies: flutter_test: diff --git a/splitio_android/test/splitio_android_test.dart b/splitio_android/test/splitio_android_test.dart index 308d056..a2def3f 100644 --- a/splitio_android/test/splitio_android_test.dart +++ b/splitio_android/test/splitio_android_test.dart @@ -23,7 +23,8 @@ void main() { } setUp(() { - _channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(_channel, (MethodCall methodCall) async { methodName = methodCall.method; methodArguments = methodCall.arguments; @@ -54,6 +55,7 @@ void main() { case 'clearAttributes': return true; } + return null; }); }); @@ -371,13 +373,13 @@ void main() { matchingKey: 'matching-key', bucketingKey: 'bucketing-key', sdkConfiguration: - SplitConfiguration(enableDebug: true, streamingEnabled: false)); + SplitConfiguration(logLevel: SplitLogLevel.error, streamingEnabled: false)); expect(methodName, 'init'); expect(methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', - 'sdkConfiguration': {'enableDebug': true, 'streamingEnabled': false}, + 'sdkConfiguration': {'logLevel': 'error', 'streamingEnabled': false}, }); }); }); diff --git a/splitio_ios/CHANGELOG.md b/splitio_ios/CHANGELOG.md index 301f867..0c8fcb4 100644 --- a/splitio_ios/CHANGELOG.md +++ b/splitio_ios/CHANGELOG.md @@ -1,3 +1,12 @@ +# 0.1.3 (May 18, 2023) +* Updated iOS SDK to `2.20.1` +* Added support for new configuration options: + * `impressionsMode` + * `syncEnabled` + * `logLevel` + * `userConsent` + * `encryptionEnabled` + # 0.1.3-rc.1 (May 18, 2023) # 0.1.2 (Aug 13, 2022) diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index 14110d7..4481a13 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_ios description: The official iOS implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_ios -version: 0.1.3-rc.1 +version: 0.1.3 environment: sdk: ">=2.16.2 <4.0.0" @@ -18,7 +18,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.1.0-rc.1 + splitio_platform_interface: ^1.1.0 dev_dependencies: flutter_test: diff --git a/splitio_ios/test/splitio_ios_test.dart b/splitio_ios/test/splitio_ios_test.dart index a68a007..d037cb3 100644 --- a/splitio_ios/test/splitio_ios_test.dart +++ b/splitio_ios/test/splitio_ios_test.dart @@ -23,7 +23,8 @@ void main() { } setUp(() { - _channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(_channel, (MethodCall methodCall) async { methodName = methodCall.method; methodArguments = methodCall.arguments; @@ -54,6 +55,7 @@ void main() { case 'clearAttributes': return true; } + return null; }); }); @@ -371,13 +373,13 @@ void main() { matchingKey: 'matching-key', bucketingKey: 'bucketing-key', sdkConfiguration: - SplitConfiguration(enableDebug: true, streamingEnabled: false)); + SplitConfiguration(logLevel: SplitLogLevel.warning, streamingEnabled: false)); expect(methodName, 'init'); expect(methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', - 'sdkConfiguration': {'enableDebug': true, 'streamingEnabled': false}, + 'sdkConfiguration': {'logLevel': 'warning', 'streamingEnabled': false}, }); }); }); diff --git a/splitio_platform_interface/CHANGELOG.md b/splitio_platform_interface/CHANGELOG.md index 74a02f8..ab885ce 100644 --- a/splitio_platform_interface/CHANGELOG.md +++ b/splitio_platform_interface/CHANGELOG.md @@ -1,3 +1,12 @@ +# 1.1.0 (May 18, 2023) +* Added support for new configuration options: + * `impressionsMode` + * `syncEnabled` + * `logLevel` + * `userConsent` + * `encryptionEnabled` +* Deprecated `enableDebug` configuration in favor of `logLevel`. + # 1.1.0-rc.1 (May 18, 2023) # 1.0.0 (Aug 13, 2022) diff --git a/splitio_platform_interface/lib/split_configuration.dart b/splitio_platform_interface/lib/split_configuration.dart index 9adb7d6..0ee7d2b 100644 --- a/splitio_platform_interface/lib/split_configuration.dart +++ b/splitio_platform_interface/lib/split_configuration.dart @@ -37,7 +37,7 @@ class SplitConfiguration { /// /// [syncEnabled] Controls the SDK continuous synchronization flags. When true, a running SDK processes rollout plan updates performed in the Split user interface (default). When false, it fetches all data upon init, which ensures a consistent experience during a user session and optimizes resources when these updates are not consumed by the app. /// - /// [UserConsent] User consent status used to control the tracking of events and impressions. Possible values are [UserConsent.granted], [UserConsent.declined], and [UserConsent.unknown]. + /// [userConsent] User consent status used to control the tracking of events and impressions. Possible values are [UserConsent.granted], [UserConsent.declined], and [UserConsent.unknown]. /// /// [encryptionEnabled] If set to true, the local database contents is encrypted. Defaults to false. /// diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml index e20f38b..1947654 100644 --- a/splitio_platform_interface/pubspec.yaml +++ b/splitio_platform_interface/pubspec.yaml @@ -2,7 +2,7 @@ name: splitio_platform_interface description: A common platform interface for the splitio plugin. # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.1.0-rc.1 +version: 1.1.0 repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_platform_interface environment: diff --git a/splitio_platform_interface/test/method_channel_platform_test.dart b/splitio_platform_interface/test/method_channel_platform_test.dart index 9f5ee07..72cb4ed 100644 --- a/splitio_platform_interface/test/method_channel_platform_test.dart +++ b/splitio_platform_interface/test/method_channel_platform_test.dart @@ -23,7 +23,8 @@ void main() { } setUp(() { - _channel.setMockMethodCallHandler((MethodCall methodCall) async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(_channel, (MethodCall methodCall) async { methodName = methodCall.method; methodArguments = methodCall.arguments; @@ -54,6 +55,7 @@ void main() { case 'clearAttributes': return true; } + return null; }); }); @@ -371,13 +373,13 @@ void main() { matchingKey: 'matching-key', bucketingKey: 'bucketing-key', sdkConfiguration: - SplitConfiguration(enableDebug: true, streamingEnabled: false)); + SplitConfiguration(logLevel: SplitLogLevel.debug, streamingEnabled: false)); expect(methodName, 'init'); expect(methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', - 'sdkConfiguration': {'enableDebug': true, 'streamingEnabled': false}, + 'sdkConfiguration': {'logLevel': 'debug', 'streamingEnabled': false}, }); }); }); diff --git a/splitio_platform_interface/test/split_event_method_call_handler_test.dart b/splitio_platform_interface/test/split_event_method_call_handler_test.dart index 2388fe3..7d2f760 100644 --- a/splitio_platform_interface/test/split_event_method_call_handler_test.dart +++ b/splitio_platform_interface/test/split_event_method_call_handler_test.dart @@ -17,9 +17,11 @@ void main() { } setUp(() { - _channel.setMockMethodCallHandler((MethodCall methodCall) { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(_channel, (MethodCall methodCall) { splitEventMethodCallHandler.handle( methodCall.method, methodCall.arguments); + return null; }); }); diff --git a/splitio_platform_interface/test/splitio_configuration_test.dart b/splitio_platform_interface/test/splitio_configuration_test.dart index 72c563f..5348041 100644 --- a/splitio_platform_interface/test/splitio_configuration_test.dart +++ b/splitio_platform_interface/test/splitio_configuration_test.dart @@ -5,7 +5,6 @@ import 'package:splitio_platform_interface/split_sync_config.dart'; void main() { test('valuesAreMappedCorrectly', () async { final SplitConfiguration config = SplitConfiguration( - enableDebug: true, eventFlushInterval: 2000, eventsPerPush: 300, eventsQueueSize: 250, From 5627622ecdeb1d362da26d907427baba4b1ead4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Tue, 23 May 2023 10:19:16 -0300 Subject: [PATCH 18/25] User consent factory methods (#76) --- splitio/lib/splitio.dart | 8 +++++ splitio/pubspec.yaml | 12 +++++-- splitio/test/splitio_platform_stub.dart | 15 ++++++++ splitio/test/splitio_test.dart | 22 ++++++++++++ .../main/java/io/split/splitio/Constants.java | 2 ++ .../split/splitio/SplitMethodParserImpl.java | 9 +++++ .../java/io/split/splitio/SplitWrapper.java | 4 +++ .../io/split/splitio/SplitWrapperImpl.java | 18 ++++++++++ .../splitio/SplitMethodParserImplTest.java | 29 +++++++++++++++ .../split/splitio/SplitWrapperImplTest.java | 16 +++++++++ splitio_android/pubspec.yaml | 4 ++- .../test/splitio_android_test.dart | 23 ++++++++++++ .../SplitTests/SplitMethodParserTests.swift | 35 +++++++++++++++++++ .../example/ios/SplitTests/SplitTests.swift | 20 +++++++++++ splitio_ios/ios/Classes/Constants.swift | 2 ++ .../ios/Classes/SplitMethodParser.swift | 6 ++++ splitio_ios/ios/Classes/SplitWrapper.swift | 26 ++++++++++++++ splitio_ios/pubspec.yaml | 4 ++- .../lib/method_channel_platform.dart | 18 ++++++++++ .../lib/splitio_platform_interface.dart | 8 +++++ .../test/method_channel_platform_test.dart | 29 +++++++++++++-- 21 files changed, 303 insertions(+), 7 deletions(-) diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 3db1151..f20b066 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -121,6 +121,14 @@ class Splitio { return _platform.split(splitName: splitName); } + Future getUserConsent() async { + return _platform.getUserConsent(); + } + + Future setUserConsent(bool enabled) async { + return _platform.setUserConsent(enabled); + } + Future _init() { return _platform.init( apiKey: _sdkKey, diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 650fcbc..1437b31 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -19,9 +19,15 @@ flutter: dependencies: flutter: sdk: flutter - splitio_android: ^0.1.3 - splitio_ios: ^0.1.3 - splitio_platform_interface: ^1.1.0 +# splitio_android: ^0.1.3 + splitio_android: + path: ../splitio_android +# splitio_ios: ^0.1.3 + splitio_ios: + path: ../splitio_ios +# splitio_platform_interface: ^1.1.0 + splitio_platform_interface: + path: ../splitio_platform_interface dev_dependencies: flutter_test: diff --git a/splitio/test/splitio_platform_stub.dart b/splitio/test/splitio_platform_stub.dart index 29f1526..369cc83 100644 --- a/splitio/test/splitio_platform_stub.dart +++ b/splitio/test/splitio_platform_stub.dart @@ -339,4 +339,19 @@ class SplitioPlatformStub return Future.value(true); } + + @override + Future getUserConsent() { + methodName = 'getUserConsent'; + + return Future.value(UserConsent.granted); + } + + @override + Future setUserConsent(bool enabled) { + methodName = 'setUserConsent'; + methodArguments['value'] = enabled; + + return Future.value(null); + } } diff --git a/splitio/test/splitio_test.dart b/splitio/test/splitio_test.dart index ac227f2..87ad6e4 100644 --- a/splitio/test/splitio_test.dart +++ b/splitio/test/splitio_test.dart @@ -101,4 +101,26 @@ void main() { expect(_platform.methodArguments, {'splitName': 'my_split'}); }); }); + + group('userConsent', () { + test('get user consent', () async { + var splitio = Splitio('api-key', 'matching-key'); + splitio.getUserConsent(); + + expect(_platform.methodName, 'getUserConsent'); + }); + + test('set user consent', () async { + var splitio = Splitio('api-key', 'matching-key'); + splitio.setUserConsent(true); + + expect(_platform.methodName, 'setUserConsent'); + expect(_platform.methodArguments, { + 'matchingKey': 'matching-key', + 'apiKey': 'api-key', + 'sdkConfiguration': {}, + 'value': true, + }); + }); + }); } diff --git a/splitio_android/android/src/main/java/io/split/splitio/Constants.java b/splitio_android/android/src/main/java/io/split/splitio/Constants.java index 461a553..35ceb63 100644 --- a/splitio_android/android/src/main/java/io/split/splitio/Constants.java +++ b/splitio_android/android/src/main/java/io/split/splitio/Constants.java @@ -26,6 +26,8 @@ static class Method { static final String SPLIT_NAMES = "splitNames"; static final String SPLIT = "split"; static final String IMPRESSION_LOG = "impressionLog"; + static final String GET_USER_CONSENT = "getUserConsent"; + static final String SET_USER_CONSENT = "setUserConsent"; } static class Argument { diff --git a/splitio_android/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java b/splitio_android/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java index d3c7f1a..fb3ad5d 100644 --- a/splitio_android/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java +++ b/splitio_android/android/src/main/java/io/split/splitio/SplitMethodParserImpl.java @@ -27,10 +27,12 @@ import static io.split.splitio.Constants.Method.GET_TREATMENTS; import static io.split.splitio.Constants.Method.GET_TREATMENTS_WITH_CONFIG; import static io.split.splitio.Constants.Method.GET_TREATMENT_WITH_CONFIG; +import static io.split.splitio.Constants.Method.GET_USER_CONSENT; import static io.split.splitio.Constants.Method.INIT; import static io.split.splitio.Constants.Method.REMOVE_ATTRIBUTE; import static io.split.splitio.Constants.Method.SET_ATTRIBUTE; import static io.split.splitio.Constants.Method.SET_ATTRIBUTES; +import static io.split.splitio.Constants.Method.SET_USER_CONSENT; import static io.split.splitio.Constants.Method.SPLIT; import static io.split.splitio.Constants.Method.SPLITS; import static io.split.splitio.Constants.Method.SPLIT_NAMES; @@ -195,6 +197,13 @@ public void onMethodCall(String methodName, Object arguments, @NonNull MethodCha case SPLIT: result.success(getSplitViewAsMap(mSplitWrapper.split(mArgumentParser.getStringArgument(SPLIT_NAME, arguments)))); break; + case GET_USER_CONSENT: + result.success(mSplitWrapper.getUserConsent()); + break; + case SET_USER_CONSENT: + mSplitWrapper.setUserConsent(mArgumentParser.getBooleanArgument(VALUE, arguments)); + result.success(null); + break; default: result.notImplemented(); break; diff --git a/splitio_android/android/src/main/java/io/split/splitio/SplitWrapper.java b/splitio_android/android/src/main/java/io/split/splitio/SplitWrapper.java index f1b9d4b..54faa28 100644 --- a/splitio_android/android/src/main/java/io/split/splitio/SplitWrapper.java +++ b/splitio_android/android/src/main/java/io/split/splitio/SplitWrapper.java @@ -24,4 +24,8 @@ interface SplitWrapper extends EvaluationWrapper, AttributesWrapper { @Nullable SplitView split(String splitName); + + String getUserConsent(); + + void setUserConsent(boolean enabled); } diff --git a/splitio_android/android/src/main/java/io/split/splitio/SplitWrapperImpl.java b/splitio_android/android/src/main/java/io/split/splitio/SplitWrapperImpl.java index ec90e0b..b445908 100644 --- a/splitio_android/android/src/main/java/io/split/splitio/SplitWrapperImpl.java +++ b/splitio_android/android/src/main/java/io/split/splitio/SplitWrapperImpl.java @@ -15,6 +15,7 @@ import io.split.android.client.SplitResult; import io.split.android.client.api.Key; import io.split.android.client.api.SplitView; +import io.split.android.client.shared.UserConsent; import io.split.android.client.utils.ConcurrentSet; import io.split.android.grammar.Treatments; @@ -222,4 +223,21 @@ public List splits() { public SplitView split(String splitName) { return mSplitFactory.manager().split(splitName); } + + @Override + public String getUserConsent() { + UserConsent userConsent = mSplitFactory.getUserConsent(); + if (userConsent == UserConsent.GRANTED) { + return "granted"; + } else if (userConsent == UserConsent.DECLINED) { + return "declined"; + } else { + return "unknown"; + } + } + + @Override + public void setUserConsent(boolean enabled) { + mSplitFactory.setUserConsent(enabled); + } } diff --git a/splitio_android/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java b/splitio_android/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java index 3245c38..538ea0e 100644 --- a/splitio_android/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java +++ b/splitio_android/android/src/test/java/io/split/splitio/SplitMethodParserImplTest.java @@ -475,4 +475,33 @@ public void initialization() { eq("bucketing-key"), argThat(splitClientConfig -> splitClientConfig.impressionListener() != null && !splitClientConfig.streamingEnabled())); } + + @Test + public void getUserConsent() { + when(mSplitWrapper.getUserConsent()).thenReturn("granted"); + mMethodParser.onMethodCall("getUserConsent", Collections.emptyMap(), mResult); + + verify(mResult).success("granted"); + verify(mSplitWrapper).getUserConsent(); + } + + @Test + public void setUserConsentEnabled() { + when(mArgumentParser.getBooleanArgument("value", Collections.singletonMap("value", true))).thenReturn(true); + when(mArgumentParser.getBooleanArgument("value", Collections.singletonMap("value", false))).thenReturn(false); + mMethodParser.onMethodCall("setUserConsent", Collections.singletonMap("value", true), mResult); + + verify(mResult).success(null); + verify(mSplitWrapper).setUserConsent(true); + } + + @Test + public void setUserConsentDisabled() { + when(mArgumentParser.getBooleanArgument("value", Collections.singletonMap("value", true))).thenReturn(true); + when(mArgumentParser.getBooleanArgument("value", Collections.singletonMap("value", false))).thenReturn(false); + mMethodParser.onMethodCall("setUserConsent", Collections.singletonMap("value", false), mResult); + + verify(mResult).success(null); + verify(mSplitWrapper).setUserConsent(false); + } } diff --git a/splitio_android/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java b/splitio_android/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java index ceb5572..200f785 100644 --- a/splitio_android/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java +++ b/splitio_android/android/src/test/java/io/split/splitio/SplitWrapperImplTest.java @@ -22,6 +22,7 @@ import io.split.android.client.SplitFactory; import io.split.android.client.SplitManager; import io.split.android.client.api.Key; +import io.split.android.client.shared.UserConsent; public class SplitWrapperImplTest { @@ -277,4 +278,19 @@ public void testSplit() { verify(managerMock).split("my_split"); } + + @Test + public void testGetUserConsent() { + when(mSplitFactory.getUserConsent()).thenReturn(UserConsent.DECLINED); + String userConsent = mSplitWrapper.getUserConsent(); + + verify(mSplitFactory).getUserConsent(); + assertEquals("declined", userConsent); + } + + @Test + public void testSetUserConsent() { + mSplitWrapper.setUserConsent(true); + verify(mSplitFactory).setUserConsent(true); + } } diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index 33d267c..33684f1 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -19,7 +19,9 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.1.0 +# splitio_platform_interface: ^1.1.0 + splitio_platform_interface: + path: ../splitio_platform_interface dev_dependencies: flutter_test: diff --git a/splitio_android/test/splitio_android_test.dart b/splitio_android/test/splitio_android_test.dart index a2def3f..34d65a4 100644 --- a/splitio_android/test/splitio_android_test.dart +++ b/splitio_android/test/splitio_android_test.dart @@ -514,4 +514,27 @@ void main() { 'attributes': {} }); }); + + group('userConsent', () { + test('get user consent', () async { + UserConsent userConsent = await _platform.getUserConsent(); + + expect(methodName, 'getUserConsent'); + expect(userConsent, UserConsent.declined); + }); + + test('set user consent enabled', () { + _platform.setUserConsent(true); + + expect(methodName, 'setUserConsent'); + expect(methodArguments, {'enabled': true}); + }); + + test('set user consent disabled', () { + _platform.setUserConsent(false); + + expect(methodName, 'setUserConsent'); + expect(methodArguments, {'enabled': false}); + }); + }); } diff --git a/splitio_ios/example/ios/SplitTests/SplitMethodParserTests.swift b/splitio_ios/example/ios/SplitTests/SplitMethodParserTests.swift index 12fa488..e2d96a2 100644 --- a/splitio_ios/example/ios/SplitTests/SplitMethodParserTests.swift +++ b/splitio_ios/example/ios/SplitTests/SplitMethodParserTests.swift @@ -297,6 +297,28 @@ class SplitMethodParserTests: XCTestCase { XCTAssert(providerHelper.splitClientConfigValue?.impressionListener != nil) XCTAssert(providerHelper.splitClientConfigValue?.streamingEnabled == false) } + + func testGetUserConsent() { + methodParser?.onMethodCall(methodName: "getUserConsent", arguments: [], result: { (_: Any?) in }) + + guard let wrapper = splitWrapper as? SplitWrapperStub else { + XCTFail() + return + } + + XCTAssert(wrapper.userConsent == "unknown") + } + + func testSetUserConsent() { + methodParser?.onMethodCall(methodName: "setUserConsent", arguments: [], result: { (_: Any?) in }) + + guard let wrapper = splitWrapper as? SplitWrapperStub else { + XCTFail() + return + } + + XCTAssert(wrapper.userConsent == "declined") + } } class SplitWrapperStub: SplitWrapper { @@ -316,6 +338,7 @@ class SplitWrapperStub: SplitWrapper { var attributeNameValue: String = "" var splitsCalled = false var splitNamesCalled = false + var userConsent = "unknown" func getClient(matchingKey: String, bucketingKey: String?) -> SplitClient? { matchingKeyValue = matchingKey @@ -440,6 +463,18 @@ class SplitWrapperStub: SplitWrapper { splitNameValue = splitName return nil } + + func getUserConsent() -> String { + return userConsent + } + + func setUserConsent(enabled: Bool) { + if (enabled) { + userConsent = "granted" + } else { + userConsent = "declined" + } + } } class SplitProviderHelperStub: SplitProviderHelper { diff --git a/splitio_ios/example/ios/SplitTests/SplitTests.swift b/splitio_ios/example/ios/SplitTests/SplitTests.swift index 7853301..e4c02ad 100644 --- a/splitio_ios/example/ios/SplitTests/SplitTests.swift +++ b/splitio_ios/example/ios/SplitTests/SplitTests.swift @@ -181,6 +181,26 @@ class SplitTests: XCTestCase { let split = splitWrapper.split(splitName: "my-split") XCTAssert(manager.splitNameValue == "my-split") } + + func testGetUserConsent() { + let manager = SplitManagerStub() + let factoryProvider = SplitFactoryProviderStub(manager: manager) + splitWrapper = DefaultSplitWrapper(splitFactoryProvider: factoryProvider) + let userConsent = splitWrapper.getUserConsent() + XCTAssert(userConsent == "unknown") + } + + func testSetUserConsent() { + let manager = SplitManagerStub() + let factoryProvider = SplitFactoryProviderStub(manager: manager) + splitWrapper = DefaultSplitWrapper(splitFactoryProvider: factoryProvider) + splitWrapper.setUserConsent(enabled: true) + let grantedUserConsent = splitWrapper.getUserConsent() + splitWrapper.setUserConsent(enabled: false) + let declinedUserConsent = splitWrapper.getUserConsent() + XCTAssert(grantedUserConsent == "granted") + XCTAssert(declinedUserConsent == "declined") + } } class SplitFactoryProviderStub: SplitFactoryProvider { diff --git a/splitio_ios/ios/Classes/Constants.swift b/splitio_ios/ios/Classes/Constants.swift index c5e6cce..bea9d65 100644 --- a/splitio_ios/ios/Classes/Constants.swift +++ b/splitio_ios/ios/Classes/Constants.swift @@ -24,6 +24,8 @@ enum Method: String { case splits = "splits" case split = "split" case impressionLog = "impressionLog" + case getUserConsent = "getUserConsent" + case setUserConsent = "setUserConsent" } enum Argument: String { diff --git a/splitio_ios/ios/Classes/SplitMethodParser.swift b/splitio_ios/ios/Classes/SplitMethodParser.swift index d4c6aba..6c8a216 100644 --- a/splitio_ios/ios/Classes/SplitMethodParser.swift +++ b/splitio_ios/ios/Classes/SplitMethodParser.swift @@ -133,6 +133,12 @@ class DefaultSplitMethodParser: SplitMethodParser { case .split: result(SplitView.asMap(splitView: splitWrapper?.split(splitName: argumentParser.getStringArgument(argumentName: .splitName, arguments: arguments) ?? ""))) break + case .getUserConsent: + result(splitWrapper?.getUserConsent()) + break + case .setUserConsent: + splitWrapper?.setUserConsent(enabled: argumentParser.getBooleanArgument(argumentName: .value, arguments: arguments)) + result(nil) default: result(FlutterMethodNotImplemented) break diff --git a/splitio_ios/ios/Classes/SplitWrapper.swift b/splitio_ios/ios/Classes/SplitWrapper.swift index f9b65d9..69e764f 100644 --- a/splitio_ios/ios/Classes/SplitWrapper.swift +++ b/splitio_ios/ios/Classes/SplitWrapper.swift @@ -16,6 +16,10 @@ protocol SplitWrapper: EvaluationWrapper, AttributesWrapper { func splits() -> [SplitView] func split(splitName: String) -> SplitView? + + func getUserConsent() -> String + + func setUserConsent(enabled: Bool) } protocol EvaluationWrapper { @@ -227,4 +231,26 @@ class DefaultSplitWrapper: SplitWrapper { return nil } } + + func getUserConsent() -> String { + if let splitFactory = splitFactory { + let userConsent: UserConsent = splitFactory.userConsent + + if (userConsent == .granted) { + return "granted" + } else if (userConsent == .declined) { + return "declined" + } else { + return "unknown" + } + } else { + return "unknown" + } + } + + func setUserConsent(enabled: Bool) { + if let splitFactory = splitFactory { + splitFactory.setUserConsent(enabled: enabled) + } + } } diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index 4481a13..a2dd2b9 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -18,7 +18,9 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.1.0 +# splitio_platform_interface: ^1.1.0 + splitio_platform_interface: + path: ../splitio_platform_interface dev_dependencies: flutter_test: diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart index b499305..7199c5b 100644 --- a/splitio_platform_interface/lib/method_channel_platform.dart +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -324,6 +324,24 @@ class MethodChannelPlatform extends SplitioPlatform { return _impressionsMethodCallHandler.stream(); } + @override + Future getUserConsent() async { + String userConsent = + (await methodChannel.invokeMethod('getUserConsent')) as String; + if (userConsent == 'granted') { + return UserConsent.granted; + } else if (userConsent == 'declined') { + return UserConsent.declined; + } else { + return UserConsent.unknown; + } + } + + @override + Future setUserConsent(bool enabled) async { + await methodChannel.invokeMethod('setUserConsent', {'value': enabled}); + } + String _buildMapKey(String matchingKey, String? bucketingKey) { return '${matchingKey}_$bucketingKey'; } diff --git a/splitio_platform_interface/lib/splitio_platform_interface.dart b/splitio_platform_interface/lib/splitio_platform_interface.dart index 729e001..3e71adb 100644 --- a/splitio_platform_interface/lib/splitio_platform_interface.dart +++ b/splitio_platform_interface/lib/splitio_platform_interface.dart @@ -47,6 +47,14 @@ abstract class _FactoryPlatform { Stream impressionsStream() { throw UnimplementedError(); } + + Future getUserConsent() { + throw UnimplementedError(); + } + + Future setUserConsent(bool enabled) { + throw UnimplementedError(); + } } abstract class _ClientPlatform { diff --git a/splitio_platform_interface/test/method_channel_platform_test.dart b/splitio_platform_interface/test/method_channel_platform_test.dart index 72cb4ed..21ed1e2 100644 --- a/splitio_platform_interface/test/method_channel_platform_test.dart +++ b/splitio_platform_interface/test/method_channel_platform_test.dart @@ -54,6 +54,8 @@ void main() { case 'removeAttribute': case 'clearAttributes': return true; + case 'getUserConsent': + return 'declined'; } return null; }); @@ -372,8 +374,8 @@ void main() { apiKey: 'api-key', matchingKey: 'matching-key', bucketingKey: 'bucketing-key', - sdkConfiguration: - SplitConfiguration(logLevel: SplitLogLevel.debug, streamingEnabled: false)); + sdkConfiguration: SplitConfiguration( + logLevel: SplitLogLevel.debug, streamingEnabled: false)); expect(methodName, 'init'); expect(methodArguments, { 'apiKey': 'api-key', @@ -514,4 +516,27 @@ void main() { 'attributes': {} }); }); + + group('userConsent', () { + test('get user consent', () async { + UserConsent userConsent = await _platform.getUserConsent(); + + expect(methodName, 'getUserConsent'); + expect(userConsent, UserConsent.declined); + }); + + test('set user consent enabled', () { + _platform.setUserConsent(true); + + expect(methodName, 'setUserConsent'); + expect(methodArguments, {'value': true}); + }); + + test('set user consent disabled', () { + _platform.setUserConsent(false); + + expect(methodName, 'setUserConsent'); + expect(methodArguments, {'value': false}); + }); + }); } From 3ce5447f5ae7e8620bfd81f390082ddc0d7431cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Tue, 23 May 2023 14:26:44 -0300 Subject: [PATCH 19/25] 0.1.4 rc.1 (#77) --- splitio/CHANGELOG.md | 2 ++ splitio/pubspec.yaml | 14 ++++---------- splitio_android/CHANGELOG.md | 2 ++ splitio_android/pubspec.yaml | 6 ++---- splitio_ios/CHANGELOG.md | 2 ++ splitio_ios/pubspec.yaml | 6 ++---- splitio_platform_interface/CHANGELOG.md | 2 ++ splitio_platform_interface/pubspec.yaml | 2 +- 8 files changed, 17 insertions(+), 19 deletions(-) diff --git a/splitio/CHANGELOG.md b/splitio/CHANGELOG.md index 84e0b1f..a93fa63 100644 --- a/splitio/CHANGELOG.md +++ b/splitio/CHANGELOG.md @@ -1,3 +1,5 @@ +# 0.1.4-rc.1 (May 23, 2023) + # 0.1.3 (May 18, 2023) * Updated iOS SDK to `2.20.1` & Android SDK to `3.2.1` diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 1437b31..4989b76 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,6 +1,6 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. -version: 0.1.3 +version: 0.1.4-rc.1 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ @@ -19,15 +19,9 @@ flutter: dependencies: flutter: sdk: flutter -# splitio_android: ^0.1.3 - splitio_android: - path: ../splitio_android -# splitio_ios: ^0.1.3 - splitio_ios: - path: ../splitio_ios -# splitio_platform_interface: ^1.1.0 - splitio_platform_interface: - path: ../splitio_platform_interface + splitio_android: ^0.1.4-rc.1 + splitio_ios: ^0.1.4-rc.1 + splitio_platform_interface: ^1.2.0-rc.1 dev_dependencies: flutter_test: diff --git a/splitio_android/CHANGELOG.md b/splitio_android/CHANGELOG.md index 09abe36..a83e4f1 100644 --- a/splitio_android/CHANGELOG.md +++ b/splitio_android/CHANGELOG.md @@ -1,3 +1,5 @@ +# 0.1.4-rc.1 (May 23, 2023) + # 0.1.3 (May 18, 2023) * Updated Android SDK to `3.2.1` * Added support for new configuration options: diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index 33684f1..ef302ad 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_android description: The official Android implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_android -version: 0.1.3 +version: 0.1.4-rc.1 environment: sdk: ">=2.16.2 <4.0.0" @@ -19,9 +19,7 @@ flutter: dependencies: flutter: sdk: flutter -# splitio_platform_interface: ^1.1.0 - splitio_platform_interface: - path: ../splitio_platform_interface + splitio_platform_interface: ^1.2.0-rc.1 dev_dependencies: flutter_test: diff --git a/splitio_ios/CHANGELOG.md b/splitio_ios/CHANGELOG.md index 0c8fcb4..17d77a2 100644 --- a/splitio_ios/CHANGELOG.md +++ b/splitio_ios/CHANGELOG.md @@ -1,3 +1,5 @@ +# 0.1.4-rc.1 (May 23, 2023) + # 0.1.3 (May 18, 2023) * Updated iOS SDK to `2.20.1` * Added support for new configuration options: diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index a2dd2b9..207570d 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_ios description: The official iOS implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_ios -version: 0.1.3 +version: 0.1.4-rc.1 environment: sdk: ">=2.16.2 <4.0.0" @@ -18,9 +18,7 @@ flutter: dependencies: flutter: sdk: flutter -# splitio_platform_interface: ^1.1.0 - splitio_platform_interface: - path: ../splitio_platform_interface + splitio_platform_interface: ^1.2.0-rc.1 dev_dependencies: flutter_test: diff --git a/splitio_platform_interface/CHANGELOG.md b/splitio_platform_interface/CHANGELOG.md index ab885ce..41761d8 100644 --- a/splitio_platform_interface/CHANGELOG.md +++ b/splitio_platform_interface/CHANGELOG.md @@ -1,3 +1,5 @@ +# 1.2.0-rc.1 (May 23, 2023) + # 1.1.0 (May 18, 2023) * Added support for new configuration options: * `impressionsMode` diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml index 1947654..ce92b0e 100644 --- a/splitio_platform_interface/pubspec.yaml +++ b/splitio_platform_interface/pubspec.yaml @@ -2,7 +2,7 @@ name: splitio_platform_interface description: A common platform interface for the splitio plugin. # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.1.0 +version: 1.2.0-rc.1 repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_platform_interface environment: From c336b523e312f32be07c7a3dabbb5a4eb778840a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Fri, 9 Jun 2023 15:32:47 -0300 Subject: [PATCH 20/25] 0.1.4 (#78) --- splitio/CHANGELOG.md | 5 +++++ splitio/pubspec.yaml | 8 ++++---- splitio_android/CHANGELOG.md | 5 +++++ splitio_android/pubspec.yaml | 4 ++-- splitio_ios/CHANGELOG.md | 5 +++++ splitio_ios/pubspec.yaml | 4 ++-- splitio_platform_interface/CHANGELOG.md | 5 +++++ splitio_platform_interface/pubspec.yaml | 2 +- 8 files changed, 29 insertions(+), 9 deletions(-) diff --git a/splitio/CHANGELOG.md b/splitio/CHANGELOG.md index a93fa63..e1f5189 100644 --- a/splitio/CHANGELOG.md +++ b/splitio/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.1.4 (May 23, 2023) + +* Added `setUserConsent` method. +* Added `getUserConsent` method. + # 0.1.4-rc.1 (May 23, 2023) # 0.1.3 (May 18, 2023) diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 4989b76..da1effe 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,6 +1,6 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. -version: 0.1.4-rc.1 +version: 0.1.4 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ @@ -19,9 +19,9 @@ flutter: dependencies: flutter: sdk: flutter - splitio_android: ^0.1.4-rc.1 - splitio_ios: ^0.1.4-rc.1 - splitio_platform_interface: ^1.2.0-rc.1 + splitio_android: ^0.1.4 + splitio_ios: ^0.1.4 + splitio_platform_interface: ^1.2.0 dev_dependencies: flutter_test: diff --git a/splitio_android/CHANGELOG.md b/splitio_android/CHANGELOG.md index a83e4f1..0fe1e38 100644 --- a/splitio_android/CHANGELOG.md +++ b/splitio_android/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.1.4 (May 23, 2023) + +* Added `setUserConsent` method. +* Added `getUserConsent` method. + # 0.1.4-rc.1 (May 23, 2023) # 0.1.3 (May 18, 2023) diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index ef302ad..a26b6df 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_android description: The official Android implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_android -version: 0.1.4-rc.1 +version: 0.1.4 environment: sdk: ">=2.16.2 <4.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.2.0-rc.1 + splitio_platform_interface: ^1.2.0 dev_dependencies: flutter_test: diff --git a/splitio_ios/CHANGELOG.md b/splitio_ios/CHANGELOG.md index 17d77a2..28f9030 100644 --- a/splitio_ios/CHANGELOG.md +++ b/splitio_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.1.4 (May 23, 2023) + +* Added `setUserConsent` method. +* Added `getUserConsent` method. + # 0.1.4-rc.1 (May 23, 2023) # 0.1.3 (May 18, 2023) diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index 207570d..0203f9d 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_ios description: The official iOS implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_ios -version: 0.1.4-rc.1 +version: 0.1.4 environment: sdk: ">=2.16.2 <4.0.0" @@ -18,7 +18,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.2.0-rc.1 + splitio_platform_interface: ^1.2.0 dev_dependencies: flutter_test: diff --git a/splitio_platform_interface/CHANGELOG.md b/splitio_platform_interface/CHANGELOG.md index 41761d8..3c2e709 100644 --- a/splitio_platform_interface/CHANGELOG.md +++ b/splitio_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.2.0 (May 23, 2023) + +* Added `setUserConsent` method. +* Added `getUserConsent` method. + # 1.2.0-rc.1 (May 23, 2023) # 1.1.0 (May 18, 2023) diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml index ce92b0e..710ce64 100644 --- a/splitio_platform_interface/pubspec.yaml +++ b/splitio_platform_interface/pubspec.yaml @@ -2,7 +2,7 @@ name: splitio_platform_interface description: A common platform interface for the splitio plugin. # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.2.0-rc.1 +version: 1.2.0 repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_platform_interface environment: From bd9137117c6769403294f61e2accb886af7aac33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Wed, 19 Jul 2023 16:42:11 -0300 Subject: [PATCH 21/25] 0.1.5 rc.1 (#80) --- splitio/CHANGELOG.md | 2 + splitio/example/ios/Podfile.lock | 10 ++-- splitio/pubspec.yaml | 6 +-- splitio_android/CHANGELOG.md | 2 + splitio_android/android/build.gradle | 2 +- .../android/split-proguard-rules.pro | 49 +++++++++++++++++-- splitio_android/pubspec.yaml | 2 +- splitio_ios/CHANGELOG.md | 2 + splitio_ios/example/ios/Podfile | 2 +- splitio_ios/example/ios/Podfile.lock | 14 +++--- splitio_ios/ios/splitio_ios.podspec | 4 +- splitio_ios/pubspec.yaml | 2 +- 12 files changed, 71 insertions(+), 26 deletions(-) diff --git a/splitio/CHANGELOG.md b/splitio/CHANGELOG.md index e1f5189..e2307a3 100644 --- a/splitio/CHANGELOG.md +++ b/splitio/CHANGELOG.md @@ -1,3 +1,5 @@ +# 0.1.5-rc.1 (Jul 19, 2023) + # 0.1.4 (May 23, 2023) * Added `setUserConsent` method. diff --git a/splitio/example/ios/Podfile.lock b/splitio/example/ios/Podfile.lock index f9b0e95..51ab010 100644 --- a/splitio/example/ios/Podfile.lock +++ b/splitio/example/ios/Podfile.lock @@ -1,9 +1,9 @@ PODS: - Flutter (1.0.0) - - Split (2.20.1) - - splitio_ios (0.2.0): + - Split (2.21.0) + - splitio_ios (0.3.0): - Flutter - - Split (~> 2.20.1) + - Split (~> 2.21.0) DEPENDENCIES: - Flutter (from `Flutter`) @@ -21,8 +21,8 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - Split: afbdfda76fe2c90bd449564eed2d83de00c0ff69 - splitio_ios: e6d3dfe108ba894129a6b249d8914e3c44f0ffc4 + Split: e4324a8fe3fcb19c36a9e4fd84a893fc6d4bbeb0 + splitio_ios: 8293cb4e46661f9403f92a0d143c5bf6ad5053d4 PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index da1effe..6fd35e1 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,6 +1,6 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. -version: 0.1.4 +version: 0.1.5-rc.1 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ @@ -19,8 +19,8 @@ flutter: dependencies: flutter: sdk: flutter - splitio_android: ^0.1.4 - splitio_ios: ^0.1.4 + splitio_android: ^0.1.5-rc.1 + splitio_ios: ^0.1.5-rc.1 splitio_platform_interface: ^1.2.0 dev_dependencies: diff --git a/splitio_android/CHANGELOG.md b/splitio_android/CHANGELOG.md index 0fe1e38..9bc1add 100644 --- a/splitio_android/CHANGELOG.md +++ b/splitio_android/CHANGELOG.md @@ -1,3 +1,5 @@ +# 0.1.5-rc.1 (Jul 19, 2023) + # 0.1.4 (May 23, 2023) * Added `setUserConsent` method. diff --git a/splitio_android/android/build.gradle b/splitio_android/android/build.gradle index 20feee8..7a017ac 100644 --- a/splitio_android/android/build.gradle +++ b/splitio_android/android/build.gradle @@ -36,7 +36,7 @@ android { } dependencies { - implementation 'io.split.client:android-client:3.2.1' + implementation 'io.split.client:android-client:3.3.0' implementation 'com.squareup.okhttp3:okhttp:3.12.13' testImplementation 'junit:junit:4.13.2' diff --git a/splitio_android/android/split-proguard-rules.pro b/splitio_android/android/split-proguard-rules.pro index 95ce5f5..8dc624d 100644 --- a/splitio_android/android/split-proguard-rules.pro +++ b/splitio_android/android/split-proguard-rules.pro @@ -1,15 +1,54 @@ # Please include these rules in your project # in order to make Split code work properly when # using proguard --keep class io.split.android.client.dtos.* { *; } +-dontwarn android.test.** +-dontwarn org.junit.** +-dontwarn com.google.common.** +-keep class io.split.android.client.utils.deserializer.EventDeserializer { *; } +-keep class io.split.android.client.dtos.** { *; } -keep class io.split.android.client.storage.db.** { *; } --keep public class io.split.android.client.service.sseclient.SseJwtToken.** { *; } --keep public class io.split.android.client.service.sseclient.SseAuthToken.** { *; } --keep public class io.split.android.client.service.sseclient.SseAuthenticationResponse.** { *; } +-keep class io.split.android.client.service.sseclient.EventStreamParser { *; } +-keep class io.split.android.client.service.sseclient.SseAuthToken { *; } +-keep class io.split.android.client.service.sseclient.SseJwtToken { *; } +-keep class io.split.android.client.service.sseclient.SseAuthenticationResponse { *; } -keep class io.split.android.client.service.sseclient.notifications.** { *; } -keepattributes Signature -keep class com.google.gson.reflect.TypeToken { *; } -keep class * extends com.google.gson.reflect.TypeToken +-dontwarn java.beans.BeanInfo +-dontwarn java.beans.FeatureDescriptor +-dontwarn java.beans.IntrospectionException +-dontwarn java.beans.Introspector +-dontwarn java.beans.PropertyDescriptor + +##---------------Begin: proguard configuration for Gson ---------- +# Gson uses generic type information stored in a class file when working with fields. Proguard +# removes such information by default, so configure it to keep all of it. +-keepattributes Signature + +# For using GSON @Expose annotation +-keepattributes *Annotation* + +# Gson specific classes +-dontwarn sun.misc.** +#-keep class com.google.gson.stream.** { *; } + +# Application classes that will be serialized/deserialized over Gson +-keep class com.google.gson.examples.android.model.** { ; } + +# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory, +# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) +-keep class * extends com.google.gson.TypeAdapter +-keep class * implements com.google.gson.TypeAdapterFactory +-keep class * implements com.google.gson.JsonSerializer +-keep class * implements com.google.gson.JsonDeserializer + +# Prevent R8 from leaving Data object members always null -keepclassmembers,allowobfuscation class * { - @com.google.gson.annotations.SerializedName ; + @com.google.gson.annotations.SerializedName ; } + +# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher. +-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken +-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken +##---------------End: proguard configuration for Gson ---------- diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index a26b6df..e806438 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_android description: The official Android implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_android -version: 0.1.4 +version: 0.1.5-rc.1 environment: sdk: ">=2.16.2 <4.0.0" diff --git a/splitio_ios/CHANGELOG.md b/splitio_ios/CHANGELOG.md index 28f9030..30d4cd1 100644 --- a/splitio_ios/CHANGELOG.md +++ b/splitio_ios/CHANGELOG.md @@ -1,3 +1,5 @@ +# 0.1.5-rc.1 (Jul 19, 2023) + # 0.1.4 (May 23, 2023) * Added `setUserConsent` method. diff --git a/splitio_ios/example/ios/Podfile b/splitio_ios/example/ios/Podfile index 727dd77..e3a7db2 100644 --- a/splitio_ios/example/ios/Podfile +++ b/splitio_ios/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '9.0' +platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/splitio_ios/example/ios/Podfile.lock b/splitio_ios/example/ios/Podfile.lock index 8ec7ad5..1040a02 100644 --- a/splitio_ios/example/ios/Podfile.lock +++ b/splitio_ios/example/ios/Podfile.lock @@ -1,9 +1,9 @@ PODS: - Flutter (1.0.0) - - Split (2.20.1) - - splitio_ios (0.2.0): + - Split (2.21.0) + - splitio_ios (0.3.0): - Flutter - - Split (~> 2.20.1) + - Split (~> 2.21.0) DEPENDENCIES: - Flutter (from `Flutter`) @@ -20,10 +20,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/splitio_ios/ios" SPEC CHECKSUMS: - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - Split: afbdfda76fe2c90bd449564eed2d83de00c0ff69 - splitio_ios: e6d3dfe108ba894129a6b249d8914e3c44f0ffc4 + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + Split: e4324a8fe3fcb19c36a9e4fd84a893fc6d4bbeb0 + splitio_ios: 8293cb4e46661f9403f92a0d143c5bf6ad5053d4 -PODFILE CHECKSUM: 6ab177d3659abbf5f15e864674366127c98cb8c0 +PODFILE CHECKSUM: 9eb4e99975f80022b373afce31dc8b3feb22df78 COCOAPODS: 1.11.3 diff --git a/splitio_ios/ios/splitio_ios.podspec b/splitio_ios/ios/splitio_ios.podspec index 8cb33f7..61453f6 100644 --- a/splitio_ios/ios/splitio_ios.podspec +++ b/splitio_ios/ios/splitio_ios.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'splitio_ios' - s.version = '0.2.0' + s.version = '0.3.0' s.summary = 'split.io official Flutter plugin.' s.description = <<-DESC split.io official Flutter plugin. @@ -15,7 +15,7 @@ split.io official Flutter plugin. s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.dependency 'Split', '~> 2.20.1' + s.dependency 'Split', '~> 2.21.0' s.platform = :ios, '9.0' # Flutter.framework does not contain a i386 slice. diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index 0203f9d..816bc78 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_ios description: The official iOS implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_ios -version: 0.1.4 +version: 0.1.5-rc.1 environment: sdk: ">=2.16.2 <4.0.0" From a99bb5404866c010c7b38541dd567e886b918396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Wed, 19 Jul 2023 17:17:17 -0300 Subject: [PATCH 22/25] 0.1.5 (#81) --- splitio/pubspec.yaml | 6 +++--- splitio_android/CHANGELOG.md | 7 +++++++ splitio_android/pubspec.yaml | 2 +- splitio_ios/CHANGELOG.md | 7 +++++++ splitio_ios/pubspec.yaml | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 6fd35e1..ea88246 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,6 +1,6 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. -version: 0.1.5-rc.1 +version: 0.1.5 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ @@ -19,8 +19,8 @@ flutter: dependencies: flutter: sdk: flutter - splitio_android: ^0.1.5-rc.1 - splitio_ios: ^0.1.5-rc.1 + splitio_android: ^0.1.5 + splitio_ios: ^0.1.5 splitio_platform_interface: ^1.2.0 dev_dependencies: diff --git a/splitio_android/CHANGELOG.md b/splitio_android/CHANGELOG.md index 9bc1add..b1d8fbe 100644 --- a/splitio_android/CHANGELOG.md +++ b/splitio_android/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.1.5 (Jul 19, 2023) + +* Updated Android SDK to `3.3.0`, which includes: + * Improved streaming architecture implementation to apply feature flag updates from the notification received which is now enhanced, improving efficiency and reliability of the whole update system. + * Added logic to do a full check of feature flags immediately when the app comes back to foreground, limited to once per minute. + # 0.1.5-rc.1 (Jul 19, 2023) # 0.1.4 (May 23, 2023) @@ -8,6 +14,7 @@ # 0.1.4-rc.1 (May 23, 2023) # 0.1.3 (May 18, 2023) + * Updated Android SDK to `3.2.1` * Added support for new configuration options: * `impressionsMode` diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index e806438..05ed5c1 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_android description: The official Android implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_android -version: 0.1.5-rc.1 +version: 0.1.5 environment: sdk: ">=2.16.2 <4.0.0" diff --git a/splitio_ios/CHANGELOG.md b/splitio_ios/CHANGELOG.md index 30d4cd1..ef6647e 100644 --- a/splitio_ios/CHANGELOG.md +++ b/splitio_ios/CHANGELOG.md @@ -1,3 +1,10 @@ +# 0.1.5 (Jul 19, 2023) + +* Updated iOS SDK to `2.21.0`, which includes: + * Improved streaming architecture implementation to apply feature flag updates from the notification received which is now enhanced, improving efficiency and reliability of the whole update system. + * Added logic to do a full check of feature flags immediately when the app comes back to foreground, limited to once per minute. + * Updated SplitResult init to be public in order to improve testability. + # 0.1.5-rc.1 (Jul 19, 2023) # 0.1.4 (May 23, 2023) diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index 816bc78..571b0dd 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_ios description: The official iOS implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_ios -version: 0.1.5-rc.1 +version: 0.1.5 environment: sdk: ">=2.16.2 <4.0.0" From b9ad43e1717d198ba63efcf24d685065ea4daf1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Tue, 15 Aug 2023 16:18:24 -0300 Subject: [PATCH 23/25] readyTimeout config parameter (#85) --- splitio/example/pubspec.lock | 131 ++++++++++-------- splitio/lib/splitio.dart | 6 +- splitio/pubspec.yaml | 11 +- splitio/test/splitio_test.dart | 8 +- .../splitio/SplitClientConfigHelper.java | 7 + splitio_android/pubspec.yaml | 5 +- .../test/splitio_android_test.dart | 4 +- .../SplitClientConfigHelperTests.swift | 4 +- .../ios/Classes/SplitClientConfigHelper.swift | 5 + splitio_ios/pubspec.yaml | 5 +- .../lib/method_channel_platform.dart | 2 +- .../lib/split_configuration.dart | 7 + splitio_platform_interface/pubspec.yaml | 2 +- .../test/method_channel_platform_test.dart | 6 +- .../test/splitio_configuration_test.dart | 5 +- 15 files changed, 128 insertions(+), 80 deletions(-) diff --git a/splitio/example/pubspec.lock b/splitio/example/pubspec.lock index 9c2f871..cad1492 100644 --- a/splitio/example/pubspec.lock +++ b/splitio/example/pubspec.lock @@ -5,58 +5,58 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.8.2" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.1" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: "1989d917fbe8e6b39806207df5a3fdd3d816cbd090fac2ce26fb45e9a71476e5" + url: "https://pub.dev" source: hosted version: "1.0.4" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -66,7 +66,8 @@ packages: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493 + url: "https://pub.dev" source: hosted version: "1.0.4" flutter_test: @@ -74,48 +75,62 @@ packages: description: flutter source: sdk version: "0.0.0" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c + url: "https://pub.dev" source: hosted version: "1.0.1" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + url: "https://pub.dev" source: hosted - version: "0.12.11" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.9.1" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.8.3" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.5" sky_engine: dependency: transitive description: flutter @@ -125,86 +140,86 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.9.1" splitio: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.1.2" + version: "0.1.6-rc.1" splitio_android: dependency: transitive description: path: "../../splitio_android" relative: true source: path - version: "0.1.2" + version: "0.1.6-rc.1" splitio_ios: dependency: transitive description: path: "../../splitio_ios" relative: true source: path - version: "0.1.2" + version: "0.1.6-rc.1" splitio_platform_interface: dependency: transitive description: path: "../../splitio_platform_interface" relative: true source: path - version: "1.0.0" + version: "1.3.0-rc.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + url: "https://pub.dev" source: hosted - version: "0.4.8" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" + version: "0.5.1" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.4" sdks: - dart: ">=2.16.2 <3.0.0" + dart: ">=3.0.0-0 <4.0.0" flutter: ">=2.5.0" diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index f20b066..0c2c2d6 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -36,7 +36,11 @@ class Splitio { Splitio(this._sdkKey, this._defaultMatchingKey, {String? bucketingKey, SplitConfiguration? configuration}) { _defaultBucketingKey = bucketingKey; - _splitConfiguration = configuration; + if (configuration != null) { + _splitConfiguration = configuration; + } else { + _splitConfiguration = SplitConfiguration(); + } _init(); } diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index ea88246..5f9c68d 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,6 +1,6 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. -version: 0.1.5 +version: 0.1.6-rc.1 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ @@ -19,9 +19,12 @@ flutter: dependencies: flutter: sdk: flutter - splitio_android: ^0.1.5 - splitio_ios: ^0.1.5 - splitio_platform_interface: ^1.2.0 + splitio_android: #^0.1.6-rc.1 + path: ../splitio_android + splitio_ios: #^0.1.6-rc.1 + path: ../splitio_ios + splitio_platform_interface: #^1.3.0-rc.1 + path: ../splitio_platform_interface dev_dependencies: flutter_test: diff --git a/splitio/test/splitio_test.dart b/splitio/test/splitio_test.dart index 87ad6e4..e6627f2 100644 --- a/splitio/test/splitio_test.dart +++ b/splitio/test/splitio_test.dart @@ -17,7 +17,7 @@ void main() { expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', - 'sdkConfiguration': {} + 'sdkConfiguration': {'readyTimeout': 10} }); }); @@ -28,7 +28,7 @@ void main() { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', - 'sdkConfiguration': {} + 'sdkConfiguration': {'readyTimeout': 10} }); }); @@ -42,7 +42,7 @@ void main() { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', - 'sdkConfiguration': {'logLevel': 'debug', 'streamingEnabled': false}, + 'sdkConfiguration': {'logLevel': 'debug', 'streamingEnabled': false, 'readyTimeout': 10}, }); }); }); @@ -118,7 +118,7 @@ void main() { expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'apiKey': 'api-key', - 'sdkConfiguration': {}, + 'sdkConfiguration': {'readyTimeout': 10}, 'value': true, }); }); diff --git a/splitio_android/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java b/splitio_android/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java index 825550e..80f58cc 100644 --- a/splitio_android/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java +++ b/splitio_android/android/src/main/java/io/split/splitio/SplitClientConfigHelper.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import io.split.android.client.ServiceEndpoints; import io.split.android.client.SplitClientConfig; @@ -43,6 +44,7 @@ class SplitClientConfigHelper { private static final String USER_CONSENT = "userConsent"; private static final String ENCRYPTION_ENABLED = "encryptionEnabled"; private static final String LOG_LEVEL = "logLevel"; + private static final String READY_TIMEOUT = "readyTimeout"; /** * Creates a {@link SplitClientConfig} object from a map. @@ -206,6 +208,11 @@ static SplitClientConfig fromMap(@NonNull Map configurationMap, } } + Integer readyTimeout = getInteger(configurationMap, READY_TIMEOUT); + if (readyTimeout != null) { + builder.ready((int) TimeUnit.SECONDS.toMillis(readyTimeout)); // Android SDK uses this parameter in millis + } + return builder.serviceEndpoints(serviceEndpointsBuilder.build()).build(); } diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index 05ed5c1..778eada 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_android description: The official Android implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_android -version: 0.1.5 +version: 0.1.6-rc.1 environment: sdk: ">=2.16.2 <4.0.0" @@ -19,7 +19,8 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.2.0 + splitio_platform_interface: #^1.3.0-rc.1 + path: ../splitio_platform_interface dev_dependencies: flutter_test: diff --git a/splitio_android/test/splitio_android_test.dart b/splitio_android/test/splitio_android_test.dart index 34d65a4..7cc1f68 100644 --- a/splitio_android/test/splitio_android_test.dart +++ b/splitio_android/test/splitio_android_test.dart @@ -373,13 +373,13 @@ void main() { matchingKey: 'matching-key', bucketingKey: 'bucketing-key', sdkConfiguration: - SplitConfiguration(logLevel: SplitLogLevel.error, streamingEnabled: false)); + SplitConfiguration(logLevel: SplitLogLevel.error, streamingEnabled: false, readyTimeout: 1)); expect(methodName, 'init'); expect(methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', - 'sdkConfiguration': {'logLevel': 'error', 'streamingEnabled': false}, + 'sdkConfiguration': {'logLevel': 'error', 'streamingEnabled': false, 'readyTimeout': 1}, }); }); }); diff --git a/splitio_ios/example/ios/SplitTests/SplitClientConfigHelperTests.swift b/splitio_ios/example/ios/SplitTests/SplitClientConfigHelperTests.swift index beaa317..e107644 100644 --- a/splitio_ios/example/ios/SplitTests/SplitClientConfigHelperTests.swift +++ b/splitio_ios/example/ios/SplitTests/SplitClientConfigHelperTests.swift @@ -26,7 +26,8 @@ class SplitClientConfigHelperTests: XCTestCase { "syncEnabled": false, "userConsent": "declined", "encryptionEnabled": true, - "logLevel": "verbose" + "logLevel": "verbose", + "readyTimeout": 10 ] let splitClientConfig = SplitClientConfigHelper.fromMap(configurationMap: configValues, impressionListener: nil) @@ -47,6 +48,7 @@ class SplitClientConfigHelperTests: XCTestCase { XCTAssertEqual(.declined, splitClientConfig.userConsent) XCTAssertTrue(splitClientConfig.encryptionEnabled) XCTAssertEqual(.verbose, splitClientConfig.logLevel) + XCTAssertEqual(10000, splitClientConfig.sdkReadyTimeOut) } func testEnableDebugLogLevelIsMappedCorrectly() { diff --git a/splitio_ios/ios/Classes/SplitClientConfigHelper.swift b/splitio_ios/ios/Classes/SplitClientConfigHelper.swift index 6d03b98..2febd19 100644 --- a/splitio_ios/ios/Classes/SplitClientConfigHelper.swift +++ b/splitio_ios/ios/Classes/SplitClientConfigHelper.swift @@ -29,6 +29,7 @@ class SplitClientConfigHelper { static private let USER_CONSENT = "userConsent" static private let ENCRYPTION_ENABLED = "encryptionEnabled" static private let LOG_LEVEL = "logLevel" + static private let READY_TIMEOUT = "readyTimeout" static func fromMap(configurationMap: [String: Any?], impressionListener: SplitImpressionListener?) -> SplitClientConfig { let config = SplitClientConfig() @@ -200,6 +201,10 @@ class SplitClientConfigHelper { } } + if let readyTimeout = configurationMap[READY_TIMEOUT] as? Int { + config.sdkReadyTimeOut = readyTimeout * 1000 // iOS SDK uses this parameter in millis + } + config.serviceEndpoints = serviceEndpointsBuilder.build() return config diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index 571b0dd..1a179bf 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_ios description: The official iOS implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_ios -version: 0.1.5 +version: 0.1.6-rc.1 environment: sdk: ">=2.16.2 <4.0.0" @@ -18,7 +18,8 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.2.0 + splitio_platform_interface: #^1.3.0-rc.1 + path: ../splitio_platform_interface dev_dependencies: flutter_test: diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart index 7199c5b..4bc0400 100644 --- a/splitio_platform_interface/lib/method_channel_platform.dart +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -33,7 +33,7 @@ class MethodChannelPlatform extends SplitioPlatform { Map arguments = { 'apiKey': apiKey, 'matchingKey': matchingKey, - 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, + 'sdkConfiguration': sdkConfiguration?.configurationMap ?? SplitConfiguration().configurationMap, // If sdkConfiguration is null, create a new SplitConfiguration to apply default values }; if (bucketingKey != null) { diff --git a/splitio_platform_interface/lib/split_configuration.dart b/splitio_platform_interface/lib/split_configuration.dart index 0ee7d2b..08496aa 100644 --- a/splitio_platform_interface/lib/split_configuration.dart +++ b/splitio_platform_interface/lib/split_configuration.dart @@ -42,6 +42,8 @@ class SplitConfiguration { /// [encryptionEnabled] If set to true, the local database contents is encrypted. Defaults to false. /// /// [logLevel] Enables logging according to the level specified. Options are [SplitLogLevel.verbose], [SplitLogLevel.none], [SplitLogLevel.debug], [SplitLogLevel.info], [SplitLogLevel.warning], and [SplitLogLevel.error]. + /// + /// [readyTimeout] Maximum amount of time in seconds to wait before firing the SDK_READY_TIMED_OUT event. Defaults to 10 seconds. SplitConfiguration({ int? featuresRefreshRate, int? segmentsRefreshRate, @@ -67,6 +69,7 @@ class SplitConfiguration { UserConsent? userConsent, bool? encryptionEnabled, SplitLogLevel? logLevel, + int? readyTimeout = 10, }) { if (featuresRefreshRate != null) { configurationMap['featuresRefreshRate'] = featuresRefreshRate; @@ -167,6 +170,10 @@ class SplitConfiguration { if (logLevel != null) { configurationMap['logLevel'] = logLevel.name; } + + if (readyTimeout != null) { + configurationMap['readyTimeout'] = readyTimeout; + } } } diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml index 710ce64..412af1f 100644 --- a/splitio_platform_interface/pubspec.yaml +++ b/splitio_platform_interface/pubspec.yaml @@ -2,7 +2,7 @@ name: splitio_platform_interface description: A common platform interface for the splitio plugin. # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.2.0 +version: 1.3.0-rc.1 repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_platform_interface environment: diff --git a/splitio_platform_interface/test/method_channel_platform_test.dart b/splitio_platform_interface/test/method_channel_platform_test.dart index 21ed1e2..d03c4f5 100644 --- a/splitio_platform_interface/test/method_channel_platform_test.dart +++ b/splitio_platform_interface/test/method_channel_platform_test.dart @@ -351,7 +351,7 @@ void main() { expect(methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', - 'sdkConfiguration': {} + 'sdkConfiguration': {'readyTimeout': 10} }); }); @@ -365,7 +365,7 @@ void main() { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', - 'sdkConfiguration': {} + 'sdkConfiguration': {'readyTimeout': 10} }); }); @@ -381,7 +381,7 @@ void main() { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', - 'sdkConfiguration': {'logLevel': 'debug', 'streamingEnabled': false}, + 'sdkConfiguration': {'logLevel': 'debug', 'streamingEnabled': false, 'readyTimeout' : 10}, }); }); }); diff --git a/splitio_platform_interface/test/splitio_configuration_test.dart b/splitio_platform_interface/test/splitio_configuration_test.dart index 5348041..0e22f77 100644 --- a/splitio_platform_interface/test/splitio_configuration_test.dart +++ b/splitio_platform_interface/test/splitio_configuration_test.dart @@ -29,6 +29,7 @@ void main() { userConsent: UserConsent.declined, encryptionEnabled: true, logLevel: SplitLogLevel.debug, + readyTimeout: 1 ); expect(config.configurationMap['eventFlushInterval'], 2000); @@ -61,11 +62,13 @@ void main() { expect(config.configurationMap['userConsent'], 'declined'); expect(config.configurationMap['encryptionEnabled'], true); expect(config.configurationMap['logLevel'], 'debug'); + expect(config.configurationMap['readyTimeout'], 1); }); test('noSpecialValuesLeavesMapEmpty', () async { final SplitConfiguration config = SplitConfiguration(); - expect(config.configurationMap.isEmpty, true); + expect(config.configurationMap.length, 1); + expect(config.configurationMap['readyTimeout'], 10); }); } From c4416d2d4daa0235719da60394ba8d7d24621b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Tue, 15 Aug 2023 17:10:26 -0300 Subject: [PATCH 24/25] 0.1.6 rc.1 (#86) --- splitio/CHANGELOG.md | 6 ++++++ splitio/pubspec.yaml | 9 +++------ splitio_android/CHANGELOG.md | 2 ++ splitio_android/pubspec.yaml | 3 +-- splitio_ios/CHANGELOG.md | 2 ++ splitio_ios/pubspec.yaml | 3 +-- splitio_platform_interface/CHANGELOG.md | 2 ++ 7 files changed, 17 insertions(+), 10 deletions(-) diff --git a/splitio/CHANGELOG.md b/splitio/CHANGELOG.md index e2307a3..09436b6 100644 --- a/splitio/CHANGELOG.md +++ b/splitio/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.1.6-rc.1 (Aug 15, 2023) + +# 0.1.5 (Jul 19, 2023) + +* Updated iOS SDK to `2.21.0` & Android SDK to `3.3.0` + # 0.1.5-rc.1 (Jul 19, 2023) # 0.1.4 (May 23, 2023) diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 5f9c68d..1d98371 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -19,12 +19,9 @@ flutter: dependencies: flutter: sdk: flutter - splitio_android: #^0.1.6-rc.1 - path: ../splitio_android - splitio_ios: #^0.1.6-rc.1 - path: ../splitio_ios - splitio_platform_interface: #^1.3.0-rc.1 - path: ../splitio_platform_interface + splitio_android: ^0.1.6-rc.1 + splitio_ios: ^0.1.6-rc.1 + splitio_platform_interface: ^1.3.0-rc.1 dev_dependencies: flutter_test: diff --git a/splitio_android/CHANGELOG.md b/splitio_android/CHANGELOG.md index b1d8fbe..4e0ea64 100644 --- a/splitio_android/CHANGELOG.md +++ b/splitio_android/CHANGELOG.md @@ -1,3 +1,5 @@ +# 0.1.6-rc.1 (Aug 15, 2023) + # 0.1.5 (Jul 19, 2023) * Updated Android SDK to `3.3.0`, which includes: diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index 778eada..68c46bf 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -19,8 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: #^1.3.0-rc.1 - path: ../splitio_platform_interface + splitio_platform_interface: ^1.3.0-rc.1 dev_dependencies: flutter_test: diff --git a/splitio_ios/CHANGELOG.md b/splitio_ios/CHANGELOG.md index ef6647e..6926aa6 100644 --- a/splitio_ios/CHANGELOG.md +++ b/splitio_ios/CHANGELOG.md @@ -1,3 +1,5 @@ +# 0.1.6-rc.1 (Aug 15, 2023) + # 0.1.5 (Jul 19, 2023) * Updated iOS SDK to `2.21.0`, which includes: diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index 1a179bf..99e1568 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -18,8 +18,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: #^1.3.0-rc.1 - path: ../splitio_platform_interface + splitio_platform_interface: ^1.3.0-rc.1 dev_dependencies: flutter_test: diff --git a/splitio_platform_interface/CHANGELOG.md b/splitio_platform_interface/CHANGELOG.md index 3c2e709..e670c30 100644 --- a/splitio_platform_interface/CHANGELOG.md +++ b/splitio_platform_interface/CHANGELOG.md @@ -1,3 +1,5 @@ +# 1.3.0-rc.1 (Aug 15, 2023) + # 1.2.0 (May 23, 2023) * Added `setUserConsent` method. From bb9ca87493a9932d75d95bf81f1e63ba1e788a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Thea?= Date: Wed, 16 Aug 2023 14:13:12 -0300 Subject: [PATCH 25/25] 0.1.6 (#87) --- splitio/CHANGELOG.md | 4 ++++ splitio/pubspec.yaml | 8 ++++---- splitio_android/CHANGELOG.md | 4 ++++ splitio_android/pubspec.yaml | 4 ++-- splitio_ios/CHANGELOG.md | 4 ++++ splitio_ios/pubspec.yaml | 4 ++-- splitio_platform_interface/CHANGELOG.md | 4 ++++ splitio_platform_interface/pubspec.yaml | 2 +- 8 files changed, 25 insertions(+), 9 deletions(-) diff --git a/splitio/CHANGELOG.md b/splitio/CHANGELOG.md index 09436b6..9d0be90 100644 --- a/splitio/CHANGELOG.md +++ b/splitio/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.1.6 (Aug 15, 2023) + +* Added `readyTimeout` configuration option. If the SDK is not ready after the amount of time (in seconds) specified by this option, the `whenTimeout` future of the client will be completed. Defaults to 10 seconds. A negative value means no timeout. + # 0.1.6-rc.1 (Aug 15, 2023) # 0.1.5 (Jul 19, 2023) diff --git a/splitio/pubspec.yaml b/splitio/pubspec.yaml index 1d98371..8b566fe 100644 --- a/splitio/pubspec.yaml +++ b/splitio/pubspec.yaml @@ -1,6 +1,6 @@ name: splitio description: Official plugin for split.io, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. -version: 0.1.6-rc.1 +version: 0.1.6 homepage: https://split.io/ repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio/ @@ -19,9 +19,9 @@ flutter: dependencies: flutter: sdk: flutter - splitio_android: ^0.1.6-rc.1 - splitio_ios: ^0.1.6-rc.1 - splitio_platform_interface: ^1.3.0-rc.1 + splitio_android: ^0.1.6 + splitio_ios: ^0.1.6 + splitio_platform_interface: ^1.3.0 dev_dependencies: flutter_test: diff --git a/splitio_android/CHANGELOG.md b/splitio_android/CHANGELOG.md index 4e0ea64..ce894f5 100644 --- a/splitio_android/CHANGELOG.md +++ b/splitio_android/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.1.6 (Aug 15, 2023) + +* Added `readyTimeout` configuration option. + # 0.1.6-rc.1 (Aug 15, 2023) # 0.1.5 (Jul 19, 2023) diff --git a/splitio_android/pubspec.yaml b/splitio_android/pubspec.yaml index 68c46bf..3381173 100644 --- a/splitio_android/pubspec.yaml +++ b/splitio_android/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_android description: The official Android implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_android -version: 0.1.6-rc.1 +version: 0.1.6 environment: sdk: ">=2.16.2 <4.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.3.0-rc.1 + splitio_platform_interface: ^1.3.0 dev_dependencies: flutter_test: diff --git a/splitio_ios/CHANGELOG.md b/splitio_ios/CHANGELOG.md index 6926aa6..b84abfb 100644 --- a/splitio_ios/CHANGELOG.md +++ b/splitio_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.1.6 (Aug 15, 2023) + +* Added `readyTimeout` configuration option. + # 0.1.6-rc.1 (Aug 15, 2023) # 0.1.5 (Jul 19, 2023) diff --git a/splitio_ios/pubspec.yaml b/splitio_ios/pubspec.yaml index 99e1568..9539b32 100644 --- a/splitio_ios/pubspec.yaml +++ b/splitio_ios/pubspec.yaml @@ -1,7 +1,7 @@ name: splitio_ios description: The official iOS implementation of splitio Flutter plugin. repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_ios -version: 0.1.6-rc.1 +version: 0.1.6 environment: sdk: ">=2.16.2 <4.0.0" @@ -18,7 +18,7 @@ flutter: dependencies: flutter: sdk: flutter - splitio_platform_interface: ^1.3.0-rc.1 + splitio_platform_interface: ^1.3.0 dev_dependencies: flutter_test: diff --git a/splitio_platform_interface/CHANGELOG.md b/splitio_platform_interface/CHANGELOG.md index e670c30..a46f530 100644 --- a/splitio_platform_interface/CHANGELOG.md +++ b/splitio_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.3.0 (Aug 15, 2023) + +* Added `readyTimeout` configuration option. + # 1.3.0-rc.1 (Aug 15, 2023) # 1.2.0 (May 23, 2023) diff --git a/splitio_platform_interface/pubspec.yaml b/splitio_platform_interface/pubspec.yaml index 412af1f..0a73b0f 100644 --- a/splitio_platform_interface/pubspec.yaml +++ b/splitio_platform_interface/pubspec.yaml @@ -2,7 +2,7 @@ name: splitio_platform_interface description: A common platform interface for the splitio plugin. # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.3.0-rc.1 +version: 1.3.0 repository: https://github.com/splitio/flutter-sdk-plugin/tree/main/splitio_platform_interface environment: