iOS

Getting started

1. Install the SDK

The fastest and easiest way to use Localytics in your project is with CocoaPods. If you cannot use CocoaPods, use the alternative installation approach.

  1. Install CocoaPods by executing
    gem install cocoapods
    
  2. If you don't already have a Podfile in your Xcode project directory, create one with the following command
    pod init
    
  3. In your Podfile add the following content
    platform :ios, '8.0'
    pod 'Localytics', '~> 4.3'
    
    The Localytics pod uses a dynamic framework. If you need to use a static binary (e.g. you support iOS 7) then use the following pod
    platform :ios, '7.0'
    pod 'LocalyticsStaticFramework', '~> 4.3'
    
  4. Install the Localytics SDK by executing the following in your Xcode project directory
    pod install
    
  5. Open the project workspace file (YOUR-PROJECT-NAME.xcworkspace) instead of the project file (YOUR-PROJECT-NAME.xcodeproj) to ensure that the Localytics dependency is properly loaded.

2. Configure test mode

You need to take a few steps to setup a URL scheme, which allows you to test marketing campaigns both before and after you release your app with Localytics. The URL scheme is defined as "amp" followed by your Localytics app key, as demonstrated below. You can find your app key in the Localytics Dashboard. If you switch between multiple Localytics app keys, as when you have separate testing and production app keys, there's no harm in registering both URL schemes.

Configure test mode URL scheme in Project Settings
  1. Enter Project Navigator view and click on your project icon. It should be at the top of the Project Navigator.
  2. Select your target from the sidebar or from the dropdown menu, then select the Info tab.
  3. Open the URL Types expander and click +.
  4. In the URL Schemes field, enter "amp" followed by your Localytics app key.

3. Initialize the SDK

In order to run the Localytics SDK, you must initialze the SDK using your Localytics app key. You can find your app key in the Localytics Dashboard.

If you have an app with extensive, engaged usage in the background, or you require more fine control over the session lifecycle (most apps don't), use the alternative session management approach.

In your application's delegate file:

  1. Import the Localytics SDK under any existing imports.

    Objective-C Swift

    @import Localytics;
    
    import Localytics
    
  2. Add the following line to the start of didFinishLaunchingWithOptions:.

    Objective-C Swift

    [Localytics autoIntegrate:@"YOUR-LOCALYTICS-APP-KEY" launchOptions:launchOptions];
    
    Localytics.autoIntegrate("YOUR-LOCALYTICS-APP-KEY", launchOptions: launchOptions)
    
  3. Compile and run your app.

More information on using the Localytics SDK within a Swift project is available here.

4. Next steps

Congratulations! You have successfully performed the basic Localytics integration and are now sending session data to Localytics. You can also use Localytics In-App Messaging to message users in your app, and you have everything you need to track where your most highly engaged users are coming from.

Note that it may take a few minutes for your first datapoints to show up within the Localytics Dashboard. In the meantime, we suggest reading the next few sections to learn how to:

  1. Track one user action as an event
  2. Track one user property as a profile attribute
  3. Integrate push messaging

We recommend doing these things before releasing your app for the first time with Localytics.

Session lifecycle

With just the basic setup above, the Localytics SDK automatically tracks user engagement and retention by tracking patterns of foregrounding and backgrounding of your app. Upon foregrounding, the Localytics SDK automatically creates and uploads a "start session" datapoint that captures many details about the user's device (e.g., device model, OS version, device IDs) and is used for powering charts within Localytics.

Upon backgrounding, the SDK marks the current time. When the user returns to the app later and it has been more than 15 seconds since the user had last backgrounded the app, the SDK will close the previous session by creating a "close session" datapoint, create a new "start session" datapoint, and upload both of these datapoints. If the user foregrounds the app within 15 seconds of the previous backgrounding, the previous session is resumed as if the user had not left the app at all. Due to this automatic session lifecycle tracking, Localytics is able to derive session length, session interval, session counts, session trending, and a number of other core metrics for exploration in the Localytics Dashboard.

Whenever the app transitions to the foreground or background, the Localytics SDK attempts to upload any datapoints which are cached on the device. Uploads are performed in batches to reduce network use and increase the likelihood of successful uploads. Data remains on the device until it is successfully uploaded, and only then does the SDK remove it from the device.

Tagging events

Track user actions in your app using events in Localytics. All events must have a name, but you can also track the details of the action with event attributes. Event attributes help to provide context about why and how the action occurred. Every event can have up to 50 attributes unique to that event with each attribute having a limit of 255 characters.

Standard events

Standard events make it easier to analyze user behavior and optimize your app marketing around common business goals such as driving user registrations or purchases. You can also tag custom events for other user behavior in your app that doesn't match one of the standard events.

Purchased

Objective-C Swift

[Localytics tagPurchased:@"Shirt"
                  itemId:@"sku-123"
                itemType:@"Apparel"
               itemPrice:@15
              attributes:extraAttributes];
Localytics.tagPurchased("Shirt", itemId: "sku-123", itemType: "Apparel", itemPrice: 15, attributes: extraAttributes)

Added to Cart

Objective-C Swift

[Localytics tagAddedToCart:@"Shirt"
                    itemId:@"sku-123"
                  itemType:@"Apparel"
                 itemPrice:@15
                attributes:extraAttributes];
Localytics.tagAddedToCart("Shirt", itemId: "sku-123", itemType: "Apparel", itemPrice: 15, attributes: extraAttributes)

Started Checkout

Objective-C Swift

[Localytics tagStartedCheckout:@50
                     itemCount:@2
                    attributes:extraAttributes];
Localytics.tagStartedCheckout(50, itemCount: 2, attributes: extraAttributes)

Completed Checkout

Objective-C Swift

[Localytics tagCompletedCheckout:@50
                       itemCount:@2
                      attributes:extraAttributes];
Localytics.tagCompletedCheckout(50, itemCount: 2, attributes: extraAttributes)

Content Viewed

Objective-C Swift

[Localytics tagContentViewed:@"Top 10"
                   contentId:@"e8z7319zbe"
                 contentType:@"Article"
                  attributes:extraAttributes];
Localytics.tagContentViewed("Top 10", contentId: "e8z7319zbe", contentType: "Article", attributes: extraAttributes)

Searched

Objective-C Swift

[Localytics tagSearched:@"Celtics"
            contentType:@"Sports"
            resultCount:@15
             attributes:extraAttributes];
Localytics.tagSearched("Celtics", contentType: "Sports", resultCount: 15, attributes: extraAttributes)

Shared

Objective-C Swift

[Localytics tagShared:@"Top 10"
            contentId:@"e8z7319zbe"
          contentType:@"Article"
           methodName:@"Twitter"
           attributes:extraAttributes];
Localytics.tagShared("Top 10", contentId: "e8z7319zbe", contentType: "Article", methodName: "Twitter", attributes: extraAttributes)

Content Rated

Objective-C Swift

[Localytics tagContentRated:@"Headlines"
                  contentId:@"8a4z5j9q"
                contentType:@"Song"
                     rating:@5
                 attributes:extraAttributes];
Localytics.tagContentRated("Headlines", contentId: "8a4z5j9q", contentType: "Song", rating: 5, attributes: extraAttributes)

Customer Registered

The LLCustomer parameter is optional - you can pass in nil. However, if you do choose to include an LLCustomer object, the appropriate identifying users properties will be automatically set.

Objective-C Swift

[Localytics tagCustomerRegistered:[LLCustomer customerWithBlock:^(LLCustomerBuilder *builder) {
          builder.customerId = @"3neRKTxbNWYKM4NJ";
          builder.firstName = @"John";
          builder.lastName = @"Smith";
          builder.fullName = @"Mr. John Smith, III";
          builder.emailAddress = @"john@smith.com";
      }]
                       methodName:@"Facebook"
                       attributes:extraAttributes];
Localytics.tagCustomerRegistered((LLCustomer { (builder) in
  builder.customerId = "3neRKTxbNWYKM4NJ"
  builder.firstName = "John"
  builder.lastName = "Smith"
  builder.fullName = "Mr. John Smith, III"
  builder.emailAddress = "john@smith.com"
}), methodName: "Facebook", attributes: extraAttributes)

Customer Logged In

The LLCustomer parameter is optional - you can pass in nil. However, if you do choose to include an LLCustomer object, the appropriate identifying users properties will be automatically set.

Objective-C Swift

[Localytics tagCustomerLoggedIn:[LLCustomer customerWithBlock:^(LLCustomerBuilder *builder) {
          builder.customerId = @"3neRKTxbNWYKM4NJ";
          builder.firstName = @"John";
          builder.lastName = @"Smith";
          builder.fullName = @"Mr. John Smith, III";
          builder.emailAddress = @"john@smith.com";
      }]
                     methodName:@"Native"
                     attributes:extraAttributes];
Localytics.tagCustomerLoggedIn((LLCustomer { (builder) in
  builder.customerId = "3neRKTxbNWYKM4NJ"
  builder.firstName = "John"
  builder.lastName = "Smith"
  builder.fullName = "Mr. John Smith, III"
  builder.emailAddress = "john@smith.com"
}), methodName: "Native", attributes: extraAttributes)

Customer Logged Out

Objective-C Swift

[Localytics tagCustomerLoggedOut:extraAttributes];
Localytics.tagCustomerLoggedOut(extraAttributes)

Invited

Objective-C Swift

[Localytics tagInvited:@"SMS"
            attributes:extraAttributes];
Localytics.tagInvited("SMS", attributes: extraAttributes)

Custom event

Objective-C Swift

[Localytics tagEvent:@"Team Favorited"];
Localytics.tagEvent("Team Favorited")

Custom event with attributes

Objective-C Swift

[Localytics tagEvent:@"Team Favorited"
          attributes:@{@"Team Name" : @"Celtics", @"City" : @"Boston"}];
Localytics.tagEvent("Team Favorited", attributes: ["Team Name" : "Celtics", "City" : "Boston"])

Identifying users

The Localytics SDK automatically captures and uploads device IDs which the Localytics backend uses to uniquely identify users. Some apps connect to their own backend systems that use different IDs for uniquely identifying users. There is often additional identifying information, such as name and email address, connected with the external IDs. Localytics provides various setters for passing this information to Localytics when it is available in your app. Using these setters ensures that you will be able to properly connect Localytics IDs to the IDs available in other systems.

To easily identify your users during your login and/or registration flow, use our customer registered and customer logged in standard events.

Customer ID

Objective-C Swift

[Localytics setCustomerId:@"3neRKTxbNWYKM4NJ"];
Localytics.setCustomerId("3neRKTxbNWYKM4NJ")

Customer first name

Objective-C Swift

[Localytics setCustomerFirstName:@"John"];
Localytics.setCustomerFirstName("John")

Customer last name

Objective-C Swift

[Localytics setCustomerLastName:@"Smith"];
Localytics.setCustomerLastName("Smith")

Customer full name

Objective-C Swift

[Localytics setCustomerFullName:@"Sir John Smith, III"];
Localytics.setCustomerFullName("Sir John Smith, III")

Customer email address

Objective-C Swift

[Localytics setCustomerEmail:@"sir.john@smith.com"];
Localytics.setCustomerEmail("sir.john@smith.com")

User profiles

Track user properties using profile attributes in Localytics. Each profile has one or more named properties that describe that user. Because they contain rich user data, profiles are excellent for creating audiences to target with personalized messaging. Each profile is identified by a unique user ID that you provide to Localytics via the SDK. If you do not set a known user ID, then the Localytics SDK automatically generates an anonymous profile ID.

Each time you set the value of a profile attribute, you can set the scope to "app-level" or "org-level". App-level profile attributes are only stored in relation to that specific Localytics app key, so they can only be used for building audiences for that one app. Org-level profile attributes are available to all apps in the org, so they can be used across multiple Localytics app keys, which might represent the same app on a different platform or multiple apps produced by your company. If you choose not to set a scope, the SDK defaults to "app-level" scope.

If you repeatedly set the same profile attribute value, the Localytics SDK and backend will take care of deduplicating the values for you so only the most recent value gets stored for that profile.

Profile names must be NSStrings and cannot be nil, empty strings, or begin with an underscore ("_"). Property values must be either single instances or an NSArray of: NSString, NSNumber, or NSDate. Passing in nil or NSNull will delete the value.

Setting a profile attribute value

Numeric value

Objective-C Swift

[Localytics setValue:@45 forProfileAttribute:@"Age" withScope:LLProfileScopeOrganization];
Localytics.setValue(45, forProfileAttribute: "Age", with: .organization)

Numeric values in a set

Objective-C Swift

[Localytics setValue:@[@8, @13] forProfileAttribute:@"Lucky Numbers" withScope:LLProfileScopeApplication];
Localytics.setValue([8, 13], forProfileAttribute: "Lucky numbers", with: .application)

Date value

Objective-C Swift

[Localytics setValue:[NSDate date] forProfileAttribute:@"Last Purchase Date" withScope:LLProfileScopeOrganization];
Localytics.setValue(NSDate(), forProfileAttribute: "Last Purchase Date", with: .organization)

Date values in a set

Objective-C Swift

NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"MM-dd-yyyy"];
[Localytics setValue:@[[dateFormatter dateFromString:@"10-01-2015"], [dateFormatter dateFromString:@"03-17-2016"]] forProfileAttribute:@"Upcoming Milestone Dates" withScope:LLProfileScopeApplication];
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy"
Localytics.setValue([dateFormatter.date(from: "10-01-2015")!, dateFormatter.date(from: "03-17-2016")!], forProfileAttribute: "Upcoming Milestone Dates", with: .application)

String value

Objective-C Swift

[Localytics setValue:@"New York, New York" forProfileAttribute:@"Hometown" withScope:LLProfileScopeOrganization];
Localytics.setValue("New York, New York", forProfileAttribute: "Hometown", with: .organization)

String values in a set

Objective-C Swift

[Localytics setValue:@[@"New York", @"California", @"South Dakota"] forProfileAttribute:@"States Visited" withScope:LLProfileScopeApplication];
Localytics.setValue(["New York", "California", "South Dakota"], forProfileAttribute: "States Visited", with: .application)

Removing a profile attribute

Objective-C Swift

[Localytics deleteProfileAttribute:@"Days Until Graduation" withScope:LLProfileScopeApplication];
Localytics.deleteProfileAttribute("Days Until Graduation", with: .application)

Adding to a set of profile attribute values

Adding a numeric value to a set

Objective-C Swift

