Ir para o console

Premiar indicações

Uma das maneiras mais eficazes de captar novos usuários é por meio de indicações. É possível usar links dinâmicos com o Realtime Database e o Cloud Functions para Firebase com o objetivo de incentivar seus usuários a convidar amigos. Para fazer isso, ofereça prêmios no app ao responsável pela indicação e ao usuário que se cadastrar por causa dela.

Principais vantagens

  • Acelere o crescimento ao oferecer um incentivo para que seus usuários convidem os amigos.
  • Os links de convite funcionam em várias plataformas.
  • Os novos usuários que abrem o app vivenciam uma primeira experiência adaptada para eles. Por exemplo, é possível conectá-los automaticamente ao amigo que os convidou.
  • Ou então, você pode conceder o prêmio quando os novos usuários completarem algumas tarefas introdutórias, como a conclusão de um tutorial.

Veja como começar.

Configurar o Firebase e o SDK do Dynamic Links

Configure um novo projeto do Firebase e instale o SDK do Dynamic Links no seu app. Veja informações para iOS, Android, C++ e Unity. A instalação do SDK do Dynamic Links permite ao Firebase enviar dados do link dinâmico para o app, inclusive após a instalação do app pelo usuário. Sem o SDK, não é possível conectar um usuário pós-instalação a um clique de pré-instalação.

Para criar um convite, primeiro crie o link que o destinatário abre para aceitá-lo. Depois, inclua esse link no texto do convite. Quando o destinatário do convite instala seu app por meio do link, ele terá uma primeira experiência personalizada, por exemplo, recebendo um prêmio no app.

O link do convite é um link dinâmico com um valor de parâmetro link que indica que ele foi enviado a partir de um usuário existente.

Há muitas maneiras de formatar os payloads do parâmetro link e vinculá-los ao seu app. Uma maneira simples é especificar o código da conta de usuário do remetente em um parâmetro de consulta como no exemplo abaixo:

https://mygame.example.com/?invitedby=SENDER_UID

Então, para criar links dinâmicos adequados para inclusão em um convite, use a API Dynamic Link Builder:

iOS (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
}

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();
                // ...
            }
        });

Kotlin

val user = FirebaseAuth.getInstance().currentUser
val uid = user!!.uid
val link = "https://mygame.example.com/?invitedby=$uid"
FirebaseDynamicLinks.getInstance().createDynamicLink()
        .setLink(Uri.parse(link))
        .setDomainUriPrefix("https://example.page.link")
        .setAndroidParameters(
                DynamicLink.AndroidParameters.Builder("com.example.android")
                        .setMinimumVersion(125)
                        .build())
        .setIosParameters(
                DynamicLink.IosParameters.Builder("com.example.ios")
                        .setAppStoreId("123456789")
                        .setMinimumVersion("1.0.1")
                        .build())
        .buildShortDynamicLink()
        .addOnSuccessListener { shortDynamicLink ->
            mInvitationUrl = shortDynamicLink.shortLink
            // ...
        }

Enviar os convites

Agora que você criou o link, já pode incluí-lo em um convite. O convite pode ser um e-mail, mensagem SMS ou qualquer outro meio, o que for mais apropriado para seu app e seu público.

Por exemplo, para enviar um convite por e-mail:

iOS (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)

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);
}

Kotlin

val referrerName = FirebaseAuth.getInstance().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)
intent.data = 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(packageManager) != null) {
    startActivity(intent)
}

Recuperar informações de referência no seu app

Quando o destinatário do convite abre o link de referência, ele será direcionado para a App Store ou a Play Store para instalar seu app, se ainda não estiver instalado. Então, quando ele abrir o app pela primeira vez, você pode recuperar as informações de referência que você incluiu no link dinâmico e usá-las para aplicar o prêmio.

É comum conceder prêmios por indicação apenas depois que o destinatário do convite se inscrever, ou mesmo após concluir alguma tarefa. Você precisa rastrear as informações recebidas do link dinâmico até que os critérios do prêmio sejam atendidos.

Uma maneira de rastrear essas informações é registrar o usuário anonimamente e armazenar os dados no registro do Realtime Database da conta anônima. Quando o destinatário se inscrever e a conta anônima for convertida em uma conta permanente, a nova conta terá o mesmo UID que a conta anônima e, como resultado, terá acesso às informações do prêmio.

Por exemplo, para salvar o UID da pessoa que fez a indicação depois que o destinatário abre seu app:

iOS (Swift)

