Skip to main content

Update tracker configuration remotely

Remote configuration allows for the configuration of the tracker without distributing an app update. When remote configuration is not used, changing the tracking configuration can require the resubmission of the whole app bundle.

With remote configuration, the tracker can download a configuration file and automatically reconfigure itself based on the downloaded file.

A tracker that will be remotely configured is created slightly differently from a locally-configured tracker, which is created using Snowplow.createTracker(). Instead, Snowplow.setup() is used. This example code will create a tracker with namespace "defaultNamespace" that can be configured remotely. Access the tracker using Snowplow.defaultTracker or Snowplow.getTracker("defaultNamespace").

  1. In your application delegate AppDelegate.swift add import SnowplowTracker.

  2. In the application(_:didFinishLaunchingWithOptions:) method, set up the SDK as follows:

// Indicate the URL where to download the config file
let remoteConfig = RemoteConfiguration(
endpoint: "https://remote-config.com",
method: .get
)

// Default configuration used when the remote config is not accessible
// Network, tracker, subject and sessionConfigurations can be provided
let defaultNetworkConfig = NetworkConfiguration(
endpoint: "https://snowplow-collector-url.com",
method: .post)
let defaultConfig = [ConfigurationBundle(namespace: "defaultNamespace", networkConfiguration: defaultNetworkConfig)]

// Optional callback for when the tracker reconfigures itself passing a list of active namespaces and the state of the configuration describing where it was fetched from
let successCallback: ([String]?) -> Void = { namespaces, state in
// This callback can be used for last minute, post-configuration, updates once the tracker instance is enabled and configured.
}

// Set up tracker with remote configuration
Snowplow.setup(remoteConfiguration: remoteConfig, defaultConfiguration: defaultConfig, onSuccess: successCallback)

On app startup, the Snowplow tracker initializer will attempt to download the remote configuration file. Meanwhile, it will initialize the tracker instance (or multiple tracker instances) based on the last cached configuration. The cached configuration is the last configuration downloaded remotely. If it's not available, the tracker initializer will spin up the default configuration passed as a parameter. Every time the initializer successfully initializes the tracker instances it calls a callback passing the list of activated namespaces and the state of the configuration which represents the source where the configuration was retrieved from (using default values, cache, or fetched from the remote endpoint). The callback can be used for last minute settings at runtime once the tracker has been instanced.

The defaultConfiguration parameter takes a list of ConfigurationBundle objects. Each one represents a tracker instance and it's own configuration. Each tracker instance is identified by a namespace which is a parameter required in each ConfigurationBundle.

The optional onSuccess callback is called for each successful configuration. For example, if the tracker has a cached configuration and it's downloading a new configuration, first the onSuccess callback will be called for the cached configuration and then it will be called when the downloaded configuration is applied. When it happens the cached configuration is substituted with the downloaded one.

The remote configuration file

The URL where the configuration file is hosted must be provided in the RemoteConfiguration object passed to the setup() method. There aren't restrictions about where to host the file but possible solutions may be S3 with Cloudfront or a Google Cloud Storage bucket.

The configuration file is simply a JSON file compliant with the Remote Config JSONSchema.

An example of remote config specification:

{
"$schema": "http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0",
"configurationVersion": 1,
"configurationBundle": [
{
"namespace": "testTracker1",
"networkConfiguration": {
"endpoint": "http://<collector.url>",
"method": "get"
},
"trackerConfiguration": {
"appId": "app-identifier",
"devicePlatform": "mob",
"logLevel": "off",
"sessionContext": false,
"applicationContext": false,
"platformContext": false,
"geoLocationContext": false,
"screenContext": false,
"screenViewAutotracking": false,
"lifecycleAutotracking": false,
"installAutotracking": false,
"exceptionAutotracking": false,
"diagnosticAutotracking": false
},
"subjectConfiguration": {
"userId": "example",
"networkUserId": "example",
"domainUserId": "example",
"useragent": "example",
"ipAddress": "example",
"timezone": "example",
"language": "example"
},
"sessionConfiguration": {
"backgroundTimeout": 1800,
"foregroundTimeout": 1800
}
}
]
}

The required fields are:

  • $schema: It specifies the format of the configuration file and it's used by the tracker to check if the format is compatible with that version of the tracker.
  • configurationVersion: It's an incremental version number that identifies the current configuration. The tracker compares this value with the configurationVersion of the cached configuration to decide whether to update or not the tracker configuration.
    caution

    The version MUST be increased on each update.

  • configurationBundle: This is a list of configurations for the various tracker instances the developer wants to activate in the app. Usually there is a unique tracker instance for the app so the configurationBundle will likely be an array of a single object like in the example above.

The elements of the configurationBundle list are just a subset of the common configuration settings described in the "Introduction" section. At the moment the configuration objects configurable remotely are:

  • networkConfiguration
  • trackerConfiguration
  • subjectConfiguration
  • sessionConfiguration

Note: The networkConfiguration is fundamental in order to set the collector endpoint, the URL where the tracked events will be sent. If the ConfigurationBundle has a namespace but not the networkConfiguration, the tracker initializer will remove the tracker instance with the corresponding namespace.

Refresh the configuration

By default the tracker will attempt to download the configuration file at the start up of the app. To check for configuration updates more often, for example at runtime or when the app comes back from background state, it's possible to use the refresh() method.

let successCallback: ([String]?) -> Void = { namespaces, state in
// This callback can be used for last minute, post-configuration, updates once the tracker instance is enabled and configured.
}

Snowplow.refresh(onSuccess: successCallback)

The method needs only the callback parameter, without any remote configuration url or default configuration, because this is intended just for the configuration updates, not for the initial setup. The tracker will be configured only if a new configuration (with a higher configurationVersion value) is available at the url indicated in the RemoteConfiguration passed earlier at start up of the app in the setup method call.

Policy for runtime settings and remote configuration updates

When the tracker initializer updates the tracker configuration, it would reset all the previous configuration with the new settings. Obviously, this can cause issues in case some runtime configuration has been applied meanwhile. To avoid this the tracker keeps track of runtime changes in the configuration settings and when a new remote configuration is downloaded and applied, it doesn't override the settings already changed at runtime.

A clear example is a runtime change on userId on SubjectController:

// Load configuration with userId = nil
Snowplow.setup(remoteConfiguration: remoteConfig, defaultConfiguration: defaultConfig, onSuccess: successCallback)

// userId is set to nil

// ...later...

tracker.subject.userId = "my-runtime-updated-userId"

// userId is is set to "my-runtime-updated-userId"

// ...later...

// Later refreshing the configuration with userId = nil
Snowplow.refresh(onSuccess: successCallback)

// userId is still set to "my-runtime-updated-userId" because it was set at runtime
Was this page helpful?