[Localytics addValues:@[@8] toSetForProfileAttribute:@"Lucky Numbers" withScope:LLProfileScopeApplication];
Localytics.addValues([8], toSetForProfileAttribute: "Lucky Numbers", with: .application)

Adding a date value to a set

Objective-C Swift

NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"MM-dd-yyyy"];
[Localytics addValues:@[[dateFormatter dateFromString:@"04-19-2015"], [dateFormatter dateFromString:@"12-24-2015"]] toSetForProfileAttribute:@"Upcoming Milestone Dates" withScope:LLProfileScopeApplication];
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy"
Localytics.addValues([dateFormatter.date(from: "4-19-2015")!, dateFormatter.date(from: "12-24-2015")!], toSetForProfileAttribute: "Upcoming Milestone Dates", with: .application)

Adding a string value to a set

Objective-C Swift

[Localytics addValues:@[@"North Dakota"] toSetForProfileAttribute:@"States Visited" withScope:LLProfileScopeApplication];
Localytics.addValues(["North Dakota"], toSetForProfileAttribute: "States Visited", with: .application)

Removing from a set of profile attribute values

Removing numeric values from a set

Objective-C Swift

[Localytics removeValues:@[@8, @9] fromSetForProfileAttribute:@"Lucky Numbers" withScope:LLProfileScopeApplication];
Localytics.removeValues([8, 9], fromSetForProfileAttribute: "Lucky Numbers", with: .application)

Removing date values from a set

Objective-C Swift

NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"MM-dd-yyyy"];
[Localytics removeValues:@[[dateFormatter dateFromString:@"03-17-2016"]] fromSetForProfileAttribute:@"Upcoming Milestone Dates" withScope:LLProfileScopeApplication];
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy"
Localytics.removeValues([dateFormatter.date(from: "03-17-2016")!], fromSetForProfileAttribute: "Upcoming Milestone Dates", with: .application)

Removing string values from a set

Objective-C Swift

[Localytics removeValues:@[@"California"] fromSetForProfileAttribute:@"States Visited" withScope:LLProfileScopeApplication];
Localytics.removeValues(["California"], fromSetForProfileAttribute: "States Visited", with: .application)

Incrementing a numeric profile attribute value

Objective-C Swift

[Localytics incrementValueBy:1 forProfileAttribute:@"Age" withScope:LLProfileScopeOrganization];
Localytics.incrementValueBy(1, forProfileAttribute: "Age", with: .organization)

Decrementing a numeric profile attribute value

Objective-C Swift

[Localytics decrementValueBy:3 forProfileAttribute:@"Days Until Graduation" withScope:LLProfileScopeApplication];
Localytics.decrementValueBy(3, forProfileAttribute: "Days Until Graduation", with: .application)

In-app messaging

In-app messaging allows you to engage with your users while they are inside your app using templated or custom HTML creatives that you define within the Localytics Dashboard.

Triggering in-app messages

When creating in-app campaigns in the Localytics Dashboard, you decide under which conditions the in-app creative should display. You can trigger in-app messages to display at session start. You can also trigger in-app messages to display when a particular event is tagged using the event name alone or using the event name combined with attribute conditions.

Sometimes, you just want to display an in-app message, but there is not an existing event tag of which to take advantage, and you don't want to create an additional event datapoint solely to display a message. In this situation, Localytics marketing triggers allow you to trigger in-app messages off of specific user behaviors that you do not have tagged as an event in the Localytics dashboard.

There are a variety of scenarios where these triggers might be relevant:

  • You have a lot of Summary Events tagged, and Summary Events do not allow for granular behavioral triggering of messages.
  • You do not want to pass a particular behavior as an Event to the Localytics dashboard because the behavior is so frequent that an Event tag would incur high data point usage.

Instrumenting marketing triggers

Instrumenting Marketing Triggers in the app’s code base is simple. It’s essentially the same as tagging an Event, but rather than using [Localytics.tagEvent_Name] you will use the following

Objective-C Swift

[Localytics triggerInAppMessage:@"Item Purchased"];
Localytics.triggerInAppMessage("Item Purchased")

Marketing triggers with attributes

To create a trigger with additional attribute conditions, use

Objective-C Swift

[Localytics triggerInAppMessage:@"Item Purchased" withAttributes:@{@"Item name" : @"Stickers"}];
Localytics.triggerInAppMessage("Item Purchased", withAttributes: ["Item name" : "Stickers"])

Because there’s no data about that trigger in the Localytics backend, you’ll need to manually send the trigger attribute mappings to Localytics via a reported Event instead, at least one time. Essentially, you must fire an actual Event that reaches Localytics server once. You can do this by:

For one session tied to your app key - for example, just in your dev build but tagged with your production key temporarily - switch that triggerInAppMessage call to a standard tagEvent call instead, and run the app through that code to actually send the event to Localytics.

This will populate the event name and attributes in the autocomplete dialog within 10 minutes. Then you can switch the tag back from tagEvent to triggerInAppMessage. From there, you will be able to target on the trigger & attributes as if it were a normal event in the dashboard.

Selecting marketing triggers in the dashboard

Once the marketing trigger has been instrumented in the app’s code base, you can use these to trigger in-app messages in the Localytics dashboard. When you get to the Scheduling page of the campaign setup process, you will chose the “Event” trigger. For these marketing triggers, you will need to type in the name of the marketing trigger in the drop down (as seen below) - it will not automatically populate as the dashboard events do.

Customizing in-app messages

Dismiss button image

By default, there is a dismiss button in the shape of an "X" on the top-left of the in-app message dialog. Use the methods below to set the dismiss button to a different image or to change the position of the button to the top-right. If you change the image, the Localytics SDK creates a copy of the referenced image based on the maximum allowed image size, so your app doesn't have to keep the image after the call.

When designing creatives in the campaign builder, you do not need to add these

To set the image by name, use

Objective-C Swift

[Localytics setInAppMessageDismissButtonImageWithName:@"custom_dismiss_button"];
Localytics.setInAppMessageDismissButtonImageWithName("custom_dismiss_button")

To set the image by image, use

Objective-C Swift

[Localytics setInAppMessageDismissButtonImage:[UIImage imageNamed:@"custom_dismiss_button"]];
Localytics.setInAppMessageDismissButtonImage(UIImage(named: "custom_dismiss_button"))

If you want to clear the image and switch back to the default, pass a nil to this method as below.

Objective-C Swift

[Localytics setInAppMessageDismissButtonImage:nil];
Localytics.setInAppMessageDismissButtonImage(nil)

Dismiss button location

To set the dismiss button location to the left, use

Objective-C Swift

[Localytics setInAppMessageDismissButtonLocation:LLInAppMessageDismissButtonLocationLeft];
Localytics.setInAppMessageDismissButtonLocation(.left)

To set the dismiss button location to the right, use

Objective-C Swift

[Localytics setInAppMessageDismissButtonLocation:LLInAppMessageDismissButtonLocationRight];
Localytics.setInAppMessageDismissButtonLocation(.right)

Customize In-Apps with a Callback

Using a messaging callback, as described in the advanced section, can allow for customization of individual in-app creatives right before they are shown to the user. The list of configurable properties are available on an object passed through the callback, the LLInAppConfiguration. Modification of these values will result in different displays for an individual creative. The list is as follows:

  • Aspect Ratio: The value passed in should be a float value representing a ratio of height to width. This property is only relevant for center in-app creatives.
  • Offset: The value passed in should be a positive pixel offset from the top or bottom of the screen. This property is only relevant for top and bottom banner in-app creatives.
  • Background Alpha: The value passed in should be a float value between 0.0 (transparent) and 1.0 (opaque).
  • Dismiss Button Image: The value passed in should be a UIImage. A helper method (LLInAppConfiguration setDismissButtonImageWithName:]) also exists to add an image by name. This value will override any values set globally with [Localytics setInAppMessageDismissButtonImage:]
  • Dismiss Button Location: The value passed in should be either LLInAppMessageDismissButtonLocationLeft for in-app creatives with a dismiss button on the left, or LLInAppMessageDismissButtonLocationRight for in-app creatives with a dismiss button on the right. This value will override any values set globally with [Localytics setInAppMessageDismissButtonLocation:]
  • Dismiss Button Hidden: The value passed in should be a boolean corresponding to the visible state of the button. If YES or true is passed in then the dismiss button will be hidden for this in app creative.

A simple example of using the callbacks to modify an in-app creative may look as follows:

Objective-C Swift

- (nonnull LLInAppConfiguration *)localyticsWillDisplayInAppMessage:(nonnull LLInAppCampaign *)campaign withConfiguration:(nonnull LLInAppConfiguration *)configuration
{
  if ([configuration isCenterInApp])
  {
    configuration.aspectRatio = 1.2;
  }
  else if ([configuration isTopBannerInApp] || [configuration isBottomBannerInApp])
  {
    configuration.offset = 50;
  }
  configuration.backgroundAlpha = 0;
  configuration.dismissButtonImage = [UIImage imageNamed:@"custom_dismiss_button"];
  configuration.dismissButtonLocation = LLInAppMessageDismissButtonLocationRight;
  configuration.dismissButtonHidden = YES;

  return configuration;
}
func localyticsWillDisplay(inAppMessage campaign: LLInAppCampaign, with configuration: LLInAppConfiguration) -> LLInAppConfiguration {
    if configuration.isCenterInApp() {
        configuration.aspectRatio = 1.2
    } else if configuration.isTopBannerInApp() || configuration.isBottomBannerInApp() {
        configuration.offset = 50
    }
    configuration.backgroundAlpha = 0
    configuration.dismissButtonImage = UIImage(named:"custom_dismiss_button")
    configuration.dismissButtonLocation = .right
    configuration.dismissButtonHidden = true

    return configuration
}

Custom Creative Javascript API

The Localytics SDK adds a localytics Javascript function to the in-app HTML that provides access to several native SDK methods and properties and handles URL navigation and in-app dismissal.

The sections below cover the details of the in-app Javascript API. It is crucial to understand these APIs when designing and uploading your own custom creatives. Note: When using the in-app message builder, calls to these APIs will be handled automatically.

Properties Accessors

  • localytics.campaign (Only available on SDK 4.3 and later): Returns a Javascript object containing information about the campaign that triggered this In-App message. The javascript object contains the campaign name as it would appear on the dashboard (name), the campaign ID (campaignId), the name of the event that triggered the In-App (eventName), and the attribute keys and values tagged on the event or trigger that launched the in-app message (eventAttributes).

    var campaign = localytics.campaign; // {"name": "App Upgrade Campaign", "campaignId": "449859", "eventName": "App Launch", "eventAttributes": {"isFirstSession": "NO"}};
    
  • localytics.identifiers: Returns a Javascript object containing a user's identifying data: customer_id, first_name, last_name, full_name, and email. Note: These values will only be populated if you have set them for the user directly via the SDK.

    var identifiers = localytics.identifiers; // {"customer_id": "3neRKTxbNWYKM4NJ", "first_name": "John", "last_name": "Smith", "full_name": "Sir John Smith, III", "email": "sir.john@smith.com"};
    
  • localytics.customDimensions: Returns a Javascript object containing the current session's custom dimensions. The keys of the object will be "c0", "c1", etc. for dimensions that have been set.

    var dimensions = localytics.customDimensions; // {"c0": "Paid", "c1": "Logged In"};
    
  • localytics.attributes: Returns a Javascript object containing the attribute keys and values tagged on the event or trigger that launched the in-app message.

    var eventAttributes = localytics.attributes; // {"Team Name": "Celtics", "City": "Boston"};
    
  • localytics.libraryVersion: Returns the Localytics SDK version as a string.

    var sdkVersion = localytics.libraryVersion; // "iOSa_4.3.0"
    

Methods

  • localytics.tagClickEvent(action) (Only available on SDK 4.3 and later): Tags an in-app clickthrough (also kown as a conversion) with an optional action attribute. If the action attribute is omitted, the default of click will be used. This method can only be called once per in-app. The first time this method is called an event will be recorded, and any subsequent calls will be ignored.

    localytics.tagClickEvent("Share");
    
  • localytics.tagEvent(event, attributes, customerValueIncrease): Tags an event with optional attributes and an optional custom value increase.

    function submitNPS(ratingValue)
    {
      var attributes = {"Raw Rating": ratingValue};
      if (ratingValue >= 9)
      {
        attributes["Rating"] = "Promoter";
      }
      else if (ratingValue <= 6)
      {
        attributes["Rating"] = "Detractor";
      }
      else
      {
        attributes["Rating"] = "Neutral";
      }
      localytics.tagEvent("In-App Rating Result", attributes);
    }
    
  • localytics.setCustomDimension(index, value): Sets a custom dimension value for a particular index.

    localytics.setCustomDimension(0, "Trial");
    
  • localytics.close(): Closes the in-app. If an in-app message viewed event hasn't been tagged (i.e. ampView), an event with ampAction equal to "X" will be tagged.

    function formSubmit()
    {
      localytics.tagEvent("Form Submit", {"Email": "john@smith.com"});
      localytics.close();
    }
    
  • localytics.setProfileAttribute(name, value, scope) (Only available on SDK 4.3 and later): Sets a profile attribute with an optional scope ("app" or "org").

    localytics.setProfileAttribute("Favorite Team", "Red Sox", "app");
    
  • localytics.deleteProfileAttribute(name, scope) (Only available on SDK 4.3 and later): Delete a profile attribute with an optional scope ("app" or "org").

    localytics.deleteProfileAttribute("Favorite Team", "app");
    
  • localytics.addProfileAttributesToSet(name, values, scope) (Only available on SDK 4.3 and later): Add profile attributes with an optional scope ("app" or "org").

    localytics.addProfileAttributesToSet("Favorite Team", ["Red Sox", "Celtics"], "org");
    
  • localytics.removeProfileAttributesFromSet(name, values, scope) (Only available on SDK 4.3 and later): Remove profile attributes with an optional scope ("app" or "org").

    localytics.removeProfileAttributesFromSet("Favorite Team", ["Red Sox", "Celtics"], "org");
    
  • localytics.incrementProfileAttribute(name, value, scope) (Only available on SDK 4.3 and later): Increment a profile attribute with an optional scope ("app" or "org").

    localytics.incrementProfileAttribute("Age", 1, "app");
    
  • localytics.decrementProfileAttribute(name, value, scope) (Only available on SDK 4.3 and later): Decrement a profile attribute with an optional scope ("app" or "org").

    localytics.decrementProfileAttribute("Days Until Graduation", 3, "org");
    
  • localytics.setCustomerFirstName(name, value, scope) (Only available on SDK 4.3 and later): Set the user's first name.

    localytics.setCustomerFirstName("John");
    
  • localytics.setCustomerLastName(name, value, scope) (Only available on SDK 4.3 and later): Set the user's last name.

    localytics.setCustomerLastName("Smith");
        
    
  • localytics.setCustomerFullName(name, value, scope) (Only available on SDK 4.3 and later): Set the user's full name.

    localytics.setCustomerFullName("Sir John Smith, III");
        
    
  • localytics.setCustomerEmail(name, value, scope) (Only available on SDK 4.3 and later): Set the user's email.

    localytics.setCustomerEmail("sir.john@smith.com");
        
    

