# Attributes

In addition of setting a custom user ID, an email address or overriding the language/region, you can assign **attributes** to your users, allowing you to improve your orchestrations targeting.

![Custom attributes](/files/WsxlF6q9WdIx4xBa8mvQ)

> **Important:**\
> \- **User IDs** must be managed using our [custom user ID](/developer/sdk/android/profile-data/attributes.md) implementation.\
> \- **Email address** must be managed using our [email subscription](/developer/sdk/android/profile-data/email-subscription.md) implementation.\
> \- **Region/language** data must be managed using our [custom region/language](/developer/sdk/android/profile-data/custom-locale.md) implementation.\
> \- Never use an existing tagging plan.\
> \- Newly tracked attributes are hidden by default. You will need to manually display them from the dashboard data page.

### Managing attributes

Before we get started on how to implement **attributes**, here are some rules you should know.

**Naming**

Attribute names are **strings**. They should be made of letters, numbers or underscores *(\[a-z0-9\_])* and can't be longer than 30 characters *(e.g. has\_premium)*.

**Values**

Values must be any of the following types:

* **String**, must not be longer than 300 characters and cannot be empty. For better results, you should make them upper/lowercase and trim the whitespaces.
* **Int/long/double**
* **Boolean**
* **Date**, `java.util.Date`. Since timezones are not supported, this will typically represent UTC dates.
* **URL**, `java.net.URI`. Must not be longer than 2048 characters and must follow the format `scheme://[authority][path][?query][#fragment]`.
* **List\<String>**, not longer than 25 items, only values of type String and must respect the string attribute limitations.

**Methods**

The custom attribute API is quite simple:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
// This method returns a NEW editor instance every call. Make sure you chain calls or store the editor
// in a local variable before calling save. Calling save() on an empty editor will do nothing.
Batch.Profile.editor().apply {
   setAttribute("age", 26) // Set an attribute
   removeAttribute("age") // Remove an attribute
   addToArray("actions", listOf("added_to_cart", "has_bought")) // Add list of strings to an array attribute. You can also add just a string.
   removeFromArray("actions", "has_bought") // Remove a string from a list attribute
   save() // Don't forget to save the changes!
}
```

{% endtab %}

{% tab title="Java" %}

```java
// This method returns a NEW editor instance every call. Make sure you chain calls or store the editor
// in a local variable before calling save. Calling save() on an empty editor will do nothing.
Batch.Profile.editor()
    .setAttribute("age", 26) // Set an attribute
    .removeAttribute("age") // Remove an attribute
    .addToArray("actions", Arrays.asList("added_to_cart", "has_bought")) // Add list of strings to an array attribute. You can also add just a string.
    .removeFromArray("actions", "has_bought") // Remove a string from an array attribute. You can also remove many items with a list.
    .save(); // Don't forget to save the changes!
}
```

{% endtab %}
{% endtabs %}

You might be tempted to write helpers or loops that open and save many transactions in a row, with each transaction only doing one operation.\
Doing so prevents Batch from optimizing disk usage and network roundtrips, which impact your user's data plan and battery life.\
Please try to batch as many operations as you can in a single transaction.

Note that this **MUST** be called when Batch is started.

> Please test your implementation using our [debug tool](/developer/sdk/ios/profile-data/debug.md) and [profile view](https://doc.batch.com/getting-started/features/customer-engagement-platform/profiles/overview) before releasing your app on the store.

### Reading attributes and tag collections

Since Batch SDK v2, updating the user's data also update the profile's data to be accessible from your project scope. This mean the following APIs only read local data related to your installation and NOT to your profile.

You may also have noticed that APIs to set Tags or Tag Collections have been removed and replaced by array attributes. These methods are backward-compatible and array attributes are converted into tag collections to not break your implementation.

#### Reading attributes

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
Batch.User.fetchAttributes(context, object: BatchAttributesFetchListener {
  override fun onSuccess(attributes: Map<String, BatchUserAttribute>) {
    // Attributes are retrieved in the form of a map
    // Values are encapsulated in an instance of BatchUserAttribute
    val attribute = attributes["age"]

    // BatchUserAttribute holds a reference to the value of the attribute
    val rawValue = attribute?.value  // Raw value is not typed

    // The type of the value is specified via a BatchUserAttribute.Type enumeration
    Log.i("attr", attribute?.type.toString()) // Prints "BatchUserAttribute.Type.LONGLONG"

    // To obtain a typed result you can use one of the five helper methods
    attribute?.getNumberValue() // Will return "26" here
    attribute?.getDateValue() // Will return null
    attribute?.getBooleanValue() // Will return null
    attribute?.getStringValue() // Will return null
    attribute?.getUriValue() // Will return null
  }
  override fun onError() {
    // Callback method called on error
  }
})
```

{% endtab %}

{% tab title="Java" %}

```java
Batch.User.fetchAttributes(context, new BatchAttributesFetchListener() {
  @Override
  public void onSuccess(Map<String, BatchUserAttribute> attributes) {
    // Attributes are retrieved in the form of a map
    // Values are encapsulated in an instance of BatchUserAttribute
    BatchUserAttribute attribute = attributes.get("age");

    // BatchUserAttribute holds a reference to the value of the attribute
    Object rawValue = attribute.value; // Raw value is not typed

    // The type of the value is specified via a BatchUserAttribute.Type enumeration
    Log.i("attr", attribute.type); // Prints "BatchUserAttribute.Type.LONGLONG"

    // To obtain a typed result you can use one of the five helper methods
    attribute.getNumberValue();     // Will return "26" here
    attribute.getDateValue();       // Will return null
    attribute.getBooleanValue();    // Will return null
    attribute.getStringValue();     // Will return null
    attribute.getUriValue();        // Will return null
  }

  @Override
  public void onError() {
    // Callback method called on error 
  }
});
```

{% endtab %}
{% endtabs %}

#### Reading tag collections

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
Batch.User.fetchTagCollections(context, object : BatchTagCollectionsFetchListener {
  override fun onSuccess(tagCollections: Map<String, Set<String>>) {
    // Tags are also retrieved in the form of a map
    // Keys are names of collections, values are sets of tags
    val tagCollection = tagCollections["actions"]!!
    Log.i("attr", tagCollection.toString()) // Prints "["has_bought"]"
  }

  override fun onError() {
    // Callback method called on error
  }
})
```

{% endtab %}

{% tab title="Java" %}

```java
Batch.User.fetchTagCollections(context, new BatchTagCollectionsFetchListener() {
  @Override
  public void onSuccess(Map<String, Set<String>> tagCollections) {
    // Tags are also retrieved in the form of a map
    // Keys are names of collections, values are sets of tags
    Set<String> tagCollection = tagCollections.get("actions");
    Log.i("attr", tagCollection); // Prints "["has_bought"]"
  }

  @Override
  public void onError() {
    // Callback method called on error
  }
});
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Note: Since tags are limited in size and are case-sensitive, reading them back might produce different results than what had been saved.
{% endhint %}

### Clearing installation data

Installation data can be cleared as following:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
Batch.User.clearInstallationData()
```

{% endtab %}

{% tab title="Java" %}

```java
Batch.User.clearInstallationData();
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Note:** This method will clear the installation data on the App scope and will **NOT** affect the profile related data.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.batch.com/developer/sdk/android/profile-data/attributes.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
