Contact Management

Identify users, update user properties and contacts, and track events.

This guide covers how to set the user identity, update contacts and custom properties, pass device identifiers, track location, and send custom and screen-name events through the Zeta iOS SDK.

On this page

Update user properties

The ZTUserManagable protocol, exposed from ZetaClient, performs user operations. Update user data using the builder pattern, or build a ZTUser object and call updateUser(_:).

ZetaClient.shared.user?.build { user in
    user.uid = "uid"
    user.firstName = "User1"
    user.lastName = "last-name"
    user.name = "name"
    user.signedUpAt = "2024-09-17T05:54:58.000Z"
    user.source = "source"
    user.email = ZTUserEmail(email: "[email protected]")
    user.emailId = "[email protected]" // only accepted if uid is not email
    user.phone = ZTUserPhone(phone: "1234")
    user.additionalProperties = [
        "zt_test_string": "zt_test_value",
        "zt_test_number": 123,
        "zt_test_boolean": true,
        "zt_test_array": ["zt_test_value1", "zt_test_value2", "zt_test_value3"],
        "zt_test_dict": ["zt_test_dict_key": "zt_test_dict_value"]
    ]
}

let user = ZTUser(name: "John Wick")
ZetaClient.shared.user?.updateUser(user)
[[ZetaClient shared].user build:^(ZTUser *user) {
    user.uid = @"uid";
    user.firstName = @"User1";
    user.lastName = @"last-name";
    user.name = @"name";
    user.signedUpAt = @"2024-09-17T05:54:58.000Z";
    user.source = @"source";
    user.emailId = @"[email protected]"; // only accepted if uid is not email

    ZTContactAdditionalInfo *ztAdditionalInfo = [[ZTContactAdditionalInfo alloc] init];
    ZTUserEmail *ztEmail = [[ZTUserEmail alloc] initWithEmail:@"[email protected]" additionalInfo:ztAdditionalInfo];
    user.email = ztEmail;

    ZTContactAdditionalInfo *ztAdditionalInfoPhone = [[ZTContactAdditionalInfo alloc] init];
    ZTUserPhone *ztPhone = [[ZTUserPhone alloc] initWithPhone:@"1234" additionalInfo:ztAdditionalInfoPhone];
    user.phone = ztPhone;

    user.additionalProperties = @{
        @"zt_test_string": @"zt_test_value",
        @"zt_test_number": @123,
        @"zt_test_boolean": @YES,
        @"zt_test_array": @[@"zt_test_value1", @"zt_test_value2", @"zt_test_value3"],
        @"zt_test_dict": @{@"zt_test_dict_key": @"zt_test_dict_value"}
    };
}];

ZTContactAdditionalInfo *ztAdditionalInfo = [[ZTContactAdditionalInfo alloc] init];
ZTUserEmail *ztEmail = [[ZTUserEmail alloc] initWithEmail:@"[email protected]" additionalInfo:ztAdditionalInfo];

ZTContactAdditionalInfo *ztAdditionalInfoPhone = [[ZTContactAdditionalInfo alloc] init];
ZTUserPhone *ztPhone = [[ZTUserPhone alloc] initWithPhone:@"1234" additionalInfo:ztAdditionalInfoPhone];

ZTLocation *location = [[ZTLocation alloc] initWithLatitude:12.933663251837913
                                                 longitude:77.62168405034637
                                              isForeground:YES];

ZTUser *user = [[ZTUser alloc]
    initWithUid:@"uid"
    emailId:@"[email protected]"
    firstName:@"User1"
    lastName:@"last-name"
    name:@"John Wick"
    signedUpAt:@"2024-09-17T05:54:58.000Z"
    email:ztEmail
    phone:ztPhone
    source:@"source"
    location:location
    additionalProperties:@{
        @"zt_test_string": @"zt_test_value",
        @"zt_test_number": @123,
        @"zt_test_boolean": @YES,
        @"zt_test_array": @[@"zt_test_value1", @"zt_test_value2", @"zt_test_value3"],
        @"zt_test_dict": @{@"zt_test_dict_key": @"zt_test_dict_value"}
    }];

[[ZetaClient shared].user updateUser:user];

emailId vs email

  • user.emailId — use when the email should be treated as the unique identifier for the user. This populates the Email field under ZMP Identifiers (as well as the Contact for this email).
  • user.email — use when you want to add the email as a contact with additional properties (preferences, subscription status). This populates the Contact section.

Important: If the uid is set as an email address in the same build call, the emailId value is ignored.

ZTContactAdditionalInfo

For contacts (email, phone), you can attach additional properties:

ZetaClient.shared.user?.build { user in
    let ztAdditionalInfo = ZTContactAdditionalInfo()
    ztAdditionalInfo.subscriptionStatus = .active
    ztAdditionalInfo.inactivityReason = "reason"
    ztAdditionalInfo.signedUpAt = "2024-09-17T05:54:58.000Z"
    ztAdditionalInfo.lastOpened = "2024-09-17T05:54:58.000Z"
    ztAdditionalInfo.doubleOptInStatus = "single"
    ztAdditionalInfo.phoneType = "mobile"
    ztAdditionalInfo.lastSent = "2024-09-17T05:54:58.000Z"
    ztAdditionalInfo.lastClicked = "2024-09-17T05:54:58.000Z"
    ztAdditionalInfo.properties = ["key": "value"]

    user.email = ZTUserEmail(email: "[email protected]", additionalInfo: ztAdditionalInfo)
}
[[ZetaClient shared].user build:^(ZTUser *user) {
    ZTContactAdditionalInfo *info = [[ZTContactAdditionalInfo alloc] init];
    info.subscriptionStatus = ZTContactSubscriptionStatusActive;
    info.inactivityReason = @"reason";
    info.signedUpAt = @"2024-09-17T05:54:58.000Z";
    info.lastOpened = @"2024-09-17T05:54:58.000Z";
    info.doubleOptInStatus = @"single";
    info.phoneType = @"mobile";
    info.lastSent = @"2024-09-17T05:54:58.000Z";
    info.lastClicked = @"2024-09-17T05:54:58.000Z";
    info.properties = @{@"key": @"value"};

    user.email = [[ZTUserEmail alloc] initWithEmail:@"[email protected]" additionalInfo:info];
}];