URL and Deep Link Navigation

Tagging Click Events

All displayed in-app messages tag a single viewed event (i.e. ampView) with an action attribute (i.e. ampAction). Each viewed event is described as an "impression" on the campaign performance page in the Localytics Dashboard. To record a campaign clickthrough (also known as a conversion) when a call-to-action (CTA) is pressed, append the ampAction=click key-value query string to the end of your web or deep link URL. Note: This query paramater is added automatically when entering a URL or deep link into the CTA field in the campaign editor in the Localytics Dashboard.

// Opening a web URL
function openHomePage()
{
  window.open("http://www.localytics.com?ampAction=click&ampExternalOpen=true");
  localytics.close();
}

// Opening a deep link URL
function openFavorites()
{
  window.open("myApp://favorites?ampAction=click");
  localytics.close();
}

If you have multiple CTAs within an in-app creative, you can use different ampAction values to distinguish each clickthrough. Any ampAction not equal to "X" will be considered a clickthrough. By using different ampAction values, you will be able to see which CTA the user clicks on the campaign performance page in the Localytics Dashboard (instead of all CTAs being grouped under a single "click").

// Opening a deep link to the favorites page
function openFavorites()
{
  window.open("myApp://favorites?ampAction=favorites");
  localytics.close();
}

// Opening a deep link to the share page
function openShare()
{
  window.open("myApp://share?ampAction=share");
  localytics.close();
}

By default, when an in-app is dismissed (by pressing the "X" button or calling localytics.close()) and a viewed event has not yet been tagged, an ampView event is automatically tagged with an ampAction equal to "X".

Opening URLs

Use the Javascript window.open(url) function when opening HTTP and HTTPS URLs and other HTML files contained within the ZIP. URLs can be opened either in the phone's default browser (e.g. Chrome or Safari) or directly within the in-app message view. To control this behavior, append the ampExternalOpen parameter to the URL.

// Opening a web URL in the phone's default browser
function openHomepage()
{
  window.open("http://www.localytics.com?ampAction=click&ampExternalOpen=true");
  localytics.close();
}

// Opening a web URL within the in-app message view
function openBlogInsideInApp()
{
  window.open("http://info.localytics.com/blog?ampAction=click&ampExternalOpen=false");
}

// Opening another HTML file contained in the ZIP within the in-app message view
function goToFeedback()
{
  window.open("feedback.html?ampAction=feedback&ampExternalOpen=false");
}

Push messaging

Push messaging allows you to keep your users up-to-date and reengage them with your app after periods of inactivity.

Before continuing, please be sure that you have completed all of the steps in Getting Started. If you already support push notifications in your app, please skip to the section below about uploading your push certificate. Note that you may still need to enable background modes.

1. Enable push and background modes capabilities

  1. Enter Project Navigator view and click on your project icon.
  2. Select your target from the expanded sidebar or from the dropdown menu next to General then select the Capabilities tab.
  3. If Push Notifications isn't enabled, click the switch to add the "Push Notifications" entitlement to your App ID. If you are using Xcode 8, ensure that an APP-NAME.entitlements file has been added to your project.
  4. If Background Modes isn't enabled, click the switch and then check the Remote notifications box. Localytics uses background modes to track when push messages are received on a user's device before the message is ever opened.
screenshot of remote notifications BackgroundModes in Info.plist

2. Register for a push token

Add the following code to your app delegate's didFinishLaunchingWithOptions: to request a push token from Apple Push Notification Services (APNS). This will not prompt the user for notification display permissions.

Objective-C Swift

if ([application respondsToSelector:@selector(registerForRemoteNotifications:)]) {
  [application registerForRemoteNotifications];
}
if #available(iOS 8.0, *) {
  application.registerForRemoteNotifications()
}

3. Register for notifications

Add the following code to your app wherever you would like the system prompt to enable notifications to appear for your users. You could call this after the Localytics initialization code in didFinishLaunchingWithOptions: in your app delegate if you would like to enable notifications at app start. Many apps find better push opt-in rates if the app prompts users to enable notifications after explaining to the user how the app uses notifications or warns the user that the prompt is about to appear. For a step-by-step guide on creating one of these explanations, see increasing notification opt-in rates with in-app permission requests.

First make sure to import the new UserNotifications framework introduced in iOS10 in whichever file you will ask for push notification permissions.

Objective-C Swift

@import UserNotifications;
import UserNotifications

Then request permission to present notifications to the user.

Objective-C Swift

if (NSClassFromString(@"UNUserNotificationCenter"))
{
  UNAuthorizationOptions options = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound |UNAuthorizationOptionAlert);
  [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:options
                                                                      completionHandler:^(BOOL granted, NSError * _Nullable error) {
                                                                        [Localytics didRequestUserNotificationAuthorizationWithOptions:options
                                                                                                                               granted:granted];
                                                                      }];
}
else if ([application respondsToSelector:@selector(registerUserNotificationSettings:)])
{
  UIUserNotificationType types = (UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound);
  UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
  [application registerUserNotificationSettings:settings];
}
else
{
  [application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
}
if #available(iOS 10.0, *), objc_getClass("UNUserNotificationCenter") != nil {
    let options: UNAuthorizationOptions = [.alert, .badge, .sound]
    UNUserNotificationCenter.current().requestAuthorization(options: options) { (granted, error) in
        Localytics.didRequestUserNotificationAuthorization(withOptions: options.rawValue, granted: granted)
    }
} else if #available(iOS 8.0, *) {
    let types: UIUserNotificationType = [.alert, .badge, .sound]
    let settings = UIUserNotificationSettings(types: types, categories: nil)
    application.registerUserNotificationSettings(settings)
} else {
    let types: UIRemoteNotificationType = [.alert, .badge, .sound]
    application.registerForRemoteNotifications(matching: types)
}
If you are using alternative session management, be sure to follow the documentation on handling the notification events.

4. Handle opened remote notifications

This step is only required if you are using the UserNotification framework introduced in iOS 10 and you are setting a UNUserNotificationCenterDelegate. If this doesn't apply to your app, please continue on to creating a univeral push notification client SSL certificate.

When using the UserNotification framework and using a UNUserNotificationCenterDelegate, you need to notify the Localytics SDK when notifications are opened. Add the following to your userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: delegate method:

Objective-C Swift

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler
{
  [Localytics didReceiveNotificationResponseWithUserInfo:response.notification.request.content.userInfo];
  completionHandler();
}
@available(iOS 10.0, *) // if also targeting iOS versions less than 10.0
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    Localytics.didReceiveNotificationResponse(userInfo: response.notification.request.content.userInfo)
    completionHandler()
}

5. Create a universal push notification client SSL certificate

To prepare for push notifications on iOS, you need to create a Universal Push Notification Client SSL Certificate that allows Localytics to connect to the Apple Push Notification service. A universal certificate allows your app to connect to both the development and production environments.

  1. Log in to the Apple Developer console and go to Certificates, Identifiers & Profiles.
  2. Under Certificates, select All and then click the Add button (+) in the upper-right corner.
  3. Under Production, select the "Apple Push Notification service SSL (Sandbox & Production)" checkbox, and click Continue. screenshot of univeral push certificate creation
  4. Choose an App ID from the App ID pop-up menu, and click Continue. Choose the explicit App ID that matches your bundle ID. If your explicit App ID doesn't appear in the list, follow our instructions for creating an explicit App ID and then return to this step.
  5. Follow the instructions on the next webpage to create a certificate request on your Mac, and click Continue.
  6. On the Generate your certificate page, click Choose File and choose the certificate request file you just created (with a .certSigningRequest extension) and then click Continue.
  7. Download the generated certificate to your Mac and then double-click the .cer file to install it in Keychain Access. Finally, click Done.

6. Verify your App ID is push enabled

  1. Log in to the Apple Developer console and go to Certificates, Identifiers & Profiles.
  2. Under Identifiers, select App IDs and then select the explicit App ID that matches the bundle ID.
  3. Verify that a green circle followed by "Enabled" appears in the Push Notifications row and the Distribution column. Because this is a universal certificate, the Development column will remain in the "Configurable" state. This is expected - you don't need to create a development specific certificate because the universal certificate will work within both development and production builds. screenshot of app id that's push enabled

7. Upload your push certificate

In order for Localytics to be able to send pushes on your behalf, you must provide Localytics with the universal push certificate you generated.

  1. On your Mac Launch Keychain Access.
  2. In the Category section, select My Certificates.
  3. Find the certificate you generated and disclose its contents. You’ll see both a certificate and a private key.
  4. Select both the certificate and the key, and choose File > Export Items. screenshot of certificate export from Keychain Access
  5. Save the certificate in the Personal Information Exchange (.p12) format. You can optionally enter a password to protect the item.
  6. Login to the Localytics Dashboard and navigate to Settings > Apps.
  7. Find your app key app in the list and click the gear icon.
  8. Click Add Certs and then click Upload Certificate.
  9. Browse to the location of the .p12 file you created and select it.
  10. Select Development from the Push Mode dropdown menu.
  11. Enter your push certificate password if you previously added one. Leave the Push Password field blank if your push certifcate doesn't have a password.
  12. Click Save.

8. Generate a development build

A development build allows you to most easily test your push integration. You cannot test push in the iOS Simulator, so you must physically connect a device and target it for a build.

  1. Connect a physical device to your computer using a USB cable.
  2. In Xcode, select the device you just connected as the destination for new builds. The destination dropdown is in the toolbar at the top to the right of the run and stop buttons.
  3. Navigate to the Product menu and click Run. You may need to unlock your device.
  4. A development build of your app will appear on your connected device. If you have logging enabled at this point, you should see messages indicating that the Localytics SDK obtained your push token and should see the push token present in the dpush field of the next upload body.

9. Test push integration

If you have integrated SDK version 4.1 or later, use the Rapid Push Verification page to confirm that you have integrated push correctly. Be sure to select the correct app in the dropdown at the top of the page.

screenshot of Rapid Push Verification page

10. Add support for rich media push attachments (optional)

Rich Push on iOS is purely optional but requires iOS 10, Localytics SDK v4.1 or higher, and the new UserNotifications framework if you wish to use this feature. See our migration guide for details on the prerequisites.

To integrate Rich Push, you'll need to create a new Notification Service Extension:

  1. Open up Xcode and navigate to File > New > Target. The following dialog will pop up:
  2. Select the Notification Service Extension option, click "Next", fill out the name, and click "Finish". Xcode will now create a new top level folder in your project with the name you provided, containing a NotificationServiceExtension class.
  3. Include the below code snippet in the didReceiveNotificationRequest:withContentHandler: method of the extension.

    Objective-C Swift

    - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
      self.contentHandler = contentHandler;
      self.bestAttemptContent = [request.content mutableCopy];
    
        // Modify the notification content here...
        NSString *imageURL = self.bestAttemptContent.userInfo[@"ll_attachment_url"];
        NSString *imageType = self.bestAttemptContent.userInfo[@"ll_attachment_type"];
        if (!imageURL || !imageType) {
            //handle non localytics rich push
            self.contentHandler(self.bestAttemptContent);
            return;
        }
        NSURL *url = [NSURL URLWithString:imageURL];
        [[[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable tempFile, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (error) {
                NSLog(@"Failed to retrieve attachment with error: %@", [error localizedDescription]);
                self.contentHandler(self.bestAttemptContent);
                return;
            }
    
            NSArray *cache = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
            NSString *cachesFolder = cache[0];
    
            NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
            NSString *fileName = [[guid stringByAppendingString:@"localytics-rich-push-attachment"] stringByAppendingPathExtension:imageType];
            NSString *cacheFile = [cachesFolder stringByAppendingPathComponent:fileName];
            NSURL *attachmentURL = [NSURL fileURLWithPath:cacheFile isDirectory:NO];
            NSError *err = nil;
            [[NSFileManager defaultManager] moveItemAtURL:tempFile toURL:attachmentURL error:&err];
            if (err) {
                NSLog(@"Failed to save attachment on disk with error: %@", [err localizedDescription]);
                self.contentHandler(self.bestAttemptContent);
                return;
            }
    
            UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:attachmentURL options:nil error:&err];
            if (attachment) {
                self.bestAttemptContent.attachments = @[attachment];
            } else {
                NSLog(@"Failed to create attachment with error: %@", [err localizedDescription]);
            }
            self.contentHandler(self.bestAttemptContent);
        }] resume];
    }
      
    
    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
      self.contentHandler = contentHandler
      self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
    
      if let bestAttemptContent =  bestAttemptContent {
          if let attachmentURL = bestAttemptContent.userInfo["ll_attachment_url"] as? String, let attachmentType = bestAttemptContent.userInfo["ll_attachment_type"] as? String {
              guard let url = URL(string: attachmentURL) else {
                  print("Failed to present attachment due to an invalid url: %@", attachmentURL)
                  contentHandler(bestAttemptContent)
                  return
              }
    
              let task = URLSession.shared.downloadTask(with: url) { (tempFile, response, error) in
                  if let error = error {
                      print("Failed to retrieve attachment with error: %@", error.localizedDescription)
                      contentHandler(bestAttemptContent)
                      return
                  }
                  guard let tempFile = tempFile else {
                      print("Failed to store contents of attachment on disk")
                      contentHandler(bestAttemptContent)
                      return
                  }
    
                  let cache = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
                  let cachesFolder = cache[0]
    
                  let guid = ProcessInfo.processInfo.globallyUniqueString
                  let cacheFile = "\(cachesFolder)/\(guid)-localytics-rich-push-attachment.\(attachmentType)"
                  let attachmentURL = URL(fileURLWithPath: cacheFile)
    
                  do {
                      try FileManager.default.moveItem(at: tempFile, to: attachmentURL)
    
                      let attachment = try UNNotificationAttachment(identifier: "localytics-rich-push-attachment", url: attachmentURL, options: nil)
                      bestAttemptContent.attachments = [attachment]
                  } catch _ {
                      print("Failed to create attachment for push notification")
                  }
    
                  contentHandler(bestAttemptContent)
              }
              task.resume()
          } else {
              //handle non localytics rich push
              contentHandler(bestAttemptContent)
          }
      }
    }
    
      
    

