How do I correctly configure Segment, Mixpanel and Branch for a React Native app? - react-native

After several days of trying different approaches, we am running out of options. Our aim is to have a Reach Native app, which can be opened/installed using a Branch link. Data of the usage of the user and how they came to be in the app (attribution) is sent eventually to Mixpanel. As we have several other places we would like to analyse usage data, and we would like to keep platform coupling weak, we opted to use Segment.
The key challenge is getting the attribution data has determined by Branch to be applied to the same distinct ID in Mixpanel as the general app usage.
E.g. the event for "Viewed article 123" is assigned to user ABC, while the event from the same person, on the same phone, during the same session, saying "App opened via QR code" is assigned to user DEF. (Those event names are just illustrative and context is actually in the metadata.)
So far I have tried a React Native led setup using
import analytics from '#segment/analytics-react-native';
import branch from '#segment/analytics-react-native-branch';
import mixpanel from '#segment/analytics-react-native-mixpanel';
analytics.setup(WRITE_KEY, {
using: [branch, mixpanel]
});
We have also tried a more native approach where Segment and Branch are initialized and share ids within MainApplication.java
Analytics analytics = new Analytics.Builder(this, WRITE_KEY).trackApplicationLifecycleEvents().recordScreenViews().build();
Analytics.setSingletonInstance(analytics);
Branch.getInstance().setRequestMetadata(
"$mixpanel_distinct_id",
Analytics.with(this).getAnalyticsContext().traits().anonymousId()
);
Branch.getInstance().setRequestMetadata(
"$segment_anonymous_id",
Analytics.with(this).getAnalyticsContext().traits().anonymousId()
);
We also tried another version where Mixpanel was also initialized in MainApplication.java and the distinct Id passed from there.
MixpanelAPI mp = MixpanelAPI.getInstance(this, MP_KEY);
Branch.getInstance().setRequestMetadata("$mixpanel_distinct_id", mp.getDistinctId());
While experimenting with these native setups (in several different permutations) we were calling Segments useNativeConfiguration method.
With records to the actual cloud routing, we have also tried every reasonable setup we can imagine. Including:
Segment -> MP AND Branch -> MP
Segment -> MP AND Segment -> Branch -> MP
Segment -> MP AND Segment <-> Branch -> MP (notice Branch is both importing and exporting Segment data)
Segment -> Branch -> MP
We have tried many different permutations of the possible configurations and none have created correctly joined-up data. We are open to replacing Segment or Branch with an alternative, but Mixpanel and React Native cannot be replaced due to business constraints.
The latest cloud configuration of "Segment -> Branch -> MP", showed the greatest promise, but even though the documentation says Identify calls are passed to Branch, upon debugging they are not. Meaning the profile of users can never be populated in Mixpanel.
Any help that can be provided would be greatly appreciated.

Ok, we think we have got this working in an acceptable way with the aforementioned technologies.
The setup we ended up with, was to initialise Analytics (Segment) and Branch at the native level. Set both "$mixpanel_distinct_id" and "$segment_anonymous_id" to be the Segment anonymous ID for different stages of the pipeline. And to break the connections between Segment and Branch. So in the end we had the two following paths:
App -> Segment -> Mixpanel
App -> Branch -> Mixpanel
As only events from Branch (prefixed [BRANCH]) in Mixpanel would have the user's attribution, we then set up a Lambda function to read these events and then call the Mixpanel API to set user properties for UTM medium, campaign and channel. To get this connected we had to reconnect Branch to export events to Segment, using an entirely separate source, to then send on to Lambda as a destination. Something like:
Branch -> Lambda -> Mixpanel
Code snippets:
// MainApplication.java
this.initializeSegment();
RNBranchModule.getAutoInstance(this);
RNBranchModule.setRequestMetadata("$segment_anonymous_id",
Analytics.with(this).getAnalyticsContext().traits().anonymousId()
);
RNBranchModule.setRequestMetadata("$mixpanel_distinct_id",
Analytics.with(this).getAnalyticsContext().traits().anonymousId()
);
private void initializeSegment() {
Analytics.Builder builder = new Analytics.Builder(this, BuildConfig.SEGMENT_WRITE_KEY)
.flushQueueSize(20)
.collectDeviceId(true)
.trackApplicationLifecycleEvents();
if (BuildConfig.DEBUG) {
builder.logLevel(Analytics.LogLevel.VERBOSE);
}
Analytics analytics = builder.build();
Analytics.setSingletonInstance(analytics);
}
import analytics from '#segment/analytics-react-native';
analytics.useNativeConfiguration();

