In-app events

Learn how to work with in-app events in the iOS SDK.

Overview

This document is a guide on implementing in-app events in the iOS SDK.

In-app events provide insights into how users interact with your app. The AppsFlyer SDK lets you log these interactions.

Anatomy of an event

In-app events consist of two parts:

  • Event name: the unique event identifier. It's usually how marketers see the event in the dashboard.
  • Event values: an object made up of key-value pairs called event parameters, which provide additional context about the event.

Event names and parameters can be predefined or custom.

👍

Tip

Quickly define and generate in-app events code for all major platforms using the in-app event generator tool.

Predefined events and parameters are exposed as SDK constants (see Event constants below), prefixed with af_. Use the constants instead of raw strings: it avoids naming mistakes, and changes to the underlying names stay transparent to you.

Custom event names and parameters are user-defined and describe scenarios specific to your app.

🚧

Attention

To avoid confusion with predefined events, don't prefix custom event names with af_.

Valid custom event names: up to 100 characters, non-English characters supported.

Valid custom event parameters: up to 1000 characters (longer values may be truncated). Pricing and revenue values should use digits and decimals only (e.g. 5 or 5.2), up to 5 digits after the decimal point.

Before you begin

Integrate In-App Events with our SDK wizard

Logging in-app events

The SDK lets you log user actions happening in the context of your app. These are commonly referred to as in-app events.

The logEvent method

The logEvent method lets you log in-app events and send them to AppsFlyer for processing.

AppsFlyerLib exposes logEvent, predefined event name constants and predefined event parameter constants.

logEvent takes 3 arguments:

- (void)logEventWithEventName:(NSString *)eventName
        eventValues:(NSDictionary<NSString * , id> * _Nullable)eventValues
        completionHandler:(void (^ _Nullable)(NSDictionary<NSString *, id> * _Nullable dictionary, NSError * _Nullable error))completionHandler;
  • The first argument (eventName) is the event name
  • The second argument (eventValues) is the event parameters NSDictionary
  • The third argument (completionHandler) is an optional completion handler (useful for Handling event submission success/failure)
📘

Note

eventValues (second argument) must be a valid NSDictionary. For more information, see Foundation JSONSerialization.

Example: Send "add to wishlist" event

For example, to log that a user added an item to their wishlist:

[[AppsFlyerLib shared]  logEvent: AFEventAddToWishlist withValues: @{
    AFEventParamPrice: @20,
    AFEventParamContentId: @"123456"
}]
AppsFlyerLib.shared().logEvent(AFEventAddToWishlist,
  withValues: [
     AFEventParamPrice: 20,
     AFEventParamContentId: "123456"
]);

In the above logEvent invocation:

Understanding event structure definitions

Before implementing an event, it helps to have a clear event structure definition. For example, a definition for an af_content_view event on an eCommerce app:

Event nameEvent parametersParameter valuesWhere/When
af_content_viewaf_price, af_content_type, af_content_idaf_price: item price. af_content_type: item category. af_content_id: item SKU.When a user navigates to an item view.
  • The event name is the value you pass as the event name argument to logEvent.
  • The event parameters list what to pass alongside it.
  • Parameter values clarify what each parameter should contain.
  • Where/When describes the trigger point in your app.

Implementing in-app event definitions

Based on the example definition above, the event should be implemented as follows:

[[AppsFlyerLib shared]  logEvent: AFEventContentView withValues: @{
    AFEventParamContentId: <ITEM_SKU>,
    AFEventParamContentType: <ITEM_TYPE>,
    AFEventParamPrice: <ITEM_PRICE>
}]
AppsFlyerLib.shared().logEvent(AFEventAddToCart,
  withValues: [
    AFEventParamContent: <ITEM_NAME>
    AFEventParamContentId: <ITEM_SKU>
    AFEventParamPrice: <ITEM_PRICE>
]);

Handling event submission success and failure

You can pass a completionHandler to logEvent when recording in-app events. The handler allows you to define logic for two scenarios:

  • An in-app event recorded successfully
  • An error occurred when recording the in-app event