11. Before you release to the App Store

Before you release your app to the App Store, you will want to test that it can properly receive not only development push messages as above, but also production push messages. We also recommend using a different Localytics app key for the production build of your app.

  1. If your production build uses a different App ID/Bundle ID than your development build, follow the creating a universal certificate and verifying your App ID is push enabled instructions to generate a universal push notification certificate for this App ID.
  2. Upload your universal push certificate to Localytics for your production app key. Follow the uploading your push certificate instructions but select Production from the Push Mode dropdown menu. You can use the same certificate (.p12) if your production build uses the same App ID/Bundle ID as your development build.
  3. Create a distribution provisioning profile. Follow the instructions for creating Ad Hoc provisioning profiles. Ad Hoc builds are just like App Store builds except that you can only install them on a limited set of predetermined devices. Both types of builds will use Apple's Production Push environment.
  4. Generate a distribution build of your app and install it on a connected test device. If you have logging enabled at this point, you should see messages indicating that the Localytics SDK obtained your push token and should see the push token present in the push (not dpush) field of the next upload body.
  5. Send yourself a test push message using the same instructions as before.
  6. Complete all of the same steps above except select App Store as the provisioning profile type. If you are using multiple Localytics app keys, e.g. one for development and one for production, make sure to upload your production push certificate to your production app key.

App inbox

App Inbox allows you to deliver personalized content to users through a dedicated inbox inside your app. Create Inbox Campaigns using templated or custom HTML creatives from within the Localytics Dashboard. Inbox messages will display in your users' inbox for a scheduled amount of time.

Before continuing, please be sure that you have completed all of the steps in Getting Started. If you have installed the SDK using the alternative installation approach, make sure you have followed the step to add the -ObjC flag to Other Linker Flags in your build settings.

To add App Inbox to your app you need to include a list of inbox messages within your app's user interface and then handle displaying the inbox message detail view. The instructions below will walk your through the process.

1. Include a list of inbox messages

You have 2 options for adding a list of inbox messages to your app:

  1. Using LLInboxViewController, which is the recommended and simplest approach.
  2. Getting inbox campaigns from the Localytics class methods and displaying them in your own UIViewController.

Using LLInboxViewController (recommended)

LLInboxViewController can be added to your app using a storyboard or by initializing a new instance programmatically.

To add LLInboxViewController to an existing storyboard:

  1. Drag a new UIViewController object into the storyboard canvas and embed it in a UINavigationController.
  2. Select the new UIViewController, open the Identity inspector, and set the class name to LLInboxViewController.
screenshot of LLInboxViewController in a storyboard

To programmatically add LLInboxViewController, create a new instance and push it onto a UINavigationController stack as follows.

Objective-C Swift

- (void)displayInbox
{
    LLInboxViewController *inboxViewController = [[LLInboxViewController alloc] init];
    [self.navigationController pushViewController:inboxViewController
                                         animated:YES];
}
func displayInbox() {
  let inboxViewController = LLInboxViewController()
  self.navigationController?.pushViewController(inboxViewController, animated: true)
}

If you prefer to present LLInboxViewController modally, include it within a UINavigationController as follows.

Objective-C Swift

- (void)displayInbox
{
    LLInboxViewController *inboxViewController = [[LLInboxViewController alloc] init];
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:inboxViewController];
    [self presentViewController:navigationController
                       animated:YES
                     completion:nil];
}
func displayInbox() {
  let inboxViewController = LLInboxViewController()
  let navigationController = UINavigationController(rootViewController: inboxViewController)
  present(navigationController, animated: true, completion: nil)
}

Using your own UIViewController

If you prefer to not use LLInboxViewController, you can get inbox data directly via the Localytics class methods and display it in your own UIViewController.

To retrieve cached inbox campaign data, call [Localytics inboxCampaigns] from a background thread as follows.

Objective-C Swift

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSArray<LLInboxCampaign *> *inboxCampaigns = [Localytics inboxCampaigns];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
  let inboxCampaigns = Localytics.inboxCampaigns()
}

To refresh inbox campaign data from the network, call [Localytics refreshInboxCampaigns:] with a completion block as follows.

Objective-C Swift

[Localytics refreshInboxCampaigns:^(NSArray<LLInboxCampaign *> *inboxCampaigns) {
    // refresh UI using inboxCampaigns
}];
Localytics.refreshInboxCampaigns { (inboxCampaigns) in
  // refresh UI using inboxCampaigns
}

2. Display a detail inbox message view

You must use LLInboxDetailViewController to display a detail inbox message view. When using LLInboxViewController the default behavior will automatically mark the inbox message as read and create an LLInboxDetailViewController and push it onto the navigation stack. If you are not using LLInboxViewController, you can get an instance from the Localytics class as follows.

Objective-C Swift

LLInboxCampaign *campaign = /* campaign from [Localytics inboxCampaigns] */;
LLInboxDetailViewController *inboxDetailViewController = [Localytics inboxDetailViewControllerForCampaign:campaign];
let campaign = /* campaign from Localytics.inboxCampaigns() */
let inboxDetailViewController = Localytics.inboxDetailViewController(for: campaign)

3. Customize the appearance and behavior

To match the look and feel of your app, there are several customization options available for App Inbox.

Changing fonts and color

To customize the font and color of the text, details, and time labels and the unread indicator color, set properties on LLInboxViewController as follows.

Objective-C Swift

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"inbox_list"])
    {
        LLInboxViewController *inboxViewController = (LLInboxViewController *) segue.destinationViewController;
        inboxViewController.textLabelColor = [UIColor redColor];
        inboxViewController.textLabelFont = [UIFont boldSystemFontOfSize:24.0f];
        inboxViewController.detailTextLabelColor = [UIColor greenColor];
        inboxViewController.detailTextLabelFont = [UIFont boldSystemFontOfSize:16.0f];
        inboxViewController.timeTextLabelColor = [UIColor blueColor];
        inboxViewController.timeTextLabelFont = [UIFont systemFontOfSize:12.0f];
        inboxViewController.unreadIndicatorColor = [UIColor yellowColor];
        inboxViewController.cellBackgroundColor = [UIColor whiteColor];
    }
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "inbox_list" {
        let inboxViewController = segue.destination as! LLInboxViewController
        inboxViewController.textLabelColor = .red
        inboxViewController.textLabelFont = .boldSystemFont(ofSize: 24)
        inboxViewController.detailTextLabelColor = .green
        inboxViewController.detailTextLabelFont = .boldSystemFont(ofSize: 16)
        inboxViewController.timeTextLabelColor = .blue
        inboxViewController.timeTextLabelFont = .boldSystemFont(ofSize: 12)
        inboxViewController.unreadIndicatorColor = .yellow
        inboxViewController.cellBackgroundColor = .white
    }
}

Objective-C Swift

- (void)displayInbox
{
    LLInboxViewController *inboxViewController = [[LLInboxViewController alloc] init];
    inboxViewController.textLabelColor = [UIColor redColor];
    inboxViewController.textLabelFont = [UIFont boldSystemFontOfSize:24.0f];
    inboxViewController.detailTextLabelColor = [UIColor greenColor];
    inboxViewController.detailTextLabelFont = [UIFont boldSystemFontOfSize:16.0f];
    inboxViewController.timeTextLabelColor = [UIColor blueColor];
    inboxViewController.timeTextLabelFont = [UIFont systemFontOfSize:12.0f];
    inboxViewController.unreadIndicatorColor = [UIColor yellowColor];
    inboxViewController.cellBackgroundColor = [UIColor whiteColor];
    [self.navigationController pushViewController:inboxViewController
                                         animated:YES];
}
func displayInbox() {
  let inboxViewController = LLInboxViewController()
  inboxViewController.textLabelColor = .red
  inboxViewController.textLabelFont = .boldSystemFont(ofSize: 24)
  inboxViewController.detailTextLabelColor = .green
  inboxViewController.detailTextLabelFont = .boldSystemFont(ofSize: 16)
  inboxViewController.timeTextLabelColor = .blue
  inboxViewController.timeTextLabelFont = .boldSystemFont(ofSize: 12)
  inboxViewController.unreadIndicatorColor = .yellow
  inboxViewController.cellBackgroundColor = .white
  self.navigationController?.pushViewController(inboxViewController, animated: true)
}

Displaying LLInboxDetailViewController modally

By default LLInboxViewController will push a new instance of LLInboxDetailViewController onto the navigation stack when an inbox campaign is selected. To present it modally, subclass LLInboxViewController and override tableView:didSelectRowAtIndexPath: as follows.

Objective-C Swift

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    LLInboxCampaign *campaign = [self campaignForRowAtIndexPath:indexPath];
    if (campaign.hasCreative)
    {
        LLInboxDetailViewController *detailViewController = [Localytics inboxDetailViewControllerForCampaign:campaign];
        UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
        [self presentViewController:navigationController
                           animated:YES
                         completion:nil];
    }

    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    if (!campaign.isRead)
    {
        // Set campaign as read and reload to refresh unread indicators
        campaign.read = YES;
        [tableView reloadData];
    }
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let campaign = campaignForRow(at: indexPath)!
    if campaign.hasCreative {
        let detailViewController = Localytics.inboxDetailViewController(for: campaign)
        let navigationController = UINavigationController(rootViewController: detailViewController)
        present(navigationController, animated: true, completion: nil)
    }

    tableView.deselectRow(at: indexPath, animated: true)

    if !campaign.isRead {
        // Set campaign as read and reload to refresh unread indicators
        campaign.isRead = true
        tableView.reloadData()
    }
}

Displaying a loading indicator

To show a UIActivityIndicatorView within LLInboxViewController while campaigns are loading, set the showsActivityIndicatorView property to YES before you display it as follows.

Objective-C Swift

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"inbox_list"])
    {
        LLInboxViewController *inboxViewController = (LLInboxViewController *) segue.destinationViewController;
        inboxViewController.showsActivityIndicatorView = YES;
    }
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "inbox_list" {
        let inboxViewController = segue.destination as! LLInboxViewController
        inboxViewController.showsActivityIndicatorView = true
    }
}

Objective-C Swift

- (void)displayInbox
{
    LLInboxViewController *inboxViewController = [[LLInboxViewController alloc] init];
    inboxViewController.showsActivityIndicatorView = YES;
    [self.navigationController pushViewController:inboxViewController
                                         animated:YES];
}
func displayInbox() {
  let inboxViewController = LLInboxViewController()
  inboxViewController.showsActivityIndicatorView = true
  self.navigationController?.pushViewController(inboxViewController, animated: true)
}

To be notified when inbox campaigns refreshing begins and ends, subclass LLInboxViewController and implement the LLInboxCampaignsRefreshingDelegate protocol methods.

Objective-C Swift

- (void)localyticsDidBeginRefreshingInboxCampaigns
{
    // show a loading indicator
}

- (void)localyticsDidFinishRefreshingInboxCampaigns
{
    // hide a loading indicator
}
override func localyticsDidBeginRefreshingInboxCampaigns() {
    // show a loading indicator
}

override func localyticsDidFinishRefreshingInboxCampaigns() {
    // hide a loading indicator
}

Setting an empty campaigns view

By default, LLInboxViewController will display text saying "No Messages" when there are no inbox campaigns. To change this behavior, set the emptyCampaignsView property as follows. All subviews of this UIView should include appropriate Auto Layout constraints because this view's leading edge, top edge, trailing edge, and bottom edge will be constrained to match the main view in LLInboxViewController.

Objective-C Swift

- (UIView *)createEmptyView
{
    UIView *emptyView = [UIView new];
    emptyView.translatesAutoresizingMaskIntoConstraints = NO;
    emptyView.backgroundColor = [UIColor whiteColor];

    UIImage *emptyImage = [UIImage imageNamed:@"empty_inbox"];
    UIImageView *emptyImageView = [[UIImageView alloc] initWithImage:emptyImage];
    emptyImageView.translatesAutoresizingMaskIntoConstraints = NO;

    [emptyView addSubview:emptyImageView];
    [emptyView addConstraint:[NSLayoutConstraint constraintWithItem:emptyImageView
                                                          attribute:NSLayoutAttributeCenterX
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:emptyView
                                                          attribute:NSLayoutAttributeCenterX
                                                         multiplier:1.0f
                                                           constant:0.0f]];
    [emptyView addConstraint:[NSLayoutConstraint constraintWithItem:emptyImageView
                                                          attribute:NSLayoutAttributeCenterY
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:emptyView
                                                          attribute:NSLayoutAttributeCenterY
                                                         multiplier:1.0f
                                                           constant:0.0f]];
    return emptyView;
}
func createEmptyView() -> UIView {
    let emptyView = UIView()
    emptyView.translatesAutoresizingMaskIntoConstraints = false
    emptyView.backgroundColor = .white

    let emptyImage = UIImage(named: "empty_inbox")
    let emptyImageView = UIImageView(image: emptyImage)
    emptyImageView.translatesAutoresizingMaskIntoConstraints = false

    emptyView.addSubview(emptyImageView)
    emptyView.addConstraint(NSLayoutConstraint(
        item: emptyImageView,
        attribute: .centerX,
        relatedBy: .equal,
        toItem: emptyView,
        attribute: .centerX,
        multiplier: 1,
        constant: 0))
    emptyView.addConstraint(NSLayoutConstraint(
        item: emptyImageView,
        attribute: .centerY,
        relatedBy: .equal,
        toItem: emptyView,
        attribute: .centerY,
        multiplier: 1,
        constant: 0))

    return emptyView
}

Objective-C Swift

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"inbox_list"])
    {
        LLInboxViewController *inboxViewController = (LLInboxViewController *) segue.destinationViewController;
        inboxViewController.emptyCampaignsView = [self createEmptyView];
    }
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "inbox_list" {
        let inboxViewController = segue.destination as! LLInboxViewController
        inboxViewController.emptyCampaignsView = createEmptyView()
    }
}

Objective-C Swift