Related

Kotlin for Volley, how can I check the JSON request for newer data in the API?

I'm working on an app that gets a list of documents/source URL from an api. I'd like to periodically check for new or updated contents within that API so users can update saved items in the database. I'm at a loss on the correct wording to search, thus Google and Stack Overflow have both failed me. My fetching function is below:
The URL for the API is https://api.afiexplorer.com
private fun fetchPubs() {
_binding.contentMain.loading.visibility = View.VISIBLE
request = JsonArrayRequest(
Request.Method.GET,
Config.BASE_URL,
JSONArray(),{ response ->
val items: List<Pubs> =
Gson().fromJson(response.toString(), object : TypeToken<List<Pubs>>() {}.type)
val sortedItems = items.sortedWith(compareBy { it.Number })
pubsList?.clear()
pubsList?.addAll(sortedItems)
// Hardcoded pubs moved to Publications Gitlab Repo
// https://gitlab.com/afi-explorer/pubs
_binding.contentMain.recyclerView.recycledViewPool.clear()
adapter?.notifyDataSetChanged()
_binding.contentMain.loading.visibility = View.GONE
setupData()
Log.i("LENGTH OF DATA", "${items.size}")
},
{error ->
println(error.printStackTrace())
Toasty.error(applicationContext, getString(string.no_internet), Toast.LENGTH_SHORT, true).show()
}
)
MyApplication.instance.addToRequestQueue(request!!)
}
private fun setupData(){
adapter = MainAdapter(applicationContext, pubsList!!, this)
_binding.contentMain.recyclerView.adapter = adapter
}
I tried using ChatGPT to see if that would get me started and that failed miserably. Also searched Google, Reddit and Stack Overflow for similar projects, but mine is a unique scenario I guess. I'm just a hobbyist and intermediate dev I guess. First time working with Volley, everything works, but I would like to find a way to send a notification (preferably not Firebase) if there is updated info within the API listed above. I'm not sure if this is actually doable.
Are you asking if you can somehow find if the remote API has changed its content? If so, how would that service advise you? If the service provider provides a web hook or similar callback you could write a server-based program to send a push notification to your Android app.
Perhaps you intent to poll the API periodically, and then you want to know if there is a change?
If you use a tool such as Postman or curl to easily see the headers of the API https://api.afiexplorer.com you will see, unfortunately, there is no Last-Modified header or ETag header which would allow you to easily determine if there was a change.
Next looking at the content of the API, the author does not provide an obvious version/change date, so no luck there.
What you could do is receive the content as a String, and perform a checksum operation on it, and if it differs you know there has been a change
or if you are deserialising the received JSON in Kotlin data classes, then out of the box, Kotlin will enable you to perform an equality operation on a previous copy of the data to know if there was a change.
This looks like an android app; if so, why don't you create a background service that makes requests to the API and updates the data as needed? You can use an AlarmManager class to set the interval threshold for polling by using the setInexactRepeating() method.
Most apps are updated in this fashion; sometimes, a separate table is created to catalog changesets.
Let me know if this helps.

How to add double tap detection to sample app gatt_sensordata_app?

I am developing an Android app that gets data from Movesense using the GATT profile from the sample app GATT Sensor Data App here.
I followed the tutorial available here. Building the app and getting the DFU worked fine. I can get IMU, HR and temperature data with no issues.
Now I'd like add a tap detection feature to my app. I understand that I have to subscribe to 'System/States', but first I need to be able to receive the system state data.
I understand that I need a modified DFU for that, but I don't understand what changes I should make in which files of the gatt_sensordata_app before rebuilding and generating the new DFU.
What changes should I make in order to broadcast /System/State data?
(I usually just deal with Android so apologies for the very basic question.)
I tried adding #include "system_states/resources.h" to GATTSensorDataClient.cpp but I don't know how to continue.
The normal data straming in the gatt_sensordata_app uses the sbem-encoding code that the build process generates when building the firmware. However /System/States is not among the paths that the code can serialize. Therefore the only possibility is to implement the States-support to the firmware.
Easiest way is to do as follows:
In your python app call data subscription with "/System/States/3" (3 == DOUBLE_TAP)
Add a special case to the switch in onNotify which matches the localResourceId to the WB_RES::LOCAL::SYSTEM_STATES_STATEID::LID
In that handler, return the data the way you want. Easiest is to copy paste the "default" handler but replace the code between getSbemLength() & writeToSbemBuffer(...) calls with your own serialization code
Full disclosure: I work for the Movesense team

