Join us in person and online for Firebase Summit on October 18, 2022. Learn how Firebase can help you accelerate app development, release your app with confidence, and scale with ease. Register now

獎勵推薦

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

獲得新用戶的最有效方法之一是通過用戶推薦。您可以將動態鏈接與Realtime DatabaseCloud Functions for Firebase一起使用,通過向推薦人和收件人提供成功推薦的應用內獎勵來鼓勵您的用戶邀請他們的朋友。

主要優勢

  • 通過激勵您的用戶邀請他們的朋友來加速增長。
  • 邀請鏈接跨平台工作。
  • 首次打開您的應用程序的新用戶將獲得您為他們定制的首次運行體驗。例如,您可以自動將他們與邀請他們的朋友聯繫起來。
  • 可以選擇延遲授予獎勵,直到新用戶完成一些介紹性任務,例如完成教程。

以下是如何開始!

設置 Firebase 和動態鏈接 SDK

設置一個新的 Firebase 項目並將動態鏈接 SDK 安裝到您的應用中。 ( iOSAndroidC++Unity )。安裝動態鏈接 SDK 允許 Firebase 將有關動態鏈接的數據傳遞給應用,包括在用戶安裝應用之後。如果沒有 SDK,就無法通過安裝前點擊連接安裝後用戶。

要創建邀請,首先創建收件人打開以接受邀請的鏈接。稍後,您將在邀請文本中包含此鏈接。當邀請的接收者通過打開鏈接安裝您的應用時,他們可以獲得定制的首次運行體驗,包括獲得應用內獎勵。

此邀請鏈接是一個動態鏈接,其link參數值表明它來自您現有的用戶。

您可以通過多種方式格式化這些link參數負載並將它們綁定到您的應用程序中。一種簡單的方法是在查詢參數中指定發件人的用戶帳戶 ID,如下例所示:

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

然後,要創建適合包含在邀請中的動態鏈接,您可以使用動態鏈接生成器 API:

迅速

注意:此 Firebase 產品不適用於 macOS、Mac Catalyst、tvOS 或 watchOS 目標。
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+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
    // ...
}

發送邀請

現在您已經創建了鏈接,您可以將其包含在邀請中。邀請可以是電子郵件、SMS 消息或任何其他媒體,具體取決於最適合您的應用和受眾的媒體。

例如,要發送電子郵件邀請:

迅速

注意:此 Firebase 產品不適用於 macOS、Mac Catalyst、tvOS 或 watchOS 目標。
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+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)
}

在您的應用中檢索推薦信息

當邀請接收者打開推薦鏈接時,他們將被定向到 App Store 或 Play Store 以安裝您的應用(如果尚未安裝)。然後,當他們第一次打開您的應用時,您可以檢索您包含在動態鏈接中的推薦信息並使用它來申請獎勵。

通常,您只想在受邀者註冊後,甚至在新用戶完成某些任務後才授予推薦獎勵。在滿足獎勵標準之前,您需要跟踪從動態鏈接獲得的獎勵信息。

跟踪此信息的一種方法是匿名登錄用戶並將數據存儲在匿名帳戶的實時數據庫記錄中。當收件人註冊並將匿名帳戶轉換為永久帳戶時,新帳戶將具有與匿名帳戶相同的 UID,因此可以訪問獎勵信息。

例如,要在收件人打開您的應用程序後保存推薦人的 UID:

迅速

注意:此 Firebase 產品不適用於 macOS、Mac Catalyst、tvOS 或 watchOS 目標。
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.
        }
      }
    }
  }
}

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

然後,當受邀者決定創建一個帳戶時,您可以將推薦信息從匿名帳戶轉移到受邀者的新帳戶。

首先,使用被邀請者想要使用的登錄方法獲取一個AuthCredential對象。例如,要使用電子郵件地址和密碼登錄:

迅速

注意:此 Firebase 產品不適用於 macOS、Mac Catalyst、tvOS 或 watchOS 目標。
let credential = EmailAuthProvider.credential(withEmail: email, password: password)

Java

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

Kotlin+KTX

val credential = EmailAuthProvider.getCredential(email, password)

然後,將此憑據鏈接到匿名帳戶:

迅速

注意:此 Firebase 產品不適用於 macOS、Mac Catalyst、tvOS 或 watchOS 目標。
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+KTX

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

新的永久帳戶可以訪問您添加到匿名帳戶的所有獎勵數據。

向推薦人和收件人授予獎勵

現在您已經從動態鏈接中檢索並保存了邀請數據,只要滿足您想要的條件,您就可以向推薦人和接收人授予推薦獎勵。

雖然您可以從客戶端應用程序寫入實時數據庫,但您通常希望只允許從應用程序讀取應用內貨幣等數據,並僅從後端執行寫入操作。此後端可以是任何能夠運行 Firebase Admin SDK 的系統,但使用 Cloud Functions 執行這些任務通常是最簡單的。

例如,假設您有一款遊戲,您想在收件人註冊後向收件人授予遊戲幣獎勵,並在收件人達到 5 級後授予推薦人。

要授予註冊獎勵,請部署一個函數來監視要創建的特定實時數據庫密鑰,並在創建時授予獎勵。例如:

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

然後,當新用戶註冊時,通過創建實時數據庫密鑰來觸發此功能。例如,觸發您在上一步中創建的linkWithCredential的成功偵聽器中的函數:

迅速

注意:此 Firebase 產品不適用於 macOS、Mac Catalyst、tvOS 或 watchOS 目標。
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+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)
        }

要在收件人達到 5 級時向推薦人授予獎勵,請部署一個函數來監視用戶記錄中level字段的更改。如果用戶從第 4 級升到第 5 級,並且該用戶記錄了推薦人,則授予獎勵:

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

推薦人和您的新用戶現在都收到了他們的獎勵。