- (void)displayInbox
{
    LLInboxViewController *inboxViewController = [[LLInboxViewController alloc] init];
    inboxViewController.emptyCampaignsView = [self createEmptyView];
    [self.navigationController pushViewController:inboxViewController
                                         animated:YES];
}
func displayInbox() {
  let inboxViewController = LLInboxViewController()
  inboxViewController.emptyCampaignsView = createEmptyView()
  self.navigationController?.pushViewController(inboxViewController, animated: true)
}

Using your own UITableViewCell class

To use your own UITableViewCell, subclass LLInboxViewController, disable automatic thumbnail downloading in viewDidLoad, and override tableView:cellForRowAtIndexPath:indexPath and tableView:heightForRowAtIndexPath: as follows.

Objective-C Swift

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.downloadsThumbnails = NO;
    [self.tableView registerClass:[UITableViewCell class]
           forCellReuseIdentifier:@"reuseIdentifier"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    LLInboxCampaign *campaign = [self campaignForRowAtIndexPath:indexPath];
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuseIdentifier"];

    /* create and populate cell with campaign data */

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 44.0f;
}
override func viewDidLoad() {
  super.viewDidLoad()

  self.downloadsThumbnails = false
  self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "reuseIdentifier")
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  let campaign = campaignForRow(at: indexPath)!
  let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier")

  /* create and populate cell with campaign data */

  return cell!
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  return 44
}

Handling detail message errors

In rare cases the message detail view may fail to load. By default, LLInboxDetailViewController will display a gray "X" in the center of the view when this occurs. To provide your own custom error view, set the creativeLoadErrorView property of the LLInboxViewController class as follows. All subviews of this UIView should include appropriate Auto Layout constraints because this view's leading edge, top edge, trailing edge, and bottom edge will be constrained to match the main view in LLInboxDetailViewController.

Objective-C Swift

- (UIView *)createErrorView
{
    UIView *errorView = [UIView new];
    errorView.translatesAutoresizingMaskIntoConstraints = NO;
    errorView.backgroundColor = [UIColor blackColor];

    UILabel *errorLabel = [UILabel new];
    errorLabel.translatesAutoresizingMaskIntoConstraints = NO;
    errorLabel.textColor = [UIColor whiteColor];
    errorLabel.font = [UIFont boldSystemFontOfSize:30];
    errorLabel.text = @"Error";

    [errorView addSubview:errorLabel];
    [errorView addConstraint:[NSLayoutConstraint constraintWithItem:errorLabel
                                                          attribute:NSLayoutAttributeCenterX
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:errorView
                                                          attribute:NSLayoutAttributeCenterX
                                                         multiplier:1.0f
                                                           constant:0.0f]];
    [errorView addConstraint:[NSLayoutConstraint constraintWithItem:errorLabel
                                                          attribute:NSLayoutAttributeCenterY
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:errorView
                                                          attribute:NSLayoutAttributeCenterY
                                                         multiplier:1.0f
                                                           constant:0.0f]];
    return errorView;
}
func createErrorView() -> UIView {
    let errorView = UIView()
    errorView.translatesAutoresizingMaskIntoConstraints = false
    errorView.backgroundColor = .black

    let errorLabel = UILabel()
    errorLabel.translatesAutoresizingMaskIntoConstraints = false
    errorLabel.textColor = .white
    errorLabel.font = .boldSystemFont(ofSize: 30)
    errorLabel.text = "Error"

    errorView.addSubview(errorLabel)
    errorView.addConstraint(NSLayoutConstraint(
        item: errorLabel,
        attribute: .centerX,
        relatedBy: .equal,
        toItem: errorView,
        attribute: .centerX,
        multiplier: 1,
        constant: 0))
    errorView.addConstraint(NSLayoutConstraint(
        item: errorLabel,
        attribute: .centerY,
        relatedBy: .equal,
        toItem: errorView,
        attribute: .centerY,
        multiplier: 1,
        constant: 0))

    return errorView
}

Objective-C Swift

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"inbox_list"])
    {
        LLInboxViewController *inboxViewController = (LLInboxViewController *) segue.destinationViewController;
        inboxViewController.creativeLoadErrorView = [self createErrorView];
    }
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "inbox_list" {
        let inboxViewController = segue.destination as! LLInboxViewController
        inboxViewController.creativeLoadErrorView = createErrorView()
    }
}

Objective-C Swift

- (void)displayInbox
{
    LLInboxViewController *inboxViewController = [[LLInboxViewController alloc] init];
    inboxViewController.creativeLoadErrorView = [self createErrorView];
    [self.navigationController pushViewController:inboxViewController
                                         animated:YES];
}
func displayInbox() {
  let inboxViewController = LLInboxViewController()
  inboxViewController.creativeLoadErrorView = createErrorView()
  self.navigationController?.pushViewController(inboxViewController, animated: true)
}

If you are subclassing LLInboxViewController and overriding tableView:cellForRowAtIndexPath:indexPath, set the creativeLoadErrorView property on LLInboxDetailViewController as follows.

Objective-C Swift

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    LLInboxCampaign *campaign = [self campaignForRowAtIndexPath:indexPath];
    if (campaign.hasCreative)
    {
        LLInboxDetailViewController *detailViewController = [Localytics inboxDetailViewControllerForCampaign:campaign];
        detailViewController.creativeLoadErrorView = [self createErrorView];
        [self.navigationController pushViewController:detailViewController animated:YES];
    }

    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    if (!campaign.isRead)
    {
        // Set campaign as read and reload to refresh unread indicators
        campaign.read = YES;
        [tableView reloadData];
    }
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let campaign = campaignForRow(at: indexPath)!
    if campaign.hasCreative {
        let detailViewController = Localytics.inboxDetailViewController(for: campaign)
        detailViewController.creativeLoadErrorView = createErrorView()
        self.navigationController?.pushViewController(detailViewController, animated: true)
    }

    tableView.deselectRow(at: indexPath, animated: true)

    if !campaign.isRead {
        // Set campaign as read and reload to refresh unread indicators
        campaign.isRead = true
        tableView.reloadData()
    }
}

Places

One of the unique aspects of mobile is that users always have their devices with them. Places lets you take advantage of this by sending users content that's personalized to their current location. With Places, you can send notifications to users the instant they enter or exit a specific location. Additionally, you can analyze data about visits to physical locations, giving you access to insights that have never before been available. Read more about setting up Places geofences.

Before continuing, please be sure that you have completed all of the steps in Getting Started and that you are using SDK version 4.0.1 or later. If you want to manually monitor geofences, follow our custom places integration instructions.

1. Add always use location description to Info.plist

Update your app's Info.plist to include the NSLocationAlwaysUsageDescription key.

  1. Enter the Project Navigator view and click on your project icon.

  2. Select your target from the sidebar or from the dropdown menu and then select the Info tab.

  3. Open the Custom iOS Target Properties expander and click the + next to the last key.

  4. Enter the key NSLocationAlwaysUsageDescription, set its type to String, and enter a description as the value. This description will be shown when the location permissions alert is presented to the user.

screenshot of NSLocationAlwaysUsageDescription in Info.plist

2. Enable location monitoring

Enable location monitoring in your AppDelegate after integrating Localytics as follows.

Objective-C Swift

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [Localytics autoIntegrate:@"YOUR-LOCALYTICS-APP-KEY" launchOptions:launchOptions];
  [Localytics setLocationMonitoringEnabled:YES];
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  Localytics.autoIntegrate("YOUR-LOCALYTICS-APP-KEY", launchOptions: launchOptions)
  Localytics.setLocationMonitoringEnabled(true)
}

If you are already using location services, make sure your CLLocationManager requests "always" authorization for location services instead of "when in use". If you request "when in use" authorization, geofence monitoring will not work.

Objective-C Swift

[manager requestAlwaysAuthorization];
manager.requestAlwaysAuthorization()

3. Register for local notifications

Add the following code to your app wherever you would like the system prompt to enable notifications to appear for your users. You could call this after the Localytics initialization code in didFinishLaunchingWithOptions: in your app delegate if you would like to enable notifications at app start. Many apps find better push opt-in rates if the app prompts users to enable notifications after explaining to the user how the app uses notifications or warns the user that the prompt is about to appear. For a step-by-step guide on creating one of these explanations, see increasing notification opt-in rates with in-app permission requests.

Objective-C Swift

if (NSClassFromString(@"UNUserNotificationCenter"))
{
  UNAuthorizationOptions options = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound |UNAuthorizationOptionAlert);
  [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:options
                                                                      completionHandler:^(BOOL granted, NSError * _Nullable error) {
                                                                        [Localytics didRequestUserNotificationAuthorizationWithOptions:options
                                                                                                                               granted:granted];
                                                                      }];
}
else if ([application respondsToSelector:@selector(registerUserNotificationSettings:)])
{
  UIUserNotificationType types = (UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound);
  UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
  [application registerUserNotificationSettings:settings];
}
if #available(iOS 10.0, *), objc_getClass("UNUserNotificationCenter") != nil {
    let options: UNAuthorizationOptions = [.alert, .badge, .sound]
    UNUserNotificationCenter.current().requestAuthorization(options: options) { (granted, error) in
        Localytics.didRequestUserNotificationAuthorization(withOptions: options.rawValue, granted: granted)
    }
} else if #available(iOS 8.0, *) {
    let types: UIUserNotificationType = [.alert, .badge, .sound]
    let settings = UIUserNotificationSettings(types: types, categories: nil)
    application.registerUserNotificationSettings(settings)
}
If you are using alternative session management, be sure to follow the documentation on handling the notification events.

4. Handle the opened notification

  1. Only if you are using alternative session management, handle the received UILocalNotification in your AppDelegate as follows.

    Objective-C Swift

    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
    {
      [Localytics handleNotification:notification.userInfo];
    }
    
    func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
      Localytics.handleNotification(notification.userInfo!)
    }
    
  2. When using the UserNotification framework and using a UNUserNotificationCenterDelegate, you need to notify the Localytics SDK when notifications are opened. Add the following to your userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: delegate method:

    Objective-C Swift

    - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler
    {
      [Localytics didReceiveNotificationResponseWithUserInfo:response.notification.request.content.userInfo];
      completionHandler();
    }
    
    @available(iOS 10.0, *) // if also targeting iOS versions less than 10.0
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        Localytics.didReceiveNotificationResponse(userInfo: response.notification.request.content.userInfo)
        completionHandler()
    }
    

Uninstall Tracking

Localytics Uninstall Tracking reports an analytics event for users that uninstall your app at high accuracy within 24 hours. Those uninstalls can be used in charts, funnels, campaign performance reports, and remarketing so you can find and address the root causes of user loss.

Before continuing, please be sure that you have completed all of the steps in Getting Started and Push Messaging and that you are using SDK version 4.0 or later (SDK v3 will only report uninstalls for iOS users with notifications enabled). You'll need a subscription to Uninstall Tracking to use this feature.

1. Register for a push token

Localytics Uninstall Tracking requires Localytics push integrated in your app. To properly track uninstalls for all users - regardless of notification permissions - you need to register for a push token as early as possible.

Basic Integration

Follow our standard guide for integrating push, ensuring you call registerForRemoteNotifications as part of the code in your app delegate’s didFinishLaunchingWithOptions method for all users.

Custom Integration

If you’re unable to use the basic integration - for example, if you have an existing integration that uses custom logic for registering for notifications later in the app’s lifecycle - you’ll need to adjust your code to separate out an early registration for push tokens independently of notification permissions.

iOS 8 separated the logic of getting a token for push (registerForRemoteNotifications) from the logic of asking for permissions to render remote and local notifications (registerUserNotificationSettings). Regardless of whether the user has been asked for notification permissions already, and whether they accept, reject, or subsequently disable notifications, you can collect a push token at app open. For Localytics to track uninstalls accurately across app upgrades and for devices with disabled notifications, you'll need to collect and send those tokens to Localytics for all users.

If your app normally asks for notification permissions later in the app - after a soft-ask, for instance - you’ll need to separate out registering for a push token on iOS 8+ from the ask for notification permissions. To do this, put the following code in your app delegate’s didFinishLaunchingWithOptions method.

Objective-C Swift

if ([application respondsToSelector:@selector(registerForRemoteNotifications:)])
{
    [application registerForRemoteNotifications];
}
if #available(iOS 8.0, *) {
    application.registerForRemoteNotifications()
}

For customers using alternative session management, ensure that you're also passing tokens to Localytics in your application:didRegisterForRemoteNotificationsWithDeviceToken call by calling Localytics:setPushToken. For details see our alternative session management guide. This is not necessary if using Localytics' automatic integration, which passes push tokens automatically after registration.

2. Ensuring Your App Ignores Uninstall Tracking Pushes

Your app delegate's application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: methods may fire when an uninstall tracking push comes in while the app is closed. You may need to ensure your app does not tag UI-related events or do background data refreshes in this situation.

The Localytics uninstall tracking push includes a key/value of localyticsUninstallTrackingPush: "true" which you can use to detect when an app launch is coming from a background uninstall tracking push.

3. Provisioning Uninstalls

Once your app's integration has been set up, contact your account manager or our support team to enable Uninstall Tracking for your app. You'll need an active subscription to Uninstalls. Let us know which Localytics apps are configured for uninstalls, along with their iOS bundle IDs.

Tracking user flow

Track screens or views within your app so you can visualize user flow within the Localytics Dashboard. We recommend exhaustively tagging every visible view in your app.

The Localytics SDK will perform duplicate suppression on two identical tagged screens that occur in a row within a single session. For example, in the set of screens {"Screen 1", "Screen 1"}, the second screen would be suppressed. However, in the set {"Screen 1", "Screen 2", "Screen 1"}, no duplicate suppression would occur.

Objective-C Swift

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [Localytics tagScreen:@"Item List"];
}
override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)
  Localytics.tagScreen("Item List")
}

Tracking revenue

There are two ways to think about Lifetime Value (LTV) in Localytics: monetary and non-monetary. If your app allows real currency transactions, our Dashboard can show LTV in USD. If your app uses virtual currencies like coins or tokens, you can configure your app to report LTV as the raw data you pass in.

You can configure each Mobile App in Localytics to have either a monetary value or raw data for Lifetime Value:

Tracking Monetary LTV

If you'd like to track LTV as monetary-based revenue, you should increment the value upon the completion of a purchase by the purchase amount. Make sure to configure your app in Localytics to show LTV as "Tracked as Money (US cents)".