[[AppsFlyerLib shared] logEventWithEventName:AFEventPurchase
        eventValues: @{
          AFEventParamRevenue: @200,
          AFEventParamCurrency: @"USD",
          AFEventParamQuantity: @2,
          AFEventParamContentId: @"092",
          AFEventParamReceiptId: @"9277"
        }
        completionHandler:^(NSDictionary<NSString *,id> * _Nullable dictionary, NSError * _Nullable error){
            if(dictionary != nil) {
                NSLog(@"In app callback success:");
                for(id key in dictionary){
                    NSLog(@"Callback response: key=%@ value=%@", key, [dictionary objectForKey:key]);
                }
            }
            if(error != nil) {
                NSLog(@"In app callback error:", error);
            }
    }];
AppsFlyerLib.shared().logEvent(name: AFEventAddToWishlist,
          values: [
             AFEventParamPrice: 20,
             AFEventParamContentId: "123456"
          ],
          completionHandler: { (response: [String : Any]?, error: Error?) in
            if let response = response {
              print("In app event callback Success: ", response)
            }
            if let error = error {
              print("In app event callback ERROR:", error)
            }
          });

In the event that an error occurs when recording the in-app event, an error code and string description are provided, as indicated in the table that follows.

Error codeDescription (NSError)
10"Event timeout. Check 'minTimeBetweenSessions' param"
11"Skipping event because 'isStopTracking' enabled"
40Network error: Error description comes from iOS
41"No dev key"
50"Status code failure" + actual response code from the server

Recording offline events

The SDK can cache in-app events that occur when no internet connection is available:

  • It sends events to AppsFlyer and waits for a response.
  • If it doesn't receive a 200 response, the events are cached.
  • Once the next 200 response arrives, cached events are re-sent, one network request per event (unbatched).
  • The cache holds up to 40 events. Anything beyond that, until the next successful response, is discarded.

Logging events before calling start

If you initialized the SDK but didn't call start, the SDK will cache in-app events until start is invoked.

If there are multiple events in the cache, they are sent to the server one after another (unbatched, one network request per event).

📘

Note

If the SDK is initialized, logEvent invocations will call SKAdNetwork's updateConversionValue even if start wasn't called or isStopped is set to true (in both SDK and S2S modes).

Logging revenue

📘

Note

For events with revenue, including in-app purchases, subscriptions, and ad revenue events, AppsFlyer customers with an ROI360 subscription should avoid using the AFEventParameterRevenue(af_revenue) parameter in their in-app events. Doing so can result in duplicate revenue being reported. Instead, they should utilize the purchase connector and the ad revenue SDK API.

af_revenue is the only event parameter that AppsFlyer counts as real revenue in the dashboard and reports.
You can send revenue with any in-app event. Use the AFEventParameterRevenue constant to include revenue in the in-app event. You can populate it with any numeric value, positive or negative.

The revenue value should not contain comma separators, currency signs, or text. A revenue event should be similar to 1234.56, for example.

Example: Purchase event with revenue

[[AppsFlyerLib shared] logEvent: AFEventPurchase 
withValues:@{
	AFEventParamContentId:@"1234567",
	AFEventParamContentType : @"category_a",
	AFEventParamRevenue: @200,
	AFEventParamCurrency:@"USD"
}];
AppsFlyerLib.shared().logEvent(AFEventPurchase, 
withValues: [
	AFEventParamContentId:"1234567",
	AFEventParamContentType : "category_a",
	AFEventParamRevenue: 200,
	AFEventParamCurrency:"USD"
]);
📘

Note

Do not add currency symbols to the revenue value.

Configuring revenue currency

You can set the currency code for an event's revenue by using the af_currency predefined event parameter:

[[AppsFlyerLib shared] logEvent: AFEventPurchase
withValues:@{
	AFEventParamRevenue: @200,
	AFEventParamCurrency:@"USD"
}];
AppsFlyerLib.shared().logEvent(AFEventPurchase, 
withValues: [
	AFEventParamRevenue: 200,
	AFEventParamCurrency:"USD"
]);
  • The currency code should be a 3 character ISO 4217 code
  • The default currency is USD

