diff --git a/FeatureProbe.podspec b/FeatureProbe.podspec new file mode 100644 index 0000000..588ddf5 --- /dev/null +++ b/FeatureProbe.podspec @@ -0,0 +1,15 @@ +Pod::Spec.new do |s| + s.name = 'FeatureProbe' + s.version = '1.0.1' + s.license = { :type => 'MIT' } + s.homepage = 'https://www.featureprobe.com/' + s.summary = 'iOS feature probe SDK' + s.source = { :git => 'git@github.com:FeatureProbe/client-sdk-ios.git', :tag => s.version } + s.source_files = 'Sources/**/*.swift' + s.authors = 'FeatreProbeTeam' + + s.platform = :ios, '9.0' + s.pod_target_xcconfig = { 'VALID_ARCHS' => 'x86_64 arm64' } + s.swift_versions = ['4.0', '4.2', '5.0', '5.5'] + s.vendored_frameworks = "FeatureProbeFFI.xcframework" + end diff --git a/FeatureProbeFFI.xcframework/Info.plist b/FeatureProbeFFI.xcframework/Info.plist new file mode 100644 index 0000000..f679b19 --- /dev/null +++ b/FeatureProbeFFI.xcframework/Info.plist @@ -0,0 +1,40 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + ios-arm64 + LibraryPath + FeatureProbeFFI.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + FeatureProbeFFI.framework + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/FeatureProbeFFI b/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/FeatureProbeFFI new file mode 100644 index 0000000..afcd14d Binary files /dev/null and b/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/FeatureProbeFFI differ diff --git a/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.h b/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.h new file mode 100644 index 0000000..cc160df --- /dev/null +++ b/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.h @@ -0,0 +1,140 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + +#pragma once + +#include +#include + +// The following structs are used to implement the lowest level +// of the FFI, and thus useful to multiple uniffied crates. +// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H. +#ifdef UNIFFI_SHARED_H + // We also try to prevent mixing versions of shared uniffi header structs. + // If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V4 + #ifndef UNIFFI_SHARED_HEADER_V4 + #error Combining helper code from multiple versions of uniffi is not supported + #endif // ndef UNIFFI_SHARED_HEADER_V4 +#else +#define UNIFFI_SHARED_H +#define UNIFFI_SHARED_HEADER_V4 +// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️ +// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️ + +typedef struct RustBuffer +{ + int32_t capacity; + int32_t len; + uint8_t *_Nullable data; +} RustBuffer; + +typedef int32_t (*ForeignCallback)(uint64_t, int32_t, RustBuffer, RustBuffer *_Nonnull); + +typedef struct ForeignBytes +{ + int32_t len; + const uint8_t *_Nullable data; +} ForeignBytes; + +// Error definitions +typedef struct RustCallStatus { + int8_t code; + RustBuffer errorBuf; +} RustCallStatus; + +// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️ +// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️ +#endif // def UNIFFI_SHARED_H + +void ffi_featureprobe_17ce_FeatureProbe_object_free( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void*_Nonnull featureprobe_17ce_FeatureProbe_new( + void*_Nonnull config,void*_Nonnull user, + RustCallStatus *_Nonnull out_status + ); +int8_t featureprobe_17ce_FeatureProbe_bool_value( + void*_Nonnull ptr,RustBuffer key,int8_t default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_bool_detail( + void*_Nonnull ptr,RustBuffer key,int8_t default_value, + RustCallStatus *_Nonnull out_status + ); +double featureprobe_17ce_FeatureProbe_number_value( + void*_Nonnull ptr,RustBuffer key,double default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_number_detail( + void*_Nonnull ptr,RustBuffer key,double default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_string_value( + void*_Nonnull ptr,RustBuffer key,RustBuffer default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_string_detail( + void*_Nonnull ptr,RustBuffer key,RustBuffer default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_json_value( + void*_Nonnull ptr,RustBuffer key,RustBuffer default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_json_detail( + void*_Nonnull ptr,RustBuffer key,RustBuffer default_value, + RustCallStatus *_Nonnull out_status + ); +void ffi_featureprobe_17ce_FPUser_object_free( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void*_Nonnull featureprobe_17ce_FPUser_new( + RustBuffer key, + RustCallStatus *_Nonnull out_status + ); +void featureprobe_17ce_FPUser_set_attr( + void*_Nonnull ptr,RustBuffer key,RustBuffer value, + RustCallStatus *_Nonnull out_status + ); +void ffi_featureprobe_17ce_FPUrl_object_free( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void ffi_featureprobe_17ce_FPUrlBuilder_object_free( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void*_Nonnull featureprobe_17ce_FPUrlBuilder_new( + RustBuffer remote_url, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FPUrlBuilder_build( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void ffi_featureprobe_17ce_FPConfig_object_free( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void*_Nonnull featureprobe_17ce_FPConfig_new( + void*_Nonnull remote_url,RustBuffer client_sdk_key,uint8_t refresh_interval,int8_t wait_first_resp, + RustCallStatus *_Nonnull out_status + ); +RustBuffer ffi_featureprobe_17ce_rustbuffer_alloc( + int32_t size, + RustCallStatus *_Nonnull out_status + ); +RustBuffer ffi_featureprobe_17ce_rustbuffer_from_bytes( + ForeignBytes bytes, + RustCallStatus *_Nonnull out_status + ); +void ffi_featureprobe_17ce_rustbuffer_free( + RustBuffer buf, + RustCallStatus *_Nonnull out_status + ); +RustBuffer ffi_featureprobe_17ce_rustbuffer_reserve( + RustBuffer buf,int32_t additional, + RustCallStatus *_Nonnull out_status + ); diff --git a/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.modulemap b/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.modulemap new file mode 100644 index 0000000..7423a34 --- /dev/null +++ b/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.modulemap @@ -0,0 +1,6 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! +module FeatureProbeFFI { + header "FeatureProbeFFI.h" + export * +} \ No newline at end of file diff --git a/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/Modules/module.modulemap b/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/Modules/module.modulemap new file mode 100644 index 0000000..7eeb8cb --- /dev/null +++ b/FeatureProbeFFI.xcframework/ios-arm64/FeatureProbeFFI.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module FeatureProbeFFI { + umbrella header "FeatureprobeFFI.h" + + export * + module * { export * } +} diff --git a/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/FeatureProbeFFI b/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/FeatureProbeFFI new file mode 100644 index 0000000..c4d36bd Binary files /dev/null and b/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/FeatureProbeFFI differ diff --git a/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.h b/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.h new file mode 100644 index 0000000..cc160df --- /dev/null +++ b/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.h @@ -0,0 +1,140 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + +#pragma once + +#include +#include + +// The following structs are used to implement the lowest level +// of the FFI, and thus useful to multiple uniffied crates. +// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H. +#ifdef UNIFFI_SHARED_H + // We also try to prevent mixing versions of shared uniffi header structs. + // If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V4 + #ifndef UNIFFI_SHARED_HEADER_V4 + #error Combining helper code from multiple versions of uniffi is not supported + #endif // ndef UNIFFI_SHARED_HEADER_V4 +#else +#define UNIFFI_SHARED_H +#define UNIFFI_SHARED_HEADER_V4 +// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️ +// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️ + +typedef struct RustBuffer +{ + int32_t capacity; + int32_t len; + uint8_t *_Nullable data; +} RustBuffer; + +typedef int32_t (*ForeignCallback)(uint64_t, int32_t, RustBuffer, RustBuffer *_Nonnull); + +typedef struct ForeignBytes +{ + int32_t len; + const uint8_t *_Nullable data; +} ForeignBytes; + +// Error definitions +typedef struct RustCallStatus { + int8_t code; + RustBuffer errorBuf; +} RustCallStatus; + +// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️ +// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️ +#endif // def UNIFFI_SHARED_H + +void ffi_featureprobe_17ce_FeatureProbe_object_free( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void*_Nonnull featureprobe_17ce_FeatureProbe_new( + void*_Nonnull config,void*_Nonnull user, + RustCallStatus *_Nonnull out_status + ); +int8_t featureprobe_17ce_FeatureProbe_bool_value( + void*_Nonnull ptr,RustBuffer key,int8_t default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_bool_detail( + void*_Nonnull ptr,RustBuffer key,int8_t default_value, + RustCallStatus *_Nonnull out_status + ); +double featureprobe_17ce_FeatureProbe_number_value( + void*_Nonnull ptr,RustBuffer key,double default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_number_detail( + void*_Nonnull ptr,RustBuffer key,double default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_string_value( + void*_Nonnull ptr,RustBuffer key,RustBuffer default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_string_detail( + void*_Nonnull ptr,RustBuffer key,RustBuffer default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_json_value( + void*_Nonnull ptr,RustBuffer key,RustBuffer default_value, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FeatureProbe_json_detail( + void*_Nonnull ptr,RustBuffer key,RustBuffer default_value, + RustCallStatus *_Nonnull out_status + ); +void ffi_featureprobe_17ce_FPUser_object_free( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void*_Nonnull featureprobe_17ce_FPUser_new( + RustBuffer key, + RustCallStatus *_Nonnull out_status + ); +void featureprobe_17ce_FPUser_set_attr( + void*_Nonnull ptr,RustBuffer key,RustBuffer value, + RustCallStatus *_Nonnull out_status + ); +void ffi_featureprobe_17ce_FPUrl_object_free( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void ffi_featureprobe_17ce_FPUrlBuilder_object_free( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void*_Nonnull featureprobe_17ce_FPUrlBuilder_new( + RustBuffer remote_url, + RustCallStatus *_Nonnull out_status + ); +RustBuffer featureprobe_17ce_FPUrlBuilder_build( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void ffi_featureprobe_17ce_FPConfig_object_free( + void*_Nonnull ptr, + RustCallStatus *_Nonnull out_status + ); +void*_Nonnull featureprobe_17ce_FPConfig_new( + void*_Nonnull remote_url,RustBuffer client_sdk_key,uint8_t refresh_interval,int8_t wait_first_resp, + RustCallStatus *_Nonnull out_status + ); +RustBuffer ffi_featureprobe_17ce_rustbuffer_alloc( + int32_t size, + RustCallStatus *_Nonnull out_status + ); +RustBuffer ffi_featureprobe_17ce_rustbuffer_from_bytes( + ForeignBytes bytes, + RustCallStatus *_Nonnull out_status + ); +void ffi_featureprobe_17ce_rustbuffer_free( + RustBuffer buf, + RustCallStatus *_Nonnull out_status + ); +RustBuffer ffi_featureprobe_17ce_rustbuffer_reserve( + RustBuffer buf,int32_t additional, + RustCallStatus *_Nonnull out_status + ); diff --git a/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.modulemap b/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.modulemap new file mode 100644 index 0000000..7423a34 --- /dev/null +++ b/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/Headers/FeatureProbeFFI.modulemap @@ -0,0 +1,6 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! +module FeatureProbeFFI { + header "FeatureProbeFFI.h" + export * +} \ No newline at end of file diff --git a/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/Modules/module.modulemap b/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/Modules/module.modulemap new file mode 100644 index 0000000..7eeb8cb --- /dev/null +++ b/FeatureProbeFFI.xcframework/ios-arm64_x86_64-simulator/FeatureProbeFFI.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module FeatureProbeFFI { + umbrella header "FeatureprobeFFI.h" + + export * + module * { export * } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..12276b7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://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 2022 FeatureProbe + + 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/Package.swift b/Package.swift new file mode 100644 index 0000000..d8bf277 --- /dev/null +++ b/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version: 5.4 +import PackageDescription + +let package = Package( + name: "FeatureProbe", + platforms: [.iOS(.v9)], + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "FeatureProbe", + targets: ["FeatureProbe"]), + ], + dependencies: [ + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .binaryTarget( + name: "FeatureProbeFFI", + path: "./FeatureProbeFFI.xcframework" + ), + .target( + name: "FeatureProbe", + dependencies: ["FeatureProbeFFI"] + ) + ] +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..034fc06 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# FeatureProbe iOS-SDK + +## Usage + +Currently support two kinds of package manager: + +1. Swift Package Manager + XCode -> File -> Add Packages -> input `https://github.com/FeatureProbe/client-sdk-ios.git` + +2. Cocoapods + add `pod 'FeatureProbe', :git => 'git@github.com:FeatureProbe/client-sdk-ios.git'` to Podfile + `pod install` or `pod update` + +```swift +import FeatureProbe + +let url = FpUrlBuilder(remoteUrl: "remote_url").build(); +let user = FpUser(key: "key123") +user.setAttr(key: "city", value: "1") +let config = FpConfig( + remoteUrl: url!, + clientSdkKey: "client-9d885a68ca2955dfb3a7c95435c0c4faad70b50d", + refreshInterval: 10, + waitFirstResp: true +) +let fp = FeatureProbe(config: config, user: user) +let toggleValue = fp.stringDetail(key: "ab_test", defaultValue: "red") +print("toogle value is \(toggleValue)") + +``` + +```objective-c +#import "FeatureProbe-Swift.h" + +NSString *urlStr = @"remote_url"; +FpUrl *url = [[[FpUrlBuilder alloc] initWithRemoteUrl: urlStr] build]; +FpUser *user = [[FpUser alloc] initWithKey:@"user_key"]; +FpConfig *config = [[FpConfig alloc] initWithRemoteUrl: url clientSdkKey:@"client-9d885a68ca2955dfb3a7c95435c0c4faad70b50d" refreshInterval: 10 waitFirstResp: true]; + +FeatureProbe *fp = [[FeatureProbe alloc] initWithConfig:config user:user]; +NSString *value = [fp stringValueWithKey:@"ab_test" defaultValue:@"red"]; +NSLog(@"value is %@", value); + +``` + +## Build +build from repo: `git@github.com:FeatureProbe/client-sdk-mobile.git` + +1. install uniffi codegen tool + +cargo install uniffi_bindgen + +2. install rust android target + +rustup target add aarch64-apple-ios +rustup target add aarch64-apple-ios-sim +rustup target add x86_64-apple-ios + +3. build xcframework + +`./build-xcframework.sh` + +4. push to git + +``` +cd client-sdk-ios +git commit -m 'xxx' +git push origin master +``` + + +## Contributing +We are working on continue evolving FeatureProbe core, making it flexible and easier to use. +Development of FeatureProbe happens in the open on GitHub, and we are grateful to the +community for contributing bugfixes and improvements. + +Please read [CONTRIBUTING](https://github.com/FeatureProbe/featureprobe/blob/master/CONTRIBUTING.md) +for details on our code of conduct, and the process for taking part in improving FeatureProbe. diff --git a/Sources/FeatureProbe/FeatureProbe.swift b/Sources/FeatureProbe/FeatureProbe.swift new file mode 100644 index 0000000..f52c0a6 --- /dev/null +++ b/Sources/FeatureProbe/FeatureProbe.swift @@ -0,0 +1,1161 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! +import Foundation + +// Depending on the consumer's build setup, the low-level FFI code +// might be in a separate module, or it might be compiled inline into +// this module. This is a bit of light hackery to work with both. +#if canImport(FeatureProbeFFI) +import FeatureProbeFFI +#endif + +fileprivate extension RustBuffer { + // Allocate a new buffer, copying the contents of a `UInt8` array. + init(bytes: [UInt8]) { + let rbuf = bytes.withUnsafeBufferPointer { ptr in + RustBuffer.from(ptr) + } + self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data) + } + + static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer { + try! rustCall { ffi_featureprobe_17ce_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) } + } + + // Frees the buffer in place. + // The buffer must not be used after this is called. + func deallocate() { + try! rustCall { ffi_featureprobe_17ce_rustbuffer_free(self, $0) } + } +} + +fileprivate extension ForeignBytes { + init(bufferPointer: UnsafeBufferPointer) { + self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress) + } +} + +// For every type used in the interface, we provide helper methods for conveniently +// lifting and lowering that type from C-compatible data, and for reading and writing +// values of that type in a buffer. + +// Helper classes/extensions that don't change. +// Someday, this will be in a libray of its own. + +fileprivate extension Data { + init(rustBuffer: RustBuffer) { + // TODO: This copies the buffer. Can we read directly from a + // Rust buffer? + self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len)) + } +} + +// A helper class to read values out of a byte buffer. +fileprivate class Reader { + let data: Data + var offset: Data.Index + + init(data: Data) { + self.data = data + self.offset = 0 + } + + // Reads an integer at the current offset, in big-endian order, and advances + // the offset on success. Throws if reading the integer would move the + // offset past the end of the buffer. + func readInt() throws -> T { + let range = offset...size + guard data.count >= range.upperBound else { + throw UniffiInternalError.bufferOverflow + } + if T.self == UInt8.self { + let value = data[offset] + offset += 1 + return value as! T + } + var value: T = 0 + let _ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0, from: range)}) + offset = range.upperBound + return value.bigEndian + } + + // Reads an arbitrary number of bytes, to be used to read + // raw bytes, this is useful when lifting strings + func readBytes(count: Int) throws -> Array { + let range = offset..<(offset+count) + guard data.count >= range.upperBound else { + throw UniffiInternalError.bufferOverflow + } + var value = [UInt8](repeating: 0, count: count) + value.withUnsafeMutableBufferPointer({ buffer in + data.copyBytes(to: buffer, from: range) + }) + offset = range.upperBound + return value + } + + // Reads a float at the current offset. + @inlinable + func readFloat() throws -> Float { + return Float(bitPattern: try readInt()) + } + + // Reads a float at the current offset. + @inlinable + func readDouble() throws -> Double { + return Double(bitPattern: try readInt()) + } + + // Indicates if the offset has reached the end of the buffer. + @inlinable + func hasRemaining() -> Bool { + return offset < data.count + } +} + +// A helper class to write values into a byte buffer. +fileprivate class Writer { + var bytes: [UInt8] + var offset: Array.Index + + init() { + self.bytes = [] + self.offset = 0 + } + + func writeBytes(_ byteArr: S) where S: Sequence, S.Element == UInt8 { + bytes.append(contentsOf: byteArr) + } + + // Writes an integer in big-endian order. + // + // Warning: make sure what you are trying to write + // is in the correct type! + func writeInt(_ value: T) { + var value = value.bigEndian + withUnsafeBytes(of: &value) { bytes.append(contentsOf: $0) } + } + + @inlinable + func writeFloat(_ value: Float) { + writeInt(value.bitPattern) + } + + @inlinable + func writeDouble(_ value: Double) { + writeInt(value.bitPattern) + } +} + +// Protocol for types that transfer other types across the FFI. This is +// analogous go the Rust trait of the same name. +fileprivate protocol FfiConverter { + associatedtype FfiType + associatedtype SwiftType + + static func lift(_ value: FfiType) throws -> SwiftType + static func lower(_ value: SwiftType) -> FfiType + static func read(from buf: Reader) throws -> SwiftType + static func write(_ value: SwiftType, into buf: Writer) +} + +// Types conforming to `Primitive` pass themselves directly over the FFI. +fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { } + +extension FfiConverterPrimitive { + static func lift(_ value: FfiType) throws -> SwiftType { + return value + } + + static func lower(_ value: SwiftType) -> FfiType { + return value + } +} + +// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`. +// Used for complex types where it's hard to write a custom lift/lower. +fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {} + +extension FfiConverterRustBuffer { + static func lift(_ buf: RustBuffer) throws -> SwiftType { + let reader = Reader(data: Data(rustBuffer: buf)) + let value = try read(from: reader) + if reader.hasRemaining() { + throw UniffiInternalError.incompleteData + } + buf.deallocate() + return value + } + + static func lower(_ value: SwiftType) -> RustBuffer { + let writer = Writer() + write(value, into: writer) + return RustBuffer(bytes: writer.bytes) + } +} +// An error type for FFI errors. These errors occur at the UniFFI level, not +// the library level. +fileprivate enum UniffiInternalError: LocalizedError { + case bufferOverflow + case incompleteData + case unexpectedOptionalTag + case unexpectedEnumCase + case unexpectedNullPointer + case unexpectedRustCallStatusCode + case unexpectedRustCallError + case unexpectedStaleHandle + case rustPanic(_ message: String) + + public var errorDescription: String? { + switch self { + case .bufferOverflow: return "Reading the requested value would read past the end of the buffer" + case .incompleteData: return "The buffer still has data after lifting its containing value" + case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1" + case .unexpectedEnumCase: return "Raw enum value doesn't match any cases" + case .unexpectedNullPointer: return "Raw pointer value was null" + case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code" + case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified" + case .unexpectedStaleHandle: return "The object in the handle map has been dropped already" + case let .rustPanic(message): return message + } + } +} + +fileprivate let CALL_SUCCESS: Int8 = 0 +fileprivate let CALL_ERROR: Int8 = 1 +fileprivate let CALL_PANIC: Int8 = 2 + +fileprivate extension RustCallStatus { + init() { + self.init( + code: CALL_SUCCESS, + errorBuf: RustBuffer.init( + capacity: 0, + len: 0, + data: nil + ) + ) + } +} + +private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T { + try makeRustCall(callback, errorHandler: { + $0.deallocate() + return UniffiInternalError.unexpectedRustCallError + }) +} + +private func rustCallWithError + (_ errorFfiConverter: F.Type, _ callback: (UnsafeMutablePointer) -> T) throws -> T + where F.SwiftType: Error, F.FfiType == RustBuffer + { + try makeRustCall(callback, errorHandler: { return try errorFfiConverter.lift($0) }) +} + +private func makeRustCall(_ callback: (UnsafeMutablePointer) -> T, errorHandler: (RustBuffer) throws -> Error) throws -> T { + var callStatus = RustCallStatus.init() + let returnedVal = callback(&callStatus) + switch callStatus.code { + case CALL_SUCCESS: + return returnedVal + + case CALL_ERROR: + throw try errorHandler(callStatus.errorBuf) + + case CALL_PANIC: + // When the rust code sees a panic, it tries to construct a RustBuffer + // with the message. But if that code panics, then it just sends back + // an empty buffer. + if callStatus.errorBuf.len > 0 { + throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf)) + } else { + callStatus.errorBuf.deallocate() + throw UniffiInternalError.rustPanic("Rust panic") + } + + default: + throw UniffiInternalError.unexpectedRustCallStatusCode + } +} + +// Public interface members begin here. + + +public protocol FeatureProbeProtocol { + func boolValue(key: String, defaultValue: Bool) -> Bool + func boolDetail(key: String, defaultValue: Bool) -> FpBoolDetail + func numberValue(key: String, defaultValue: Double) -> Double + func numberDetail(key: String, defaultValue: Double) -> FpNumDetail + func stringValue(key: String, defaultValue: String) -> String + func stringDetail(key: String, defaultValue: String) -> FpStrDetail + func jsonValue(key: String, defaultValue: String) -> String + func jsonDetail(key: String, defaultValue: String) -> FpJsonDetail + +} + +public class FeatureProbe: FeatureProbeProtocol { + fileprivate let pointer: UnsafeMutableRawPointer + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. + required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + public convenience init(config: FpConfig, user: FpUser) { + self.init(unsafeFromRawPointer: try! + + rustCall() { + + featureprobe_17ce_FeatureProbe_new( + FfiConverterTypeFpConfig.lower(config), + FfiConverterTypeFpUser.lower(user), $0) +}) + } + + deinit { + try! rustCall { ffi_featureprobe_17ce_FeatureProbe_object_free(pointer, $0) } + } + + + + + public func boolValue(key: String, defaultValue: Bool) -> Bool { + return try! FfiConverterBool.lift( + try! + rustCall() { + + featureprobe_17ce_FeatureProbe_bool_value(self.pointer, + FfiConverterString.lower(key), + FfiConverterBool.lower(defaultValue), $0 + ) +} + ) + } + public func boolDetail(key: String, defaultValue: Bool) -> FpBoolDetail { + return try! FfiConverterTypeFpBoolDetail.lift( + try! + rustCall() { + + featureprobe_17ce_FeatureProbe_bool_detail(self.pointer, + FfiConverterString.lower(key), + FfiConverterBool.lower(defaultValue), $0 + ) +} + ) + } + public func numberValue(key: String, defaultValue: Double) -> Double { + return try! FfiConverterDouble.lift( + try! + rustCall() { + + featureprobe_17ce_FeatureProbe_number_value(self.pointer, + FfiConverterString.lower(key), + FfiConverterDouble.lower(defaultValue), $0 + ) +} + ) + } + public func numberDetail(key: String, defaultValue: Double) -> FpNumDetail { + return try! FfiConverterTypeFpNumDetail.lift( + try! + rustCall() { + + featureprobe_17ce_FeatureProbe_number_detail(self.pointer, + FfiConverterString.lower(key), + FfiConverterDouble.lower(defaultValue), $0 + ) +} + ) + } + public func stringValue(key: String, defaultValue: String) -> String { + return try! FfiConverterString.lift( + try! + rustCall() { + + featureprobe_17ce_FeatureProbe_string_value(self.pointer, + FfiConverterString.lower(key), + FfiConverterString.lower(defaultValue), $0 + ) +} + ) + } + public func stringDetail(key: String, defaultValue: String) -> FpStrDetail { + return try! FfiConverterTypeFpStrDetail.lift( + try! + rustCall() { + + featureprobe_17ce_FeatureProbe_string_detail(self.pointer, + FfiConverterString.lower(key), + FfiConverterString.lower(defaultValue), $0 + ) +} + ) + } + public func jsonValue(key: String, defaultValue: String) -> String { + return try! FfiConverterString.lift( + try! + rustCall() { + + featureprobe_17ce_FeatureProbe_json_value(self.pointer, + FfiConverterString.lower(key), + FfiConverterString.lower(defaultValue), $0 + ) +} + ) + } + public func jsonDetail(key: String, defaultValue: String) -> FpJsonDetail { + return try! FfiConverterTypeFpJsonDetail.lift( + try! + rustCall() { + + featureprobe_17ce_FeatureProbe_json_detail(self.pointer, + FfiConverterString.lower(key), + FfiConverterString.lower(defaultValue), $0 + ) +} + ) + } + +} + + +fileprivate struct FfiConverterTypeFeatureProbe: FfiConverter { + typealias FfiType = UnsafeMutableRawPointer + typealias SwiftType = FeatureProbe + + static func read(from buf: Reader) throws -> FeatureProbe { + let v: UInt64 = try buf.readInt() + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if (ptr == nil) { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + static func write(_ value: FeatureProbe, into buf: Writer) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) + } + + static func lift(_ pointer: UnsafeMutableRawPointer) throws -> FeatureProbe { + return FeatureProbe(unsafeFromRawPointer: pointer) + } + + static func lower(_ value: FeatureProbe) -> UnsafeMutableRawPointer { + return value.pointer + } +} + +public protocol FPUserProtocol { + func setAttr(key: String, value: String) + +} + +public class FpUser: FPUserProtocol { + fileprivate let pointer: UnsafeMutableRawPointer + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. + required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + public convenience init(key: String) { + self.init(unsafeFromRawPointer: try! + + rustCall() { + + featureprobe_17ce_FPUser_new( + FfiConverterString.lower(key), $0) +}) + } + + deinit { + try! rustCall { ffi_featureprobe_17ce_FPUser_object_free(pointer, $0) } + } + + + + + public func setAttr(key: String, value: String) { + try! + rustCall() { + + featureprobe_17ce_FPUser_set_attr(self.pointer, + FfiConverterString.lower(key), + FfiConverterString.lower(value), $0 + ) +} + } + +} + + +fileprivate struct FfiConverterTypeFpUser: FfiConverter { + typealias FfiType = UnsafeMutableRawPointer + typealias SwiftType = FpUser + + static func read(from buf: Reader) throws -> FpUser { + let v: UInt64 = try buf.readInt() + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if (ptr == nil) { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + static func write(_ value: FpUser, into buf: Writer) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) + } + + static func lift(_ pointer: UnsafeMutableRawPointer) throws -> FpUser { + return FpUser(unsafeFromRawPointer: pointer) + } + + static func lower(_ value: FpUser) -> UnsafeMutableRawPointer { + return value.pointer + } +} + +public protocol FPUrlProtocol { + +} + +public class FpUrl: FPUrlProtocol { + fileprivate let pointer: UnsafeMutableRawPointer + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. + required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + + deinit { + try! rustCall { ffi_featureprobe_17ce_FPUrl_object_free(pointer, $0) } + } + + + + + +} + + +fileprivate struct FfiConverterTypeFpUrl: FfiConverter { + typealias FfiType = UnsafeMutableRawPointer + typealias SwiftType = FpUrl + + static func read(from buf: Reader) throws -> FpUrl { + let v: UInt64 = try buf.readInt() + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if (ptr == nil) { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + static func write(_ value: FpUrl, into buf: Writer) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) + } + + static func lift(_ pointer: UnsafeMutableRawPointer) throws -> FpUrl { + return FpUrl(unsafeFromRawPointer: pointer) + } + + static func lower(_ value: FpUrl) -> UnsafeMutableRawPointer { + return value.pointer + } +} + +public protocol FPUrlBuilderProtocol { + func build() -> FpUrl? + +} + +public class FpUrlBuilder: FPUrlBuilderProtocol { + fileprivate let pointer: UnsafeMutableRawPointer + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. + required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + public convenience init(remoteUrl: String) { + self.init(unsafeFromRawPointer: try! + + rustCall() { + + featureprobe_17ce_FPUrlBuilder_new( + FfiConverterString.lower(remoteUrl), $0) +}) + } + + deinit { + try! rustCall { ffi_featureprobe_17ce_FPUrlBuilder_object_free(pointer, $0) } + } + + + + + public func build() -> FpUrl? { + return try! FfiConverterOptionTypeFpUrl.lift( + try! + rustCall() { + + featureprobe_17ce_FPUrlBuilder_build(self.pointer, $0 + ) +} + ) + } + +} + + +fileprivate struct FfiConverterTypeFpUrlBuilder: FfiConverter { + typealias FfiType = UnsafeMutableRawPointer + typealias SwiftType = FpUrlBuilder + + static func read(from buf: Reader) throws -> FpUrlBuilder { + let v: UInt64 = try buf.readInt() + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if (ptr == nil) { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + static func write(_ value: FpUrlBuilder, into buf: Writer) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) + } + + static func lift(_ pointer: UnsafeMutableRawPointer) throws -> FpUrlBuilder { + return FpUrlBuilder(unsafeFromRawPointer: pointer) + } + + static func lower(_ value: FpUrlBuilder) -> UnsafeMutableRawPointer { + return value.pointer + } +} + +public protocol FPConfigProtocol { + +} + +public class FpConfig: FPConfigProtocol { + fileprivate let pointer: UnsafeMutableRawPointer + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. + required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + public convenience init(remoteUrl: FpUrl, clientSdkKey: String, refreshInterval: UInt8, waitFirstResp: Bool) { + self.init(unsafeFromRawPointer: try! + + rustCall() { + + featureprobe_17ce_FPConfig_new( + FfiConverterTypeFpUrl.lower(remoteUrl), + FfiConverterString.lower(clientSdkKey), + FfiConverterUInt8.lower(refreshInterval), + FfiConverterBool.lower(waitFirstResp), $0) +}) + } + + deinit { + try! rustCall { ffi_featureprobe_17ce_FPConfig_object_free(pointer, $0) } + } + + + + + +} + + +fileprivate struct FfiConverterTypeFpConfig: FfiConverter { + typealias FfiType = UnsafeMutableRawPointer + typealias SwiftType = FpConfig + + static func read(from buf: Reader) throws -> FpConfig { + let v: UInt64 = try buf.readInt() + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if (ptr == nil) { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + static func write(_ value: FpConfig, into buf: Writer) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) + } + + static func lift(_ pointer: UnsafeMutableRawPointer) throws -> FpConfig { + return FpConfig(unsafeFromRawPointer: pointer) + } + + static func lower(_ value: FpConfig) -> UnsafeMutableRawPointer { + return value.pointer + } +} + +public struct FpBoolDetail { + public var value: Bool + public var ruleIndex: UInt16? + public var version: UInt64? + public var reason: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(value: Bool, ruleIndex: UInt16?, version: UInt64?, reason: String) { + self.value = value + self.ruleIndex = ruleIndex + self.version = version + self.reason = reason + } +} + + +extension FpBoolDetail: Equatable, Hashable { + public static func ==(lhs: FpBoolDetail, rhs: FpBoolDetail) -> Bool { + if lhs.value != rhs.value { + return false + } + if lhs.ruleIndex != rhs.ruleIndex { + return false + } + if lhs.version != rhs.version { + return false + } + if lhs.reason != rhs.reason { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + hasher.combine(ruleIndex) + hasher.combine(version) + hasher.combine(reason) + } +} + + +fileprivate struct FfiConverterTypeFpBoolDetail: FfiConverterRustBuffer { + fileprivate static func read(from buf: Reader) throws -> FpBoolDetail { + return try FpBoolDetail( + value: FfiConverterBool.read(from: buf), + ruleIndex: FfiConverterOptionUInt16.read(from: buf), + version: FfiConverterOptionUInt64.read(from: buf), + reason: FfiConverterString.read(from: buf) + ) + } + + fileprivate static func write(_ value: FpBoolDetail, into buf: Writer) { + FfiConverterBool.write(value.value, into: buf) + FfiConverterOptionUInt16.write(value.ruleIndex, into: buf) + FfiConverterOptionUInt64.write(value.version, into: buf) + FfiConverterString.write(value.reason, into: buf) + } +} + +public struct FpNumDetail { + public var value: Double + public var ruleIndex: UInt16? + public var version: UInt64? + public var reason: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(value: Double, ruleIndex: UInt16?, version: UInt64?, reason: String) { + self.value = value + self.ruleIndex = ruleIndex + self.version = version + self.reason = reason + } +} + + +extension FpNumDetail: Equatable, Hashable { + public static func ==(lhs: FpNumDetail, rhs: FpNumDetail) -> Bool { + if lhs.value != rhs.value { + return false + } + if lhs.ruleIndex != rhs.ruleIndex { + return false + } + if lhs.version != rhs.version { + return false + } + if lhs.reason != rhs.reason { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + hasher.combine(ruleIndex) + hasher.combine(version) + hasher.combine(reason) + } +} + + +fileprivate struct FfiConverterTypeFpNumDetail: FfiConverterRustBuffer { + fileprivate static func read(from buf: Reader) throws -> FpNumDetail { + return try FpNumDetail( + value: FfiConverterDouble.read(from: buf), + ruleIndex: FfiConverterOptionUInt16.read(from: buf), + version: FfiConverterOptionUInt64.read(from: buf), + reason: FfiConverterString.read(from: buf) + ) + } + + fileprivate static func write(_ value: FpNumDetail, into buf: Writer) { + FfiConverterDouble.write(value.value, into: buf) + FfiConverterOptionUInt16.write(value.ruleIndex, into: buf) + FfiConverterOptionUInt64.write(value.version, into: buf) + FfiConverterString.write(value.reason, into: buf) + } +} + +public struct FpStrDetail { + public var value: String + public var ruleIndex: UInt16? + public var version: UInt64? + public var reason: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(value: String, ruleIndex: UInt16?, version: UInt64?, reason: String) { + self.value = value + self.ruleIndex = ruleIndex + self.version = version + self.reason = reason + } +} + + +extension FpStrDetail: Equatable, Hashable { + public static func ==(lhs: FpStrDetail, rhs: FpStrDetail) -> Bool { + if lhs.value != rhs.value { + return false + } + if lhs.ruleIndex != rhs.ruleIndex { + return false + } + if lhs.version != rhs.version { + return false + } + if lhs.reason != rhs.reason { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + hasher.combine(ruleIndex) + hasher.combine(version) + hasher.combine(reason) + } +} + + +fileprivate struct FfiConverterTypeFpStrDetail: FfiConverterRustBuffer { + fileprivate static func read(from buf: Reader) throws -> FpStrDetail { + return try FpStrDetail( + value: FfiConverterString.read(from: buf), + ruleIndex: FfiConverterOptionUInt16.read(from: buf), + version: FfiConverterOptionUInt64.read(from: buf), + reason: FfiConverterString.read(from: buf) + ) + } + + fileprivate static func write(_ value: FpStrDetail, into buf: Writer) { + FfiConverterString.write(value.value, into: buf) + FfiConverterOptionUInt16.write(value.ruleIndex, into: buf) + FfiConverterOptionUInt64.write(value.version, into: buf) + FfiConverterString.write(value.reason, into: buf) + } +} + +public struct FpJsonDetail { + public var value: String + public var ruleIndex: UInt16? + public var version: UInt64? + public var reason: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(value: String, ruleIndex: UInt16?, version: UInt64?, reason: String) { + self.value = value + self.ruleIndex = ruleIndex + self.version = version + self.reason = reason + } +} + + +extension FpJsonDetail: Equatable, Hashable { + public static func ==(lhs: FpJsonDetail, rhs: FpJsonDetail) -> Bool { + if lhs.value != rhs.value { + return false + } + if lhs.ruleIndex != rhs.ruleIndex { + return false + } + if lhs.version != rhs.version { + return false + } + if lhs.reason != rhs.reason { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + hasher.combine(ruleIndex) + hasher.combine(version) + hasher.combine(reason) + } +} + + +fileprivate struct FfiConverterTypeFpJsonDetail: FfiConverterRustBuffer { + fileprivate static func read(from buf: Reader) throws -> FpJsonDetail { + return try FpJsonDetail( + value: FfiConverterString.read(from: buf), + ruleIndex: FfiConverterOptionUInt16.read(from: buf), + version: FfiConverterOptionUInt64.read(from: buf), + reason: FfiConverterString.read(from: buf) + ) + } + + fileprivate static func write(_ value: FpJsonDetail, into buf: Writer) { + FfiConverterString.write(value.value, into: buf) + FfiConverterOptionUInt16.write(value.ruleIndex, into: buf) + FfiConverterOptionUInt64.write(value.version, into: buf) + FfiConverterString.write(value.reason, into: buf) + } +} +fileprivate struct FfiConverterUInt8: FfiConverterPrimitive { + typealias FfiType = UInt8 + typealias SwiftType = UInt8 + + static func read(from buf: Reader) throws -> UInt8 { + return try lift(buf.readInt()) + } + + static func write(_ value: UInt8, into buf: Writer) { + buf.writeInt(lower(value)) + } +} +fileprivate struct FfiConverterUInt16: FfiConverterPrimitive { + typealias FfiType = UInt16 + typealias SwiftType = UInt16 + + static func read(from buf: Reader) throws -> UInt16 { + return try lift(buf.readInt()) + } + + static func write(_ value: SwiftType, into buf: Writer) { + buf.writeInt(lower(value)) + } +} +fileprivate struct FfiConverterUInt64: FfiConverterPrimitive { + typealias FfiType = UInt64 + typealias SwiftType = UInt64 + + static func read(from buf: Reader) throws -> UInt64 { + return try lift(buf.readInt()) + } + + static func write(_ value: SwiftType, into buf: Writer) { + buf.writeInt(lower(value)) + } +} +fileprivate struct FfiConverterDouble: FfiConverterPrimitive { + typealias FfiType = Double + typealias SwiftType = Double + + static func read(from buf: Reader) throws -> Double { + return try lift(buf.readDouble()) + } + + static func write(_ value: Double, into buf: Writer) { + buf.writeDouble(lower(value)) + } +} +fileprivate struct FfiConverterBool : FfiConverter { + typealias FfiType = Int8 + typealias SwiftType = Bool + + static func lift(_ value: Int8) throws -> Bool { + return value != 0 + } + + static func lower(_ value: Bool) -> Int8 { + return value ? 1 : 0 + } + + static func read(from buf: Reader) throws -> Bool { + return try lift(buf.readInt()) + } + + static func write(_ value: Bool, into buf: Writer) { + buf.writeInt(lower(value)) + } +} +fileprivate struct FfiConverterString: FfiConverter { + typealias SwiftType = String + typealias FfiType = RustBuffer + + static func lift(_ value: RustBuffer) throws -> String { + defer { + value.deallocate() + } + if value.data == nil { + return String() + } + let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len)) + return String(bytes: bytes, encoding: String.Encoding.utf8)! + } + + static func lower(_ value: String) -> RustBuffer { + return value.utf8CString.withUnsafeBufferPointer { ptr in + // The swift string gives us int8_t, we want uint8_t. + ptr.withMemoryRebound(to: UInt8.self) { ptr in + // The swift string gives us a trailing null byte, we don't want it. + let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1)) + return RustBuffer.from(buf) + } + } + } + + static func read(from buf: Reader) throws -> String { + let len: Int32 = try buf.readInt() + return String(bytes: try buf.readBytes(count: Int(len)), encoding: String.Encoding.utf8)! + } + + static func write(_ value: String, into buf: Writer) { + let len = Int32(value.utf8.count) + buf.writeInt(len) + buf.writeBytes(value.utf8) + } +} +// Helper code for FpConfig class is found in ObjectTemplate.swift +// Helper code for FpUrl class is found in ObjectTemplate.swift +// Helper code for FpUrlBuilder class is found in ObjectTemplate.swift +// Helper code for FpUser class is found in ObjectTemplate.swift +// Helper code for FeatureProbe class is found in ObjectTemplate.swift +// Helper code for FpBoolDetail record is found in RecordTemplate.swift +// Helper code for FpJsonDetail record is found in RecordTemplate.swift +// Helper code for FpNumDetail record is found in RecordTemplate.swift +// Helper code for FpStrDetail record is found in RecordTemplate.swift + +fileprivate struct FfiConverterOptionUInt16: FfiConverterRustBuffer { + typealias SwiftType = UInt16? + + static func write(_ value: SwiftType, into buf: Writer) { + guard let value = value else { + buf.writeInt(Int8(0)) + return + } + buf.writeInt(Int8(1)) + FfiConverterUInt16.write(value, into: buf) + } + + static func read(from buf: Reader) throws -> SwiftType { + switch try buf.readInt() as Int8 { + case 0: return nil + case 1: return try FfiConverterUInt16.read(from: buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionUInt64: FfiConverterRustBuffer { + typealias SwiftType = UInt64? + + static func write(_ value: SwiftType, into buf: Writer) { + guard let value = value else { + buf.writeInt(Int8(0)) + return + } + buf.writeInt(Int8(1)) + FfiConverterUInt64.write(value, into: buf) + } + + static func read(from buf: Reader) throws -> SwiftType { + switch try buf.readInt() as Int8 { + case 0: return nil + case 1: return try FfiConverterUInt64.read(from: buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypeFpUrl: FfiConverterRustBuffer { + typealias SwiftType = FpUrl? + + static func write(_ value: SwiftType, into buf: Writer) { + guard let value = value else { + buf.writeInt(Int8(0)) + return + } + buf.writeInt(Int8(1)) + FfiConverterTypeFpUrl.write(value, into: buf) + } + + static func read(from buf: Reader) throws -> SwiftType { + switch try buf.readInt() as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeFpUrl.read(from: buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + + +/** + * Top level initializers and tear down methods. + * + * This is generated by uniffi. + */ +public enum FeatureProbeLifecycle { + /** + * Initialize the FFI and Rust library. This should be only called once per application. + */ + func initialize() { + + // No initialization code needed + + } +} \ No newline at end of file diff --git a/Sources/FeatureProbe/ObjcFeatureProbe.swift b/Sources/FeatureProbe/ObjcFeatureProbe.swift new file mode 100644 index 0000000..1d3a233 --- /dev/null +++ b/Sources/FeatureProbe/ObjcFeatureProbe.swift @@ -0,0 +1,233 @@ +import Foundation + +@objc(FpConfig) +public final class OFpConfig: NSObject { + var config: FpConfig + + @objc public init(remoteUrl: OFpUrl, clientSdkKey: String, refreshInterval: UInt8, waitFirstResp: Bool) { + let remoteUrl = remoteUrl._url + config = FpConfig(remoteUrl: remoteUrl, clientSdkKey: clientSdkKey, refreshInterval: refreshInterval, waitFirstResp: waitFirstResp) + } + +} + +@objc(FeatureProbe) +public final class OcFeatureProbe: NSObject { + var fp: FeatureProbe + + @objc public init(config: OFpConfig, user: OFpUser) { + let config = config.config + let user = user.user + fp = FeatureProbe(config: config, user: user) + } + + @objc public func boolValue(key: String, defaultValue: Bool) -> Bool { + fp.boolValue(key: key, defaultValue: defaultValue) + } + + @objc public func boolDetail(key: String, defaultValue: Bool) -> OFpBoolDetail { + let d = fp.boolDetail(key: key, defaultValue: defaultValue) + return OFpBoolDetail(detail: d) + } + + @objc public func numberValue(key: String, defaultValue: Double) -> Double { + fp.numberValue(key: key, defaultValue: defaultValue) + } + + @objc public func numberDetail(key: String, defaultValue: Double) -> OFpNumberDetail { + let d = fp.numberDetail(key: key, defaultValue: defaultValue) + return OFpNumberDetail(detail: d) + } + + @objc public func stringValue(key: String, defaultValue: String) -> String { + fp.stringValue(key: key, defaultValue: defaultValue) + } + + @objc public func stringDetail(key: String, defaultValue: String) -> OFpStringDetail { + let d = fp.stringDetail(key: key, defaultValue: defaultValue) + return OFpStringDetail(detail: d) + } + + @objc public func jsonValue(key: String, defaultValue: String) -> String { + fp.jsonValue(key: key, defaultValue: defaultValue) + } + + @objc public func jsonDetail(key: String, defaultValue: String) -> OFpJsonDetail { + let d = fp.jsonDetail(key: key, defaultValue: defaultValue) + return OFpJsonDetail(detail: d) + } + +} + +@objc(FpUser) +public final class OFpUser: NSObject { + var user: FpUser + + @objc public init(key: String) { + let u = FpUser(key: key) + user = u + } + + @objc public func setAttr(key: String, value: String) { + user.setAttr(key: key, value: value) + } +} + +@objc(FpUrl) +public final class OFpUrl: NSObject { + var _url: FpUrl + + public init(url: FpUrl) { + _url = url + } +} + +@objc(FpUrlBuilder) +public final class OFpUrlBuilder: NSObject { + var builder: FpUrlBuilder + + @objc public init(remoteUrl: String) { + builder = FpUrlBuilder(remoteUrl: remoteUrl) + } + + @objc public func build() -> OFpUrl? { + let url = builder.build(); + if url == nil { + return nil + } + return OFpUrl(url: url!) + } + +} + +@objc(FpBoolDetail) +public final class OFpBoolDetail: NSObject { + var _detail: FpBoolDetail + + public init(detail: FpBoolDetail) { + _detail = detail + } + + @objc public var value: Bool { + _detail.value + } + + @objc public var ruleIndex: NSNumber { + if _detail.ruleIndex == nil { + return -1 + } else { + return _detail.ruleIndex! as NSNumber + } + } + + @objc public var version: NSNumber { + if _detail.version == nil { + return -1 + } else { + return _detail.version! as NSNumber + } + } + + @objc public var reason: String { + _detail.reason + } +} + +@objc(FpNumberDetail) +public final class OFpNumberDetail: NSObject { + var _detail: FpNumDetail + + public init(detail: FpNumDetail) { + _detail = detail + } + + @objc public var value: Double { + _detail.value + } + + @objc public var ruleIndex: NSNumber { + if _detail.ruleIndex == nil { + return -1 + } else { + return _detail.ruleIndex! as NSNumber + } + } + + @objc public var version: NSNumber { + if _detail.version == nil { + return -1 + } else { + return _detail.version! as NSNumber + } + } + + @objc public var reason: String { + _detail.reason + } +} + +@objc(FpStringDetail) +public final class OFpStringDetail: NSObject { + var _detail: FpStrDetail + + public init(detail: FpStrDetail) { + _detail = detail + } + + @objc public var value: String { + _detail.value + } + + @objc public var ruleIndex: NSNumber { + if _detail.ruleIndex == nil { + return -1 + } else { + return _detail.ruleIndex! as NSNumber + } + } + + @objc public var version: NSNumber { + if _detail.version == nil { + return -1 + } else { + return _detail.version! as NSNumber + } + } + + @objc public var reason: String { + _detail.reason + } +} + +@objc(FpJsonDetail) +public final class OFpJsonDetail: NSObject { + var _detail: FpJsonDetail + + public init(detail: FpJsonDetail) { + _detail = detail + } + + @objc public var value: String { + _detail.value + } + + @objc public var ruleIndex: NSNumber { + if _detail.ruleIndex == nil { + return -1 + } else { + return _detail.ruleIndex! as NSNumber + } + } + + @objc public var version: NSNumber { + if _detail.version == nil { + return -1 + } else { + return _detail.version! as NSNumber + } + } + + @objc public var reason: String { + _detail.reason + } +}