LTV must be an integer amount, and the Localytics system requires you pass the number of USD cents as the LTV value in order to track money. For example, if the purchase amount is "USD $2.99", you should pass the integer "299" as the LTV. If the cents don't matter to you, feel free to round up to whole dollar values, but continue to pass the value as cents. If you want to track the rounded value of "USD $3.00", you should pass "300" as the value.

Currently, Localyics only allows LTV tracking in USD. If you want to track other currencies, you could convert the monetary amount to USD on the device before sending to Localytics.

Tracking Non-Monetary LTV

Another way to measure LTV is to track a non-monetary value important to your app. Examples include the number seconds spent engaged with content, or the amount of virtual currency earned. To track these values, send the corresponding integer value to Localytics. Make sure to configure your app in Localytics to show LTV as "Raw Value". Otherwise, Localytics will automatically divide your values by 100 and display a "$" in front of the values in the Dashboard.

LTV Examples

Increment user lifetime value (LTV) on any event using the optional LTV incrementer parameter as seen below.

You can increment LTV with our standard purchased and completed checkout events as follows. The item price and total price values will be used respectively.

Objective-C Swift

[Localytics tagPurchased:@"Shirt"
                  itemId:@"sku-123"
                itemType:@"Apparel"
               itemPrice:@15
              attributes:extraAttributes];
[Localytics tagCompletedCheckout:@50
                       itemCount:@2
                      attributes:extraAttributes];
Localytics.tagPurchased("Shirt", itemId: "sku-123", itemType: "Apparel", itemPrice: 15, attributes: extraAttributes)
Localytics.tagCompletedCheckout(50, itemCount: 2, attributes: extraAttributes)

You can also increment LTV using a custom event by including a customerValueIncrease value.

Objective-C Swift

[Localytics tagEvent:@"Item Purchased"
          attributes:@{@"Item Name" : @"Stickers", @"Aisle" : @"Knick-Knacks"}
   customerValueIncrease:@499];
Localytics.tagEvent("Item Purchased", attributes: ["Item Name": "Stickers", "Aisle": "Knick-Knacks"], customerValueIncrease: 499)

Setting custom dimensions

Custom dimensions are special fields that are used for splitting and filtering data within the Localytics Dashboard and are useful for storing user-level information. Custom dimensions are like sticky event attributes in the sense that once their value is set, it remains set until the value is changed again. Unlike event attributes, custom dimension values are attached to all three types of datapoints (session start, session close, and event) the same way that standard dimensions are which makes their values available within almost every report in the Localytics Dashboard.

Custom dimensions start at an index of 0 and the number of custom dimensions available to you depends on the level of your subscription. Name all of your custom dimensions in the Localytics Dashboard > Settings > Apps > (find app) > Gear icon > Custom Dimensions.

Whenever a datapoint is created, all custom dimension values are attached to the datapoint. Therefore, it is ideal to set custom dimensions as soon as their value is known in all code paths in your app. It is not uncommon to use an analytics callback to set custom dimension values before the start of a session (and the creation of a "session start" datapoint).

Setting a value

Objective-C Swift

[Localytics setValue:@"Trial" forCustomDimension:0];
Localytics.setValue("Trial", forCustomDimension: 0)

Initializing dimensions before session start

Set the value of all custom dimensions as early as possible in your app's code path even if you only set the value to "Not applicable" or "Not available". Having at least some value for each custom dimension instead of nothing will prevent the display of "[Unspecified]" values in the Dashboard and help to make it much more obvious to distinguish intentionally missing values from unintentionally missing values.

  1. In your app delegate header file, ensure that your app delegate conforms to the LLAnalyticsDelegate protocol as follows.

    Objective-C Swift

    @interface YourAppDelegate : UIResponder <UIApplicationDelegate, LLAnalyticsDelegate>
    
    class AppDelegate: UIResponder, UIApplicationDelegate, LLAnalyticsDelegate {
    
  2. Modify your app delegate as follows to register the Localytics analytics delegate and set any custom dimension values before the first session opens. You will need to modify the custom dimension code below to match your exact custom dimension values.

    Objective-C Swift

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // ...Localytics initialization code here...
    
        [Localytics setAnalyticsDelegate:self];
    
        // ... rest of didFinishLaunchingWithOptions ...
    
        return YES;
    }
    
    - (void)localyticsSessionWillOpen:(BOOL)isFirst isUpgrade:(BOOL)isUpgrade isResume:(BOOL)isResume
    {
        if (isFirst)
        {
            [Localytics setValue:@"Logged Out" forCustomDimension:0];
            [Localytics setValue:@"0" forCustomDimension:1];
            // ...rest of custom dimensions go here...
        }
    }
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
      // ...Localytics initialization code here...
    
      Localytics.setAnalyticsDelegate(self)
    
      // ... rest of didFinishLaunchingWithOptions ...
    
      return true
    }
    
    func localyticsSessionWillOpen(_ isFirst: Bool, isUpgrade: Bool, isResume: Bool) {
      if isFirst {
        Localytics.setValue("Logged Out", forCustomDimension: 0)
        Localytics.setValue("0", forCustomDimension: 1)
        // ...rest of custom dimensions go here...
      }
    }
    

Clearing a value

Though it may be more appropriate to set a custom dimension value back to an initial state, sometimes you may need to completely clear the value. Setting an an empty string will NOT clear the value. To clear a value use the following code:

Objective-C Swift

[Localytics setValue:nil forCustomDimension:0];
Localytics.setValue(nil, forCustomDimension:0)

Tracking user acquisition source

Use Localytics to track the origin of your new users and attribute them to a specific acquisition source. Localytics employs a variety of attribution methods, but at the core, all methods work by matching a shared identifier from both before and after app installation.

In order for your app to be able to access the device identifiers that are required for most iOS acquisition tracking to work, you must include AdSupport.framework as discussed elsewhere in this guide. You've likely already done this via CocoaPods or the alternative installation approach, but it's worth checking just to be sure.

Advanced

Installing without CocoaPods

If you are unable to install the SDK using CocoaPods, the following alternative approach will install the SDK and its dependencies.

  1. Delete any existing Localytics files from your project.
  2. Download the latest version of the SDK.
  3. Unzip it and drag Localytics.framework into the top level of your Xcode project in the Project Navigator (Command + 1). When prompted, use the checkboxes to add the framework to the specific target for which you are compiling. Check the "copy items if needed" box.
  4. Add AdSupport.framework, libsqlite3.tbd, libz.tbd, CoreLocation.framework, and SystemConfiguration.framework to your project.
    1. Enter Project Navigator view and click on your project icon. It should be at the top of the Project Navigator.
    2. Select your target from the sidebar or from the dropdown menu, then select the Build Phases tab.
    3. Open the Link Binaries with Libraries expander and click +. Find each library from the list, clicking Add each time.
  5. Add the -ObjC other linker flag to your build settings.
    1. Enter Project Navigator view and click on your project icon. It should be at the top of the Project Navigator.
    2. Select the Build Settings tab.
    3. Select the All option and type Other Linker Flags into the search box.
    4. Double click on the right side of the Other Linker Flags and add the text -ObjC.

You are now ready to continue with the setup process here.

Alternative session management

The standard Localytics session management approach relies on the Localytics SDK to automatically listen for indications of state change (e.g. foreground / background transitions) in your app and then to track the state change accordingly. Most apps are able to use the standard approach. If you are unable to use the standard session management approach, the instructions below will help you to initialize the SDK to achieve the same result as the standard approach.

Use the following initialization instructions instead of those in Getting started. If you have an app with extensive, engaged user activity in the background, as is common with streaming media apps, you may find it necessary reposition calls to openSession and closeSession to achieve the session behavior you desire. Enabling logging when modifying session behavior is often very helpful.

Note that the push notification code will not do anything unless you have also followed the push setup instructions.

In your application's delegate file:

  1. Import the Localytics SDK under any existing imports.

    Objective-C Swift

    @import Localytics;
    
    import Localytics
    
  2. Add the following code to the start of application:didFinishLaunchingWithOptions:.

    Objective-C Swift

    [Localytics integrate:@"YOUR-LOCALYTICS-APP-KEY"];
    
    if (application.applicationState != UIApplicationStateBackground)
    {
        [Localytics openSession];
    }
    
    Localytics.integrate("YOUR-LOCALYTICS-APP-KEY")
    
    if application.applicationState != .background {
      Localytics.openSession()
    }
    
  3. Add the following code to the start of both applicationDidBecomeActive: and applicationWillEnterForeground:.

    Objective-C Swift

    [Localytics openSession];
    [Localytics upload];
    
    Localytics.openSession()
    Localytics.upload()
    
  4. Add the following code to the start of applicationDidEnterBackground:.

    Objective-C Swift

    [Localytics dismissCurrentInAppMessage];
    [Localytics closeSession];
    [Localytics upload];
    
    Localytics.dismissCurrentInAppMessage()
    Localytics.closeSession()
    Localytics.upload()
    
  5. Add the following code to the end of application:openURL:sourceApplication:annotation:. This app delegate method was deprecated in iOS 9, but using it is still the easiest way to ensure the proper function of the Localytics SDK on iOS 7 and later.

    Objective-C Swift

    return [Localytics handleTestModeURL:url];
    
    return Localytics.handleTestModeURL(url)
    
  6. Add the following code to the end of application:didRegisterForRemoteNotificationsWithDeviceToken:.

    Objective-C Swift

    [Localytics setPushToken:deviceToken];
    
    Localytics.setPushToken(deviceToken)
    
  7. Add the following code to the end of application:didFailToRegisterForRemoteNotificationsWithError:.

    Objective-C Swift

    NSLog(@"Failed to register for remote notifications: %@", [error description]);
    
    print("Failed to register for remote notifications: \(error.localizedDescription)")
    
  8. Add the following code to the end of application:didReceiveRemoteNotification:fetchCompletionHandler:.

    Objective-C Swift

    [Localytics handleNotification:userInfo];
    completionHandler(UIBackgroundFetchResultNoData);
    
    Localytics.handleNotification(userInfo)
    completionHandler(.noData)
    

    Only if your app delegate cannot implement the fetchCompletionHandler version of this method:

    1. Add the following code to the end of application:didReceiveRemoteNotification:.

      Objective-C Swift

      [Localytics handleNotification:userInfo];
      
      Localytics.handleNotification(userInfo)
      
    2. Add the following code to the end of application:didFinishLaunchingWithOptions:.

      Objective-C Swift

      if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey])
      {
          [Localytics handleNotification:[launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]];
      }
      
      if let notificationInfo = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? [AnyHashable: Any] {
          Localytics.handleNotification(notificationInfo)
      }
      
  9. Add the following code to the end of application:didFinishLaunchingWithOptions:.

    Objective-C Swift

    if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
        [Localytics handleNotification:launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]];
    }
    
    if let notificationInfo = launchOptions?[UIApplicationLaunchOptionsKey.localNotification] as? [AnyHashable: Any] {
        Localytics.handleNotification(notificationInfo)
    }
    
  10. This step is only required if you are using the UserNotification framework introduced in iOS 10 and you are setting a UNUserNotificationCenterDelegate. If this doesn't apply to your app, please continue on to the next step.

    When using the UserNotification framework and using a UNUserNotificationCenterDelegate, you need to notify the Localytics SDK when notifications are opened. Add the following to your userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: delegate method:

    Objective-C Swift

    - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler
    {
      [Localytics didReceiveNotificationResponseWithUserInfo:response.notification.request.content.userInfo];
      completionHandler();
    }
    
    @available(iOS 10.0, *) // if also targeting iOS versions less than 10.0
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        Localytics.didReceiveNotificationResponse(userInfo: response.notification.request.content.userInfo)
        completionHandler()
    }
    
  11. Add the following code to the end of application:didReceiveLocalNotification:.

    Objective-C Swift

    [Localytics handleNotification:notification.userInfo];
    
    Localytics.handleNotification(notification.userInfo)
    
  12. Add the following code to the end of application:didRegisterUserNotificationSettings:.

    Objective-C Swift

    [Localytics didRegisterUserNotificationSettings:notificationSettings]
    
    Localytics.didRegister(notificationSettings)
    
  13. Ensure that there is not any Localytics session-related code in applicationWillResignActive:. This will only apply if you have an older Localytics integration.

  14. Compile and run your app.

Swift

The Localytics SDK is written in Objective-C but to simplify the process of integrating it with Swift the public APIs have been annotated with nullable and nonnull annotations, collections take advantage of light weight generics, and the SDK is available as a dynamic framework. Calling SDK methods is a straight forward translation from bracket notation to dot notation.

Access to the original AppDelegate

If you are adding methods to the AppDelegate, for access from other parts of the application, it is necessary to take additional steps in Swift. Add the following code to the AppDelegate:

class AppDelegate: UIResponder, UIApplicationDelegate {
    static var originalAppDelegate:AppDelegate!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    AppDelegate.originalAppDelegate = self
        

This will make a globally accessible variable that points to the original AppDelegate. It can be used as follows from anywhere in the application:

AppDelegate.originalAppDelegate.someMethod()

Callbacks

Analytics callbacks

These analytics callbacks are useful for setting the value of a custom dimension or profile attribute before a session opens, firing an event at the beginning or end of a session, or taking action in your app based on an auto-tagged campaign performance event in the Localytics SDK. All Localytics analytics callbacks are called on an internal background thread.

  1. In your app delegate header file, ensure that your app delegate conforms to the LLAnalyticsDelegate protocol as follows.

    Objective-C Swift

    @interface YourAppDelegate : UIResponder <UIApplicationDelegate, LLAnalyticsDelegate>
    
    class AppDelegate: UIResponder, UIApplicationDelegate, LLAnalyticsDelegate {
    
  2. Modify your app delegate as follows to register the Localytics analytics delegate and implement any of the analytics callbacks you require.

    Objective-C Swift

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // ...Localytics initialization code here...
    
        [Localytics setAnalyticsDelegate:self];
    
        // ... rest of didFinishLaunchingWithOptions ...
    
        return YES;
    }
    
    - (void)localyticsSessionWillOpen:(BOOL)isFirst
                            isUpgrade:(BOOL)isUpgrade
                             isResume:(BOOL)isResume
    {
        // ... do something ...
    }
    
    - (void)localyticsSessionDidOpen:(BOOL)isFirst
                           isUpgrade:(BOOL)isUpgrade
                            isResume:(BOOL)isResume
    {
        // ... do something ...
    }
    
    - (void)localyticsDidTagEvent:(NSString *)
                       attributes:(NSDictionary *)attributes
            customerValueIncrease:(NSNumber *)customerValueIncrease
    {
        // ... do something ...
    }
    
