GrowthBook - SDK

安装

CocoaPods

CocoaPods 是 Cocoa 项目的依赖管理工具。有关使用和安装说明,请访问其网站。 要使用 CocoaPods 将 GrowthBook 集成到您的 Xcode 项目中,请在您的 Podfile 中指定它

source 'https://github.com/CocoaPods/Specs.git'
pod 'GrowthBook-IOS'
pod install
Swift Package Manager - SPM

Swift Package Manager 是一种用于自动化 Swift 代码分发的工具,并已集成到 swift 编译器中。

设置好 Swift 包后,将 GrowthBook 添加为依赖项非常简单,只需将其添加到 Package.swiftdependencies 值即可。

dependencies: [
    .package(url: "https://github.com/growthbook/growthbook-swift.git")
]

集成

集成非常简单

  1. 创建一个 Growth Book,有几种方法:使用 API 密钥和主机 URL,仅主机 URL,仅 JSON
  2. 在应用程序启动时,按照以下说明进行 SDK 初始化

现在,您可以在 Growth Book 应用程序中启动/停止测试、调整覆盖率和变体权重,以及将获胜变体应用于 100% 的流量,而无需将代码更改部署到您的站点。

var sdkInstance: GrowthBookSDK = GrowthBookBuilder(apiHost: <GrowthBook/API_HOST>,
    clientKey: <GrowthBook/Client_KEY>,
    attributes: <[String: Any]>,
    trackingCallback: { experiment, experimentResult in 

    }, backroundSync: Bool?).initializer()

如果您打算使用数据加密,您还必须提供加密密钥。

var sdkInstance: GrowthBookSDK = GrowthBookBuilder(apiHost: <GrowthBook/API_HOST>,
    clientKey: <GrowthBook/Client_KEY>,
    attributes: <[String: Any]>,
    trackingCallback: { experiment, experimentResult in 

    }).initializer()
var sdkInstance: GrowthBookSDK = GrowthBookBuilder(apiHost: <GrowthBook/API_HOST>,
    clientKey: <GrowthBook/Client_KEY>,
    attributes: <[String: Any]>,
    trackingCallback: { experiment, experimentResult in 

    }).initializer()

在初始化时可以设置其他属性

var sdkInstance: GrowthBookSDK = GrowthBookBuilder(apiHost: <GrowthBook/API_HOST>,
    clientKey: <GrowthBook/Client_KEY>,
    attributes: <[String: Any]>,
    trackingCallback: { experiment, experimentResult in 

    })
    .setRefreshHandler { isRefreshed in
        
    } // Get Callbacks when SDK refreshed its cache
    .setNetworkDispatcher(networkDispatcher: <Network Dispatcher>) // Pass Network client to be used for API Calls
    .setEnabled(isEnabled: true) // Enable / Disable experiments
    .setQAMode(isEnabled: true) // Enable / Disable QA Mode
    .setForcedVariations(forcedVariations: <[String: Int]>) // Pass Forced Variations
    .setLogLevel(<LoggerLevel>) // Set log level for SDK Logger, by default log level is set to `info`
    .setCacheDirectory(<CacheDirectory>) // This function configures the cache directory used by the application to the designated directory type. Subsequent cache-related operations will target this directory.
    .setStickyBucketService(stickyBucketService: StickyBucketService()) // This function creates a sticky bucket service.
    .initializer()

用法

func evalFeature(id: String) -> FeatureResult
func run(experiment: Experiment) -> ExperimentResult
func refreshCache()
func getGBContext() -> Context
func getFeatures() -> Features
func getFeatureValue(feature id: String, defaultValue: JSON) -> JSON
func isOn(feature id: String) -> Bool
func setEncryptedFeatures(encryptedString: String, encryptionKey: String, subtle: CryptoProtocol? = nil)

模型