func application(_ app: UIApplication, open url: URL, options:
    [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
  if let isDynamicLink = DynamicLinks.dynamicLinks()?.shouldHandleDynamicLink(fromCustomSchemeURL: url),
      isDynamicLink {
    let dynamicLink = DynamicLinks.dynamicLinks()?.dynamicLink(fromCustomSchemeURL: url)
    return handleDynamicLink(dynamicLink)
  }
  // Handle incoming URL with other methods as necessary
  // ...
  return false
}

@available(iOS 8.0, *)
func application(_ application: UIApplication,
    continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
  guard let dynamicLinks = DynamicLinks.dynamicLinks() else { return false }
  let handled = dynamicLinks.handleUniversalLink(userActivity.webpageURL!) { (dynamicLink, error) in
    if (dynamicLink != nil) && !(error != nil) {
      self.handleDynamicLink(dynamicLink)
    }
  }
  if !handled {
    // Handle incoming URL with other methods as necessary
    // ...
  }
  return handled
}

func handleDynamicLink(_ dynamicLink: DynamicLink?) -> Bool {
  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.
        }
      }
    }
  }
  return true
}

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);
                }
            });
}

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // ...

    FirebaseDynamicLinks.getInstance()
            .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 = FirebaseAuth.getInstance().currentUser
                if (user == null &&
                        deepLink != null &&
                        deepLink.getBooleanQueryParameter("invitedby", false)) {
                    val referrerUid = deepLink.getQueryParameter("invitedby")
                    createAnonymousAccountWithReferrerInfo(referrerUid)
                }
            }
}

private fun createAnonymousAccountWithReferrerInfo(referrerUid: String?) {
    FirebaseAuth.getInstance()
            .signInAnonymously()
            .addOnSuccessListener {
                // Keep track of the referrer in the RTDB. Database calls
                // will depend on the structure of your app's RTDB.
                val user = FirebaseAuth.getInstance().currentUser
                val userRecord = FirebaseDatabase.getInstance().reference
                        .child("users")
                        .child(user!!.uid)
                userRecord.child("referred_by").setValue(referrerUid)
            }
}

Depois, quando o destinatário do convite criar uma conta, será possível transferir as informações de referência da conta anônima para a nova conta do destinatário do convite.

Primeiro, gere um objeto AuthCredential usando o método de login que o convidado quer usar. Por exemplo, para fazer login com endereço de e-mail e senha:

iOS (Swift)

let credential = EmailAuthProvider.credential(withEmail: email, password: password)

Java

AuthCredential credential = EmailAuthProvider.getCredential(email, password);

Kotlin

val credential = EmailAuthProvider.getCredential(email, password)

Em seguida, vincule esta credencial à conta anônima:

iOS (Swift)

if let user = Auth.auth().currentUser {
  user.link(with: credential) { (user, error) in
    // 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.
            }
        });

Kotlin

FirebaseAuth.getInstance().currentUser!!
        .linkWithCredential(credential)
        .addOnSuccessListener {
            // Complete any post sign-up tasks here.
        }

A nova conta permanente tem acesso a todos os dados de prêmio que você adicionou à conta anônima.

Conceder prêmios a quem fez a indicação e ao destinatário

Agora que você recuperou e salvou os dados do convite a partir do link dinâmico, poderá conceder prêmios por indicação a quem indicou e ao destinatário sempre que os critérios determinados por você forem atendidos.

Embora seja possível fazer gravações no Realtime Database a partir do seu app cliente, você poderá permitir apenas acesso de leitura a dados, como moeda do app nos seus aplicativos, e executar operações de gravação somente a partir do seu back-end. Esse back-end pode ser qualquer sistema capaz de executar o SDK Admin do Firebase, mas geralmente é mais fácil usar o Cloud Functions para realizar essas tarefas.

Por exemplo, suponha que você tenha um jogo e queira conceder um prêmio na moeda do jogo ao destinatário depois que ele se inscrever e à pessoa que fez a indicação depois que o destinatário atingir o nível 5.

Para conceder o prêmio pela inscrição, implante uma função que monitore a criação de uma chave específica do Realtime Database e conceda o prêmio quando isso acontecer. Por exemplo:

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;
            });
          }
        });
    });

Então, quando um novo usuário se inscrever, acione essa função ao criar a chave do Realtime Database. Por exemplo, acione a função no listener do linkWithCredential que você criou na etapa anterior:

iOS (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())
    }
  }
}

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);
            }
        });

Kotlin

FirebaseAuth.getInstance().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 = FirebaseAuth.getInstance().currentUser
            val userRecord = FirebaseDatabase.getInstance().reference
                    .child("users")
                    .child(user!!.uid)
            userRecord.child("last_signin_at").setValue(ServerValue.TIMESTAMP)
        }

Para conceder um prêmio ao responsável pela indicação quando o destinatário atingir o nível 5, implante uma função que monitore as alterações no campo level nos seus registros de usuários. Se um usuário passar do nível 4 para o nível 5 e a pessoa que fez a indicação para ele estiver registrada, conceda o prêmio:

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;
            });
          }
        });
      }
    });

Com isso, tanto o responsável pela indicação quanto o novo usuário receberão prêmios.