How to implement Android playstore in-app update with react-native - android

How can I implement play store in-app update with my react-native app, which is already live on play store?
I have already implemented code push and also internal version check via API calls to force the user to update the app.
With Codepush: code push only works for javascript changes. However, there are many scenarios where we will need the whole app to be updated when there is a native code change.
With API check: We need to watch when the update is getting live, and update the version number kept in the backend to force the user to update the app.
Though both these solutions are kind of patchy, I would like to implement the elegant approach of the play-store in-app update, but couldn't find a clue on how to get it done.

I did not find any react-native package for this, so I had to implement it for my self.
You can check the complete tutorial here and below downloadable files here.
But in short, here is what you need to do.
Open android folder in your react-native project with Android Studio and add implementation 'com.google.android.play:core:1.7.3' at the end of dependencies section of the build.gradle(app) file. Like below,
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
.......
implementation 'com.google.android.play:core:1.7.3' // add it at the end
}
Cick sync after adding the dependency.
Download InAppUpdateModule.java and InAppUpdatePackage.java files and place in them in the same directory of MainActivity.java(android/app/src/main/java/<package>/)
Change the package names in both InAppUpdateModule.java and InAppUpdatePackage.java to your project package name.
Now Open MainApplication.java and add our InAppUpdatePackage into getPackages method like below,
#Override
protected List<ReactPackage> getPackages() {
#SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new InAppUpdatePackage());
return packages;
}
Download InAppUpdate.js and place it into your react-native project.
Import the InAppUpdate.js in any js file, wherever you want to use. And use it like below.
componentDidMount () {
InAppUpdate.checkUpdate() // this is how you check for update
}
That's it.
You can download all the files from here

There's a fairly new package that does that: sp-react-native-in-app-updates.
It also handles the iOS case by prompting the user with a native alert that redirects to the App Store.

One more option (Better than the APi check):
Check the latest version from play store and act accordingly, with https://github.com/kimxogus/react-native-version-check
VersionCheck.needUpdate()
.then(async res => {
console.log(res.isNeeded); // true
if (res.isNeeded) {
Linking.openURL(await VersionCheck.getStoreUrl()); // open store if update is needed.
}
});

You can use sp-react-native-in-app-updates library for inapp update.
npm i sp-react-native-in-app-updates
This is a react-native native module that works on both iOS and Android, and checks the stores (play/app) for a new version of your app and can prompt your user for an update.
It uses embedded in-app-updates via Play-Core on Android (to check & download google play patches natively from within the app), and react-native-siren on iOS (to check & navigate the user to the AppStore).

Related

Is it possible to get dependency version at runtime, including from library itself?

Background
Suppose I make an Android library called "MySdk", and I publish it on Jitpack/Maven.
The user of the SDK would use it by adding just the dependency of :
implementation 'com.github.my-sdk:MySdk:1.0.1'
What I'd like to get is the "1.0.1" part from it, whether I do it from within the Android library itself (can be useful to send to the SDK-server which version is used), or from the app that uses it (can be useful to report about specific issues, including via Crashlytics).
The problem
I can't find any reflection or gradle task to reach it.
What I've tried
Searching about it, if I indeed work on the Android library (that is used as a dependency), all I've found is that I can manage the version myself, via code.
Some said I could use BuildConfig of the package name of the library, but then it means that if I forget to update the code a moment before I publish the dependency, it will use the wrong value. Example of using this method:
plugins {
...
}
final def sdkVersion = "1.0.22"
android {
...
buildTypes {
release {
...
buildConfigField "String", "SDK_VERSION", "\"" + sdkVersion + "\""
}
debug {
buildConfigField "String", "SDK_VERSION", "\"" + sdkVersion + "-unreleased\""
}
}
Usage is just checking the value of BuildConfig.SDK_VERSION (after building).
Another possible solution is perhaps from gradle task inside the Android-library, that would be forced to be launched whenever you build the app that uses this library. However, I've failed to find how do it (found something here)
The question
Is it possible to query the dependency version from within the Android library of the dependency (and from the app that uses it, of course), so that I could use it during runtime?
Something automatic, that won't require me to update it before publishing ?
Maybe using Gradle task that is defined in the library, and forced to be used when building the app that uses the library?
You can use a Gradle task to capture the version of the library as presented in the build.gradle dependencies and store the version information in BuildConfig.java for each build type.
The task below captures the version of the "appcompat" dependency as an example.
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.0'
}
task CaptureLibraryVersion {
def libDef = project.configurations.getByName('implementation').allDependencies.matching {
it.group.equals("androidx.appcompat") && it.name.equals("appcompat")
}
if (libDef.size() > 0) {
android.buildTypes.each {
it.buildConfigField 'String', 'LIB_VERSION', "\"${libDef[0].version}\""
}
}
}
For my example, the "appcompat" version was 1.4.0. After the task is run, BuildConfig.java contains
// Field from build type: debug
public static final String LIB_VERSION = "1.4.0";
You can reference this field in code with BuildConfig.LIB_VERSION. The task can be automatically run during each build cycle.
The simple answer to your question is 'yes' - you can do it. But if you want a simple solution to do it so the answer transforms to 'no' - there is no simple solution.
The libraries are in the classpath of your package, thus the only way to access their info at the runtime would be to record needed information during the compilation time and expose it to your application at the runtime.
There are two major 'correct' ways and you kinda have described them in your question but I will elaborate a bit.
The most correct way and relatively easy way is to expose all those variables as BuildConfig or String res values via gradle pretty much as described here. You can try to generify the approach for this using local-prefs(or helper gradle file) to store versions and use them everywhere it is needed. More info here, here, and here
The second correct, but much more complicated way is to write a gradle plugin or at least some set of tasks for collecting needed values during compile-time and providing an interface(usually via your app assets or res) for your app to access them during runtime. A pretty similar thing is already implemented for google libraries in Google Play services Plugins so it would be a good place to start.
All the other possible implementations are variations of the described two or their combination.
You can create buildSrc folder and manage dependencies in there.
after that, you can import & use Versions class in anywhere of your app.

