Adobe Experience Platform Mobile SDKs – Using Launch as Tag Manager (almost) like on the web

[screenshot]

This article is part of the Adobe Experience Platform Mobile SDKs mini-series. It is about what you can do in Launch versus coding in the app.

You can find the overview here.

The one thing a lot of people ask is this: if the AEP Mobile SDKs use Launch, does that mean I can use Rules for my tracking? Or do I still have to tell my developers to implement?

This is the post that tries to partially answer that question. This is what you’ve been waiting for!

Most of it has been written by my colleague Ben Wedenik, but first, an introduction.

Tag Management for Mobile tracking

The functionality of the AEP Mobile SDKs and the Analytics Extension is so that the trackState() and trackAction() calls behave like they did on the legacy Mobile Services SDKs: call them, and they’ll make the app send a tracking call to Analytics.

This is how it works, out of the box.

You still have Launch in the picture, though, and Launch comes with the handy concept of Extensions.

So, the idea is to write an Extension that provides a call that will trigger a Rule, transport some data, but – crucially – not trigger an Analytics call.

Your app developers can then use that new call, and you can create Rules in Launch which can decide, when and what to track.

How cool is that?

But is it possible? Well, that’s what Ben tried and managed to do.

He built two Extensions, one mobile, the other a standard web Extension. The former provides code that is compiled into the app, while the latter provides the UI that people can see in Launch when they use the Event Type.

The former is added to your app using gradle, while the latter is added in Launch itself, as you add all Extensions.

With that out of the way, here is what Ben wrote:

Configuring Analytics tracking in Launch?

When I started using the new Adobe Launch Experience Platform Mobile SDK (yes that’s a long name), I was euphoric because finally you had the abilities of a modern Tag Manager combined with the strengths of a native SDK.

In the web, you basically control all the tracking behavior from the Tag Manager, which produces a single JavaScript file, which is either hosted directly by Adobe (via Akamai) or self-hosted. As you update your tracking rules, you publish a new version of the library, replacing the old one. This means, every time a visitor browses your website, the latest version will be used. So far so good.

In a mobile App, you don’t have this feature. Everything you want to serve is pre-compiled. This includes your tracking setup.

Basically, you have two important methods you have to implement in your app.

  1. trackState() – used when the user navigates to a new state / screen, and
  2. trackAction() – should be called whenever a user performs an action you want to track

In the web-interface of Adobe Launch you can use the mobile property to create rules (like you are used to with web-properties).

That sounds easy, right? So why not just call trackState and trackAction as often as possible? We could still decide in Launch which of the calls should produce an Adobe Analytics tracking call and define which parameters / context data we want to use. Sounds too good to be true? Well it is…

I had to realize that trackState and trackAction produce an Analytics tracking call no matter what rules you define in Launch. You can add further tracking requests but cannot change or cancel them. That is simply the way Launch and the Analytics Extension are built.

So, I thought to myself “why live with that limitation, when I can simply write a mobile Launch extension which gives me exactly what I need!”

DISCLAIMER: This is not a step-by-step tutorial on how to build a mobile or web extension. I assume you are familiar with Launch and some Java for Android development.

Building an Event Type Extension for Mobile

What do we need?

We need equivalents of those two methods, trackState and trackAction, which should only trigger an event containing all the context data in Launch but not a tracking call per-se.

For the sake of simplicity, let’s implement a single method triggerEvent.

You basically need to implement two things:

  1. A mobile Extension for the Launch web-interface, and
  2. a mobile native Extension for the SDK itself (Android / iOS)

As I’m not really an iOS person, I only implemented this on Android

This is what I needed to do in order to create and use my native Extension:

  1. Create a class which extends com.adobe.marketing.mobile.Extension
  2. Define a custom type
  3. Define a custom source
  4. Register the Extension on application start
  5. Call the new extension method triggerEvent
package com.wedenik.launchsdk1;
import android.util.Log;
import com.adobe.marketing.mobile.*;
import java.util.Map;