Porting PHP API over to Parse

I am a PHP dev looking to port my API over to the Parse platform.
Am I right in thinking that you only need cloud code for complex operations? For example, consider the following methods:
// Simple function to fetch a user by id
function getUser($userid) {
return (SELECT * FROM users WHERE userid=$userid LIMIT 1)
}
// another simple function, fetches all of a user's allergies (by their user id)
function getAllergies($userid) {
return (SELECT * FROM allergies WHERE userid=$userid)
}
// Creates a script (story?) about the user using their user id
// Uses their name and allergies to create the story
function getScript($userid) {
$user = getUser($userid)
$allergies = getAllergies($userid).
return "My name is {$user->getName()}. I am allergic to {$allergies}"
}
Would I need to implement getUser()/getAllergies() endpoints in Cloud Code? Or can I simply use Parse.Query("User")... thus leaving me with only the getScript() endpoint to implement in cloud code?
Cloud code is for computation heavy operations that should not be performed on the client, i.e. handling a large dataset.
It is also for performing beforeSave/afterSave and similar hooks.
In your example, providing you have set up a reasonable data model, none of the operations require cloud code.
Your approach sounds reasonable. I tend to put simply queries that will most likely not change on the client side, but it all depends on your scenario. When developing mobile apps I tend to put a lot of code in cloud code. I've found that it speeds up my development cycle. For example, if someone finds a bug and it's in cloud code, make the fix, run parse deploy, done! The change is available to all mobile environments instantly!!! If that same code is in my mobile app, it really sucks, cause now I have to fix the bug, rebuild, push it to the app store/google play, wait x number of days for it to be approved, have the users download it... you see where I'm going here.
Take for example your
SELECT * FROM allergies WHERE userid=$userid query.
Even though this is a simple query, what if you want to sort it? maybe add some additional filtering?
These are the kinds of things I think of when deciding where to put the code. Hope this helps!
As a side note, I have also found cloud code very handy when needing to add extra security to my apps.

Worklight Direct update

