Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question userAttributes vs appAttributes (and misc)

Discussion in 'Unity Remote Config' started by Maeslezo, Oct 26, 2023.

  1. Maeslezo


    Jun 16, 2015

    I've been studying the Remote Config Documentation ( and I have a few questions. I can split these questions in smaller threads if requested

    userAttributes vs appAttributes
    I understand you can use this structs to add information to be used in the Game Overrides rules.
    In the documentation, it says:
    But then, they are used like this:
    So score is used in the app structure and in the user structure.
    Taking into account that any value can be used in both structures, what is the point of having two structures? What is the difference, semantically speaking?

    Unity Attributes
    There are several attributes that you can use in your rules (, unity.cpu,
    unity.appVersion, etc.) You don't need to send these attributes, right? They are sent automatically by the remote config service

    In the documentation, you can find
    Code (CSharp):
    1.         var settings = RemoteConfigService.Instance.GetConfig("settings");
    2.         var specialConfigType = RemoteConfigService.Instance.GetConfig("specialConfigType");
    What does it mean different types of config? I understand that settings are the actual settings we're fetching, but what else could it be? Is there any other type of config ("specialConfigType")?

    Options.SetEnvironmentName vs RemoteConfigService.Instance.SetEnvironmentID
    I've found that you can set the environment in two ways, one with the environment name and other with the environment id. Are they equivalent? Or do I need to set both? Could it be possible (although incorrect by design), to set an environment for analytics and other environment for remote settings? Or the second call is going to override the first one?
    Code (CSharp):
    1.         var options = new InitializationOptions()
    2.             .SetEnvironmentName("environmentName");
    4.         await UnityServices.InitializeAsync(options);
    6.         RemoteConfigService.Instance.SetEnvironmentID("environmentId"); //equivalent?
    I've seen you can define a filter in the fetch call. The filter definition is

    Code (CSharp):
    1.     public struct filterAttributes {
    2.       // Optionally declare variables for attributes to filter on any of following parameters:
    3.         public string[] key;
    4.         public string[] type;
    5.         public string[] schemaId;
    6.     }
    I understand that key is to filter by key names and type is to filter by key types. But, what are we filtering with schemaId?

    Thank you
  2. vd_unity


    Unity Technologies

    Sep 11, 2014
    Hi Maeslezo,

    thanks for your questions!
    I will try to answer those to the best of my understanding:

    1. userAttributes vs appAttributes
    As you correctly stated, there is no difference in priority when adding those attributes to your request.
    Basically, both of those attributes are there to help you with the segmentation for the Game Overrides,
    therefore separation into two categories is only there for organizational purposes, as some of them might pertain more to the user (e.g. customId) or the app (e.g. level).

    2. Unity Attributes
    Those are indeed sent by Unity, and you do not need to worry about setting those, however, you can use them for the segmentation, here is the list of typical Unity attributes in the request:
    Code (CSharp):
    1. "attributes": {
    2.     "unity": {
    3.       "osVersion": "Mac OS X 13.6.0",
    4.       "appVersion": "0.1",
    5.       "rootedJailbroken": false,
    6.       "model": "MacBookPro16,1",
    7.       "cpu": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
    8.       "cpuCount": 16,
    9.       "cpuFrequency": 2300,
    10.       "ram": 32768,
    11.       "vram": 8176,
    12.       "screen": "3840 x 2160 @ 30.0000003Hz",
    13.       "dpi": 72,
    14.       "language": "en",
    15.       "appName": "com.DefaultCompany.rotatingCubeCCD",
    16.       "appInstallMode": "Editor",
    17.       "appInstallStore": "",
    18.       "graphicsDeviceId": 0,
    19.       "graphicsDeviceVendorId": 0,
    20.       "graphicsName": "AMD Radeon Pro 5500M",
    21.       "graphicsDeviceVendor": "Apple",
    22.       "graphicsVersion": "Metal",
    23.       "graphicsShader": 50,
    24.       "maxTextureSize": 16384,
    25.       "platform": "OSXEditor"
    26.     },
    so you can segment on any of them e.g
    Code (CSharp):
    1. unity.platform == 'iOS'
    more details here

    3. RemoteConfigService.Instance.GetConfig
    The majority of developers only use one configuration type, "settings" by default.
    For specific internal users, we developed a unique configuration type, however despite being documented, this capability is not generally accessible.

    4. Options.SetEnvironmentName vs RemoteConfigService.Instance.SetEnvironmentID
    As you correctly stated, there are two ways to set the environment
    .SetEnvironmentName(environmentName) vs .SetEnvironmentID(environmentId).

    The first method comes from the core services, and it takes the name string as a parameter, and core provides environmentId which in turn calls the .SetEnvironmentID(environmentId) from RC anyways, and sets up the payload for RC request.
    They both do the same thing, but the one provided by RC is more direct, however, not everyone knows the environmentId, as it is easier to remember the name.

    Of course, it is enough to use only one of those methods, and if you know the environmentId, SetEnvironmentID(environmentId) is more direct. If you omit both methods, you will get the default environment.

    One small caveat - if you use SetEnvironmentName, the environmentId is only obtained in core when signing in to a player. If you fetch your remote config prior to signing in, it will by default always send you back your production values.

    5. SchemaId
    Using schemas is also a feature that is currently available only to some internal customers, but will be publicly available very soon.
    In simple terms, using a web dashboard, user will be able to define a schema for single-level JSON keys, so errors when updating a key can be prevented (let's say we define one type for the key and someone tries to update that key with another type - if schema is applied to that setting, an update will be rejected)

    In the context of filtering attributes during the Fetch call, that schema filter serves the purpose of reducing the response payload only to settings under certain schema

    Sorry for this somewhat long answer, RC was one of the first packages developed, and it had to cater to many standards and packages that were developed after, which inadvertently introduced additional unwanted complexity.
    In its true nature, RC is a simple key-value store, able to retrieve correct settings based on users' requests, utilizing environments and campaigns.

    Hope this helps,
    Maeslezo likes this.
  3. Maeslezo


    Jun 16, 2015
    Thank you for your kind response, it has been very enlightening.

    Very interesting this paragraph:
    Actually we were having a bug because our analytics were all to the production environment no matter which environment we set. I think this explain the issue.

    Can we get which environment are we using in runtime? So we can check that we're actually using the intended environment

    await UnityServices.InitializeAsync(options) doesn't return anything

    We would rather use RemoteConfigService.Instance.SetEnvironmentID("environmentId"), so we don't have to anonymously signIn, but unfortunally there's not an awaitable version of the method ( RemoteConfigService.Instance.SetEnvironmentIDAsync)
  4. vd_unity


    Unity Technologies

    Sep 11, 2014
    Thanks, @Maeslezo for the follow-up!

    The environment is set during the runtime, as both .SetEnvironmentName(environmentName) and .SetEnvironmentID(environmentId) are just setting the environmentId in the payload which will be used within RC request later by the Fetch() method.
    Of course, if you do not set the environment by using any of those, the config from the default environment ("production") will be returned.
    After the payload is sent back and consumed by the backend, the response will look something like this:
    Code (CSharp):
    2. {
    3.   "configs": {
    4.     "settings": {
    5.       "colorRed": 0.9,
    6.       "rotateY": 23.0,
    7.       "rotSpeed": 27.0,
    8.       "colorGreen": 0.1,
    9.       "rotateZ": 120.0,
    10.       "colorBlue": 0.9,
    11.       "rotateX": 12.0
    12.     }
    13.   },
    14.   "metadata": {
    15.     "configAssignmentHash": "f242cd5faf3851f344b393b2a7e2e317e44a782e",
    16.     "assignmentId": "477b17a6-dcf5-4305-b07c-0ba3f3489ca0",
    17.     "environmentId": "0d39eef9-c96b-486d-bbc0-7219b8554e51"
    18.   }
    19. }
    where you can see the corresponding environmentId among the metadata.

    If you need to check the environmentId after the RC request in the runtime, there is a runtime config variable which stores the current environmentId, and can be accessed via:
    Code (CSharp):
    1. RemoteConfigService.Instance.appConfig.environmentId
    To your point regarding awaitable version of the method SetEnvironmentIDAsync,
    instead of setting the environment in an asynchronous manner, we should probably unify Remote Config with the "sign-in" flow of the rest of the services.

    Thanks again for the follow-up, I hope this clarifies things a bit!
  5. Maeslezo


    Jun 16, 2015
    What I meant is that you get the default environment ALTOUGHT you set any environment, if you don't sign anonymously.

    In other words, signing is mandatory in order to set the environment correctly

    For example:
    Code (CSharp):
    2. var options = new InitializationOptions();
    3. options.SetEnvironmentName("not default environment");
    4. await Unity.Services.Core.UnityServices.InitializeAsync(options);
    6. RuntimeConfig remoteConfig = await RemoteConfigService.Instance.FetchConfigsAsync(new userAttributes(), new appAttributes());
    8. //remoteConfig.environmentId is the default environment, altought "not default environment" has been set
    Code (CSharp):
    2. var options = new InitializationOptions();
    3. options.SetEnvironmentName("not default environment");
    4. await Unity.Services.Core.UnityServices.InitializeAsync(options);
    5. if (!AuthenticationService.Instance.IsSignedIn)
    6. {
    7.    await AuthenticationService.Instance.SignInAnonymouslyAsync();
    8. }
    10. RuntimeConfig remoteConfig = await RemoteConfigService.Instance.FetchConfigsAsync(new userAttributes(), new appAttributes());
    12. //remoteConfig.environmentId is the "not default environment", which was the environment set
  6. vd_unity


    Unity Technologies

    Sep 11, 2014
    Thanks @Maeslezo!
    Yes, you are correct - if you are using options.SetEnvironmentName() method from the Core, setting a non-default environment will not work without the player signing in.

    Unfortunately, the only current way to have a non-default environment set and not sign in the player is if you use the RCs method
    RemoteConfigService.Instance.SetEnvironmentID(), but in that case you have to know the environmentId.

    Although this flow is not ideal, we are aware of it and will work to improve it.
    Maeslezo likes this.
  7. Maeslezo


    Jun 16, 2015
    All clear.

    Thank you for your help
    vd_unity likes this.