public class DemoExtension extends Extension {
    /**
     * Registers the extension with the Mobile SDK. This method should be called once.     
     * */
     static void registerExtension() {
        MobileCore.registerExtension(DemoExtension.class, new ExtensionErrorCallback<ExtensionError>() {
            @Override
            public void error(ExtensionError extensionError) {
                MobileCore.log(LoggingMode.ERROR, "Test Application!", "There was an error registering the SkeletonExtension: " + extensionError.getErrorName());
            }
        });
    }
    protected DemoExtension(ExtensionApi extensionApi) {
        super(extensionApi);
    }
    @Override
    protected String getName() {
        return "com.wedenik.launchsdk1.DemoExtension";
    }
    static void triggerEvent(Map<String, Object> contextData) {
        // The custom Type and Source are crucial here!
        // Of course you could also pass those values as parameters.
        Event demoEvent = new Event.Builder("DemoEvent","com.wedenik.launchsdk1.customType","com.wedenik.launchsdk1.customSource")
            .setEventData(contextData).build();
        ExtensionErrorCallback<ExtensionError> errorCallback = new ExtensionErrorCallback<ExtensionError>() {
            @Override
            public void error(final ExtensionError extensionError) {
                Log.e("triggerEvent", String.format("An error occurred while dispatchingthe event: %d %s", extensionError.getErrorCode(), extensionError.getErrorName()));
            }
        };
        MobileCore.dispatchEvent(demoEvent, errorCallback);
    }
}

Let’s have a look on how to initialize and register the extension.

In the main application we use the onCreate method:

public class MainApplication extends Application {
    // You get this value from the Launch interface under environments
    private final String LAUNCH_ENVIRONMENT_ID = "xxx/yyy/launch-zzz-development";
    @Override
    public void onCreate() {
        super.onCreate();
        MobileCore.setApplication(this);
        MobileCore.setLogLevel(LoggingMode.VERBOSE);
        MobileCore.configureWithAppID(LAUNCH_ENVIRONMENT_ID);
        // register Adobe core extensionstry {
            Analytics.registerExtension();
            Identity.registerExtension();
            Lifecycle.registerExtension();
            Signal.registerExtension();
            UserProfile.registerExtension();
            // there we go!
            DemoExtension.registerExtension();
        } catch (InvalidInitException e) {
            e.printStackTrace();
        }
    }
    // [... more code goes here ...]
}

So far so good.

Now we only have to call our triggerEvent method whenever we want to create a tracking call, providing the context data.

In my demo I simply use a floating button which triggers the event whenever clicked.

FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Map<String, String> contextData = new HashMap<String, String>();
        contextData.put("action", " buttonClicked ");
        contextData.put("pageName", "landingPage");
        contextData.put("buttonName", "floating button");
        contextData.put("buttonID", R.id.fab);
        DemoExtension.triggerEvent(contextData);
    }
});

We are done!

The call to triggerEvent will not issue the tracking call, but only provide us with an event in Launch, based on which we can then define rules to create the Analytics call.

To do so, we further need the web extension mentioned above, to have a custom event type available.

I used the @adobe/reactor-scaffold npm tool to create a scaffold for the mobile extension.

As we want to have a custom Event type available, you may want to add something like this to the extension.json file:

"events": [
    {
        "displayName": "DemoEvent",
        "name": "demoevent",
        "schema": {
            "$schema": "http://json-schema.org/draft-04/schema#",
            "type": "object",
            "allOf": [
                {
                    "$ref": "https://assets.adobedtm.com/activation/reactor/schemas/1.0/extension-definitions-mobile.json#/definitions/eventConditions"
                }
            ]
        },
        "viewPath": "events/demoevent.html"
    }
]

Now comes the interesting part. How does the link between the call to your Java function and the event in the rule work?

This is done via the custom type and custom source.

We defined this in our DemoExtension within the triggerEvent method. The glue is hidden in events/demoevent.html, which defines the view (and JS in this case) of the custom Event in the Adobe Launch web interface:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Event</title>
</head>
<body>
    Nothing to do here

    <script src="https://assets.adobedtm.com/activation/reactor/extensionbridge/extensionbridge.min.js"></script>

    <script>
        window.extensionBridge.register({
            init: function (info) {
                if (info.settings) {
                    // nothing to do
                }
            },
            getSettings: function () {
                return {
                    "conditions": [
                        {
                            "type": "matcher",
                            "definition": {
                                "key": "~type",
                                "matcher": "eq",
                                "values": ["com.wedenik.launchsdk1.customType"]
                            }
                        },
                        {
                            "type": "matcher",
                            "definition": {
                                "key": "~source",
                                "matcher": "eq",
                                "values": ["com.wedenik.launchsdk1.customSource"]
                            }
                        }
                    ]
                }
            },
            validate: function () {
                return true;
            }
        });
    </script>
