Una de las maneras más eficaces de obtener usuarios nuevos es a través de las referencias. Puedes usar Dynamic Links junto con Realtime Database y Cloud Functions para Firebase a fin de animar a los usuarios a que inviten a sus amigos. Para ello, ofrece recompensas en la app por las referencias exitosas tanto al referente como a quien acepte la invitación.
Ventajas clave
- Acelera el crecimiento mediante un incentivo para que tus usuarios inviten a sus amigos.
- Los vínculos de invitación funcionan en todas las plataformas.
- Los usuarios nuevos que abren la app por primera vez obtienen una experiencia inicial personalizada. Por ejemplo, puedes conectarlos automáticamente con el amigo que los invitó.
- De forma opcional, demora la entrega de las recompensas hasta que los usuarios nuevos realicen alguna tarea introductoria, como completar un instructivo.
Aquí te explicaremos cómo comenzar a usar los Dynamic Links.
Configura Firebase y el SDK de Dynamic Links
Configura un proyecto nuevo de Firebase y, luego, instala el SDK de Dynamic Links en la app (iOS, Android, C++ y Unity). Con el SDK de Dynamic Links instalado, Firebase podrá pasar datos acerca del vínculo dinámico a la app una vez que el usuario la instale. Si no tienes el SDK, no hay manera de mostrarle el contenido del vínculo al usuario si tiene que instalar la aplicación como paso intermedio.
Crea vínculos de invitación
A fin de crear una invitación, primero crea el vínculo que abrirá el destinatario para aceptarla. Luego, incluye este vínculo en el texto de la invitación. Cuando el destinatario de una invitación usa el vínculo para instalar tu app, obtiene una experiencia inicial personalizada, que incluye la recepción de una recompensa en la app.
Este vínculo de invitación es un Dynamic Link con un valor de parámetro link
que indica que proviene del usuario existente.
Existen muchas formas de dar formato a estas cargas útiles del parámetro link
y vincularlas
a la app. Una manera sencilla de hacerlo es especificando el ID de la cuenta de usuario del remitente en un
parámetro de consulta, como en este ejemplo:
https://mygame.example.com/?invitedby=SENDER_UID
Después, a fin de crear Dynamic Links adecuados para incluirlos en una invitación, puedes usar la API de Dynamic Link Builder:
Swift
guard let uid = Auth.auth().currentUser?.uid else { return }
let link = URL(string: "https://mygame.example.com/?invitedby=\(uid)")
let referralLink = DynamicLinkComponents(link: link!, domain: "example.page.link")
referralLink.iOSParameters = DynamicLinkIOSParameters(bundleID: "com.example.ios")
referralLink.iOSParameters?.minimumAppVersion = "1.0.1"
referralLink.iOSParameters?.appStoreID = "123456789"
referralLink.androidParameters = DynamicLinkAndroidParameters(packageName: "com.example.android")
referralLink.androidParameters?.minimumVersion = 125
referralLink.shorten { (shortURL, warnings, error) in
if let error = error {
print(error.localizedDescription)
return
}
self.invitationUrl = shortURL
}
Kotlin+KTX
val user = Firebase.auth.currentUser!! val uid = user.uid val invitationLink = "https://mygame.example.com/?invitedby=$uid" Firebase.dynamicLinks.shortLinkAsync { link = Uri.parse(invitationLink) domainUriPrefix = "https://example.page.link" androidParameters("com.example.android") { minimumVersion = 125 } iosParameters("com.example.ios") { appStoreId = "123456789" minimumVersion = "1.0.1" } }.addOnSuccessListener { shortDynamicLink -> mInvitationUrl = shortDynamicLink.shortLink // ... }
Java
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); String uid = user.getUid(); String link = "https://mygame.example.com/?invitedby=" + uid; FirebaseDynamicLinks.getInstance().createDynamicLink() .setLink(Uri.parse(link)) .setDomainUriPrefix("https://example.page.link") .setAndroidParameters( new DynamicLink.AndroidParameters.Builder("com.example.android") .setMinimumVersion(125) .build()) .setIosParameters( new DynamicLink.IosParameters.Builder("com.example.ios") .setAppStoreId("123456789") .setMinimumVersion("1.0.1") .build()) .buildShortDynamicLink() .addOnSuccessListener(new OnSuccessListener<ShortDynamicLink>() { @Override public void onSuccess(ShortDynamicLink shortDynamicLink) { mInvitationUrl = shortDynamicLink.getShortLink(); // ... } });
Envía las invitaciones
Ahora que ya creaste el vínculo, puedes incluirlo en una invitación. La invitación puede ser un correo electrónico, un mensaje SMS o cualquier otro medio, según lo que sea adecuado para la app y el público.
Por ejemplo, para enviar una invitación por correo electrónico:
Swift
guard let referrerName = Auth.auth().currentUser?.displayName else { return }
let subject = "\(referrerName) wants you to play MyExampleGame!"
let invitationLink = invitationUrl?.absoluteString
let msg = "<p>Let's play MyExampleGame together! Use my <a href=\"\(invitationLink)\">referrer link</a>!</p>"
if !MFMailComposeViewController.canSendMail() {
// Device can't send email
return
}
let mailer = MFMailComposeViewController()
mailer.mailComposeDelegate = self
mailer.setSubject(subject)
mailer.setMessageBody(msg, isHTML: true)
myView.present(mailer, animated: true, completion: nil)
Kotlin+KTX
val referrerName = Firebase.auth.currentUser?.displayName val subject = String.format("%s wants you to play MyExampleGame!", referrerName) val invitationLink = mInvitationUrl.toString() val msg = "Let's play MyExampleGame together! Use my referrer link: $invitationLink" val msgHtml = String.format("<p>Let's play MyExampleGame together! Use my " + "<a href=\"%s\">referrer link</a>!</p>", invitationLink) val intent = Intent(Intent.ACTION_SENDTO).apply { data = Uri.parse("mailto:") // only email apps should handle this putExtra(Intent.EXTRA_SUBJECT, subject) putExtra(Intent.EXTRA_TEXT, msg) putExtra(Intent.EXTRA_HTML_TEXT, msgHtml) } intent.resolveActivity(packageManager)?.let { startActivity(intent) }
Java
String referrerName = FirebaseAuth.getInstance().getCurrentUser().getDisplayName(); String subject = String.format("%s wants you to play MyExampleGame!", referrerName); String invitationLink = mInvitationUrl.toString(); String msg = "Let's play MyExampleGame together! Use my referrer link: " + invitationLink; String msgHtml = String.format("<p>Let's play MyExampleGame together! Use my " + "<a href=\"%s\">referrer link</a>!</p>", invitationLink); Intent intent = new Intent(Intent.ACTION_SENDTO); intent.setData(Uri.parse("mailto:")); // only email apps should handle this intent.putExtra(Intent.EXTRA_SUBJECT, subject); intent.putExtra(Intent.EXTRA_TEXT, msg); intent.putExtra(Intent.EXTRA_HTML_TEXT, msgHtml); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); }
Recupera la información de referencia en tu app
Cuando el destinatario de la invitación abra el vínculo de referencia, se lo dirigirá a la App Store o a Play Store para instalar la app si aún no está instalada. Luego, cuando abra la app por primera vez, podrás recuperar la información de la referencia que incluiste en el Dynamic Link y usarla para entregar la recompensa.
En términos generales, lo ideal es proporcionar la recompensa por la referencia solo después de que el destinatario de la invitación se registre o incluso después de que el usuario nuevo complete alguna tarea. Hasta que se cumplan los criterios para la recompensa, debes hacer un seguimiento de la información respectiva que obtuviste con el Dynamic Link.
Una forma de hacer un seguimiento de esta información es acceder con un usuario anónimo y guardar los datos en el registro de Realtime Database de la cuenta anónima. Cuando el destinatario se registra y la cuenta anónima se convierte en una cuenta permanente, esta cuenta nueva tendrá el mismo UID que la cuenta anónima y, por lo tanto, tendrá acceso a la información de la recompensa.
Por ejemplo, para guardar el UID del referente una vez que el destinatario haya abierto la app:
Swift
struct MyApplication: App {
var body: some Scene {
WindowGroup {
VStack {
Text("Example text")
}
.onOpenURL { url in
if DynamicLinks.dynamicLinks()?.shouldHandleDynamicLink(fromCustomSchemeURL: url) ?? false {
let dynamicLink = DynamicLinks.dynamicLinks()?.dynamicLink(fromCustomSchemeURL: url)
handleDynamicLink(dynamicLink)
}
// Handle incoming URL with other methods as necessary
// ...
}
}
}
}
func handleDynamicLink(_ dynamicLink: DynamicLink?) {
guard let dynamicLink = dynamicLink else { return false }
guard let deepLink = dynamicLink.url else { return false }
let queryItems = URLComponents(url: deepLink, resolvingAgainstBaseURL: true)?.queryItems
let invitedBy = queryItems?.filter({(item) in item.name == "invitedby"}).first?.value
let user = Auth.auth().currentUser
// If the user isn't signed in and the app was opened via an invitation
// link, sign in the user anonymously and record the referrer UID in the
// user's RTDB record.
if user == nil && invitedBy != nil {
Auth.auth().signInAnonymously() { (user, error) in
if let user = user {
let userRecord = Database.database().reference().child("users").child(user.uid)
userRecord.child("referred_by").setValue(invitedBy)
if dynamicLink.matchConfidence == .weak {
// If the Dynamic Link has a weak match confidence, it is possible
// that the current device isn't the same device on which the invitation
// link was originally opened. The way you handle this situation
// depends on your app, but in general, you should avoid exposing
// personal information, such as the referrer's email address, to
// the user.
}
}
}
}
}
Kotlin+KTX
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... Firebase.dynamicLinks .getDynamicLink(intent) .addOnSuccessListener(this) { pendingDynamicLinkData -> // Get deep link from result (may be null if no link is found) var deepLink: Uri? = null if (pendingDynamicLinkData != null) { deepLink = pendingDynamicLinkData.link } // // If the user isn't signed in and the pending Dynamic Link is // an invitation, sign in the user anonymously, and record the // referrer's UID. // val user = Firebase.auth.currentUser if (user == null && deepLink != null && deepLink.getBooleanQueryParameter("invitedby", false)) { val referrerUid = deepLink.getQueryParameter("invitedby") createAnonymousAccountWithReferrerInfo(referrerUid) } } } private fun createAnonymousAccountWithReferrerInfo(referrerUid: String?) { Firebase.auth .signInAnonymously() .addOnSuccessListener { // Keep track of the referrer in the RTDB. Database calls // will depend on the structure of your app's RTDB. val user = Firebase.auth.currentUser val userRecord = Firebase.database.reference .child("users") .child(user!!.uid) userRecord.child("referred_by").setValue(referrerUid) } }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... FirebaseDynamicLinks.getInstance() .getDynamicLink(getIntent()) .addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() { @Override public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) { // Get deep link from result (may be null if no link is found) Uri deepLink = null; if (pendingDynamicLinkData != null) { deepLink = pendingDynamicLinkData.getLink(); } // // If the user isn't signed in and the pending Dynamic Link is // an invitation, sign in the user anonymously, and record the // referrer's UID. // FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); if (user == null && deepLink != null && deepLink.getBooleanQueryParameter("invitedby", false)) { String referrerUid = deepLink.getQueryParameter("invitedby"); createAnonymousAccountWithReferrerInfo(referrerUid); } } }); } private void createAnonymousAccountWithReferrerInfo(final String referrerUid) { FirebaseAuth.getInstance() .signInAnonymously() .addOnSuccessListener(new OnSuccessListener<AuthResult>() { @Override public void onSuccess(AuthResult authResult) { // Keep track of the referrer in the RTDB. Database calls // will depend on the structure of your app's RTDB. FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); DatabaseReference userRecord = FirebaseDatabase.getInstance().getReference() .child("users") .child(user.getUid()); userRecord.child("referred_by").setValue(referrerUid); } }); }
Después, cuando el destinatario de la invitación decida crear una cuenta, podrás transferir la información de referencia de la cuenta anónima a la cuenta nueva del destinatario de la invitación.
En primer lugar, obtén un objeto AuthCredential
mediante el método de acceso que el invitado desee usar. Por ejemplo, para acceder con una dirección de correo electrónico y una contraseña:
Swift
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
Kotlin+KTX
val credential = EmailAuthProvider.getCredential(email, password)
Java
AuthCredential credential = EmailAuthProvider.getCredential(email, password);
Luego, vincula esta credencial a la cuenta anónima:
Swift
if let user = Auth.auth().currentUser {
user.link(with: credential) { (user, error) in
// Complete any post sign-up tasks here.
}
}
Kotlin+KTX
Firebase.auth.currentUser!! .linkWithCredential(credential) .addOnSuccessListener { // Complete any post sign-up tasks here. }
Java
FirebaseAuth.getInstance().getCurrentUser() .linkWithCredential(credential) .addOnSuccessListener(new OnSuccessListener<AuthResult>() { @Override public void onSuccess(AuthResult authResult) { // Complete any post sign-up tasks here. } });
La cuenta nueva y permanente tiene acceso a todos los datos de recompensas que agregaste a la cuenta anónima.
Ofrece recompensas al referente y al destinatario
Ahora que recuperaste y guardaste los datos de la invitación del Dynamic Link, puedes recompensar al referente y al destinatario por la referencia si se cumplen los criterios que elegiste.
Si bien es posible escribir en Realtime Database desde la app cliente, a menudo es conveniente permitir el acceso de solo lectura a datos como la moneda virtual de tu app y realizar operaciones de escritura solo desde el backend. Este backend podría ser cualquier sistema capaz de ejecutar el SDK de Firebase Admin, pero con frecuencia es más fácil usar Cloud Functions para realizar estas tareas.
Por ejemplo, imagina que tienes un juego y deseas ofrecer una recompensa en la moneda virtual al destinatario después de que se registre y al referente después de que el destinatario alcance el nivel 5.
Para proporcionar la recompensa por registrarse, implementa una función que revise si se crea una clave específica de Realtime Database y proporcione la recompensa cuando eso suceda. Por ejemplo:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.grantSignupReward = functions.database.ref('/users/{uid}/last_signin_at')
.onCreate(event => {
var uid = event.params.uid;
admin.database().ref(`users/${uid}/referred_by`)
.once('value').then(function(data) {
var referred_by_somebody = data.val();
if (referred_by_somebody) {
var moneyRef = admin.database()
.ref(`/users/${uid}/inventory/pieces_of_eight`);
moneyRef.transaction(function (current_value) {
return (current_value || 0) + 50;
});
}
});
});
Luego, cuando se registre un usuario nuevo, activa esta función mediante la creación de la clave en Realtime Database. Por ejemplo, activa la función en el objeto
de escucha de ejecución correcta de linkWithCredential
, que creaste en el paso anterior:
Swift
if let user = Auth.auth().currentUser {
user.link(with: credential) { (user, error) in
// Complete any post sign-up tasks here.
// Trigger the sign-up reward function by creating the "last_signin_at" field.
// (If this is a value you want to track, you would also update this field in
// the success listeners of your Firebase Authentication signIn calls.)
if let user = user {
let userRecord = Database.database().reference().child("users").child(user.uid)
userRecord.child("last_signin_at").setValue(ServerValue.timestamp())
}
}
}
Kotlin+KTX
Firebase.auth.currentUser!! .linkWithCredential(credential) .addOnSuccessListener { // Complete any post sign-up tasks here. // Trigger the sign-up reward function by creating the // "last_signin_at" field. (If this is a value you want to track, // you would also update this field in the success listeners of // your Firebase Authentication signIn calls.) val user = Firebase.auth.currentUser!! val userRecord = Firebase.database.reference .child("users") .child(user.uid) userRecord.child("last_signin_at").setValue(ServerValue.TIMESTAMP) }
Java
FirebaseAuth.getInstance().getCurrentUser() .linkWithCredential(credential) .addOnSuccessListener(new OnSuccessListener<AuthResult>() { @Override public void onSuccess(AuthResult authResult) { // Complete any post sign-up tasks here. // Trigger the sign-up reward function by creating the // "last_signin_at" field. (If this is a value you want to track, // you would also update this field in the success listeners of // your Firebase Authentication signIn calls.) FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); DatabaseReference userRecord = FirebaseDatabase.getInstance().getReference() .child("users") .child(user.getUid()); userRecord.child("last_signin_at").setValue(ServerValue.TIMESTAMP); } });
Para proporcionarle una recompensa al referente cuando el destinatario alcance el nivel 5, implementa
una función que supervise los cambios en el campo level
de tus registros de usuario. Si un usuario pasa del nivel 4 al 5 y tiene un referente registrado, proporciona la recompensa:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.rewardReferrals = functions.database.ref('/users/{uid}/level')
.onUpdate(event => {
var level = event.data.val();
var prev_level = event.data.previous.val();
if (prev_level == 4 && level == 5) {
var referrerRef = event.data.ref.parent.child('referred_by');
return referrerRef.once('value').then(function(data) {
var referrerUid = data.val();
if (referrerUid) {
var moneyRef = admin.database()
.ref(`/users/${referrerUid}/inventory/pieces_of_eight`);
return moneyRef.transaction(function (current_value) {
return (current_value || 0) + 50;
});
}
});
}
});
De esta manera, tanto el referente como el usuario nuevo recibirán sus recompensas.