    - (void)localyticsSessionWillClose
    {
        // ... do something ...
    }
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
      // ...Localytics initialization code here...
    
      Localytics.setAnalyticsDelegate(self)
    
      // ... rest of didFinishLaunchingWithOptions ...
    
      return true
    }
    
    func localyticsSessionWillOpen(_ isFirst: Bool, isUpgrade: Bool, isResume: Bool) {
      // ... do something ...
    }
    
    func localyticsSessionDidOpen(_ isFirst: Bool, isUpgrade: Bool, isResume: Bool) {
      // ... do something ...
    }
    
    func localyticsDidTagEvent(_ eventName: String, attributes: [String : Any]?, customerValueIncrease: NSNumber?) {
      // ... do something ...
    }
    
    func localyticsSessionWillClose() {
      // ... do something ...
    }
    

Messaging callbacks

Messaging callbacks are useful for understanding when Localytics in-app messages are about to appear to prevent conflicts with other views in your app. All Localytics messaging callbacks are called on the main thread.

  1. In your app delegate header file, ensure that your app delegate conforms to the LLMessagingDelegate protocol as follows.

    Objective-C Swift

    @interface YourAppDelegate : UIResponder <UIApplicationDelegate, LLMessagingDelegate>
    
    class AppDelegate: UIResponder, UIApplicationDelegate, LLMessagingDelegate {
    
  2. Modify your app delegate as follows to register the Localytics messaging delegate and implement any of the analytics callbacks you require.

    Objective-C Swift

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // ...Localytics initialization code here...
    
        [Localytics setMessagingDelegate:self];
    
        // ... rest of didFinishLaunchingWithOptions ...
    
        return YES;
    }
    
    - (BOOL)localyticsShouldShowInAppMessage:(nonnull LLInAppCampaign *)campaign
    {
        return YES; // return NO to suppress the in-app.
    }
    
    - (BOOL)localyticsShouldDelaySessionStartInAppMessages
    {
        return NO; // return YES to delay the triggering of messages set to display on session start.
        // Later, to trigger these messages call Localytics.triggerSessionStartInAppMessages().
    }
    
    - (nonnull LLInAppConfiguration *)localyticsWillDisplayInAppMessage:(nonnull LLInAppCampaign *)campaign withConfiguration:(nonnull LLInAppConfiguration *)configuration
    {
        // ... optionally modify the aspect ratio for center in-app creatives, offset for banner in-app creatives and background alpha ...
        return configuration;
    }
    
    - (void)localyticsWillDisplayInAppMessage
    {
        // ... do something ...
    }
    
    - (void)localyticsDidDisplayInAppMessage
    {
        // ... do something ...
    }
    
    - (void)localyticsWillDismissInAppMessage
    {
        // ... do something ...
    }
    
    - (void)localyticsDidDismissInAppMessage
    {
        // ... do something ...
    }
    
    - (BOOL)localyticsShouldDisplayPlacesCampaign:(nonnull LLPlacesCampaign *)campaign
    {
        return YES; // return NO to suppress the notification
    }
    
    /*
     * If your app is using the UserNotification framework and the device is running iOS 10 or later,
     * localyticsWillDisplayNotificationContent:forPlacesCampaign will be called instead.
     */
    - (nonnull UILocalNotification *)localyticsWillDisplayNotification:(nonnull UILocalNotification *)notification forPlacesCampaign:(nonnull LLPlacesCampaign *)campaign
    {
        // ... optionally modify the title, sound, or badge ...
        return notification;
    }
    
    /*
     * Only called if your app is using the UserNotification framework and the device is running iOS 10 or later.
     * Otherwise, localyticsWillDisplayNotification:forPlacesCampaign will be called.
     */
    - (nonnull UNMutableNotificationContent *)localyticsWillDisplayNotificationContent:(nonnull UNMutableNotificationContent *)notification forPlacesCampaign:(nonnull LLPlacesCampaign *)campaign
    {
        // ... optionally modify the title, sound, body, etc. ...
        return notification;
    }
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
      // ...Localytics initialization code here...
    
      Localytics.setMessagingDelegate(self)
    
      // ... rest of didFinishLaunchingWithOptions ...
    
      return true
    }
    
    func localyticsShouldShow(inAppMessage campaign: LLInAppCampaign) -> Bool {
        return true; // return false to suppress the in-app.
    }
    
    func localyticsShouldDelaySessionStartInAppMessages() -> Bool {
        return false; // return true to delay the triggering of messages set to display on session start.
    }
    
    func localyticsWillDisplay(inAppMessage campaign: LLInAppCampaign, with configuration: LLInAppConfiguration) -> LLInAppConfiguration {
        // ... optionally modify the aspect ratio for center in-app campaigns, offset for banner in-app campaigns and background alpha ...
        return configuration;
    }
    
    func localyticsWillDisplayInAppMessage() {
      // ... do something ...
    }
    
    func localyticsDidDisplayInAppMessage() {
      // ... do something ...
    }
    
    func localyticsWillDismissInAppMessage() {
      // ... do something ...
    }
    
    func localyticsDidDismissInAppMessage() {
      // ... do something ...
    }
    
    func localyticsShouldDisplay(_ campaign: LLPlacesCampaign) -> Bool {
      return true // return false to suppress the notification
    }
    
    /*
     * If your app is using the UserNotification framework and the device is running iOS 10 or later,
     * localyticsWillDisplayNotificationContent:forPlacesCampaign will be called instead.
     */
    func localyticsWillDisplay(_ notification: UILocalNotification, for campaign: LLPlacesCampaign) -> UILocalNotification {
      // ... optionally modify the title, sound, or badge ...
      return notification
    }
    
    /*
     * Only called if your app is using the UserNotification framework and the device is running iOS 10 or later.
     * Otherwise, localyticsWillDisplayNotification:forPlacesCampaign will be called.
     */
     @available(iOS 10.0, *)
     func localyticsWillDisplay(_ notification: UNMutableNotificationContent, for campaign: LLPlacesCampaign) -> UNMutableNotificationContent {
      // ... optionally modify the title, sound, body, etc. ...
      return content
    }
    

Location callbacks

Location callbacks are useful for understanding when Localytics responds location changes. localyticsDidUpdateLocation is called on an internal background thread; the others are called on the main thread.

  1. In your app delegate header file, ensure that your app delegate conforms to the LLLocationDelegate protocol as follows.

    Objective-C Swift

    @interface YourAppDelegate : UIResponder <UIApplicationDelegate, LLLocationDelegate>
    
    class AppDelegate: UIResponder, UIApplicationDelegate, LLLocationDelegate {
    
  2. Modify your app delegate as follows to register the Localytics messaging delegate and implement any of the analytics callbacks you require.

    Objective-C Swift

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // ...Localytics initialization code here...
    
        [Localytics setLocationDelegate:self];
    
        // ... rest of didFinishLaunchingWithOptions ...
    
        return YES;
    }
    
    - (void)localyticsDidUpdateLocation:(nonnull CLLocation *)location
    {
        // ... do something
    }
    
    - (void)localyticsDidUpdateMonitoredRegions:(nonnull NSArray<LLRegion *> *)addedRegions removeRegions:(nonnull NSArray<LLRegion *> *)removedRegions
    {
        // ... do something
    }
    
    - (void)localyticsDidTriggerRegions:(nonnull NSArray<LLRegion *> *)regions withEvent:(LLRegionEvent)event
    {
        // ... do something
    }
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
      // ...Localytics initialization code here...
    
      Localytics.setLocationDelegate(self)
    
      // ... rest of didFinishLaunchingWithOptions ...
    
      return true
    }
    
    func localyticsDidUpdateLocation(_ location: CLLocation) {
      // ... do something
    }
    
    func localyticsDidUpdateMonitoredRegions(_ addedRegions: [LLRegion], remove removedRegions: [LLRegion]) {
      // ... do something
    }
    
    func localyticsDidTriggerRegions(_ regions: [LLRegion], with event: LLRegionEvent) {
      // ... do something
    }
    

Tracking location

Most apps don't track user geolocation so Localytics uses an IP-to-location lookup service to determine which country they are located in. If your app does track user geolocation, you can use the following code to set latitude and longitude in Localytics much more precisely. The exact latitude and longitude are not available within the Localytics Dashboard, but are available in the export logs. Localytics will always attach the most recently provided location to each datapoint, similar to custom dimensions, so it is best to set the location anytime there is a significant location change and before each session start.

You can set the user’s current location in the class that conforms to the CLLocationManagerDelegate as follows.

Objective-C Swift

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocationCoordinate2D coordinate = [locations lastObject].coordinate;
    [Localytics setLocation:coordinate];
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    let coordinate = locations.last?.coordinate
    Localytics.setLocation(coordinate!)
}

Custom Places integration

With the standard Places integration the Localytics SDK will automatically monitor geofences and update the device's location using the lowest power setting. However, if you prefer to get location updates yourself and monitor geofences, you can use the APIs described below to notify the Localytics SDK when geofences are entered and exited.

1. Getting geofences to monitor

On each location update get the closest 20 geofences to monitor for the given location as follows. The LLGeofence object contains a CircularRegion, name, and attributes.

Objective-C Swift

NSArray *geofences = [Localytics geofencesToMonitor:coordinate];
let geofences = Localytics.geofencesToMonitor(coordinate)

2. Notifying the SDK of geofence enter and exit triggers

For Places analytics and messaging functionality, notify the SDK when a geofence is entered or exited as follows.

Objective-C Swift

CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:coordinate
                                                             radius:radius
                                                         identifier:identifier];
[Localytics triggerRegion:region withEvent:LLRegionEventEnter];
let region = CLCircularRegion(center: coordinate, radius: radius, identifier: identifier)
Localytics.triggerRegion(region, with: .enter)

3. Notifying the SDK of location updates

Get more accurate location data for your events and sessions by setting the current location of the device by following the steps in Tracking location.

Setting a custom identifier

Once a custom identifier is set, it is attached to each subsequent datapoint, similar to the behavior of custom dimensions. Unlike custom dimensions, however, custom identifiers only end up in export data. They are useful for attaching multiple unique identifiers to a given datapoint for use in joining your Localytics export data with other sources in an enterprise data warehouse. Custom identifiers are not used by the Localytics backend or Dashboard for anything and are just passed through to the export data.

Objective-C Swift

[Localytics setValue:@"Black" forIdentifier:@"Hair Color"];
Localytics.setValue("Black", forIdentifier:"Hair Color")

Creating an explicit App ID

  1. Log in to the Apple Developer console and go to Certificates, Identifiers & Profiles.
  2. Under Identifiers, select App IDs and then click the Add button (+) in the upper-right corner.
  3. Enter a description, select your Team ID as the App ID Prefix, enter an Explicit App ID that exactly matches your Bundle ID, and then click Continue.
  4. Verify the information you entered and then click Register.

Handling data protection

When data protection is enabled at the provisioning profile level or on Localytics' files, some SDK features may not function properly. Specifically, features that make use of background modes, such as Places and using content-available with push messaging, won't work when the device is locked and NSFileProtectionComplete is used or when NSFileProtectionCompleteUntilFirstUserAuthentication is used and the device hasn't been unlocked since the last boot.

We recommend excluding Localytics files from data protection to ensure full analytics and marketing functionality. There are 2 options for encrypting files while excluding Localytics data:

Option 1: Switch from provisioning-profile-level data protection to per-file data protection

Instead of using profile-level protection, use NSFileManager options when creating individual files to turn on complete protection for those specific files. The Localytics SDK will be able to create its files without Data Protection.

Option 2: Keep provisioning-profile-level data protection, but remove the file protection from Localytics’ individual files.

Set NSFileProtectionNone recursively on Localytics folders and files when the SDK is initialized as follows.

  1. In your app delegate header file, ensure that your app delegate conforms to the LLAnalyticsDelegate protocol as follows.

    Objective-C Swift

    @interface YourAppDelegate : UIResponder <UIApplicationDelegate, LLAnalyticsDelegate>
    
    class AppDelegate: UIResponder, UIApplicationDelegate, LLAnalyticsDelegate {
    
  2. Set the LLAnalyticsDelegate after initializing the SDK in didFinishLaunchingWithOptions:.

    Objective-C Swift

    [Localytics autoIntegrate:@"YOUR-LOCALYTICS-APP-KEY" launchOptions:launchOptions];
    [Localytics setAnalyticsDelegate:self];
    
    Localytics.autoIntegrate("YOUR-LOCALYTICS-APP-KEY", launchOptions: launchOptions)
    Localytics.setAnalyticsDelegate(self)
    
  3. Add the excludeProtectionForFilePath: method to your app delegate.

    Objective-C Swift

    - (void)excludeProtectionForFilePath:(NSString *)filePath
    {
      NSFileManager *manager = [NSFileManager defaultManager];
      if ([manager fileExistsAtPath:filePath])
      {
        NSError *error;
        BOOL success = [manager setAttributes:@{NSFileProtectionKey: NSFileProtectionNone}
                                ofItemAtPath:filePath
                                       error:&error];
        NSLog(@"Set attributes for file path: %@. Success = %@. Error: %@", filePath, @(success), error);
      }
    }
    
    func excludeProtectionForFilePath(_ filePath: String) {
        let manager = FileManager.default
        if (manager.fileExists(atPath: filePath)) {
            do {
                try manager.setAttributes([FileAttributeKey.protectionKey: FileProtectionType.none], ofItemAtPath: filePath)
                print("Successfully set attributes for file path: \(filePath).")
            } catch {
                print("Failed to set attributes for file path: \(filePath).")
            }
        }
    }
    
  4. Exlude file protection when a new session is opened.

    Objective-C Swift

    - (void)localyticsSessionDidOpen:(BOOL)isFirst isUpgrade:(BOOL)isUpgrade isResume:(BOOL)isResume
    {
      if (!isResume)
      {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *localyticsDirectory = [[paths firstObject] stringByAppendingPathComponent:@".localytics"];
        NSFileManager *manager = [NSFileManager defaultManager];
        BOOL isDirectory;
        if ([manager fileExistsAtPath:localyticsDirectory isDirectory:&isDirectory])
        {
          if (isDirectory)
          {
            [self excludeProtectionForFilePath:localyticsDirectory];
            NSArray *subpaths = [manager subpathsAtPath:localyticsDirectory];
            for (NSString *path in subpaths)
            {
              [self excludeProtectionForFilePath:[localyticsDirectory stringByAppendingPathComponent:path]];
            }
          }
        }
      }
    }
    
    func localyticsSessionDidOpen(_ isFirst: Bool, isUpgrade: Bool, isResume: Bool) {
        if !isResume {
            let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
            let localyticsDirectory = (paths.first! as NSString).appendingPathComponent(".localytics")
            let manager = FileManager.default
            var isDirectory : ObjCBool = ObjCBool(false)
            if (manager.fileExists(atPath: localyticsDirectory, isDirectory: &isDirectory)) {
                if isDirectory.boolValue {
                    excludeProtectionForFilePath(localyticsDirectory)
                    let subpaths = manager.subpaths(atPath: localyticsDirectory)
                    for path in subpaths! {
                        excludeProtectionForFilePath((localyticsDirectory as NSString).appendingPathComponent(path))
                    }
                }
            }
        }
    }
    

Sending a test mode push from the Dashboard

After completing push integration you can create a test campaign to send yourself a push message to be sure that everything is properly configured.

  1. Login to the Localytics Dashboard, navigate to Messaging, select your app key from the dropdown at the top of the screen, and click the + button at the top right of the screen.
  2. Name your campaign, e.g. "Push Test", and click Continue to Creatives.
  3. Under Message Body, enter a sample push alert message, e.g. "Push test example message".
  4. Select One Time and Immediately then click Continue and Confirm.
  5. Click Test on Device.
  6. Click Enable Test Mode.
  7. Use one of the options mentioned on the screen to open the test mode URL on your connected device. If the URL doesn't cause your app to open at this point, please ensure that you have completed all steps of Getting Started, especially the test mode step.
  8. Background your app by pressing the home button.
  9. Your test push message will appear on your connected device. If the push does not immediately appear, check that your push certificate is correct, then wait a few minutes and retry.

Push Notifications Entitlement

Along with the release of iOS 10, Xcode 8 does not automatically copy the aps-environment entitlement from provisioning profiles at build time (release notes).

To ensure that push notifications function properly, enable Push Notifications in the Capabilities section of your project's settings as shown in the screenshot below. An APP-NAME.entitlements file will be added to your project.

screenshot of remote notifications BackgroundModes in Info.plist

Handling foreground notifications

This step is only possible if you are using the UserNotification framework introduced in iOS 10 and you are setting a UNUserNotificationCenterDelegate.

In iOS 10 Apple provides a new callback which will only be triggered if the app receives a notification while in the foreground. The below code snippet should be implemented on the application's UNUserNotificationCenterDelegate (typically the AppDelegate) to present the incoming notification.

Objective-C Swift

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
  completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
}
@available(iOS 10.0, *) // if also targeting iOS versions less than 10.0
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    completionHandler([.alert, .sound, .badge])
}

