> ## Documentation Index
> Fetch the complete documentation index at: https://moengage.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracking user attributes

> Track user attributes and set unique identifiers for cross-platform identification in the iOS SDK.

User Attributes are pieces of information you know about a user. They could be demographics like age or gender, account-specific like plan, or whether a user has seen a particular A/B test variation. User attributes are a customer identity you can reference throughout the customer’s lifetime.

# Identifying Users

For SDK versions below 9.23.0 refer to [this document](https://www.moengage.com/docs/developer-guide/ios-sdk/data-tracking/basic/setting-unique-id-for-sdk-versions-below-9-23-0).

Setting identifiers is important to:

* To tie user behavior across platforms, i.e., iOS, Android, Web, etc.
* This is to ensure unnecessary or stale users are not created.
* To identify users across installs/re-installs.

## Single Identifier

Call the API below to pass the identifier on to the MoEngage SDK.

<CodeGroup>
  ```swift Swift wrap theme={null}
  MoEngageSDKAnalytics.sharedInstance.identifyUser(identity: "identifier")
  ```

  ```objective-c Objective C wrap theme={null}
  [[MoEngageSDKAnalytics sharedInstance] identifyUserWithIdentity:@"identifier" workspaceId:nil];
  ```
</CodeGroup>

<Note>
  This method is a replacement for the deprecated ***setUniqueId()***. If you are using \*\*\*setUniqueId() \*\*\*in your application, consider replacing it with ***identifyUser()**\*\*.*
</Note>

## Multiple Identifiers

If your application has multiple identifiers using which you identify a user you can pass all the identifiers to the SDK using the below API.

<CodeGroup>
  ```swift Swift wrap theme={null}
  MoEngageSDKAnalytics.sharedInstance.identifyUser(
      identities: [
          "identifierName1": "identifierValue1", "identifierName2": "identifierValue2"
      ]
  )
  ```

  ```objective-c Objective C wrap theme={null}
  [[MoEngageSDKAnalytics sharedInstance] identifyUserWithIdentities:@{
      @"identifierName1": @"identifierValue1", @"identifierName2": @"identifierValue2"
  } workspaceId:nil];
  ```
</CodeGroup>

If you call `identifyUser()` multiple times with different identifier names, the SDK will append this identifier to the already set identifiers.

<Info>
  **Information**

  Updates are made to SDK functions to improve user identification and session management.

  * **Forced Logout**: The MoEngage SDK no longer automatically logs out the previous user when a new user is detected on the device. Logout should now be explicitly called for workspaces enabled with Identity resolution to avoid data corruption.
  * **SetUniqueID**: *IdentifyUser* function supports multiple identifiers, which replaces the need of using *SetUniqueID* function for user identification. Note that *SetUniqueID* is marked for removal in the future releases of SDK versions - it is important to use *identifyUser* instead especially if you are using Identity resolution in your workspace.
  * **SetAlias**: For workspaces with the Identity resolution feature enabled, MoEngage SDK stores the previous identifier values. When *IdentifyUser* function is used to track the new identifier values, MoEngage SDK detects the change in identifier value and reports accordingly.
  * If you call the *IdentifyUser* function without logging out, then the existing logged-in user's ID is updated.
</Info>

Refer to our help [document](https://www.moengage.com/docs/user-guide/data/user-data/unified-identity-identity-resolution) to learn more about the feature.

## Logout

The application needs to notify the MoEngage SDK whenever the user is logged out of the application. Call the API whenever the user is logged out of the application to notify the SDK.

<CodeGroup>
  ```swift Swift wrap theme={null}
  MoEngageSDKAnalytics.sharedInstance.resetUser()
  ```

  ```objective-c Objective C wrap theme={null}
  [[MoEngageSDKAnalytics sharedInstance] resetUser];
  ```
</CodeGroup>

# Default User Attributes

Some default SDK User Attribute can be set for eg. email-id, mobile number, gender, user name, birthday. The default attributes tracked by SDK can be set as shown below:

<CodeGroup>
  ```swift Swift wrap theme={null}
  MoEngageSDKAnalytics.sharedInstance.setName(userName)
  MoEngageSDKAnalytics.sharedInstance.setLastName(userLastname)
  MoEngageSDKAnalytics.sharedInstance.setFirstName(userFirstName)
  MoEngageSDKAnalytics.sharedInstance.setEmailID(userEmailID)
  MoEngageSDKAnalytics.sharedInstance.setMobileNumber(userPhoneNo)
  MoEngageSDKAnalytics.sharedInstance.setGender(.male) //Use MoEngageUserGender enum
  MoEngageSDKAnalytics.sharedInstance.setDateOfBirth(userBirthdate)//userBirthdate should be a Date instance
  MoEngageSDKAnalytics.sharedInstance.setLocation(MoEngageGeoLocation(withLatitude: userLocationLat, andLongitude: userLocationLng)) //userLocationLat and userLocationLng are double values of the location coordinates
  ```

  ```objective-c Objective C wrap theme={null}
  [[MoEngageSDKAnalytics sharedInstance] setName:userName];
  [[MoEngageSDKAnalytics sharedInstance] setLastName:userLastname];
  [[MoEngageSDKAnalytics sharedInstance] setFirstName:userFirstName];
  [[MoEngageSDKAnalytics sharedInstance] setEmailID:userEmailID];
  [[MoEngageSDKAnalytics sharedInstance] setMobileNumber:userPhoneNo];
  [[MoEngageSDKAnalytics sharedInstance] setGender:MoEngageUserGenderMale]; // Use MoEngageUserGender enum
  [[MoEngageSDKAnalytics sharedInstance] setDateOfBirth:userBirthdate];//userBirthdate should be a NSDate instance  
  [[MoEngageSDKAnalytics sharedInstance] setLocation:[[MoEngageGeoLocation alloc] initWithLatitude:userLocationLat andLongitude:userLocationLng]];//userLocationLat and userLocationLng are double values of the location coordinates
  ```
</CodeGroup>

<Note>
  * User Phone No / Mobile Number must be tracked as a string to work properly in MoEngage systems.
  * For more information on supported data types and data tracking policies, refer to [Data Tracking Policies](/user-guide/data/key-concepts/data-tracking-policies).
</Note>

For the full list of validation rules and what happens in `DEBUG` versus release builds, refer to [Validations and restrictions](#validations-and-restrictions).

# Custom User Attributes

To set custom attributes just provide custom keys different to the ones present in [here](https://www.moengage.com/docs/user-guide/getting-started/integration/default-ios-sdk#default-user-attributes). Following is an example:

<CodeGroup>
  ```swift Swift wrap theme={null}
  MoEngageSDKAnalytics.sharedInstance.setUserAttribute("Bengaluru", withAttributeName: "Current_city")
  MoEngageSDKAnalytics.sharedInstance.setUserAttribute(["Bengaluru","Delhi","Chennai"], withAttributeName: "Current_cities")
  ```

  ```objective-c Objective C wrap theme={null}
  [[MoEngageSDKAnalytics sharedInstance] setUserAttribute:@"Bengaluru" withAttributeName:@"Current_city"];
  NSArray *myArray = @[@"Bengaluru", @"Delhi", @"Chennai"];
  [[MoEngageSDKAnalytics sharedInstance] setUserAttribute:myArray withAttributeName:@"Current_cities"];
  ```
</CodeGroup>

For the full list of validation rules, refer to [Validations and restrictions](#validations-and-restrictions).

## JSON Attributes

From MoEngage-iOS-SDK ***v9.17.5***, we have added support for JSON and array of JSON user attributes. Following is an example:

<CodeGroup>
  ```swift Swift wrap theme={null}
  MoEngageSDKAnalytics.sharedInstance.setUserAttribute(["item-id" : 123,"item-type" : "books","item-cost" : ["amount" : 100,"currency" : "USD"]],withAttributeName: "product")

  MoEngageSDKAnalytics.sharedInstance.setUserAttribute([["item-id" : 123,"item-cost" : ["amount" : 100,"currency" : "USD"]],["item-id" : 323,"item-cost" : ["amount" : 90,"currency" : "USD"]]],withAttributeName: "products")
  ```

  ```objective-c Objective C wrap theme={null}
  [MoEngageSDKAnalytics.sharedInstance setUserAttribute:@{@"item-id" : @123,@"item-type" : @"books",@"item-cost" : @{@"amount" : @100,@"currency" : @"USD"}} withAttributeName:@"product"];

  [MoEngageSDKAnalytics.sharedInstance setUserAttribute:@[@{@"item-id" : @123,@"item-cost" : @{@"amount" : @100,@"currency" : @"USD"}} , @{@"item-id" : @323,@"item-cost" : @{@"amount" : @90,@"currency" : @"USD"}}] withAttributeName:@"products"];
  ```
</CodeGroup>

# Portfolio-Level User Attributes

<Info>
  **Prerequisites**

  * iOS SDK version 10.07.0 or above.
  * Multiple projects must be configured in your MoEngage workspace. For more information, refer to [Portfolio](/user-guide/settings/account/portfolio/portfolio) or contact your MoEngage account manager.
</Info>

In a [Portfolio](/user-guide/settings/account/portfolio/portfolio) enabled workspace, user attributes can be tracked either at the project level (scoped to a specific project) or at the portfolio level (shared across all projects in the workspace).

## Set Portfolio-Level Attributes

To set a user attribute at the portfolio level, use the API shown below.

<CodeGroup>
  ```swift Swift wrap theme={null}
  // Project-level attribute (default — same as existing behaviour)
  MoEngageSDKAnalytics.sharedInstance.setUserAttribute("value", withAttributeName: "attribute_name", level: .project)

  // Portfolio-level attribute (shared across all projects in the portfolio)
  MoEngageSDKAnalytics.sharedInstance.setUserAttribute("value", withAttributeName: "attribute_name", level: .portfolio)
  ```

  ```objective-c Objective C wrap theme={null}
  // Project-level attribute (default)
  [[MoEngageSDKAnalytics sharedInstance] setUserAttribute:@"value"
                                        withAttributeName:@"attribute_name"
                                                    level:MoEngageUserAttributeLevelProject];

  // Portfolio-level attribute
  [[MoEngageSDKAnalytics sharedInstance] setUserAttribute:@"value"
                                        withAttributeName:@"attribute_name"
                                                    level:MoEngageUserAttributeLevelPortfolio];
  ```
</CodeGroup>

<Warning>
  Setting a unique ID at the portfolio level is not supported and causes a fatal exception in Xcode debug builds. Use the project-level `identifyUser` APIs to set identifiers. See [Identifying Users](#identifying-users).
</Warning>

# Date and Time User Attributes

Date and time attributes can be set as user attributes. For this refer to the methods in the code block below:

<CodeGroup>
  ```swift Swift wrap theme={null}
  //1. Track UserAttribute using Date instance
  MoEngageSDKAnalytics.sharedInstance.setUserAttributeDate(Date(), withAttributeName: "Date Attr 1")

  //2. Track UserAttribute using ISO Date String in format "yyyy-MM-dd'T'HH:mm:ss'Z'"
  MoEngageSDKAnalytics.sharedInstance.setUserAttributeISODate("2020-01-12T18:45:59Z", withAttributeName: "Date Attr 2")

  //3. Track UserAttribute using Epoch value
  MoEngageSDKAnalytics.sharedInstance.setUserAttributeEpochTime(663333, withAttributeName: "Date Attr 3")
  ```

  ```objective-c Objective C wrap theme={null}
  //1. Track UserAttribute using Date instance
  [[MoEngageSDKAnalytics sharedInstance] setUserAttributeDate:[NSDate date] withAttributeName:@"DateAttr1"];
   
  //2. Track UserAttribute using ISO Date String in format "yyyy-MM-dd'T'HH:mm:ss'Z'"
  [[MoEngageSDKAnalytics sharedInstance] setUserAttributeISODate:@"2020-01-12T18:45:59Z" withAttributeName:@"DateAttr2"];
   
  //3. Track UserAttribute using Epoch value
  double timestampEpochValue = [[NSDate date] timeIntervalSince1970];
  [[MoEngageSDKAnalytics sharedInstance] setUserAttributeEpochTime:timestampEpochValue withAttributeName:@"LastPurchaseDate"];
  ```
</CodeGroup>

# Location Attributes

The location of a user or any location can be set as user attribute. For this use [*setLocation(\_:withAttributeName:)*](https://moengage.github.io/ios-api-reference/Classes/MoEngageSDKAnalytics.html#/c:@M@MoEngageAnalytics@objc\(cs\)MoEngageSDKAnalytics\(im\)setLocation:withAttributeName:) method and pass lat, the long value of the location as shown in the following example:

<CodeGroup>
  ```swift Swift wrap theme={null}
  MoEngageSDKAnalytics.sharedInstance.setLocation(MoEngageGeoLocation.init(withLatitude: 72.90909, andLongitude: 12.34567), withAttributeName: "attribute name")
  ```

  ```objective-c Objective C wrap theme={null}
  [[MoEngageSDKAnalytics sharedInstance] setLocation:[[MoEngageGeoLocation alloc] initWithLatitude:23.33 andLongitude:26.22] withAttributeName:@"attribute name"];
  ```
</CodeGroup>

# Validations and restrictions

User attributes have two layers of validation that apply across both `DEBUG` and release builds:

* **Naming and format rules** — these always apply, regardless of build configuration. Refer to [Naming and format rules](#naming-and-format-rules) below.
* **Type validation** — invalid attribute values (unsupported types such as `null`, custom models, `NaN`, `Infinity`, empty collections, `NSURL`, or `Date`/`MoEngageGeoLocation` nested inside an Array or Dictionary) cause a fatal exception in `DEBUG` builds and are silently dropped in release builds. Refer to [Supported attribute value types](#supported-attribute-value-types) below.

<Info>
  In the iOS SDK, `DEBUG` mode means the SDK was initialized using `initializeDefaultTestInstance(_:)` (TEST workspace) and the app is running attached to Xcode. This is different from a release build that has debug symbols enabled.
</Info>

## Naming and format rules

* **Attribute names must be non-empty.** Passing an empty string (`""`) as an attribute name causes a fatal exception in `DEBUG` builds. In release builds, the attribute is dropped.
* **No dot (`.`) in attribute names.**
* **Attribute names must not start with a dollar sign (`$`).**
* **Reserved prefix.** You cannot use `moe_` as a prefix when naming events, event attributes, or user attributes. It is a system prefix, and using it might result in periodic blocklisting without prior communication.
* **Reserved keywords.** Do not use any of the following keys when tracking user attributes — they are reserved for SDK and system use:
  * `USER_ATTRIBUTE_UNIQUE_ID`
  * `USER_ATTRIBUTE_USER_EMAIL`
  * `USER_ATTRIBUTE_USER_MOBILE`
  * `USER_ATTRIBUTE_USER_NAME`
  * `USER_ATTRIBUTE_USER_GENDER`
  * `USER_ATTRIBUTE_USER_FIRST_NAME`
  * `USER_ATTRIBUTE_USER_LAST_NAME`
  * `USER_ATTRIBUTE_USER_BDAY`
  * `USER_ATTRIBUTE_NOTIFICATION_PREF`
  * `USER_ATTRIBUTE_OLD_ID`
  * `USER_ATTRIBUTE_DND_START_TIME`
  * `USER_ATTRIBUTE_DND_END_TIME`
  * `MOE_TIME_FORMAT`
  * `MOE_TIME_TIMEZONE`
  * `MOE_GAID`
  * `MOE_ISLAT`
  * `INSTALL`
  * `UPDATE`
  * `status`
  * `user_id`
  * `source`

## Supported attribute value types

Attribute values must be one of: `String`, `Number`, `Date`, `MoEngageGeoLocation`, `Dictionary`, or `Array` (of strings or numbers). `NSURL` is not supported — convert to a string before passing it.

If an unsupported value is passed:

* In `DEBUG` builds, the SDK throws a fatal exception and crashes the app so you can catch data issues early.
* In release builds, the SDK drops the specific invalid attribute. Other attributes set on the user are unaffected.

Common triggers for the `DEBUG` exception:

* Passing `null` (`NSNull()`).
* Passing custom models or UI elements (for example, `UIColor`, `UIImage`).
* Passing invalid numbers like `NaN` or `Infinity`.
* Passing empty Arrays or Dictionaries.
* Nesting `Date` or `MoEngageGeoLocation` objects inside an Array or Dictionary. These must be passed directly as values.

<Info>
  If you are upgrading an existing app and these strict `DEBUG` validations cause disruptive crashes while you refactor your tracking code, you can temporarily opt out by calling `disableIntegrationValidator()` during SDK initialization. Use this strictly as a stopgap for phased upgrades, and aim to remove the opt-out once your attribute call sites are properly validated.
</Info>

## Filtering unsupported attribute values

If you are upgrading to a newer version of the iOS SDK and your app passes attribute values whose types are not validated at the call site, add a type-check guard before calling `setUserAttribute`. This prevents `DEBUG` crashes and ensures only valid data reaches the SDK in all builds.

<CodeGroup>
  ```swift Swift wrap theme={null}
  // Helper to check if a value is a supported user attribute type.
    // Call this before passing any dynamically-typed value to setUserAttribute(_:withAttributeName:).
    func isSupportedUserAttributeValue(_ value: Any) -> Bool {
        switch value {
        case let d as Double: return !d.isNaN && !d.isInfinite
        case let f as Float:  return !f.isNaN && !f.isInfinite
        case is String, is Bool, is Int, is Int8, is Int16, is Int32, is Int64,
             is UInt, is UInt8, is UInt16, is UInt32, is UInt64, is NSNumber:
            return true
        case let arr as [Any]:          return !arr.isEmpty
        case let dict as [String: Any]: return !dict.isEmpty
        default: return false
        }
    }

    // Usage — value comes from a server response, external model, or unknown source
    if isSupportedUserAttributeValue(dynamicValue) {
        MoEngageSDKAnalytics.sharedInstance.setUserAttribute(dynamicValue, withAttributeName: "attribute_name")
    }
    // Values that fail the check (custom objects, NSNull, NaN, empty collections, etc.)
    // are skipped without crashing in both DEBUG and Release builds.

  ```

  ```objective-c Objective C wrap theme={null}
  // Check if a value is a supported user attribute type before passing to the SDK.
    - (BOOL)isSupportedUserAttributeValue:(id)value {
        if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]]) {
            if ([value isKindOfClass:[NSNumber class]]) {
                double d = [(NSNumber *)value doubleValue];
                if (isnan(d) || isinf(d)) return NO;
            }
            return YES;
        }
        if ([value isKindOfClass:[NSArray class]])      return [(NSArray *)value count] > 0;
        if ([value isKindOfClass:[NSDictionary class]]) return [(NSDictionary *)value count] > 0;
        return NO;
    }

    // Usage
    if ([self isSupportedUserAttributeValue:dynamicValue]) {
        [[MoEngageSDKAnalytics sharedInstance] setUserAttribute:dynamicValue
                                             withAttributeName:@"attribute_name"];
    }
    // Unsupported values are skipped without crashing in both DEBUG and Release builds.
  ```
</CodeGroup>

<Note>
  `Date` and `MoEngageGeoLocation` values must be passed using the dedicated `setUserAttributeDate`, `setUserAttributeISODate`, `setUserAttributeEpochTime`, and `setLocation:withAttributeName:` methods — not through `setUserAttribute:withAttributeName:`.
</Note>