/// Defines the GrowthBook context.
class Context {
    /// api host
    public let apiHost: String?
    /// unique client key
    public let clientKey: String?
    /// Encryption key for encrypted features.
    let encryptionKey: String?
    /// Switch to globally disable all experiments. Default true.
    let isEnabled: Bool
    /// Map of user attributes that are used to assign variations
    var attributes: JSON
    /// Force specific experiments to always assign a specific variation (used for QA)
    let forcedVariations: JSON?
    /// If true, random assignment is disabled and only explicitly forced variations are used.
    let isQaMode: Bool
    /// A function that takes experiment and result as arguments.
    let trackingCallback: (Experiment, ExperimentResult) -> Void

    // Keys are unique identifiers for the features and the values are Feature objects.
    // Feature definitions - To be pulled from API / Cache
    var features: Features
}
/// A Feature object consists of possible values plus rules for how to assign values to users.
class Feature {
    /// The default value (should use null if not specified)
    let defaultValue: JSON?
    /// Array of Rule objects that determine when and how the defaultValue gets overridden
    let rules: [FeatureRule]?
}

/// Rule object consists of various definitions to apply to calculate feature value
struct FeatureRule {
    /// Optional targeting condition
    let condition: JSON?
    /// What percent of users should be included in the experiment (between 0 and 1, inclusive)
    let coverage: Float?
    /// Immediately force a specific value (ignore every other option besides condition and coverage)
    let force: JSON?
    /// Run an experiment (A/B test) and randomly choose between these variations
    let variations: [JSON]?
    /// The globally unique tracking key for the experiment (default to the feature key)
    let key: String?
    /// How to weight traffic between variations. Must add to 1.
    let weights: [Float]?
    /// A tuple that contains the namespace identifier, plus a range of coverage for the experiment.
    let namespace: [JSON]?
    /// What user attribute should be used to assign variations (defaults to id)
    let hashAttribute: String?
    /// Hash version of hash function
    let hashVersion: Float?
    /// A more precise version of `coverage`
    let range: BucketRange?
    /// Ranges for experiment variations
    let ranges: [BucketRange]?
    /// Meta info about the experiment variations
    let meta: [VariationMeta]?
    /// Array of filters to apply to the rule
    let filters: [Filter]?
    /// Seed to use for hashing
    let seed: String?
    /// Human-readable name for the experiment
    let name: String?
    /// The phase id of the experiment
    let phase: String?
    /// Array of tracking calls to fire
    let tracks: [TrackData]?
}

/// Enum For defining feature value source
enum FeatureSource: String {
    /// Queried Feature doesn't exist in GrowthBook
    case unknownFeature
    /// Default Value for the Feature is being processed
    case defaultValue
    /// Forced Value for the Feature is being processed
    case force
    /// Experiment Value for the Feature is being processed
    case experiment
}

 /// Result for Feature
class FeatureResult {
    /// The assigned value of the feature
    let value: JSON?
    /// The assigned value cast to a boolean
    public var isOn: Bool = false
    /// The assigned value cast to a boolean and then negated
    public var isOff: Bool = true
    /// One of "unknownFeature", "defaultValue", "force", or "experiment"
    let source: String
    /// When source is "experiment", this will be the Experiment object used
    let experiment: Experiment?
    /// When source is "experiment", this will be an ExperimentResult object
    let experimentResult: ExperimentResult?
}
/// Defines a single experiment
class Experiment {
    /// The globally unique tracking key for the experiment
    let key: String
    /// The different variations to choose between
    let variations: [JSON]
    /// A tuple that contains the namespace identifier, plus a range of coverage for the experiment
    let namespace: [JSON]?
    /// All users included in the experiment will be forced into the specific variation index
    let hashAttribute: String?
    /// How to weight traffic between variations. Must add to 1.
    var weights: [Float]?
    /// If set to false, always return the control (first variation)
    var isActive: Bool
    /// What percent of users should be included in the experiment (between 0 and 1, inclusive)
    var coverage: Float?
    /// Optional targeting condition
    var condition: JSON?
    /// All users included in the experiment will be forced into the specific variation index
    var force: Int?
    /// Array of ranges, one per variation
    let ranges: [BucketRange]?
    /// Meta info about the variations
    let meta: [VariationMeta]?
    /// Array of filters to apply
    let filters: [Filter]?
    /// The hash seed to use
    let seed: String?
    /// Human-readable name for the experiment
    let name: String?
    /// Id of the current experiment phase
    let phase: String?
}