Delaying Session Start in-apps

There are certain scenarios (like when presenting a splash screen) in which In-apps set to trigger on Session Start should be delayed. In order to delay those messages you must implement the messaging callback. An example of how to delay those in-apps would look as follows:

Objective-C Swift

- (BOOL)localyticsShouldDelaySessionStartInAppMessages
{
  if ([self isSplashScreenShowing])
  {
    return YES;
  }
  return NO;
}
func localyticsShouldDelaySessionStartInAppMessages() -> Bool {
  if self.isSplashScreenShowing() {
    return true
  }
  return false
}

Then in the future when in-apps that are triggered by Session Start are valid, call:

Objective-C Swift

[Localytics triggerInAppMessagesForSessionStart];
        
Localytics.triggerInAppMessagesForSessionStart()
        

Troubleshooting

To gain more insight into whether your Localytics tagging is occuring when expected, you can enable logging in your app using the code below. When looking at logs, a 202 response code indicates that an upload was successful. Be sure to disable logging in production builds. Localytics log output is required when contacting support.

Objective-C Swift

[Localytics setLoggingEnabled:YES];
Localytics.setLoggingEnabled(true)

Updating the SDK

This section contains guides to help developers using older Localytics SDK versions to update their Localytics integration since a breaking change occurred.

Most Localytics SDK updates do not change existing interfaces and dependencies, though sometimes might add new interfaces. Because there are not breaking changes, you can effortlessly update to the latest Localytics SDK version before each of your app releases.

Localytics will infrequently introduce breaking changes as part of the introduction of new functionality or to lay a better foundation for future feature development.

Updating from 4.0 to 4.1+

Follow the steps in each section below to make the appropriate adjustments for updating from 4.0 to 4.1 or later.

Push Notifications Entitlement

Along with the release of iOS 10, Xcode 8 does not automatically copy the aps-environment entitlement from provisioning profiles at build time (release notes).

To ensure that push notifications function properly, enable Push Notifications in the Capabilities section of your project's settings as shown in the screenshot below. An APP-NAME.entitlements file will be added to your project.

screenshot of remote notifications BackgroundModes in Info.plist

UserNotifications

iOS 10 introduced the UserNotifications framework that supports the handling, delivery, and modification of local and remote notifications, allows scheduling of local notifications based on time or location, and includes new content types and presentation styles.

Migrating your app to using the UserNotifications framework is required when using Xcode 8. When you migrate, you need to follow the steps below to ensure push tokens are properly registered and push opens are properly tracked.

  1. Update your remote notification registration to the following:

  2. First make sure to import the new UserNotifications framework introduced in iOS10 in whichever file you will ask for push notification permissions.

    Objective-C Swift

    @import UserNotifications;
    
    import UserNotifications
    

    Then request permission to present notifications to the user.

    Objective-C Swift

    if (NSClassFromString(@"UNUserNotificationCenter"))
    {
      UNAuthorizationOptions options = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound |UNAuthorizationOptionAlert);
      [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:options
                                                                          completionHandler:^(BOOL granted, NSError * _Nullable error) {
                                                                            [Localytics didRequestUserNotificationAuthorizationWithOptions:options
                                                                                                                                   granted:granted];
                                                                          }];
    }
    else if ([application respondsToSelector:@selector(registerUserNotificationSettings:)])
    {
      UIUserNotificationType types = (UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound);
      UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
      [application registerUserNotificationSettings:settings];
    }
    else
    {
      [application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
    }
    
    if #available(iOS 10.0, *), objc_getClass("UNUserNotificationCenter") != nil {
        let options: UNAuthorizationOptions = [.alert, .badge, .sound]
        UNUserNotificationCenter.current().requestAuthorization(options: options) { (granted, error) in
            Localytics.didRequestUserNotificationAuthorization(withOptions: options.rawValue, granted: granted)
        }
    } else if #available(iOS 8.0, *) {
        let types: UIUserNotificationType = [.alert, .badge, .sound]
        let settings = UIUserNotificationSettings(types: types, categories: nil)
        application.registerUserNotificationSettings(settings)
    } else {
        let types: UIRemoteNotificationType = [.alert, .badge, .sound]
        application.registerForRemoteNotifications(matching: types)
    }
    
  3. When using the UserNotification framework and using a UNUserNotificationCenterDelegate, you need to notify the Localytics SDK when notifications are opened. Add the following to your userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: delegate method:

    Objective-C Swift

    - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler
    {
      [Localytics didReceiveNotificationResponseWithUserInfo:response.notification.request.content.userInfo];
      completionHandler();
    }
    
    @available(iOS 10.0, *) // if also targeting iOS versions less than 10.0
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        Localytics.didReceiveNotificationResponse(userInfo: response.notification.request.content.userInfo)
        completionHandler()
    }
    

Updating from v3 to v4

v4 of the Localytics iOS SDK includes several public API changes, support for standard events, and the new Places product.

Follow the steps in each section below to make the appropriate adjustments for updating to v4.

Callbacks

v4 only supports a single callback for the AnalyticsDelegate protocol and the MessagingDelegate protocol. Therefore, the API has changed from add to set. If you are using one of these delegates, confirm that you are only setting this listener in one place in your app.

Replace the following method calls:

[Localytics addAnalyticsDelegate:self];
[Localytics addMessagingDelegate:self];

with the set versions as follows.

[Localytics setAnalyticsDelegate:self];
[Localytics setMessagingDelegate:self];

Push messaging

If you were using alternative session management, the method for handling push receives and opens has changed from handlePushNotificationOpened to handleNotification.

Update the following

[Localytics handlePushNotificationOpened:userInfo];

to

[Localytics handleNotification:userInfo];

Options

If you were previously changing the default session timeout interval, use the new setOptions method.

Update the following

[Localytics setSessionTimeoutInterval:30];

to

[Localytics setOptions:@{@"session_timeout": @30}];

If you were previously suppressing the collection of advertising identifiers (IDFA), use the new setOptions method.

Update the following

[Localytics setCollectAdvertisingIdentifier:NO];

to

[Localytics setOptions:@{@"collect_adid": @NO}];

Updating to 3.5+

Customers who integrated using alternative session management and who are using in-app messaging must remove all Localytics calls from the applicationWillResignActive method when upgrading to 3.5.0 so that split screen apps handle in-app messages correctly. If you fail to make this change, split screen apps will dismiss in-app messages when entering or exiting split screen or when changing the split size.

Updating from 3.0 to 3.1+

Use this guide if you have integrated the Localytics library 3.0 into your app and are ready to upgrade to 3.1.

  1. Enable background push. Push reporting will not work well without it.
  2. If you are tracking push, remove application:didReceiveRemoteNotification: if your application uses it and replace it with application:didReceiveRemoteNotification:fetchCompletionHandler:.
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
    {
      [Localytics handlePushNotificationOpened:userInfo];
      completionHandler(UIBackgroundFetchResultNoData);
    }
    
  3. If your integration is manual, and you use application:didReceiveRemoteNotification:fetchCompletionHandler:, be sure to remove the following code from your application delegate's application:didFinishLaunchingWithOptions: method.
    [[LocalyticsSession shared] handleRemoteNotification:[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]];
    
  4. If your integration is manual, and you're using in-app messaging, you must remove all Localytics calls from the applicationWillResignActive method so that your app handles in-app messages correctly.

Updating from v2 to v3

v3 of the Localytics iOS SDK represents a substantial architectural change as well as a simplification of the API.

Most notably, the API forgoes the singleton pattern utilized in v2 and instead makes use of class-level methods. In addition, all methods which were previously part of LocalyticsSession and LocalyticsAmpSession are now available combined under Localytics.

V3 of the Localytics iOS SDK also lays the foundation for upcoming features. Architecture updates allow us to quickly introduce new features without requiring backwards-incompatible changes. The v3 SDK supports

  • background event tagging, enabling you to track events that occur outside a session
  • new Profile APIs, enabling you to increment, decrement, add to set, and remove from set

Changes to the method signatures make the iOS and Android Localytics SDKs almost identical. We've aligned the method signatures on iOS and Android, making it easier to keep your integrations up to date. The new method signatures also simplify development in new languages and alternative platforms.

Profiles data now defaults to an app-level scope. Previously all Profiles data defaulted to an org-level scope.

  1. Do a project-level regular expression replace of \[Localytics(Amp)?Session shared\] with Localytics.
  2. Enable background push. Push reporting will not work well without it.
  3. If you are tracking push, remove application:didReceiveRemoteNotification: if your application uses it and replace it with application:didReceiveRemoteNotification:fetchCompletionHandler:.
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
    {
      [Localytics handlePushNotificationOpened:userInfo];
      completionHandler(UIBackgroundFetchResultNoData);
    }
    
  4. If your integration is manual, and you use application:didReceiveRemoteNotification:fetchCompletionHandler:, be sure to remove the following code from your application delegate's application:didFinishLaunchingWithOptions: method.
    [[LocalyticsSession shared] handleRemoteNotification:[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]];
    
  5. If your integration is manual, and you're using in-app messaging, you must remove all Localytics calls from the applicationWillResignActive method so that your app handles in-app messages correctly.

Legacy SDKs

This section contains a reference for some older Localytics SDK version integrations, APIs, and code snippets.

v3 SDK

Callbacks

Analytics callbacks

These analytics callbacks are useful for setting the value of a custom dimension or profile attribute before a session opens, firing an event at the beginning or end of a session, or taking action in your app based on an auto-tagged campaign performance event in the Localytics SDK. All Localytics analytics callbacks are called on an internal background thread.

  1. In your app delegate header file, ensure that your app delegate conforms to the LLAnalyticsDelegate protocol as follows.
    @interface YourAppDelegate : UIResponder <UIApplicationDelegate, LLAnalyticsDelegate>
    
  2. Modify your app delegate as follows to register the Localytics analytics delegate and implement any of the analytics callbacks you require.
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // ...Localytics initialization code here...
    
        [Localytics addAnalyticsDelegate:self];
    
        // ... rest of didFinishLaunchingWithOptions ...
    
        return YES;
    }
    
    - (void)localyticsSessionWillOpen:(BOOL)isFirst
                            isUpgrade:(BOOL)isUpgrade
                             isResume:(BOOL)isResume
    {
        // ... do something ...
    }
    
    - (void)localyticsSessionDidOpen:(BOOL)isFirst
                           isUpgrade:(BOOL)isUpgrade
                            isResume:(BOOL)isResume
    {
        // ... do something ...
    }
    
    - (void)localyticsDidTagEvent:(NSString *)
                       attributes:(NSDictionary *)attributes
            customerValueIncrease:(NSNumber *)customerValueIncrease
    {
        // ... do something ...
    }
    
    - (void)localyticsSessionWillClose
    {
        // ... do something ...
    }
    
Messaging callbacks

Messaging callbacks are useful for understanding when Localytics in-app messages are about to appear to prevent conflicts with other views in your app. All Localytics messaging callbacks are called on the main thread.

  1. In your app delegate header file, ensure that your app delegate conforms to the LLMessagingDelegate protocol as follows.
    @interface YourAppDelegate : UIResponder <UIApplicationDelegate, LLMessagingDelegate>
    
  2. Modify your app delegate as follows to register the Localytics messaging delegate and implement any of the analytics callbacks you require.
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // ...Localytics initialization code here...
    
        [Localytics addMessagingDelegate:self];
    
        // ... rest of didFinishLaunchingWithOptions ...
    
        return YES;
    }
    
    - (void)localyticsWillDisplayInAppMessage
    {
        // ... do something ...
    }
    
    - (void)localyticsDidDisplayInAppMessage
    {
        // ... do something ...
    }
    
    - (void)localyticsWillDismissInAppMessage
    {
        // ... do something ...
    }
    
    - (void)localyticsDidDismissInAppMessage
    {
        // ... do something ...
    }