Does anybody know what if direct update updates everything that lives in the common directory structure. I used the same code base for multiple apps, the only change being certain settings within a js file that tells the application how to act. Is there a directory i can put that js file that would be safe from the direct update feature?
I cant seen to find any specific information on IBM's website.
I think you guys need to be careful which terms you are using in order to not confuse people who may be looking for similar help.
Environments are specific to the OS you are using. iOS, Blackberry, Android, and etc. environments.
Skins are based on the environment, and aren't generic to all platform. When you create a skin you must choose which environment you are running in.
So to correct some, direct updates will update all skin resources in targeted environments.
For example: You have an app with Android and iOS versions
When you create skins, you are creating essentially a responsive type of design to your parameters. For instance, if you have a 2.3 vs 4.2 Android OS, you can set a look and feel for both. However, these utilize a single web resource base. The APK would be the same for both versions of the app (by default) and have 2 available skins. On runtime utilizing IBM Worklight's 'Runtime Skinning' (hence the name) it goes through the parameter check for the OS and loads that skins overriding web code.
You could technically override all of the web code to be completely different for both skins, but that would be bulky and inefficient.
When you direct update you are updating all the resources of that particular environment (to include both skins), not the common folder/environment.
So an updated Android (both skins) would have updated web resources (if you deployed the android wlapp) and an iOS version would stay the same.
If you look at the Android project after build (native -> assets -> www -> default or skin) you can find the shared web resources generated by the common environment. However that is only put there every time you do a new build.
In the picture, I have an older version of the Android built for both skins on the left. On the right is a preview of the newer common resources after deploying only the common.wlapp. So you can see that they are separate.
Sorry if it was long winded, but I thought I would be thorough.
To answer the original question, have you thought of having all the parameters of the store loaded from user input or a setup? If you are trying to connect to 3 different store, create some form for settings control that will access different back ends or specific adapters. You could also create 3 different config.js that load depending on the parameters that you set so that you set. The other option is to set different versions of your apps specific to the store.
Example. Version 1.11, 1.12,1.13 can be 3 versions of the same app for store 1, 2, & 3. They can be modified and change and have 3 sets of web resources. When you need to update, jump up to version 1.21, 1.22,1.23. It seems a bit of a work around, but it may be your best bet at getting 3 versions of the same app to fall within the single application category. (keep 3 config.js types for modifying for the 3 stores).
To the best of my knowledge Direct Update will update every web resource of the skin you're using (html, css, js). However, I'm no expert with it.
If you're supporting only Android and iOS applications and need a way to store settings I recommend JSONStore. Otherwise look into Cordova Storage, Local Storage or IndexedDB.
Using a JSONStore collection called settings will allow you to store data on disk inside the app's directory. It will persist until you call one of the removal methods like destroy or until the application is uninstalled. There are also ways of linking collections to Worklight Adapters to pull/push data from/to a server. The links below will provide further details.
the only change being certain settings within a js
Create a collection for your settings:
var options = {};
options.onSuccess = function () {
//... what to do after init finished
};
options.onFailure = function () {
//... what to do if init fails
}
var settings = WL.JSONStore.initCollection('settings',
{background: 'string', itemsPerPage: 'number'}, options);
You can add new settings after initCollection onSuccess has been called:
settings.add({background: 'red', itemsPerPage: 20}, options);
You can find settings stored after initCollection onSuccess has been called:
settings.findAll({onSuccess: function (results) {
console.log(JSON.stringify(results));
}});
You can read more about JSONStore in the Getting Started Modules. See Modules: 7.9, 7.10, 7.11, 7.12. There is further information inside the API Documentation in IBM InfoCenter. The methods described above are: initCollection, add and findAll.
Since version 5.0.3 I think, direct update will not update all the webresources, only those of the skin you are using.
say you have skin def and skin skin2
you are on def
make change to def on the server -> you will get a direct update for
def only
make change to skin2 on server-> no direct update for you.
you are on skin2:
make change to skin2 on server -> direct update for skin2 only
make change to def javascript which also resides on skin2 ( and therefor end result is def+skin2 concatination), update only skin2
make change to def,just to a picture(also checking pic extension from application-descriptor: ") -> no direct update
Thats how direct update works.
Please also share some more details about what is the problem, I see you use a js file, where do you change it? what do you mean excatly, give a better (simplified) real life example, because it is unclear what you are trying.

How to store app-wide variables when using CommonJS?

I am developing apps using Titanium and trying to implement the CommonJS approach. I like the modular setup, but I am wondering how best to deal with things like a shopping cart: temporary, user-created data that needs to last through the lifetime of the app.
I can see three approaches:
1. Create a special module for such a Cart. It will be created the first time it's require()d, and you can access the cart in its current state from any other module by require()ing it from those modules.
Pass a quasi global Cart object to every module that needs it. This is in breach of the letter and the spirit of CommonJS.
Store the Cart in local memory using Ti.App.Properties. This way, the cart is retained even when the user quits the app.
Any thoughts on what would be best?
The solution I'd prefer is to create a CommonJS module the following way:
function ShoppingCart(options) {
// do some setup for the shopping cart
}
ShoppingCart.prototype.add(product, qty)
// add product to cart
}
ShoppingCart.prototype.remove(product, qty)
// remove product from cart
}
ShoppingCart.prototype.clear()
// empty cart (and create new, empty one)
}
// etc.
ShoppingCart = new ShoppingCart();
module.exports = ShoppingCart;
How to access?
var Cart = require('path/to/ShoppingCart');
Cart.add();
Cart.remove();
Cart.clear();
This creates a kind of singleton which is created the first time you're calling it and it is kept until the app is finished (removed from memory) or you implement the clear method and clean it by yourself. You can also persist the data using this singleton, it's up to you which parts you gonna implement. It's similar to your first idea.
Your second idea has several disadvantages because data access isn't encapsulated into a module and data is persisted always so you need to detect if it is old and can be removed or not.
Finally it depends on your task. Do you need persisten storage you should combine the module with a database. Do you need this information only during runtime, the module is enough.