ZTContactAdditionalInfo properties

PropertyTypeDescription
preference[String]Optional. Defaults to ["standard"] if not provided.
subscriptionStatusZTContactSubscriptionStatusContact's subscription state. Supported values: new, active, inactive, none.
properties[String: Any]Optional. Additional custom contact properties.

Notes

  • All date-time values must be in ISO 8601 format, UTC.
  • preference of a contact defaults to standard. Other preferences can be added or customized inside ZMP Settings; only values defined there may be passed in preference.
  • double_opt_in_status is optional. Default null; accepts single or double.
  • phone_type is optional. Default null; accepts mobile or landline.
  • additionalProperties on ZTUser accepts arbitrary key-value pairs and appears under the User Properties section in ZMP.

Starting and clearing the user identity session

The SDK tracks user session-specific properties and events. Setting uid or emailId starts the session.

ZetaClient.shared.user?.build { user in
    user.uid = "uid"
    user.emailId = "[email protected]"
}

let user = ZTUser(uid: "john123", emailId: "[email protected]")
ZetaClient.shared.user?.updateUser(user)
[[ZetaClient shared].user build:^(ZTUser *user) {
    user.uid = @"uid";
    user.emailId = @"[email protected]";
}];

ZTUser *user = [[ZTUser alloc] initWithUid:@"john123" emailId:@"[email protected]"];
[[ZetaClient shared].user updateUser:user];

Note: emailId is ignored if the uid is also an email address.

Clear the identity session when the user changes inside your app:

ZetaClient.shared.user?.clear()
[[ZetaClient shared].user clear];

Note: Call clear() whenever the user profile changes in your app (sign-out, account switch, etc.).

Passing IDFA, IDFV, and push tokens

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    ZetaClient.shared.user?.updateDeviceToken(token: deviceToken)
}

ZetaClient.shared.user?.updateIDFA(idfa)
ZetaClient.shared.user?.updateIDFV(idfv)
- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [[ZetaClient shared].user updateDeviceTokenWithToken:deviceToken];
}

[[ZetaClient shared].user updateIDFA:idfa];
[[ZetaClient shared].user updateIDFV:idfv];

Location tracking

updateLocation(_:) accepts coordinates plus a foreground/background flag. The foreground/background flag indicates whether the location was collected with the app in the foreground or background. The provided location is cached in memory and attached to subsequent events as a property.

ZetaClient.shared.user?.updateLocation(
    ZTLocation(
        latitude: 12.933663251837913,
        longitude: 77.62168405034637,
        isForeground: true
    )
)
ZTLocation *location = [[ZTLocation alloc]
    initWithLatitude:12.933663251837913
    longitude:77.62168405034637
    isForeground:YES];

[[ZetaClient shared].user updateLocation:location];

Unique client ID

The SDK supports host apps providing a unique_client_id to ZMP. Before using it, review these conditions:

  • No two profiles may have the same unique_client_id name:value pair. If a profile is updated with a unique_client_id matching another profile, the profiles are merged.
  • A unique_client_id pair is discarded if the same key already exists with a different value.
  • Enabling support for a unique client ID requires site-configuration updates. Coordinate with your account team to ensure the configuration is correct. Using an incorrect or mismatched client ID name may result in errors.
ZetaClient.shared.user?.setUniqueClientId(value, forKey: name)
[[ZetaClient shared].user setUniqueClientId:value forKey:name];

Event tracking

Auto-tracked events

EventWhen fired
app_installedFirst launch only.
app_openedApp transitions from background to foreground.
app_closedApp transitions from foreground to background.
app_terminatedApp is closed.

Screen name tracking

Use trackScreenName(screen:deeplink:properties:) to track screen navigation. The provided screen name is cached in memory and attached to subsequent events as a property.

  • screen — screen name.
  • deeplink — optional. The deeplink URL that opened this screen.
  • properties — optional. Additional properties.
ZetaClient.shared.event?.trackScreenName(
    screen: "Events Screen",
    deeplink: "app://home",
    properties: ["screen": "Home"]
)
[[ZetaClient shared].event
    trackScreenNameWithScreen:@"Events Screen"
    deeplink:@"app://home"
    properties:@{@"screen": @"Home"}];

Custom events

Send a custom event by passing a name and a dictionary of properties. Properties must be JSON-encodable; if any property value is not JSON-encodable, the entire event is discarded.

  • eventName — a meaningful name that describes the event.
  • properties[String: Any], JSON-encodable.
ZetaClient.shared.event?.send(
    name: "EventTest",
    properties: [
        "events-array": ["screen1", "screen2"],
        "events-dict": ["screen": "events"]
    ]
)
[[ZetaClient shared].event
    sendWithName:@"EventTest"
    properties:@{
        @"events-array": @[@"screen1", @"screen2"],
        @"events-dict": @{@"screen": @"events"}
    }];

Next

See also