新規ユーザーを獲得する最も効果的な方法の 1 つは、ユーザーの紹介です。 Dynamic Links をRealtime DatabaseおよびCloud Functions for Firebaseと共に使用して、紹介者と受信者の両方に紹介が成功した場合にアプリ内報酬を提供することで、ユーザーが友達を招待するように促すことができます。
主な利点
- ユーザーが友達を招待するインセンティブを提供することで、成長を加速させます。
- 招待リンクはプラットフォーム間で機能します。
- 初めてアプリを開く新規ユーザーは、カスタマイズされた初回実行エクスペリエンスを取得します。たとえば、招待した友達と自動的に接続できます。
- 必要に応じて、新しいユーザーがチュートリアルの完了などの導入タスクを完了するまで、報酬の付与を遅らせます。
始める方法は次のとおりです。
Firebase と Dynamic Links SDK を設定する
新しい Firebase プロジェクトを設定し、Dynamic Links SDK をアプリにインストールします。 ( iOS 、 Android 、 C++ 、 Unity )。 Dynamic Links SDK をインストールすると、ユーザーがアプリをインストールした後など、Firebase は Dynamic Link に関するデータをアプリに渡すことができます。 SDK がなければ、インストール後のユーザーをインストール前のクリックにつなげることはできません。
招待リンクを作成する
招待を作成するには、まず受信者が招待を受け入れるために開くリンクを作成します。後で、招待状のテキストにこのリンクを含めます。招待状の受信者がリンクを開いてアプリをインストールすると、アプリ内報酬の受け取りなど、カスタマイズされた初回実行エクスペリエンスを得ることができます。
この招待リンクは、既存のユーザーからのものであることを示すlink
パラメータ値を持つダイナミック リンクです。
これらのlink
パラメーター ペイロードをフォーマットしてアプリに結び付ける方法は多数あります。簡単な方法の 1 つは、次の例のようにクエリ パラメータで送信者のユーザー アカウント ID を指定することです:
https://mygame.example.com/?invitedby=SENDER_UID
次に、招待状に含めるのに適したダイナミック リンクを作成するために、Dynamic Link Builder API を使用できます。
迅速
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(); // ... } });
招待状を送る
リンクを作成したので、招待状に含めることができます。招待状は、アプリと対象ユーザーに最も適したものに応じて、電子メール、SMS メッセージ、またはその他の媒体にすることができます。
たとえば、招待メールを送信するには:
迅速
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); }
アプリで紹介情報を取得する
招待の受信者が紹介リンクを開くと、App Store または Play Store にリダイレクトされ、アプリがまだインストールされていない場合はインストールされます。次に、ユーザーがアプリを初めて開いたときに、ダイナミック リンクに含めた紹介情報を取得し、それを使用して報酬を適用できます。
通常、招待の受信者がサインアップした後、または新しいユーザーが何らかのタスクを完了した後にのみ、紹介報酬を付与します。報酬基準が満たされるまで、ダイナミック リンクから取得した報酬情報を追跡する必要があります。
この情報を追跡する 1 つの方法は、ユーザーを匿名でサインインし、匿名アカウントの Realtime Database レコードにデータを保存することです。受取人がサインアップし、匿名アカウントが永久アカウントに変換されると、新しいアカウントは匿名アカウントと同じ UID を持つようになり、その結果、報酬情報にアクセスできるようになります。
たとえば、受信者がアプリを開いた後に参照元の UID を保存するには、次のようにします。
迅速
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); } }); }
次に、招待の受信者がアカウントを作成することを決定したら、紹介情報を匿名アカウントから招待受信者の新しいアカウントに引き継ぐことができます。
まず、招待者が使用したいサインイン メソッドを使用してAuthCredential
オブジェクトを取得します。たとえば、電子メール アドレスとパスワードでサインインするには、次のようにします。
迅速
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
Kotlin+KTX
val credential = EmailAuthProvider.getCredential(email, password)
Java
AuthCredential credential = EmailAuthProvider.getCredential(email, password);
次に、この資格情報を匿名アカウントにリンクします。
迅速
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. } });
新しい永続的なアカウントは、匿名アカウントに追加したすべての報酬データにアクセスできます。
紹介者と受信者に報酬を付与する
ダイナミック リンクから招待データを取得して保存したので、必要な基準が満たされるたびに、紹介者と受信者に紹介報酬を付与できます。
クライアント アプリから Realtime Database に書き込むことはできますが、多くの場合、アプリからのアプリ内通貨などのデータへの読み取りアクセスのみを許可し、バックエンドからのみ書き込み操作を実行する必要があります。このバックエンドは、Firebase Admin SDK を実行できる任意のシステムである可能性がありますが、多くの場合、Cloud Functions を使用してこれらのタスクを実行するのが最も簡単です。
たとえば、ゲームがあり、受信者がサインアップした後にゲーム内通貨の報酬を受信者に付与し、受信者がレベル 5 に達した後に紹介者に付与したいとします。
サインアップの報酬を付与するには、特定の Realtime Database キーが作成されるのを監視する関数をデプロイし、作成されたときに報酬を付与します。例えば:
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;
});
}
});
});
次に、新しいユーザーがサインアップしたときに、Realtime Database キーを作成してこの関数をトリガーします。たとえば、前の手順で作成したlinkWithCredential
の成功リスナーで関数をトリガーします。
迅速
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); } });
受信者がレベル 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;
});
}
});
}
});
紹介者と新しいユーザーの両方が報酬を受け取りました。