{"_id":"55e680e185a9741900314e96","user":"55d2bd8e2463351700f67dd7","version":{"_id":"55e67aab9cc7c62b00c4a1ed","project":"55e67aaa9cc7c62b00c4a1ea","__v":10,"createdAt":"2015-09-02T04:27:23.612Z","releaseDate":"2015-09-02T04:27:23.612Z","categories":["55e67aac9cc7c62b00c4a1ee","55e67b5556007d23005fee7d","55e67b5dde6fef23009480ca","55e680efde6fef23009480db","55e6829485a9741900314e99","561c61b4ad272c0d00a892df","586c014c0abf1d0f000d04d4","58991d2ad207df0f0002186b","589b8e1fdbb7cd190026732c","58b8ca5e3265d70f001788d4"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.0.0","version":"1.0"},"category":{"_id":"55e67b5556007d23005fee7d","__v":35,"pages":["55e67b91de6fef23009480cb","55e67c28de6fef23009480ce","55e67c3cde6fef23009480d1","55e680e185a9741900314e96","55e682b7de6fef23009480dc","55ecd69bb2714d2100f227c0","55ecd6c554a67b1700edcf40","55ecd6edae66a30d00446ce7","55ecd777b2714d2100f227c2","55ecda5a7659d21700a7ef9b","55ecdaf6ae66a30d00446cf3","55ed006a0d968e2100de831e","55ed0258c9d5b3350072ae7a","55ee66a073d3941700f760e5","55f5ef61a1dea80d00a5dec5","55f5f38618b39b0d00c27eb0","55f9f86417b9d00d00969e29","55f9fdc8aba81f0d00a1156d","561b10b4c89cc30d0082154f","561b110a03bce90d00c4bd11","561c6195ad272c0d00a892dd","561c6ca0be5fb20d00077754","564f3e66c133343500286ca0","565b955b922c9a0d00d1f124","566784d2919aaa0d008e32db","566e1ee5972a290d00552084","569c58d77c3e44170014114f","56a2692f6928550d006c8327","56ca6c868014e417002bfe54","56cfa5386c5d7a13005eec0f","56e73d86555c030e00a52a73","56e7460c9000b120000ffe2e","56e8c19e99c6400e003820cf","56e8c53fc88bf80e00f8bed8","56f06ff4d386ce0e008e9b21"],"project":"55e67aaa9cc7c62b00c4a1ea","version":"55e67aab9cc7c62b00c4a1ed","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2015-09-02T04:30:13.305Z","from_sync":false,"order":1,"slug":"install-in-your-app","title":"Install in your app"},"__v":117,"parentDoc":null,"project":"55e67aaa9cc7c62b00c4a1ea","updates":["5601e23ffb9f160d00f29875","56b27e0d2d964617005992ba","582488e6d90fa027009b2590"],"next":{"pages":[],"description":""},"createdAt":"2015-09-02T04:53:53.688Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":3,"body":"The strength of Carnival's targeting and segmentation relies on knowing the correct information about your users. The more information we know about a user, the better you can target them when you send messages.\n\nWhen a user installs an app, we collect a few attributes automatically. These attributes are helpful for targeting and segmenting your users.\n\n### Automatically Tracked Attributes\n\n- App Version\n- Language\n- Time Zone\n- Badge Number (iOS only)\n- Device ID\n- Device Make & Model\n- Operating System & Version\n- Carnival SDK version\n- Location based on IP Address\n- Notifications Allowed\n- Platform\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"Attributes tracked automatically are refreshed at every app load.\"\n}\n[/block]\nBeyond the automatically tracked attributes, Carnival also lets you store custom information about a user for further segmenting and targeting. This feature is called [User Attributes](#user-attributes).\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Setting a User ID\"\n}\n[/block]\nIf your app has a login, you can set a unique User ID with Carnival.  This allows you to associate multiple devices to a single user when targeting via the API. You may also want to collect the [user's email ](http://docs.carnival.io/v1.0/docs/collecting-user-data#setting-a-user-email) as well. \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// setting a User ID after login\\n[Carnival setUserId::::at:::\\\"user_id_1234\\\" withResponse:^(NSError *error) {\\n    NSLog(@\\\"Error setting user id – %@\\\", error.localizedDescription);\\n}];\\n\\n// clearing a User ID after logout\\n[Carnival setUserId:nil withResponse:^(NSError *error) {\\n    \\n}];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"// setting a User ID after login\\nCarnival.setUserId(\\\"user_id_1234\\\") { error in\\n    println(\\\"setUserID returned with possible error: \\\\(error)\\\")\\n}\\n\\n// clearing a User ID after logout\\nCarnival.setUserId(nil) { error in\\n    println(\\\"setUserID returned with possible error: \\\\(error)\\\")\\n}\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"// setting a User ID after login\\nCarnival.setUserId(\\\"user_id_1234\\\", new CarnivalHandler<Void> (){\\n\\n    @Override\\n    public void onSuccess (Void value){\\n        //Do Something\\n    }\\n\\n    @Override\\n    public void onFailure (Error error){\\n        //Do Something\\n    }\\n});\\n\\n\\n// clearing a User ID after logout\\nCarnival.setUserId(null, new CarnivalHandler<Void> (){\\n\\n    @Override\\n    public void onSuccess (Void value){\\n        //Do Something\\n    }\\n\\n    @Override\\n    public void onFailure (Error error){\\n        //Do Something\\n    }\\n});\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"Carnival.setUserId(\\n  function callback(data) {\\n      console.log('setUserId successfully returned');\\n  },\\n  function errorHandler(err) {\\n      console.log('setUserId returned error: ' + err);\\n  },\\n  'user_id_1234'\\n);\",\n      \"language\": \"csharp\",\n      \"name\": \"Cordova (JavaScript)\"\n    },\n    {\n      \"code\": \"Carnival.SetUserId(\\\"user_id_1234);\\n// You can also attach the standard error hander too.\",\n      \"language\": \"csharp\",\n      \"name\": \"Unity (C#)\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"Setting a User ID is optional, but it is strongly recommended. It is a prerequisite to use:\\n\\n* Sending notifications to specific users, including using the [Notifications API](/docs/notifications) with `user_id` audiences\\n* [Users API](/docs/users)\\n* [Users Events API](/docs/users-events)\\n* [Audience creation via CSV](http://marketing.carnival.io/docs/introduction-to-audiences#section-by-csv)\\n\\nWithout User ID you will be able to set attributes and events only at the device level.\"\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Setting a User Email\"\n}\n[/block]\nIf your app has a login, you can set a unique user email with Carnival. This email is then matched up with users tracked in your Sailthru account. **This is a prerequisite if you plan to [integrate Sailthru with Carnival](http://marketing.carnival.io/v1.0/docs/sailthru-integration).**  It is recommended to collect both user ID and user email.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// setting a User Email after login\\n[Carnival setUserEmail:@\\\"firstname@example.com\\\" withResponse:^(NSError *error) {\\n    NSLog(@\\\"Error setting user email – %@\\\", error.localizedDescription);\\n}];\\n\\n// clearing a User Email after logout\\n[Carnival setUserEmail:nil withResponse:^(NSError *error) {\\n    NSLog(@\\\"Error setting user email – %@\\\", error.localizedDescription);\\n}];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"// setting a User Email after login\\nCarnival.setUserEmail(\\\"firstname@example.com\\\") { error in\\n    println(\\\"setUserEmail returned with possible error: \\\\(error)\\\")\\n}\\n\\n// clearing a User Email after logout\\nCarnival.setUserEmail(nil) { error in\\n    println(\\\"setUserEmail returned with possible error: \\\\(error)\\\")\\n}\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"// setting a User Email after login\\nCarnival.setUserEmail(\\\"firstname@example.com\\\", new CarnivalHandler<Void> (){\\n\\n    @Override\\n    public void onSuccess (Void value){\\n        //Do Something\\n    }\\n\\n    @Override\\n    public void onFailure (Error error){\\n        //Do Something\\n    }\\n});\\n\\n\\n// clearing a User Email after logout\\nCarnival.setUserEmail(null, new CarnivalHandler<Void> (){\\n\\n    @Override\\n    public void onSuccess (Void value){\\n        //Do Something\\n    }\\n\\n    @Override\\n    public void onFailure (Error error){\\n        //Do Something\\n    }\\n});\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"Carnival.setUserEmail(\\n  function callback(data) {\\n      console.log('setUserEmail successfully returned');\\n  },\\n  function errorHandler(err) {\\n      console.log('setUserEmail returned error: ' + err);\\n  },\\n  'firstname@example.com'\\n);\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"User Attributes\"\n}\n[/block]\nUser Attributes provide a powerful & flexible way for developers to store extra meta data for the purposes of grouping, segmenting and targeting users at a later stage.\n\nEach User/Device can have multiple attributes. User Attributes are unique to a device and app, and persist between app opens. They are set on the SDK side and saved back to the Carnival platform with the appropriate SDK methods.\n\nEach User Attribute has a name, a type and a value.\n\nFor example;\n\n- `first_name` (String)\n- `lifetime_value` (Float)\n- `number_of_items_purchased` (Integer)\n- `is_premium_subscriber` (Boolean)\n\nThe following attribute datatypes are supported;\n \n * Integer (32 bit)\n * Float\n * Date\n * String\n * Boolean\n * Array (of Integer, Floats, Dates and Strings)\n\n### To set some User attributes from the SDK\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Construct the object\\nCarnivalAttributes *attributes  = [[CarnivalAttributes alloc] init];\\n\\n// Set one or more attributes\\n[attributes setString:@\\\"Handbags\\\" forKey:@\\\"last_visited_category\\\"];\\n[attributes setStrings:@[@\\\"world\\\", @\\\"sports\\\"] forKey:@\\\"subscriptions\\\"];\\n[attributes setFloat:104.87 forKey:@\\\"customer_ltv\\\"];\\n[attributes setInteger:3 forKey:@\\\"products_in_cart\\\"];\\n[attributes setFloats:@[@(36.99), @(42.3)] forKey:@\\\"cart_items_unit_price\\\"];\\n[attributes setIntegers:@[@(2), @(1)] forKey:@\\\"cart_item_quantities\\\"];\\n[attributes setBool:YES forKey:@\\\"user_did_use_facebook_login\\\"];\\n[attributes setDates:@[[NSDate date]] forKey:@\\\"checkout_started\\\"];\\n\\n// Optional: choose if you want to add new attributes to the existing (update), or if you want to overwrite the current attributes with the new payload (replace). By default, we update.\\n[attributes setAttributesMergeRule:CarnivalAttributesMergeRuleUpdate];\\n\\n// Send to Carnival\\n[Carnival setAttributes:attributes withResponse:^(NSError * _Nullable error) {\\n  if (error) {\\n    NSLog(@\\\"Error - %@\\\", [error debugDescription]);\\n  }\\n}];\\n\\n// Remove an attribute\\n[Carnival removeAttributeWithKey:@\\\"products_in_cart\\\" withResponse:nil];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"// Construct the object\\nlet attributes = CarnivalAttributes()\\n\\n// Set one or more attributes\\nattributes.setString(\\\"Handbags\\\", forKey: \\\"last_visited_category\\\")\\nattributes.setStrings([\\\"world\\\", \\\"sports\\\"], forKey: \\\"subscriptions\\\")\\nattributes.setFloat(104.87, forKey: \\\"customer_ltv\\\")\\nattributes.setInteger(3, forKey: \\\"products_in_cart\\\")\\nattributes.setFloats([36.99, 42.3], forKey: \\\"cart_items_unit_price\\\")\\nattributes.setIntegers([2, 1], forKey: \\\"cart_item_quantities\\\")\\nattributes.setBool(true, forKey: \\\"user_did_use_facebook_login\\\")\\nattributes.setDates(NSDate(), forKey: \\\"checkout_started\\\")\\n\\n// Optional: choose if you want to add new attributes to the existing (update), or if you want to overwrite the current attributes with the new payload (replace). By default, we update.\\nattributes.setAttributesMergeRule(.Update)\\n\\n// Send to Carnival\\nCarnival.setAttributes(attributes) { error in\\n  print(\\\"setAttributes returned with possible error: \\\\(error)\\\")\\n}\\n\\n// Remove an attribute\\nCarnival.removeAttributeWithKey(\\\"products_in_cart\\\", nil)\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"// Construct the object\\nAttributeMap attributes = new AttributeMap();\\n\\n// Set one or more attributes\\nattributes.putString(\\\"last_visited_category\\\", \\\"Handbags\\\");\\n\\nArrayList<String> subscriptions = new ArrayList<>(Arrays.asList(\\\"world\\\", \\\"sports\\\"));\\nattributes.putStringArray(\\\"subscriptions\\\", subscriptions);\\n\\nattributes.putFloat(\\\"customer_ltv\\\", 104.87f);\\n\\nArrayList<Float> cartItemsUnitPrice = new ArrayList<>(Arrays.asList(36.99f, 42.3f));\\nattributes.putFloatArray(\\\"cart_items_unit_price\\\", cartItemsUnitPrice);\\n\\nattributes.putInt(\\\"products_in_cart\\\", 3);\\n\\nArrayList<Integer> cartItemQuantities = new ArrayList<>(Arrays.asList(2, 1));\\nattributes.putIntArray(\\\"cart_item_quantities\\\", cartItemQuantities);\\n\\nattributes.putBoolean(\\\"user_did_user_facebook_login\\\", true);\\nattributes.putDate(\\\"checkout_started\\\", new Date());\\n\\n// Optional: choose if you want to add new attributes to the existing (update), or if you want to overwrite the current attributes with the new payload (replace). By default, we update.\\nattributes.setMergeRules(AttributeMap.RULE_UPDATE);\\n\\n// Send to Carnival\\nCarnival.setAttributes(attributes, new Carnival.AttributesHandler() {\\n  @Override\\n  public void onSuccess() {\\n    // Handle success here\\n  }\\n\\n  @Override\\n  public void onFailure(Error error) {\\n    // Handle failure here\\n    Log.d(\\\"YOUR_LOG_TAG\\\", \\\"setAttributes returned with possible error: \\\" + error.getLocalizedMessage());\\n  }\\n});\\n\\n// Remove an attribute\\nCarnival.removeAttribute(\\\"products_in_cart\\\");\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"var attributeMap = new Carnival.AttributeMap();\\n\\n// Optional: choose if you want to add new attributes to the existing (update), or if you want to overwrite the current attributes with the new payload (replace). By default, we update.\\nattributeMap.setMergeRule(Carnival.AttributeMap.MergeRule.Update);\\n\\nattributeMap.setString('last_visited_category', 'Handbags');\\nattributeMap.setStringArray('subscriptions', ['world', 'sports']);\\nattributeMap.setFloat('customer_ltv', 104.87);\\nattributeMap.setFloatArray('cart_items_unit_price', [36.99, 42.3]);\\nattributeMap.setInteger('products_in_cart', 3);\\nattributeMap.setIntegerArray('cart_items_quantities', [2, 1]);\\nattributeMap.setDate('checkout_started', new Date());\\nattributeMap.setDateArray('stories_last_shared', [new Date(), new Date()]);\\nattributeMap.setBoolean('user_did_use_facebook_login', true);\\n\\nCarnival.setAttributes(\\n  function callback() {\\n    console.log('setAttributes successfully returned');\\n  },\\n  function errorHandler(err) {\\n    console.log('setAttributes returned error: ' + err);\\n  },\\n  attributeMap\\n);\\n\\n// Remove an attribute\\nCarnival.removeAttribute(function callback(data) {\\n  console.log('removeAtribute successfully returned');\\n\\t},\\n\\tfunction errorHandler(err) { \\n  \\tconsole.log('removeAttribute error: ' + err);\\n\\t}, 'products_in_cart');\\n});\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    },\n    {\n      \"code\": \"\\n// You can use a response handler to handle any errors returned by the platform.\\nCarnival.OnErrorEvent += (object sender, CarnivalErrorEventArgs e) => {\\n\\tDebug.Log (e.ErrorDescription);\\n};\\n\\nCarnival.SetAttribute(True, \\\"User_Has_Tweeted\\\");\\nCarnival.SetAttribute(\\\"Sam\\\", \\\"User_Name\\\");\\nCarnival.SetAttribute(54, \\\"User_Level\\\");\\nCarnival.SetAttribute(4.32, \\\"User_Average_Score\\\");\\n\\n// Removing user attributes\\nCarnival.RemoveAttribute (\\\"Latest_Open\\\");\",\n      \"language\": \"csharp\",\n      \"name\": \"Unity (C#)\"\n    }\n  ]\n}\n[/block]\n### Custom Attribute Limits\nThere are limits in place on the maximum number of custom attributes allowed as well as the length (size) of strings and arrays.\n\n* A maximum of **50** custom attributes is allowed per device. If you exceed this amount any new attributes being set will be discarded.\n* String values that have more than **255** characters will be truncated.\n* Arrays with more than **50** elements will be truncated.\n\n### Key name restrictions\n\n * Only letters, numbers, underscore and dashes are valid characters.\n * Leading spaces will be removed.\n * Invalid characters other than spaces and dots will be removed.\n * Spaces and dots will be replaced for underscores.\n * Keys will be truncated to a maximum of 255 characters.\n * Invalid keys will simply be discarded and no error will be returned.\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"Custom attributes are refreshed in real time.\"\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Custom Events\"\n}\n[/block]\nCarnival’s Custom Events provide a simple event tracking solution for measuring actions in your app. _Events_ can be logged to track actions that users take, such as screen views, or social network shares.  Unlike user attributes, events occur over time and graphs of the events can be seen within the analytics section of the Carnival dashboard.  \n\nCarnival keeps record of the count and the last occurrence of an event for each user, so you can target and segment based on;\n \n- Whether a user has performed this event or not\n- When they last performed this event\n- How often they have performed this event\n\n### To log a Custom Event\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[Carnival logEvent:@\\\"Checkout started\\\"];\\n[Carnival logEvent:@\\\"Article shared\\\"];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"Carnival.logEvent(\\\"Checkout started\\\")\\nCarnival.logEvent(\\\"Article shared\\\")\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"Carnival.logEvent(\\\"Checkout started\\\");\\nCarnival.logEvent(\\\"Article shared\\\");\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"Carnival.logEvent('Checkout started');\\nCarnival.logEvent('Article shared');\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    },\n    {\n      \"code\": \"Carnival.LogEvent (\\\"Checkout started\\\");\\nCarnival.LogEvent (\\\"Article shared\\\");\",\n      \"language\": \"csharp\",\n      \"name\": \"Unity (C#)\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"NOTE\",\n  \"body\": \"The maximum amount of unique events which can be registered **per device** is limited to 50. Events are updated in batch, close to real-time, every few seconds.\"\n}\n[/block]\n\n[block:api-header]\n{\n  \"title\": \"Sending Events together with Attributes\"\n}\n[/block]\nSometimes you may need to send particular user properties along with an action. This is useful for example when you want to track product views and keep a list of the last browsed items, or in similar scenario when you need to keep track of an action and a list of recent items.\n\n1. You organize data into arrays using multi-value attributes.\n2. Values in the array are sorted from most recent to the oldest. This way, you can ensure that the first item (the one at index 0) is the most recent\n3. You track a relevant event (for example, `Item browsed`). This way you keep track of how many times this event occurred, and the date of last occurrence is also the date of when the most recent item was browsed.\n\nAdopting this technique has some advantages:\n- You can keep a recent history of the user's activity\n- You can keep track of the date of last occurrence for both the latest item and the latest event\n- You don't pollute the attribute/event space by setting unnecessary attributes, which means you can get extra room to store more attributes in the future.\n\nTo do so, you can simply send an event, then take advantage of multi-value attributes to store the last item information.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// You are storing a list of recent items in a NSArray of any given type\\n// (strings in this example).\\n\\nNSString *item = @\\\"NEW_ITEM_ID\\\";\\nNSArray *items = @[@[item] arrayByAddingObjectsFromArray:previousItems];\\n\\n\\n[Carnival logEvent:@\\\"Item browsed\\\"];\\n\\nCarnivalAttributes *attributes  = [[CarnivalAttributes alloc] init];\\n[attributes setStrings:items forKey:@\\\"item_id\\\"];\\n\\n// Send to Carnival\\n[Carnival setAttributes:attributes withResponse:^(NSError * _Nullable error) {\\n  if (error) {\\n    NSLog(@\\\"Error - %@\\\", [error debugDescription]);\\n  }\\n}];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"// You are storing a list of recent items in an array of any given type\\n// (strings in this example).\\n\\nlet item = \\\"NEW_ITEM_ID\\\"\\nlet items = previousItems.insert(item, at: 0);\\n\\nCarnival.logEvent(\\\"Item browsed\\\")\\nlet attributes = CarnivalAttributes()\\nattributes.setStrings(items, forKey: \\\"item_id\\\")\\nCarnival.setAttributes(attributes) { error in\\n  print(\\\"setAttributes returned with possible error: \\\\(error)\\\")\\n}\\n\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"// You are storing a list of recent items in an ArrayList of any given type\\n// (strings in this example).\\n\\n\\nString item = \\\"NEW_ITEM_ID\\\";\\nArrayList<String> items = previousItems.add(0, item);\\n\\nCarnival.logEvent(\\\"Item browsed\\\");\\nAttributeMap attributes = new AttributeMap();\\nattributes.putStringArray(\\\"item_id\\\", items);\\n\\n// Send to Carnival\\nCarnival.setAttributes(attributes, new Carnival.AttributesHandler() {\\n  @Override\\n  public void onSuccess() {\\n    // Handle success here\\n  }\\n\\n  @Override\\n  public void onFailure(Error error) {\\n    // Handle failure here\\n    Log.d(\\\"YOUR_LOG_TAG\\\", \\\"setAttributes returned with possible error: \\\" + error.getLocalizedMessage());\\n  }\\n});\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"// You are storing a list of recent items in an array.\\n\\nlet item = 'NEW_ITEM_ID';\\nitems.unshift(item);\\n\\nCarnival.logEvent('Item browsed');\\nlet attributeMap = new Carnival.AttributeMap();\\nattributeMap.setStringArray('item_id', items);\\n\\nCarnival.setAttributes(\\n  function callback() {\\n    console.log('setAttributes successfully returned');\\n  },\\n  function errorHandler(err) {\\n    console.log('setAttributes returned error: ' + err);\\n  },\\n  attributeMap\\n);\",\n      \"language\": \"javascript\",\n      \"name\": \"Javascript\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Auto-Analytics Tracking\"\n}\n[/block]\n### On iOS\nThe Carnival iOS SDK automatically integrates with other analytics providers to capture some analytics. At the moment we capture only event data. This means that if you use the frameworks below to log events, they'll also be logged to Carnival as an event also. \n\nThe providers we integrate with are:\n * Google Analytics\n * Mixpanel\n * Adobe Analytics (Formerly Omniture)\n * Localytics\n * Amplitutde\n * Flurry\n \nAuto-Analytics is opt-in. To enable Auto-Analytics, use the enableAutoAnalytics method. \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"//Call as early as possible, perhaps straight after startEngine\\n\\n[Carnival enableAutoAnalytics:@[CarnivalAutoAnalyticsSourceGoogleAnalytics, CarnivalAutoAnalyticsSourceAdobe, CarnivalAutoAnalyticsSourceMixpanel, CarnivalAutoAnalyticsSourceLocalytics]];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"//Call as early as possible, perhaps straight after startEngine\\n\\nCarnival.enableAutoAnalytics([CarnivalAutoAnalyticsSourceGoogleAnalytics, CarnivalAutoAnalyticsSourceAdobe, CarnivalAutoAnalyticsSourceMixpanel, CarnivalAutoAnalyticsSourceLocalytics])\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    }\n  ]\n}\n[/block]\n#### Methods captured\n##### Google Analytics\n * `+ (id)createEventWithCategory:(NSString *)category action:(NSString *)action label:(NSString *)label value:(NSString *)value` \n\nWe discard `category`, `label` and `value`, and log a Carnival event with the `action` as the `name`.\n\n##### Adobe Analytics\n * `+ (void)trackAction:(NSString *)action data:(id)data`\nWe discard `data` and log a Carnival event with the `action` as the `name`.\n\n##### Mixpanel\n * `- (void)track:(NSString *)event properties:(NSString *)properties`\nWe discard `properties` and log a Carnival event with the `event` as the `name`.\n\n##### Localytics\n * `- (void)tagEvent:(NSString *)eventName attributes:(NSDictionary *)attributes customerValueIncrease:(NSNumber *)customerValueIncrease;`\n\nWe discard `attributes` and `customerValueIncrease`, and log a Carnival event with the `eventName` as the `name`.\n\n##### Flurry\n * `+ (NSInteger)logEvent:(NSString *)eventName withParameters:(NSDictionary *)parameters timed:(BOOL)timed`\n * `+ (NSInteger)logEvent:(NSString *)eventName timed:(BOOL)timed`\n\n\nWe discard `eventName`, `parameters` and `timed`, and log a Carnival event with the `eventName` as the `name`.\n\n##### Amplitude\n * `- (void)logEvent:(NSString *)eventType withEventProperties:(NSDictionary *)eventProperties withApiProperties:(NSDictionary *)apiProperties withUserProperties:(NSDictionary *)userProperties withGroups:(NSDictionary *)groups withTimestamp:(NSNumber *)timestamp outOfSession:(BOOL)outOfSession` (Other event methods call this method)\n\n\nWe discard most parameters, and log a Carnival event with the `eventType` as the `name`.\n\n\n### On Android\nFor Auto-Analytics Tracking on Android, the `Carnival.logEvent()` call now takes a source parameter for when forwarding events from other analytics frameworks to Carnival. This allows you to target based on events you already track.\n\n```java\nCarnival.logEvent(\"source\", \"myEvent\");\n```\n\nA selection of pre-written integrations have been [provided](https://github.com/carnivalmobile/carnival-android-sdk/blob/master/Analytics%20Integration/AnalyticIntegration.java), allowing you to just include one file, replace your event logging calls and then turn on or off the frameworks you want to use by commenting them out in the source file provided.\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"User Attributes don't record events that happen over time, and don't appear in graphs in the platform. They are simply metadata for a user. For recording events that happen over time, so you can target users by behavior, you should use our custom events feature.\",\n  \"title\": \"User Attributes or Custom Events?\"\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Tracking Location\"\n}\n[/block]\nBy default the Carnival platform collects a last known IP location for each user.  This can be used for coarse location segmentation and targeting with no extra development effort or permissions in your app.\n\nDepending on local laws, you may need to obtain the express consent from your app users in order to track IP location. \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[Carnival setGeoIPTrackingEnabled:NO];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"Carnival.setGeoIPTrackingEnabled(false)\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"Carnival.setGeoIpTrackingEnabled(false);\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    }\n  ]\n}\n[/block]\nIn order to support more granular location, it is up to the app to decide and manage the accuracy of capturing location information, while considering the user's battery life. It's best practice to use only the accuracy you need for messaging (Block, City, State, Country).\n\nLocation updates can then be passed through to Carnival for segmentation.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// On iOS, using Objective-C\\n[Carnival updateLocation:myLocation]; //Takes an instance of a CLLocation object as a argument.\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objectve-C)\"\n    },\n    {\n      \"code\": \"// On iOS, using Swift\\nCarnival.updateLocation(myLocation) //Takes an instance of a CLLocation object as a argument.\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"// On iOS, using Java\\nCarnival.updateLocation(myLocation); //Takes an instance of a Location object as a argument.\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"// On Cordova, using JavaScript\\nCarnival.updateLocation(lat, lon); //Takes lattitude and longitude as arguments\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    },\n    {\n      \"code\": \"// On Cordova, using C#\\nCarnival.UpdateLocation (lat, lon); //Takes lattitude and longitude as arguments\",\n      \"language\": \"csharp\",\n      \"name\": \"Unity (C#)\"\n    }\n  ]\n}\n[/block]\nBelow are some short tutorials for collecting a user's location on Both iOS and Android:\n\n* [Location Tracking on iOS](doc:location-tracking-on-ios) \n* [Location Tracking on Android](https://developer.android.com/training/location/index.html)\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Clearing Device Data\"\n}\n[/block]\nAt times, such as a logging out flow or a user settings screen, you might want to clear device data. You're able to clear data for Events, Custom Attributes and Message Stream.\n\nWarning: By clearing events or custom attributes, the device may become eligible for an automated message triggered based on leaving an audience. Double check your set up before using this method. \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[Carnival clearDeviceData:CarnivalDeviceDataTypeEvents | CarnivalDeviceDataTypeAttributes | CarnivalDeviceDataTypeMessageStream withResponse:^(NSError * _Nullable error) { \\n  // Possible error here\\n}];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"  Carnival.clearDeviceData(.Attributes | .MessageStream | .Events) { (error) in\\n      // Possible error here\\n  }\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"//Carnival.ATTRIBUTES|Carnival.MESSAGE_STREAM|Carnival.EVENTS\\nCarnival.clearDevice(Carnival.CLEAR_ALL, new Carnival.CarnivalHandler<Void>() {\\n    @Override\\n    public void onSuccess(Void value) {\\n\\n    }\\n\\n    @Override\\n    public void onFailure(Error error) {\\n\\n    }\\n});\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"// Clear one or more types. Specify one or more of these values.\\nCarnival.clearDevice(\\n  Carnival.DeviceValues.Attributes | \\n  Carnival.DeviceValues.MessageStream |\\n  Carnival.DeviceValues.Events);\\n\\n// Clear all device data\\nCarnival.clearDevice(Carnival.DeviceValues.ClearAll);\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    }\n  ]\n}\n[/block]","excerpt":"","slug":"collecting-user-data","type":"basic","title":"Collecting User Data"}

Collecting User Data


The strength of Carnival's targeting and segmentation relies on knowing the correct information about your users. The more information we know about a user, the better you can target them when you send messages. When a user installs an app, we collect a few attributes automatically. These attributes are helpful for targeting and segmenting your users. ### Automatically Tracked Attributes - App Version - Language - Time Zone - Badge Number (iOS only) - Device ID - Device Make & Model - Operating System & Version - Carnival SDK version - Location based on IP Address - Notifications Allowed - Platform [block:callout] { "type": "info", "body": "Attributes tracked automatically are refreshed at every app load." } [/block] Beyond the automatically tracked attributes, Carnival also lets you store custom information about a user for further segmenting and targeting. This feature is called [User Attributes](#user-attributes). [block:api-header] { "type": "basic", "title": "Setting a User ID" } [/block] If your app has a login, you can set a unique User ID with Carnival. This allows you to associate multiple devices to a single user when targeting via the API. You may also want to collect the [user's email ](http://docs.carnival.io/v1.0/docs/collecting-user-data#setting-a-user-email) as well. [block:code] { "codes": [ { "code": "// setting a User ID after login\n[Carnival setUserId:@\"user_id_1234\" withResponse:^(NSError *error) {\n NSLog(@\"Error setting user id – %@\", error.localizedDescription);\n}];\n\n// clearing a User ID after logout\n[Carnival setUserId:nil withResponse:^(NSError *error) {\n \n}];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "// setting a User ID after login\nCarnival.setUserId(\"user_id_1234\") { error in\n println(\"setUserID returned with possible error: \\(error)\")\n}\n\n// clearing a User ID after logout\nCarnival.setUserId(nil) { error in\n println(\"setUserID returned with possible error: \\(error)\")\n}", "language": "swift", "name": "iOS (Swift)" }, { "code": "// setting a User ID after login\nCarnival.setUserId(\"user_id_1234\", new CarnivalHandler<Void> (){\n\n @Override\n public void onSuccess (Void value){\n //Do Something\n }\n\n @Override\n public void onFailure (Error error){\n //Do Something\n }\n});\n\n\n// clearing a User ID after logout\nCarnival.setUserId(null, new CarnivalHandler<Void> (){\n\n @Override\n public void onSuccess (Void value){\n //Do Something\n }\n\n @Override\n public void onFailure (Error error){\n //Do Something\n }\n});", "language": "java", "name": "Android (Java)" }, { "code": "Carnival.setUserId(\n function callback(data) {\n console.log('setUserId successfully returned');\n },\n function errorHandler(err) {\n console.log('setUserId returned error: ' + err);\n },\n 'user_id_1234'\n);", "language": "csharp", "name": "Cordova (JavaScript)" }, { "code": "Carnival.SetUserId(\"user_id_1234);\n// You can also attach the standard error hander too.", "language": "csharp", "name": "Unity (C#)" } ] } [/block] [block:callout] { "type": "info", "body": "Setting a User ID is optional, but it is strongly recommended. It is a prerequisite to use:\n\n* Sending notifications to specific users, including using the [Notifications API](/docs/notifications) with `user_id` audiences\n* [Users API](/docs/users)\n* [Users Events API](/docs/users-events)\n* [Audience creation via CSV](http://marketing.carnival.io/docs/introduction-to-audiences#section-by-csv)\n\nWithout User ID you will be able to set attributes and events only at the device level." } [/block] [block:api-header] { "type": "basic", "title": "Setting a User Email" } [/block] If your app has a login, you can set a unique user email with Carnival. This email is then matched up with users tracked in your Sailthru account. **This is a prerequisite if you plan to [integrate Sailthru with Carnival](http://marketing.carnival.io/v1.0/docs/sailthru-integration).** It is recommended to collect both user ID and user email. [block:code] { "codes": [ { "code": "// setting a User Email after login\n[Carnival setUserEmail:@\"firstname@example.com\" withResponse:^(NSError *error) {\n NSLog(@\"Error setting user email – %@\", error.localizedDescription);\n}];\n\n// clearing a User Email after logout\n[Carnival setUserEmail:nil withResponse:^(NSError *error) {\n NSLog(@\"Error setting user email – %@\", error.localizedDescription);\n}];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "// setting a User Email after login\nCarnival.setUserEmail(\"firstname@example.com\") { error in\n println(\"setUserEmail returned with possible error: \\(error)\")\n}\n\n// clearing a User Email after logout\nCarnival.setUserEmail(nil) { error in\n println(\"setUserEmail returned with possible error: \\(error)\")\n}", "language": "swift", "name": "iOS (Swift)" }, { "code": "// setting a User Email after login\nCarnival.setUserEmail(\"firstname@example.com\", new CarnivalHandler<Void> (){\n\n @Override\n public void onSuccess (Void value){\n //Do Something\n }\n\n @Override\n public void onFailure (Error error){\n //Do Something\n }\n});\n\n\n// clearing a User Email after logout\nCarnival.setUserEmail(null, new CarnivalHandler<Void> (){\n\n @Override\n public void onSuccess (Void value){\n //Do Something\n }\n\n @Override\n public void onFailure (Error error){\n //Do Something\n }\n});", "language": "java", "name": "Android (Java)" }, { "code": "Carnival.setUserEmail(\n function callback(data) {\n console.log('setUserEmail successfully returned');\n },\n function errorHandler(err) {\n console.log('setUserEmail returned error: ' + err);\n },\n 'firstname@example.com'\n);", "language": "javascript", "name": "Cordova (JavaScript)" } ] } [/block] [block:api-header] { "type": "basic", "title": "User Attributes" } [/block] User Attributes provide a powerful & flexible way for developers to store extra meta data for the purposes of grouping, segmenting and targeting users at a later stage. Each User/Device can have multiple attributes. User Attributes are unique to a device and app, and persist between app opens. They are set on the SDK side and saved back to the Carnival platform with the appropriate SDK methods. Each User Attribute has a name, a type and a value. For example; - `first_name` (String) - `lifetime_value` (Float) - `number_of_items_purchased` (Integer) - `is_premium_subscriber` (Boolean) The following attribute datatypes are supported; * Integer (32 bit) * Float * Date * String * Boolean * Array (of Integer, Floats, Dates and Strings) ### To set some User attributes from the SDK [block:code] { "codes": [ { "code": "// Construct the object\nCarnivalAttributes *attributes = [[CarnivalAttributes alloc] init];\n\n// Set one or more attributes\n[attributes setString:@\"Handbags\" forKey:@\"last_visited_category\"];\n[attributes setStrings:@[@\"world\", @\"sports\"] forKey:@\"subscriptions\"];\n[attributes setFloat:104.87 forKey:@\"customer_ltv\"];\n[attributes setInteger:3 forKey:@\"products_in_cart\"];\n[attributes setFloats:@[@(36.99), @(42.3)] forKey:@\"cart_items_unit_price\"];\n[attributes setIntegers:@[@(2), @(1)] forKey:@\"cart_item_quantities\"];\n[attributes setBool:YES forKey:@\"user_did_use_facebook_login\"];\n[attributes setDates:@[[NSDate date]] forKey:@\"checkout_started\"];\n\n// Optional: choose if you want to add new attributes to the existing (update), or if you want to overwrite the current attributes with the new payload (replace). By default, we update.\n[attributes setAttributesMergeRule:CarnivalAttributesMergeRuleUpdate];\n\n// Send to Carnival\n[Carnival setAttributes:attributes withResponse:^(NSError * _Nullable error) {\n if (error) {\n NSLog(@\"Error - %@\", [error debugDescription]);\n }\n}];\n\n// Remove an attribute\n[Carnival removeAttributeWithKey:@\"products_in_cart\" withResponse:nil];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "// Construct the object\nlet attributes = CarnivalAttributes()\n\n// Set one or more attributes\nattributes.setString(\"Handbags\", forKey: \"last_visited_category\")\nattributes.setStrings([\"world\", \"sports\"], forKey: \"subscriptions\")\nattributes.setFloat(104.87, forKey: \"customer_ltv\")\nattributes.setInteger(3, forKey: \"products_in_cart\")\nattributes.setFloats([36.99, 42.3], forKey: \"cart_items_unit_price\")\nattributes.setIntegers([2, 1], forKey: \"cart_item_quantities\")\nattributes.setBool(true, forKey: \"user_did_use_facebook_login\")\nattributes.setDates(NSDate(), forKey: \"checkout_started\")\n\n// Optional: choose if you want to add new attributes to the existing (update), or if you want to overwrite the current attributes with the new payload (replace). By default, we update.\nattributes.setAttributesMergeRule(.Update)\n\n// Send to Carnival\nCarnival.setAttributes(attributes) { error in\n print(\"setAttributes returned with possible error: \\(error)\")\n}\n\n// Remove an attribute\nCarnival.removeAttributeWithKey(\"products_in_cart\", nil)", "language": "swift", "name": "iOS (Swift)" }, { "code": "// Construct the object\nAttributeMap attributes = new AttributeMap();\n\n// Set one or more attributes\nattributes.putString(\"last_visited_category\", \"Handbags\");\n\nArrayList<String> subscriptions = new ArrayList<>(Arrays.asList(\"world\", \"sports\"));\nattributes.putStringArray(\"subscriptions\", subscriptions);\n\nattributes.putFloat(\"customer_ltv\", 104.87f);\n\nArrayList<Float> cartItemsUnitPrice = new ArrayList<>(Arrays.asList(36.99f, 42.3f));\nattributes.putFloatArray(\"cart_items_unit_price\", cartItemsUnitPrice);\n\nattributes.putInt(\"products_in_cart\", 3);\n\nArrayList<Integer> cartItemQuantities = new ArrayList<>(Arrays.asList(2, 1));\nattributes.putIntArray(\"cart_item_quantities\", cartItemQuantities);\n\nattributes.putBoolean(\"user_did_user_facebook_login\", true);\nattributes.putDate(\"checkout_started\", new Date());\n\n// Optional: choose if you want to add new attributes to the existing (update), or if you want to overwrite the current attributes with the new payload (replace). By default, we update.\nattributes.setMergeRules(AttributeMap.RULE_UPDATE);\n\n// Send to Carnival\nCarnival.setAttributes(attributes, new Carnival.AttributesHandler() {\n @Override\n public void onSuccess() {\n // Handle success here\n }\n\n @Override\n public void onFailure(Error error) {\n // Handle failure here\n Log.d(\"YOUR_LOG_TAG\", \"setAttributes returned with possible error: \" + error.getLocalizedMessage());\n }\n});\n\n// Remove an attribute\nCarnival.removeAttribute(\"products_in_cart\");", "language": "java", "name": "Android (Java)" }, { "code": "var attributeMap = new Carnival.AttributeMap();\n\n// Optional: choose if you want to add new attributes to the existing (update), or if you want to overwrite the current attributes with the new payload (replace). By default, we update.\nattributeMap.setMergeRule(Carnival.AttributeMap.MergeRule.Update);\n\nattributeMap.setString('last_visited_category', 'Handbags');\nattributeMap.setStringArray('subscriptions', ['world', 'sports']);\nattributeMap.setFloat('customer_ltv', 104.87);\nattributeMap.setFloatArray('cart_items_unit_price', [36.99, 42.3]);\nattributeMap.setInteger('products_in_cart', 3);\nattributeMap.setIntegerArray('cart_items_quantities', [2, 1]);\nattributeMap.setDate('checkout_started', new Date());\nattributeMap.setDateArray('stories_last_shared', [new Date(), new Date()]);\nattributeMap.setBoolean('user_did_use_facebook_login', true);\n\nCarnival.setAttributes(\n function callback() {\n console.log('setAttributes successfully returned');\n },\n function errorHandler(err) {\n console.log('setAttributes returned error: ' + err);\n },\n attributeMap\n);\n\n// Remove an attribute\nCarnival.removeAttribute(function callback(data) {\n console.log('removeAtribute successfully returned');\n\t},\n\tfunction errorHandler(err) { \n \tconsole.log('removeAttribute error: ' + err);\n\t}, 'products_in_cart');\n});", "language": "javascript", "name": "Cordova (JavaScript)" }, { "code": "\n// You can use a response handler to handle any errors returned by the platform.\nCarnival.OnErrorEvent += (object sender, CarnivalErrorEventArgs e) => {\n\tDebug.Log (e.ErrorDescription);\n};\n\nCarnival.SetAttribute(True, \"User_Has_Tweeted\");\nCarnival.SetAttribute(\"Sam\", \"User_Name\");\nCarnival.SetAttribute(54, \"User_Level\");\nCarnival.SetAttribute(4.32, \"User_Average_Score\");\n\n// Removing user attributes\nCarnival.RemoveAttribute (\"Latest_Open\");", "language": "csharp", "name": "Unity (C#)" } ] } [/block] ### Custom Attribute Limits There are limits in place on the maximum number of custom attributes allowed as well as the length (size) of strings and arrays. * A maximum of **50** custom attributes is allowed per device. If you exceed this amount any new attributes being set will be discarded. * String values that have more than **255** characters will be truncated. * Arrays with more than **50** elements will be truncated. ### Key name restrictions * Only letters, numbers, underscore and dashes are valid characters. * Leading spaces will be removed. * Invalid characters other than spaces and dots will be removed. * Spaces and dots will be replaced for underscores. * Keys will be truncated to a maximum of 255 characters. * Invalid keys will simply be discarded and no error will be returned. [block:callout] { "type": "info", "body": "Custom attributes are refreshed in real time." } [/block] [block:api-header] { "type": "basic", "title": "Custom Events" } [/block] Carnival’s Custom Events provide a simple event tracking solution for measuring actions in your app. _Events_ can be logged to track actions that users take, such as screen views, or social network shares. Unlike user attributes, events occur over time and graphs of the events can be seen within the analytics section of the Carnival dashboard. Carnival keeps record of the count and the last occurrence of an event for each user, so you can target and segment based on; - Whether a user has performed this event or not - When they last performed this event - How often they have performed this event ### To log a Custom Event [block:code] { "codes": [ { "code": "[Carnival logEvent:@\"Checkout started\"];\n[Carnival logEvent:@\"Article shared\"];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "Carnival.logEvent(\"Checkout started\")\nCarnival.logEvent(\"Article shared\")", "language": "swift", "name": "iOS (Swift)" }, { "code": "Carnival.logEvent(\"Checkout started\");\nCarnival.logEvent(\"Article shared\");", "language": "java", "name": "Android (Java)" }, { "code": "Carnival.logEvent('Checkout started');\nCarnival.logEvent('Article shared');", "language": "javascript", "name": "Cordova (JavaScript)" }, { "code": "Carnival.LogEvent (\"Checkout started\");\nCarnival.LogEvent (\"Article shared\");", "language": "csharp", "name": "Unity (C#)" } ] } [/block] [block:callout] { "type": "info", "title": "NOTE", "body": "The maximum amount of unique events which can be registered **per device** is limited to 50. Events are updated in batch, close to real-time, every few seconds." } [/block] [block:api-header] { "title": "Sending Events together with Attributes" } [/block] Sometimes you may need to send particular user properties along with an action. This is useful for example when you want to track product views and keep a list of the last browsed items, or in similar scenario when you need to keep track of an action and a list of recent items. 1. You organize data into arrays using multi-value attributes. 2. Values in the array are sorted from most recent to the oldest. This way, you can ensure that the first item (the one at index 0) is the most recent 3. You track a relevant event (for example, `Item browsed`). This way you keep track of how many times this event occurred, and the date of last occurrence is also the date of when the most recent item was browsed. Adopting this technique has some advantages: - You can keep a recent history of the user's activity - You can keep track of the date of last occurrence for both the latest item and the latest event - You don't pollute the attribute/event space by setting unnecessary attributes, which means you can get extra room to store more attributes in the future. To do so, you can simply send an event, then take advantage of multi-value attributes to store the last item information. [block:code] { "codes": [ { "code": "// You are storing a list of recent items in a NSArray of any given type\n// (strings in this example).\n\nNSString *item = @\"NEW_ITEM_ID\";\nNSArray *items = @[@[item] arrayByAddingObjectsFromArray:previousItems];\n\n\n[Carnival logEvent:@\"Item browsed\"];\n\nCarnivalAttributes *attributes = [[CarnivalAttributes alloc] init];\n[attributes setStrings:items forKey:@\"item_id\"];\n\n// Send to Carnival\n[Carnival setAttributes:attributes withResponse:^(NSError * _Nullable error) {\n if (error) {\n NSLog(@\"Error - %@\", [error debugDescription]);\n }\n}];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "// You are storing a list of recent items in an array of any given type\n// (strings in this example).\n\nlet item = \"NEW_ITEM_ID\"\nlet items = previousItems.insert(item, at: 0);\n\nCarnival.logEvent(\"Item browsed\")\nlet attributes = CarnivalAttributes()\nattributes.setStrings(items, forKey: \"item_id\")\nCarnival.setAttributes(attributes) { error in\n print(\"setAttributes returned with possible error: \\(error)\")\n}\n", "language": "swift", "name": "iOS (Swift)" }, { "code": "// You are storing a list of recent items in an ArrayList of any given type\n// (strings in this example).\n\n\nString item = \"NEW_ITEM_ID\";\nArrayList<String> items = previousItems.add(0, item);\n\nCarnival.logEvent(\"Item browsed\");\nAttributeMap attributes = new AttributeMap();\nattributes.putStringArray(\"item_id\", items);\n\n// Send to Carnival\nCarnival.setAttributes(attributes, new Carnival.AttributesHandler() {\n @Override\n public void onSuccess() {\n // Handle success here\n }\n\n @Override\n public void onFailure(Error error) {\n // Handle failure here\n Log.d(\"YOUR_LOG_TAG\", \"setAttributes returned with possible error: \" + error.getLocalizedMessage());\n }\n});", "language": "java", "name": "Android (Java)" }, { "code": "// You are storing a list of recent items in an array.\n\nlet item = 'NEW_ITEM_ID';\nitems.unshift(item);\n\nCarnival.logEvent('Item browsed');\nlet attributeMap = new Carnival.AttributeMap();\nattributeMap.setStringArray('item_id', items);\n\nCarnival.setAttributes(\n function callback() {\n console.log('setAttributes successfully returned');\n },\n function errorHandler(err) {\n console.log('setAttributes returned error: ' + err);\n },\n attributeMap\n);", "language": "javascript", "name": "Javascript" } ] } [/block] [block:api-header] { "type": "basic", "title": "Auto-Analytics Tracking" } [/block] ### On iOS The Carnival iOS SDK automatically integrates with other analytics providers to capture some analytics. At the moment we capture only event data. This means that if you use the frameworks below to log events, they'll also be logged to Carnival as an event also. The providers we integrate with are: * Google Analytics * Mixpanel * Adobe Analytics (Formerly Omniture) * Localytics * Amplitutde * Flurry Auto-Analytics is opt-in. To enable Auto-Analytics, use the enableAutoAnalytics method. [block:code] { "codes": [ { "code": "//Call as early as possible, perhaps straight after startEngine\n\n[Carnival enableAutoAnalytics:@[CarnivalAutoAnalyticsSourceGoogleAnalytics, CarnivalAutoAnalyticsSourceAdobe, CarnivalAutoAnalyticsSourceMixpanel, CarnivalAutoAnalyticsSourceLocalytics]];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "//Call as early as possible, perhaps straight after startEngine\n\nCarnival.enableAutoAnalytics([CarnivalAutoAnalyticsSourceGoogleAnalytics, CarnivalAutoAnalyticsSourceAdobe, CarnivalAutoAnalyticsSourceMixpanel, CarnivalAutoAnalyticsSourceLocalytics])", "language": "swift", "name": "iOS (Swift)" } ] } [/block] #### Methods captured ##### Google Analytics * `+ (id)createEventWithCategory:(NSString *)category action:(NSString *)action label:(NSString *)label value:(NSString *)value` We discard `category`, `label` and `value`, and log a Carnival event with the `action` as the `name`. ##### Adobe Analytics * `+ (void)trackAction:(NSString *)action data:(id)data` We discard `data` and log a Carnival event with the `action` as the `name`. ##### Mixpanel * `- (void)track:(NSString *)event properties:(NSString *)properties` We discard `properties` and log a Carnival event with the `event` as the `name`. ##### Localytics * `- (void)tagEvent:(NSString *)eventName attributes:(NSDictionary *)attributes customerValueIncrease:(NSNumber *)customerValueIncrease;` We discard `attributes` and `customerValueIncrease`, and log a Carnival event with the `eventName` as the `name`. ##### Flurry * `+ (NSInteger)logEvent:(NSString *)eventName withParameters:(NSDictionary *)parameters timed:(BOOL)timed` * `+ (NSInteger)logEvent:(NSString *)eventName timed:(BOOL)timed` We discard `eventName`, `parameters` and `timed`, and log a Carnival event with the `eventName` as the `name`. ##### Amplitude * `- (void)logEvent:(NSString *)eventType withEventProperties:(NSDictionary *)eventProperties withApiProperties:(NSDictionary *)apiProperties withUserProperties:(NSDictionary *)userProperties withGroups:(NSDictionary *)groups withTimestamp:(NSNumber *)timestamp outOfSession:(BOOL)outOfSession` (Other event methods call this method) We discard most parameters, and log a Carnival event with the `eventType` as the `name`. ### On Android For Auto-Analytics Tracking on Android, the `Carnival.logEvent()` call now takes a source parameter for when forwarding events from other analytics frameworks to Carnival. This allows you to target based on events you already track. ```java Carnival.logEvent("source", "myEvent"); ``` A selection of pre-written integrations have been [provided](https://github.com/carnivalmobile/carnival-android-sdk/blob/master/Analytics%20Integration/AnalyticIntegration.java), allowing you to just include one file, replace your event logging calls and then turn on or off the frameworks you want to use by commenting them out in the source file provided. [block:callout] { "type": "info", "body": "User Attributes don't record events that happen over time, and don't appear in graphs in the platform. They are simply metadata for a user. For recording events that happen over time, so you can target users by behavior, you should use our custom events feature.", "title": "User Attributes or Custom Events?" } [/block] [block:api-header] { "type": "basic", "title": "Tracking Location" } [/block] By default the Carnival platform collects a last known IP location for each user. This can be used for coarse location segmentation and targeting with no extra development effort or permissions in your app. Depending on local laws, you may need to obtain the express consent from your app users in order to track IP location. [block:code] { "codes": [ { "code": "[Carnival setGeoIPTrackingEnabled:NO];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "Carnival.setGeoIPTrackingEnabled(false)", "language": "swift", "name": "iOS (Swift)" }, { "code": "Carnival.setGeoIpTrackingEnabled(false);", "language": "java", "name": "Android (Java)" } ] } [/block] In order to support more granular location, it is up to the app to decide and manage the accuracy of capturing location information, while considering the user's battery life. It's best practice to use only the accuracy you need for messaging (Block, City, State, Country). Location updates can then be passed through to Carnival for segmentation. [block:code] { "codes": [ { "code": "// On iOS, using Objective-C\n[Carnival updateLocation:myLocation]; //Takes an instance of a CLLocation object as a argument.", "language": "objectivec", "name": "iOS (Objectve-C)" }, { "code": "// On iOS, using Swift\nCarnival.updateLocation(myLocation) //Takes an instance of a CLLocation object as a argument.", "language": "swift", "name": "iOS (Swift)" }, { "code": "// On iOS, using Java\nCarnival.updateLocation(myLocation); //Takes an instance of a Location object as a argument.", "language": "java", "name": "Android (Java)" }, { "code": "// On Cordova, using JavaScript\nCarnival.updateLocation(lat, lon); //Takes lattitude and longitude as arguments", "language": "javascript", "name": "Cordova (JavaScript)" }, { "code": "// On Cordova, using C#\nCarnival.UpdateLocation (lat, lon); //Takes lattitude and longitude as arguments", "language": "csharp", "name": "Unity (C#)" } ] } [/block] Below are some short tutorials for collecting a user's location on Both iOS and Android: * [Location Tracking on iOS](doc:location-tracking-on-ios) * [Location Tracking on Android](https://developer.android.com/training/location/index.html) [block:api-header] { "type": "basic", "title": "Clearing Device Data" } [/block] At times, such as a logging out flow or a user settings screen, you might want to clear device data. You're able to clear data for Events, Custom Attributes and Message Stream. Warning: By clearing events or custom attributes, the device may become eligible for an automated message triggered based on leaving an audience. Double check your set up before using this method. [block:code] { "codes": [ { "code": "[Carnival clearDeviceData:CarnivalDeviceDataTypeEvents | CarnivalDeviceDataTypeAttributes | CarnivalDeviceDataTypeMessageStream withResponse:^(NSError * _Nullable error) { \n // Possible error here\n}];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": " Carnival.clearDeviceData(.Attributes | .MessageStream | .Events) { (error) in\n // Possible error here\n }", "language": "swift", "name": "iOS (Swift)" }, { "code": "//Carnival.ATTRIBUTES|Carnival.MESSAGE_STREAM|Carnival.EVENTS\nCarnival.clearDevice(Carnival.CLEAR_ALL, new Carnival.CarnivalHandler<Void>() {\n @Override\n public void onSuccess(Void value) {\n\n }\n\n @Override\n public void onFailure(Error error) {\n\n }\n});", "language": "java", "name": "Android (Java)" }, { "code": "// Clear one or more types. Specify one or more of these values.\nCarnival.clearDevice(\n Carnival.DeviceValues.Attributes | \n Carnival.DeviceValues.MessageStream |\n Carnival.DeviceValues.Events);\n\n// Clear all device data\nCarnival.clearDevice(Carnival.DeviceValues.ClearAll);", "language": "javascript", "name": "Cordova (JavaScript)" } ] } [/block]