Revision control

Copy as Markdown

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
import Glean
/// This is the main experiments API, which is exposed through the global [Nimbus] object.
///
/// Application developers are encouraged to build against this API protocol, and use the `Nimbus.create` method
/// to create the correct implementation for them.
///
/// Feature developers configuring their features shoiuld use the methods in `NimbusFeatureConfiguration`.
/// These are safe to call from any thread. Developers building UI tools for the user or QA to modify experiment
/// enrollment will mostly use `NimbusUserConfiguration` methods. Application developers integrating
/// `Nimbus` into their app should use the methods in `NimbusStartup`.
///
public protocol NimbusInterface: FeaturesInterface, NimbusStartup,
NimbusUserConfiguration, NimbusBranchInterface, NimbusMessagingProtocol,
NimbusEventStore, NimbusQueues {}
public typealias NimbusApi = NimbusInterface
public protocol NimbusBranchInterface {
/// Get the currently enrolled branch for the given experiment
///
/// - Parameter featureId The string feature id that applies to the feature under experiment.
/// - Returns A String representing the branch-id or "slug"; or `nil` if not enrolled in this experiment.
///
/// - Note: Consumers of this API should switch to using the Feature Variables API
func getExperimentBranch(experimentId: String) -> String?
}
public extension FeaturesInterface {
/// Get the variables needed to configure the feature given by `featureId`.
///
/// By default this sends an exposure event.
///
/// - Parameters:
/// - featureId The string feature id that identifies to the feature under experiment.
///
/// - Returns a `Variables` object used to configure the feature.
func getVariables(featureId: String) -> Variables {
return getVariables(featureId: featureId, sendExposureEvent: true)
}
}
public protocol NimbusStartup {
/// Open the database and populate the SDK so as make it usable by feature developers.
///
/// This performs the minimum amount of I/O needed to ensure `getExperimentBranch()` is usable.
///
/// It will not take in to consideration previously fetched experiments: `applyPendingExperiments()`
/// is more suitable for that use case.
///
/// This method uses the single threaded worker scope, so callers can safely sequence calls to
/// `initialize` and `setExperimentsLocally`, `applyPendingExperiments`.
///
func initialize()
/// Fetches experiments from the RemoteSettings server.
///
/// This is performed on a background thread.
///
/// Notifies `.nimbusExperimentsFetched` to observers once the experiments has been fetched from the
/// server.
///
/// Notes:
/// * this does not affect experiment enrollment, until `applyPendingExperiments` is called.
/// * this will overwrite pending experiments previously fetched with this method, or set with
/// `setExperimentsLocally`.
///
func fetchExperiments()
/// Calculates the experiment enrollment from experiments from the last `fetchExperiments` or
/// `setExperimentsLocally`, and then informs Glean of new experiment enrollment.
///
/// Notifies `.nimbusExperimentsApplied` once enrollments are recalculated.
///
func applyPendingExperiments() -> Operation
func applyLocalExperiments(fileURL: URL) -> Operation
/// Set the experiments as the passed string, just as `fetchExperiments` gets the string from
/// the server. Like `fetchExperiments`, this requires `applyPendingExperiments` to be called
/// before enrollments are affected.
///
/// The string should be in the same JSON format that is delivered from the server.
///
/// This is performed on a background thread.
///
/// - Parameter experimentsJson string representation of the JSON document in the same format
/// delivered by RemoteSettings.
///
func setExperimentsLocally(_ experimentsJson: String)
/// A utility method to load a file from resources and pass it to `setExperimentsLocally(String)`.
///
/// - Parameter fileURL the URL of a JSON document in the app `Bundle`.
///
func setExperimentsLocally(_ fileURL: URL)
/// Testing method to reset the enrollments and experiments database back to its initial state.
func resetEnrollmentsDatabase() -> Operation
/// Enable or disable fetching of experiments.
///
/// This is performed on a background thread.
///
/// This is only used during QA of the app, and not meant for application developers.
/// Application developers should allow users to opt out with `setGlobalUserParticipation`
/// instead.
///
/// - Parameter enabled
func setFetchEnabled(_ enabled: Bool)
/// The complement for [setFetchEnabled].
///
/// This is only used during QA of the app, and not meant for application developers.
///
/// - Returns true if fetch is allowed
func isFetchEnabled() -> Bool
/// Dump the state of the Nimbus SDK to the rust log.
/// This is only useful for testing.
func dumpStateToLog()
}
public protocol NimbusUserConfiguration {
/// Opt out of a specific experiment
///
/// - Parameter experimentId The string id or "slug" of the experiment for which to opt out of
///
func optOut(_ experimentId: String)
/// Opt in to a specific experiment with a particular branch.
///
/// For data-science reasons: This should not be utilizable by the the user.
///
/// - Parameters:
/// - experimentId The id or slug of the experiment to opt in
/// - branch The id or slug of the branch with which to enroll.
///
func optIn(_ experimentId: String, branch: String)
/// Call this when toggling user preferences about sending analytics.
func resetTelemetryIdentifiers()
/// Control the opt out for all experiments at once. This is likely a user action.
///
var globalUserParticipation: Bool { get set }
/// Get the list of currently enrolled experiments
///
/// - Returns A list of `EnrolledExperiment`s
///
func getActiveExperiments() -> [EnrolledExperiment]
/// For a given experiment id, returns the branches available.
///
/// - Parameter experimentId the specifies the experiment.
/// - Returns a list of one more branches for the given experiment, or `nil` if no such experiment exists.
func getExperimentBranches(_ experimentId: String) -> [Branch]?
/// Get the list of currently available experiments for the `appName` as specified in the `AppContext`.
///
/// - Returns A list of `AvailableExperiment`s
///
func getAvailableExperiments() -> [AvailableExperiment]
}
public protocol NimbusEventStore {
/// Records an event to the Nimbus event store.
///
/// The method obtains the event counters for the `eventId` that is passed in, advances them if
/// needed, then increments the counts by `count`. If an event counter does not exist for the `eventId`,
/// one will be created.
///
/// - Parameter count the number of events seen just now. This is usually 1.
/// - Parameter eventId string representing the id of the event which should be recorded.
func recordEvent(_ count: Int, _ eventId: String)
/// Records an event to the Nimbus event store.
///
/// The method obtains the event counters for the `eventId` that is passed in, advances them if
/// needed, then increments the counts by 1. If an event counter does not exist for the `eventId`,
/// one will be created.
///
/// - Parameter eventId string representing the id of the event which should be recorded.
func recordEvent(_ eventId: String)
/// Records an event as if it were emitted in the past.
///
/// This method is only likely useful during testing, and so is by design synchronous.
///
/// - Parameter count the number of events seen just now. This is usually 1.
/// - Parameter eventId string representing the id of the event which should be recorded.
/// - Parameter timeAgo the duration subtracted from now when the event are said to have happened.
/// - Throws NimbusError if timeAgo is negative.
func recordPastEvent(_ count: Int, _ eventId: String, _ timeAgo: TimeInterval) throws
/// Advance the time of the event store into the future.
///
/// This is not needed for normal operation, but is especially useful for testing queries,
/// without having to wait for actual time to pass.
///
/// - Parameter bySeconds the number of seconds to advance into the future. Must be positive.
/// - Throws NimbusError is [bySeconds] is negative.
func advanceEventTime(by duration: TimeInterval) throws
/// Clears the Nimbus event store.
///
/// This should only be used in testing or cases where the previous event store is no longer viable.
func clearEvents()
}
public typealias NimbusEvents = NimbusEventStore
public protocol NimbusQueues {
/// Waits for the fetch queue to complete
func waitForFetchQueue()
/// Waits for the db queue to complete
func waitForDbQueue()
}
/// Notifications emitted by the `NotificationCenter`.
///
public extension Notification.Name {
static let nimbusExperimentsFetched = Notification.Name("nimbusExperimentsFetched")
static let nimbusExperimentsApplied = Notification.Name("nimbusExperimentsApplied")
}
/// This struct is used during in the `create` method to point `Nimbus` at the given `RemoteSettings` server.
///
public struct NimbusServerSettings {
public init(url: URL, collection: String = remoteSettingsCollection) {
self.url = url
self.collection = collection
}
public let url: URL
public let collection: String
}
public let remoteSettingsCollection = "nimbus-mobile-experiments"
public let remoteSettingsPreviewCollection = "nimbus-preview"
/// Name, channel and specific context of the app which should agree with what is specified in Experimenter.
/// The specific context is there to capture any context that the SDK doesn't need to be explicitly aware of.
///
public struct NimbusAppSettings {
public init(appName: String, channel: String, customTargetingAttributes: [String: Any] = [String: Any]()) {
self.appName = appName
self.channel = channel
self.customTargetingAttributes = customTargetingAttributes
}
public let appName: String
public let channel: String
public let customTargetingAttributes: [String: Any]
}
/// This error reporter is passed to `Nimbus` and any errors that are caught are reported via this type.
///
public typealias NimbusErrorReporter = (Error) -> Void
/// `ExperimentBranch` is a copy of the `Branch` without the `FeatureConfig`.
public typealias Branch = ExperimentBranch