</body>
</html>

Good job!

Now you can push your extension to Launch and create rules which listen on your custom event.

In the action of that rule, you can issue an Analytics tracking call and use the respective context data.

This approach gives you the full flexibility that you are used to from web-based tracking.

Back over to Jan.

Notes

Nice work, Ben!

There are a couple of open questions:

  • How do I get my mobile Extension code into my app using gradle?
  • Can I use Data Elements to refer to the payload of the triggerEvent call?
  • What would a Rule look like, in detail?
  • Conceptually, how would I add a data layer to my app? Would that be possible?

I’m nothing if not curious, so I’ll follow Ben’s example.

Let’s see how it works in my app.

First, I create the Extension, using npx @adobe/reactor-scaffold. I make extra sure I select it to be a mobile Extension, then fill in all the things I need.

Like Ben, I know nothing about iOS. But I do know Android and Java, so I can answer the related questions the scaffolding tool asks me.

I change the name field in the extension.json file (remember? It’s unique, so always add something specific to you!).

[screenshot]
Always change the name in your Extensions
I copy Ben’s code into the HTML file that has been generated by the scaffolding tool, change the values in the matchers, then package and upload the Extension.

I head over into Launch, add the Extension to my Mobile Property, then create a Rule. For the Event, I select “Simple Trigger”, “Simple Trigger”, simple.

[screenshot]
Simple Trigger Event Type
For the Action, I select the “Analytics” Extension and the “Track” Action Type.

[screenshot]
Specify the State for Analytics Track Action Type
Within the configuration of the latter, I type a “State” name (which will become the pageName, remember), then save all of it, add it to a Library, and build that Library.

In my app code, I change what happens when the “Settings” Activity is called:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_settings);
    // create contextData map
    Map<String,Object> contextData = new HashMap<>();
    // add language = en
    contextData.put("state", "Settings");
    contextData.put("language", "en");
    // MobileCore.trackState("Settings", contextData);
    MobileCore.log(LoggingMode.VERBOSE, "W4DMobile Settings", "Settings calling simpleState...");
    SimpleTriggerExtension.triggerSimpleState("Settings", contextData);
}

Note: I commented the trackState() call and added a triggerSimpleState() call, instead (which is what I called it)

Now when I run the app, I can see an Analytics call go out, and low and behold, the pageName is “Settings via Launch”! WOAH!

[screenshot]
pageName in the log – it’s the one from Launch!
(I even tried changing that a bunch of times, and each time the app picked it up. So cool!)

Very few open questions left, such as:

  • Can i build an Extension with two methods (triggerState() and triggerAction(), maybe)?
  • How is the contextData that I pass into the trigger surfaced in Launch? How do I use it in an Action?
  • Can I build Data Elements based on that?

I’m afraid you’ll have to be patient.

The big, old question is still open, too, and that one I think I can answer:

What can I change in Launch, and when do I need to ship an updated app?

The answer, my friend, is this:

Your app has to contain code, and Launch cannot change that code. But Launch can use code in your app, sort of like rewiring, like an old telephone switch board.

If you provide Event Types, Condition Types, Data Element Types, and Action Types in your app, then with Launch, you can wire them in any way possible, such as using a “Simple Event” Event Type to trigger an “Analytics Track” Action Type.

Does that make sense?

6 thoughts on “Adobe Experience Platform Mobile SDKs – Using Launch as Tag Manager (almost) like on the web

  1. Hi Jan. Thank you for this post, it was just what I was looking for. I would like to know if Adobe Launch team plan to implement this extension soon or if we should implement ir our own.

    From my point of view, it is the natural way of work with Launch and its powerful rule engine, and this extension is generic and reusable enough to be implemented as a core functionality. What is your opinion? Thanks.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.