/// The result of running an Experiment given a specific Context
class ExperimentResult {
    /// Whether or not the user is part of the experiment
    let inExperiment: Bool
    /// The array index of the assigned variation
    let variationId: Int
    /// The array value of the assigned variation
    let value: JSON
    /// The user attribute used to assign a variation
    let hashAttribute: String?
    /// The value of that attribute
    let valueHash: String?
    /// The unique key for the assigned variation
    let key: String
    /// The human-readable name of the assigned variation
    let name: String?
    /// The hash value used to assign a variation (float from `0` to `1`)
    let bucket: Float?
    /// Used for holdout groups
    let passthrough: Bool?
}

/// Meta info about the variations
public struct VariationMeta {
    /// Used to implement holdout groups
    let passthrough: Bool?
    /// A unique key for this variation
    let key: String?
    /// A human-readable name for this variation
    let name: String?
}

///Used for remote feature evaluation to trigger the `TrackingCallback`
public struct TrackData {
    let experiment: Experiment
    let result: ExperimentResult
}

流式更新

要启用流式更新,请将 backgroundSync 变量设置为“true”

var sdkInstance: GrowthBookSDK = GrowthBookBuilder(apiHost: <GrowthBook/API_KEY>, clientKey: <GrowthBook/ClientKey>, attributes: <[String: Any]>, trackingCallback: { experiment, experimentResult in 
    }, refreshHandler: { isRefreshed in
    }, backgroundSync: true)
    .initializer()

远程评估

此模式通过仅在私有服务器上评估功能标志,将后端 SDK 的安全优势带到前端。 使用远程评估可确保客户端永远看不到目标规则或未使用的功能变体中的任何敏感信息。 请注意,远程评估不应在后端上下文中使用。

您必须在 SDK 连接设置中启用远程评估。 云客户还需要自行托管 GrowthBook 代理服务器或自定义远程评估后端。

要使用远程评估,请将 remoteEval: true 属性添加到您的 SDK 实例。 每当用户属性或其他依赖项更改时,都会进行新的评估 API 调用。 您可以选择通过设置 cacheKeyAttributes 属性(一个属性名称数组,当更改时,会触发新的评估调用)将这些 API 调用限制为特定的属性更改。

var sdkInstance: GrowthBookSDK = GrowthBookBuilder(apiHost: <GrowthBook/API_KEY>, clientKey: <GrowthBook/ClientKey>, attributes: <[String: Any]>, trackingCallback: { experiment, experimentResult in 
    }, refreshHandler: { isRefreshed in
    }, remoteEval: true)
    .initializer()

注意

如果您想在使用远程评估的同时实现 Sticky Bucketing,您必须配置您的远程评估后端以支持 Sticky Bucketing。 您无需为客户端 SDK 提供 StickyBucketService 实例。

Sticky Bucketing

Sticky bucketing 确保用户看到相同的实验变体,即使在用户会话、用户登录状态或实验参数发生更改时也是如此。 有关更多信息,请参见Sticky Bucketing 文档。 如果您的组织和实验支持 sticky bucketing,您必须实现 StickyBucketService 的一个实例才能使用 Sticky Bucketing。 为了简单起见,可以使用浏览器的 LocalStorage 进行存储(可以为其他环境进行 polyfill)。

许可

本项目使用 MIT 许可证。 核心 GrowthBook 应用程序将始终保持开放和免费,尽管我们将来可能会添加一些商业企业附加组件。