Basic usage of #react-native-firebase/admob gives: "TypeError: (0, _admob.default) is not a function". Is it deprecated? or Why doesnt work?

Is #react-native-firebase/admob deprecated? or just.. Why it doesn't work?
I am using #react-native-firebase/admob (https://rnfb-docs.netlify.app/admob/usage).
Everything works fine before to use "admob()". When I add admob() to the code appears this error:
"TypeError: (0, _admob.default) is not a function"
Do someone know why?
My code below (basic usage):
import React from 'react';
import { Text, View} from 'react-native';
import admob, { MaxAdContentRating } from '#react-native-firebase/admob';
import { InterstitialAd, RewardedAd, BannerAd, TestIds } from '#react-native-
firebase/admob';
import { BannerAdSize} from '#react-native-firebase/admob';
class App extends React.Component{
componentDidMount(){
// this was taked of official page: https://rnfb-docs.netlify.app/admob/usage#installation
admob()
.setRequestConfiguration({
// Update all future requests suitable for parental guidance
maxAdContentRating: MaxAdContentRating.PG,
// Indicates that you want your content treated as child-directed for purposes of COPPA.
tagForChildDirectedTreatment: true,
// Indicates that you want the ad request to be handled in a
// manner suitable for users under the age of consent.
tagForUnderAgeOfConsent: true,
})
.then(() => {
// Request config successfully set!
});
}
render(){
return(
<View style={{
alignItems:"center",
justifyContent:"center",
height:"100%"}}>
<Text style={{color:"black"}}>
Hola
</Text>
<BannerAd
unitId={TestIds.BANNER}
size={BannerAdSize.FULL_BANNER} />
</View>
)
}
}
export default App;
Despite #CodeJoe Answer, I still got confused by different Documentations for the React Native Firebase that where around, hence I spend lots of time and energy to get around it.
I open an issue here where is confirmed that Google removed the AdMob Package since v11.5.0.
AdMob is no longer in Firebase APIs, so this module no longer wraps it, there is no documentation to correct.
However it did exist as recently as v11.5.0 here and if you browse the repository at that point, you may consider the e2e tests for AdMob at the time a primer on "How To Use AdMob" https://github.com/invertase/react-native-firebase/tree/0217bff5cbbf233d7915efb4dbbdfe00e82dff23/packages/admob/e2e
Please, don't be like me and check the correct Documentation and website:
Correct
https://rnfirebase.io
Wrong Wrong Wrong, this refers to an older verison
https://rnfb-docs.netlify.app
The internet has a long memory, so there are stale copies of the docs out and about yes, but rnfirebase.io is always the official and current doc site
Admob was removed completely from the firebase ecosystem by Google so it does not exist here no. There are some community packages for it, our v11.5 version that has it, and we hope to peel our implementation out and update it for the community but it takes time and we are unfortunately still backlogged on official firebase apis, so it has not happened yet
So for AdMob solution I would use another Library, and use react-native-firebase for the Solutions that they currently provide
Alternative Library (August 2021)
DISCLAIMER
React Native Firebase is a great library still for the other packages they provide (Firebase, Analitycs...) and the Admob version 11.5 is still a solution. These are just suggestion for alternatives for Admob.
react-native-admob-alpha Simple and fresh library, recently updated.
react-native-admob-native-ads Another brand new library, they implement Admob Native Advanced Ads.
Expo AdMob (Available also for bare React-Native Projects)
To complete your searching I'll add that Admob is removed from React Native Firebase and there is no plan to implement it again. Only re-host code on an external repository.
Last supported version is 11.5.0
It means if you would like to use RN Firebase Admob before re-host you need to use all other services (like RNF analytics) with this version.
For more info please check https://github.com/invertase/react-native-firebase/issues/5329#issuecomment-843272057
Remember to use
dependencies{
"#react-native-firebase/admob": "11.5.0",
"#react-native-firebase/app": "11.5.0",
}
instead of
dependencies{
"#react-native-firebase/admob": "^11.5.0",
"#react-native-firebase/app": "^11.5.0",
}```
I could solve it.
SOLVED
Just check in the file "package.json" that packages of firebase has the same version, example:
dependencies{
"#react-native-firebase/admob": "^11.5.0",
"#react-native-firebase/app": "^11.5.0"
}
TIP
Works to similars errors.
I am able to use 11.5.0 downgrade trick in react native 0.65.1 for my RewardedAds. I edited the package.json file as said. It didn't work but I managed to run it in a different way:
Close any running react-native related terminals. Uninstall #react-native-firebase/app.
npm uninstall #react-native-firebase/app
Install the #react-native-firebase/app version 11.5.0 directly with this command.
npm install #react-native-firebase/app#11.5.0
After the installation, go to package.json>dependencies and do both packages versions the same(11.5.0) and remove ^.
"#react-native-firebase/admob": "11.5.0",
"#react-native-firebase/app": "11.5.0",
Start the react-native with fresh cache then run-android.
npx react-native start --reset-cache
npx react-native run-android

Full offline option using AWS DataStore and then allow an optional activation for the the cloud sync features in Android

I've started a project using AwsAppsync.
The app is going to be offline first. And we want to offer the option to sync in the cloud as an extra option later if the customer wants it. So, I configured my project like this:
In build.gradle added the following:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
classpath 'com.amplifyframework:amplify-tools-gradle-plugin:0.2.1'
}
}
apply plugin: 'com.amplifyframework.amplifytools'
And added those dependencies:
implementation 'com.amplifyframework:core:0.10.0'
implementation 'com.amplifyframework:aws-api:0.10.0' // If using cloud sync
implementation 'com.amplifyframework:aws-datastore:0.10.0'
And then using the Amplify CLI:
copy
Generate models at any time by executing this Amplify CLI command:
amplify codegen models
After this in the onCreate of the activity:
ModelProvider modelProvider = AmplifyModelProvider.getInstance();
Amplify.addPlugin(AWSDataStorePlugin.forModels(modelProvider));
Amplify.configure(getApplicationContext());
With this i can Query, update and delete the data.
Is this enough for a full offline experience?
Now, based on the documentation, to add online sync feature, I have to do the following:
Using the Amplify CLI:
amplify push
This will create the amplifyconfiguration.json with the sync configuration.
Now, this information seems to be set in build time. So, how can I enable/disable this feature to be used just when I need it? The documentation mention the following:
ModelProvider modelProvider = AmplifyModelProvider.getInstance();
Amplify.addPlugin(AWSDataStorePlugin.forModels(modelProvider));
Amplify.addPlugin(new AWSApiPlugin()); // If using remote model synchronization
Amplify.configure(getApplicationContext());
So, would be adding that "new AWSApiPlugin()" the only thing I need to do to enable/disable this feature? Thanks!
Any tutorial/book good enough to explain this? I've read several documentation(including the official) and I wasn't able to find something about this particular case. It is always with online sync from the get go.
From a high level, yes this is fairly accurate. DataStore doesn't require a backend it will just work as a local DB of sorts that you can use for offline including queries. When you add in a backend with amplify push (this deploys an AppSync API backed by DynamoDB) and connect it with AWSApiPlugin it will begin syncing. The best tutorial is in the docs: https://docs.amplify.aws

Verify non-Google Play app installs using Play core library

Some context: Most of us may have faced this ResourceNotFoundException when we migrated to Android app bundle release method. It is evident that the issue is because of Side-loading the app. Reference here.
Google recently announced solution to this problem. Using play core library we can identify whether the app is side-loaded or not (Identifies the missing split apks). If the app is side-loaded it shows "Installation failed" popup and redirects to play store, where user can properly install the app via the Google Play store.
Problem: Everything works fine until the installation of missing split apks from play store. Now when I relaunch the app, it immediately crashes with an error saying.
Default FirebaseApp is not initialised in this process
Note: Directly downloading the app from play store works perfectly fine without any crash. The crash happens only when the app re-downloads because of side loading issue.
Code:
Project's build.gradle:
buildscript {
dependencies {
classpath 'com.android.tools.build:bundletool:0.9.0'
}
}
App module's build.gradle:
implementation 'com.google.android.play:core:1.6.1'
Class that extends Application:
public void onCreate() {
if (MissingSplitsManagerFactory.create(this).disableAppIfMissingRequiredSplits()) {
// Skip app initialization.
return;
}
super.onCreate();
.....
}
Any help would be really great.
I have solved this issue with the latest version of Play core library:
App module's build.gradle:
implementation "com.google.android.play:core:1.7.2"
Other implementation remains same.
A class that extends Application:
public void onCreate() {
if (MissingSplitsManagerFactory.create(this).disableAppIfMissingRequiredSplits()) {
// Skip app initialization.
return;
}
super.onCreate();
.....
}
How to test:
A better way to test it properly is to release the app bundle with these above fixes in play store internal test channel (Add yourself as a tester).
Simulate installing invalid apks - Use bundletool to get .apks file out of bundle, extract it and install base_master.apk using adb command
adb install base_master.apk.
Launch the app, you should see "Installation failed" dialog and it redirects to Play store, clicking on Update, play store will install the missing apks.
Launching the app should work properly by now.
Hope this helps

How can I import firebase in-app-messaging-display api's source code into my android project?

I am trying to customize the firebase in-app-messaging-display's UI of "Image Only" and "Modal" mode. So I turned to the official documentation, but it is quite simple, by saying:
Creating your own display is a two step process:
1.Write your own implementation of the FirebaseInAppMessagingDisplay class.
2.Register that implemenation with the headless Firebase In-App Messaging SDK.
I wonder how can I import in-app-messaging-display's source code into my project and make it work as a library.
I have downloaded its source code from github:https://github.com/firebase/firebase-android-sdk/tree/master/firebase-inappmessaging-display, tried to import it as a module, but after I selected the Source directory, Android Studio hints that: Specify location of the Gradle
or Android Eclipse project. I also have tried to copy the source code into my project's libs directory and added this: include ':libs:firebase-inappmessaging-display' into my settings.gradle file and this: implementation project(':libs:firebase-inappmessaging-display') into my app's gradle dependency. When sync building Android Studio reports errors like this:
ERROR: Unable to resolve dependency for ':XXXXXXXX': Could not resolve project :libs:firebase-inappmessaging-display.
Any suggestion will be highly appreciated.
The information on the doc is little bit confusing. I am also stuck with the same problem for long time. Actually its very simple.
Add these dependencies in your app level gradle file.
implementation 'com.google.firebase:firebase-core:16.0.8'
implementation ("com.google.firebase:firebase-inappmessaging:17.0.3")
Register Your DisplayMessage component on starting activity.
import com.google.firebase.inappmessaging.FirebaseInAppMessaging
import com.google.firebase.inappmessaging.FirebaseInAppMessagingDisplay
///////
override fun onStart() {
super.onStart()
Log.e("MESSAGE", "activity started")
var firebaseInAppMessagingDisplay = FirebaseInAppMessagingDisplay { inAppMessage, cb ->
// You can show the message here.
// The variable inAppMessage has all information about the campaign that we putting in console (title, content, image url.. etc)
Log.e("MESSAGE", "Display Message callback invoked")
}
FirebaseInAppMessaging.getInstance().setMessageDisplayComponent(firebaseInAppMessagingDisplay)
}

Categories

Resources