To learn about currency settings, display, and currency conversion, see our guide on revenue currency.

Logging net revenue

To report net revenue alongside gross revenue, add a custom parameter with the key af_net_revenue to the event values. The value represents net revenue according to your own business logic, for example revenue after store fees and/or taxes, gross margin, or revenue after service fees.

There is no predefined SDK constant for this parameter, so pass the string key directly. Follow the same formatting rules as af_revenue, and the same currency rules (currency via af_currency, default USD).

[[AppsFlyerLib shared] logEvent: AFEventPurchase
withValues:@{
	AFEventParamRevenue: @200,
	@"af_net_revenue": @140,
	AFEventParamCurrency: @"USD"
}];
AppsFlyerLib.shared().logEvent(AFEventPurchase,
withValues: [
	AFEventParamRevenue: 200,
	"af_net_revenue": 140,
	AFEventParamCurrency: "USD"
]);
📘

Note

For apps with ROI360 store revenue enabled, af_net_revenue is populated automatically on the in-app purchase and subscription events recorded by ROI360 store revenue, so you do not need to send it for those events.

Logging negative revenue

There may be situations where you want to record negative revenue. For example, a user receives a refund or cancels a subscription.

To log negative revenue:

[[AppsFlyerLib shared] logEvent: @"cancel_purchase" 
withValues:@{
	AFEventParamContentId:@"1234567",
	AFEventParamContentType : @"category_a",
	AFEventParamRevenue: @-1.99,
	AFEventParamCurrency:@"USD"
}];
AppsFlyerLib.shared().logEvent("cancel_purchase", 
withValues: [
	AFEventParamContentId:"1234567",
	AFEventParamContentType : "category_a",
	AFEventParamRevenue: -1.99,
	AFEventParamCurrency:"USD"
]);

Notice the following in the code above:

  • The revenue value is preceded by a minus sign
  • The event name is a custom event called cancel_purchase - that's how the marketer identifies negative revenue events in the dashboard and raw-data reports

Validating purchases

AppsFlyer provides server verification for in-app purchases. For more information see Validate and log purchase

Event constants

Predefined event names

Predefined event name constants follow a AFEventEventName naming convention. For example, AFEventAddToCart.

Event nameiOS constant name
"af_level_achieved"
AFEventLevelAchieved
"af_add_payment_info"
AFEventAddPaymentInfo
"af_add_to_cart"
AFEventAddToCart
"af_add_to_wishlist"
AFEventAddToWishlist
"af_complete_registration"
AFEventCompleteRegistration
"af_tutorial_completion"
AFEventTutorial_completion
"af_initiated_checkout"
AFEventInitiatedCheckout
"af_purchase"
AFEventPurchase
"af_rate"
AFEventRate
AFEventSearch
"af_spent_credits"
AFEventSpentCredits
"af_achievement_unlocked"
AFEventAchievementUnlocked
"af_content_view"
AFEventContentView
"af_list_view"
AFEventListView
"af_travel_booking"
AFEventTravelBooking
"af_share"
AFEventShare
"af_invite"
AFEventInvite
"af_login"
AFEventLogin
"af_re_engage"
AFEventReEngage
"af_update"
AFEventUpdate
"af_opened_from_push_notification"
AFEventOpenedFromPushNotification
"af_location_coordinates"
AFEventLocation
"af_customer_segment"
AFEventCustomerSegment
"af_subscribe"
AFEventSubscribe
"af_start_trial"
AFEventStartTrial
"af_ad_click"
AFEventAdClick
"af_ad_view"
AFEventAdView

Predefined event parameters

Predefined event parameter constants follow a AFEventParamParameterName naming convention. For example, AFEventParamRevenue.

