واحدة من أكثر الطرق فاعلية للحصول على مستخدمين جدد هي من خلال إحالات المستخدمين. يمكنك استخدام الروابط الديناميكية جنبًا إلى جنب مع Realtime Database و Cloud Functions لـ Firebase لتشجيع المستخدمين على دعوة أصدقائهم من خلال تقديم مكافآت داخل التطبيق للإحالات الناجحة لكل من المُحيل والمستلم.
الفوائد الرئيسية
- تسريع النمو من خلال توفير حافز للمستخدمين لدعوة أصدقائهم.
- تعمل روابط الدعوة عبر الأنظمة الأساسية.
- يحصل المستخدمون الجدد الذين يفتحون تطبيقك لأول مرة على تجربة التشغيل الأولى التي تخصصها لهم. على سبيل المثال ، يمكنك ربطهم تلقائيًا بالصديق الذي دعاهم.
- اختياريًا ، يمكنك تأخير منح المكافآت حتى يكمل المستخدمون الجدد بعض المهام التمهيدية ، مثل إكمال برنامج تعليمي.
إليك كيفية البدء!
إعداد Firebase و Dynamic Links SDK
قم بإعداد مشروع Firebase جديد وتثبيت Dynamic Links SDK في تطبيقك. ( iOS ، Android ، C ++ ، Unity ). يسمح تثبيت Dynamic Links SDK لـ Firebase بتمرير البيانات حول الرابط الديناميكي إلى التطبيق ، بما في ذلك بعد تثبيت المستخدم للتطبيق. بدون SDK ، لا توجد طريقة لتوصيل مستخدم ما بعد التثبيت بنقرة التثبيت المسبق.
إنشاء روابط دعوة
لإنشاء دعوة ، قم أولاً بإنشاء الرابط الذي يفتحه المستلم لقبول الدعوة. لاحقًا ، ستقوم بتضمين هذا الرابط في نص الدعوة. عندما يقوم مستلم الدعوة بتثبيت تطبيقك عن طريق فتح الرابط ، يمكنه الحصول على تجربة تشغيل مخصصة لأول مرة ، بما في ذلك تلقي مكافأة داخل التطبيق.
رابط الدعوة هذا عبارة عن رابط ديناميكي مع قيمة معلمة link
تشير إلى أنه من مستخدم موجود لديك.
هناك العديد من الطرق التي يمكنك من خلالها تنسيق حمولات معلمات link
هذه وربطها بتطبيقك. تتمثل إحدى الطرق البسيطة في تحديد معرف حساب المستخدم الخاص بالمرسل في معلمة استعلام كما في المثال التالي:
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(); // ... } });
إرسال الدعوات
الآن بعد أن أنشأت الرابط ، يمكنك إدراجه في دعوة. يمكن أن تكون الدعوة رسالة بريد إلكتروني أو رسالة نصية قصيرة أو أي وسيلة أخرى ، اعتمادًا على ما هو أكثر ملاءمة لتطبيقك وجمهورك.
على سبيل المثال ، لإرسال دعوة بالبريد الإلكتروني:
سويفت
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 لتثبيت تطبيقك إذا لم يكن مثبتًا بالفعل. بعد ذلك ، عندما يفتحون تطبيقك لأول مرة ، يمكنك استرداد معلومات الإحالة التي قمت بتضمينها في الرابط الديناميكي واستخدامها لتطبيق المكافأة.
عادة ، تريد منح مكافآت الإحالة فقط بعد تسجيل مستلم الدعوة ، أو حتى بعد أن يكمل المستخدم الجديد بعض المهام فقط. حتى يتم استيفاء معايير المكافأة ، تحتاج إلى تتبع معلومات المكافأة التي حصلت عليها من الرابط الديناميكي.
طريقة واحدة لتتبع هذه المعلومات هي تسجيل دخول المستخدم بشكل مجهول وتخزين البيانات في سجل Realtime Database الخاص بالحساب المجهول. عندما يقوم المستلم بالتسجيل ويتم تحويل الحساب المجهول إلى حساب دائم ، سيكون للحساب الجديد نفس المعرف الفريد للحساب المجهول ، ونتيجة لذلك ، سيتمكن من الوصول إلى معلومات المكافأة.
على سبيل المثال ، لحفظ 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 ، ولكن غالبًا ما يكون من الأسهل استخدام وظائف السحابة لأداء هذه المهام.
على سبيل المثال ، لنفترض أن لديك لعبة وترغب في منح مكافأة بعملة داخل اللعبة إلى المستلم بعد تسجيل المستلم ، وإلى المُحيل بعد وصول المستلم إلى المستوى 5.
لمنح مكافأة التسجيل ، انشر وظيفة تراقب إنشاء مفتاح قاعدة بيانات Realtime معين ، وتمنح المكافأة عندما تكون كذلك. علي سبيل المثال:
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;
});
}
});
}
});
لقد تلقى كل من المُحيل والمستخدم الجديد مكافآتهما الآن.