analytics.js icon indicating copy to clipboard operation
analytics.js copied to clipboard

analytics.js traits behave differently to Analytics for Android

Open richardscarrott opened this issue 4 years ago • 2 comments

I'm designing our segment.io web integration and in the process I've found that analytics.js behaves differently to Analytics for Android with regards to traits.

The Android Identify documentation says:

Additionally, traits are sent in the context.traits field with every message.

For example:

window.analytics.indentify({
  first_name: 'Barry',
  last_name: 'From Eastenders'
});

// Sends
{
  // ...
  "type": "identify",
  "context": {
     // ...
  },
  "traits": {
    "first_name": "Barry",
    "last_name": "From Eastenders"
  }
}

Then a subsequent call to track or page:

window.analytics.track('Button Clicked', { button_name: 'Support' });

// Sends
{
  // ...
  "type": "track",
  "context": {
    // ...
    // Analytics for Android includes the traits from our previous identify call here, but analytics.js does not
    // "traits": {
    //    "first_name": "Barry",
    //    "last_name": "From Eastenders"
    // }
   },
  "properties": {
    "button_name": "Support"
  }
}

I'm not sure what iOS Analytics does or if the SDKs differ by design but I think it's a feature we'd like.

I have implemented it via a middleware, but before I went ahead with this it would be good to get some feed back re: why it's not done by default / whether it's a good or bad practice?

window.analytics.addSourceMiddleware(({ payload, next }) => {
  const traits = window.analytics.user().traits();
  payload.obj.context.traits = {
    ...traits,
    ...payload.obj.context.traits
  };
  next(payload);
});

richardscarrott avatar Jan 29 '21 21:01 richardscarrott

I'm facing the same issue. Analytics iOS does do that. Analytics.JS is the only one falling out of the bucket here

pbassut avatar Mar 27 '21 12:03 pbassut

@pbassut we're using the following middleware in production which seems to be working ok.

export const contextTraitsMiddleware = () =>
  createMiddleware(({ payload, next }) => {
    const traits = window.analytics.user().traits();
    switch (payload.action()) {
      case 'page':
      case 'track':
        payload.obj.context.traits = {
          ...traits,
          ...payload.obj.context.traits
        };
        break;
    }
    next(payload);
  });

// ...
  
window.analytics.addSourceMiddleware(contextTraitsMiddleware());
const createMiddleware = (
  fn: (input: SegmentAnalytics.SourceMiddlewareInput) => void
) => (input: Parameters<typeof fn>[0]) => {
  // Wait until analytics is ready so we can access the full API (e.g. window.analytics.user().traits())
  window.analytics.ready(() => {
    try {
      fn(input);
    } catch (ex) {
      // Report errors to our servers (and the console) as segment appears to swallow them (perhaps they report it to their own servers?)
      reportError(ex);
      throw ex;
    }
  });
};

richardscarrott avatar Mar 31 '21 08:03 richardscarrott