Event parameter nameiOS constant nameType
"af_content"
AFEventParamContentString
"af_achievement_id"
AFEventParamAchievementIdString
"af_level"
AFEventParamLevelString
"af_score"
AFEventParamScoreString
"af_success"
AFEventParamSuccessString
"af_price"
AFEventParamPricefloat
"af_content_type"
AFEventParamContentTypeString
"af_content_id"
AFEventParamContentIdString
"af_content_list"
AFEventParamContentListString[]
"af_currency"
AFEventParamCurrencyString
"af_quantity"
AFEventParamQuantityint
"af_registration_method"
AFEventParamRegistrationMethodString
"af_payment_info_available"
AFEventParamPaymentInfoAvailableString
"af_max_rating_value"
AFEventParamMaxRatingValueString
"af_rating_value"
AFEventParamRatingValueString
"af_search_string"
AFEventParamSearchStringString
"af_date_a"
AFEventParamDateAString
"af_date_b"
AFEventParamDateBString
"af_destination_a"
AFEventParamDestinationAString
"af_destination_b"
AFEventParamDestinationBString
"af_description"
AFEventParamDescriptionString
"af_class"
AFEventParamClassString
"af_event_start"
AFEventParamEventStartString
"af_event_end"
AFEventParamEventEndString
"af_lat"
AFEventParamLatString
"af_long"
AFEventParamLongString
"af_customer_user_id"
AFEventParamCustomerUserIdString
"af_validated"
AFEventParamValidatedboolean
"af_revenue"
AFEventParamRevenuefloat
"af_projected_revenue"
AFEventProjectedParamRevenuefloat
"af_receipt_id"
AFEventParamReceiptIdString
"af_tutorial_id"
AFEventParamTutorialIdString
"af_virtual_currency_name"
AFEventParamVirtualCurrencyName
AFEventParamDeepLinkString
"af_old_version"
AFEventParamOldVersionString
"af_new_version"
AFEventParamNewVersionString
"af_review_text"
AFEventParamReviewTextString
"af_coupon_code"
AFEventParamCouponCodeString
"af_order_id"
AFEventParamOrderIdString
"af_param_1"
AFEventParam1String
"af_param_2"
AFEventParam2String
"af_param_3"
AFEventParam3String
"af_param_4"
AFEventParam4String
"af_param_5"
AFEventParam5String
"af_param_6"
AFEventParam6String
"af_param_7"
AFEventParam7String
"af_param_8"
AFEventParam8String
"af_param_9"
AFEventParam9String
"af_param_10"
AFEventParam10String
"af_departing_departure_date"
AFEventParamDepartingDepartureDateString
"af_returning_departure_date"
AFEventParamReturningDepartureDateString
"af_destination_list"
AFEventParamDestinationListString[]
"af_city"
AFEventParamCityString
"af_region"
AFEventParamRegionString
"af_country"
AFEventParamCountryString
"af_departing_arrival_date"
AFEventParamDepartingArrivalDateString
"af_returning_arrival_date"
AFEventParamReturningArrivalDateString
"af_suggested_destinations"
AFEventParamSuggestedDestinationsString[]
"af_travel_start"
AFEventParamTravelStartString
"af_travel_end"
AFEventParamTravelEndString
"af_num_adults"
AFEventParamNumAdultsString
"af_num_children"
AFEventParamNumChildrenString
"af_num_infants"
AFEventParamNumInfantsString
"af_suggested_hotels"
AFEventParamSuggestedHotelsString[]
"af_user_score"
AFEventParamUserScoreString
"af_hotel_score"
AFEventParamHotelScoreString
"af_purchase_currency"
AFEventParamPurchaseCurrencyString
"af_preferred_neighborhoods"
AFEventParamPreferredNeighborhoods //array of stringString[]
"af_preferred_num_stops"
AFEventParamPreferredNumStopsString
"af_adrev_ad_type"
AFEventParamAdRevenueAdTypeString
"af_adrev_network_name"
AFEventParamAdRevenueNetworkNameString
"af_adrev_placement_id"
AFEventParamAdRevenuePlacementIdString
"af_adrev_ad_size"
AFEventParamAdRevenueAdSizeString
"af_adrev_mediated_network_name"
AFEventParamAdRevenueMediatedNetworkNameString
"af_preferred_price_range"
AFEventParamPreferredPriceRangeint[] - basically a tuple(min,max) but we'll use array of int and use two values
"af_preferred_star_ratings"
AFEventParamPreferredStarRatingsint[] - basically a tuple(min,max) but we'll use array of int and use two values

Did this page help you?