讓新用戶安裝您的應用程序的最有效方法之一是讓您的用戶能夠與他們的朋友分享您應用程序中的內容。使用動態鏈接,您可以創建出色的用戶對用戶共享體驗:從朋友那裡收到內容推薦的用戶可以單擊鏈接並直接轉到您應用程序中的共享內容,即使他們必須轉到應用程序商店或 Google Play 商店以先安裝您的應用程序。
通過結合用戶推薦的粘性和動態鏈接的持久性,您可以創建用戶到用戶的共享和推薦功能,通過將新用戶直接吸引到您的應用程序內容或提供使推薦人和被推薦人互惠互利的促銷活動來吸引新用戶.
主要優勢
- 首次打開您的應用程序的新用戶會獲得定制的首次運行體驗,該體驗是根據他們的朋友想要與他們分享的內容進行上下文化的。例如,您可以顯示與他們共享的內容,或自動將他們與邀請他們的朋友聯繫起來。
- 讓用戶可以輕鬆地跨平台與他們的朋友分享內容,無論他們的朋友是否安裝了您的應用程序。
這是開始的方法!
設置 Firebase 和動態鏈接 SDK
設置一個新的 Firebase 項目並將動態鏈接 SDK 安裝到您的應用中。
安裝動態鏈接 SDK 允許 Firebase 將有關動態鏈接的數據傳遞給應用程序,包括在用戶安裝應用程序之後。
創建動態鏈接
現在是時候設置用戶可以發送給朋友的鏈接了。如果您用戶的朋友還沒有安裝該應用程序,請不要擔心;動態鏈接可以為您解決這個問題。
對於您希望共享的每個內容元素,創建一個動態鏈接。
創建動態鏈接時,您需要提供 HTTP 或 HTTPS URL 作為link
參數,用於標識您共享的內容。如果您有一個包含同等內容的網站,您應該使用您網站的 URL。這將確保這些鏈接在不支持動態鏈接的平台上正確呈現,例如桌面瀏覽器。例如:
https://example.page.link/?link=https://www.example.com/content?item%3D1234&apn=com.example.android&ibi=com.example.ios&isi=12345
您還可以通過添加 URL 編碼參數向數據負載添加額外信息——例如,指示該鏈接是針對特定用戶的,例如在遊戲邀請中。
https://example.page.link/?link=https://www.example.com/invitation?gameid%3D1234%26referrer%3D555&apn=com.example.android&ibi=com.example.ios&isi=12345
在共享這些鏈接之前,您可能希望使用Firebase Dynamic Links URL shortener API生成更友好的 URL。一個簡短的動態鏈接類似於以下示例:
https://example.page.link/WXYZ
無論您使用哪個鏈接,當用戶在其設備上打開動態鏈接時,由apn
參數(在 Android 上)或ibi
和isi
參數(在 iOS 上)指定的應用會將用戶帶到 Play Store 或 App Store 以安裝應用如果尚未安裝。然後,當安裝並打開應用程序時,“鏈接”參數中指定的 URL 將傳遞給應用程序。
添加發送動態鏈接的“分享”按鈕
首先,看一下這個基於房間的聊天應用程序的簡單示例,例如 Hangouts,它生成鏈接以邀請人們加入聊天室。
iOS


安卓


迅速
func generateContentLink() -> URL { let baseURL = URL(string: "https://your-custom-name.page.link")! let domain = "https://your-app.page.link" let linkBuilder = DynamicLinkComponents(link: baseURL, domainURIPrefix: domain) linkBuilder?.iOSParameters = DynamicLinkIOSParameters(bundleID: "com.your.bundleID") linkBuilder?.androidParameters = DynamicLinkAndroidParameters(packageName: "com.your.packageName") // Fall back to the base url if we can't generate a dynamic link. return linkBuilder?.link ?? baseURL }
目標-C
- (NSURL *)generateContentLink { NSURL *baseURL = [NSURL URLWithString:@"https://your-custom-name.page.link"]; NSString *domain = @"https://your-app.page.link"; FIRDynamicLinkComponents *builder = [[FIRDynamicLinkComponents alloc] initWithLink:baseURL domainURIPrefix:domain]; builder.iOSParameters = [FIRDynamicLinkIOSParameters parametersWithBundleID:@"com.your.bundleID"]; builder.androidParameters = [FIRDynamicLinkAndroidParameters parametersWithPackageName:@"com.your.packageName"]; // Fall back to the base url if we can't generate a dynamic link. return builder.link ?: baseURL; }
Kotlin+KTX
fun generateContentLink(): Uri { val baseUrl = Uri.parse("https://your-custom-name.page.link") val domain = "https://your-app.page.link" val link = FirebaseDynamicLinks.getInstance() .createDynamicLink() .setLink(baseUrl) .setDomainUriPrefix(domain) .setIosParameters(DynamicLink.IosParameters.Builder("com.your.bundleid").build()) .setAndroidParameters(DynamicLink.AndroidParameters.Builder("com.your.packageName").build()) .buildDynamicLink() return link.uri }
Java
public static Uri generateContentLink() { Uri baseUrl = Uri.parse("https://your-custom-name.page.link"); String domain = "https://your-app.page.link"; DynamicLink link = FirebaseDynamicLinks.getInstance() .createDynamicLink() .setLink(baseUrl) .setDomainUriPrefix(domain) .setIosParameters(new DynamicLink.IosParameters.Builder("com.your.bundleid").build()) .setAndroidParameters(new DynamicLink.AndroidParameters.Builder("com.your.packageName").build()) .buildDynamicLink(); return link.getUri(); }
擁有動態鏈接後,您可以向 UI 添加一個共享按鈕,以啟動標準平台共享流程:
迅速
lazy private var shareController: UIActivityViewController = { let activities: [Any] = [ "Learn how to share content via Firebase", URL(string: "https://firebase.google.com")! ] let controller = UIActivityViewController(activityItems: activities, applicationActivities: nil) return controller }() @IBAction func shareButtonPressed(_ sender: Any) { let inviteController = UIStoryboard(name: "Main", bundle: nil) .instantiateViewController(withIdentifier: "InviteViewController") self.navigationController?.pushViewController(inviteController, animated: true) }
目標-C
- (UIActivityViewController *)shareController { if (_shareController == nil) { NSArray *activities = @[ @"Learn how to share content via Firebase", [NSURL URLWithString:@"https://firebase.google.com"] ]; UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:activities applicationActivities:nil]; _shareController = controller; } return _shareController; } - (IBAction)shareLinkButtonPressed:(UIView *)sender { if (![sender isKindOfClass:[UIView class]]) { return; } self.shareController.popoverPresentationController.sourceView = sender; [self presentViewController:self.shareController animated:YES completion:nil]; }
Kotlin+KTX
private fun onShareClicked() { val link = DynamicLinksUtil.generateContentLink() val intent = Intent(Intent.ACTION_SEND) intent.type = "text/plain" intent.putExtra(Intent.EXTRA_TEXT, link.toString()) startActivity(Intent.createChooser(intent, "Share Link")) }
Java
private void onShareClicked() { Uri link = DynamicLinksUtil.generateContentLink(); Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TEXT, link.toString()); startActivity(Intent.createChooser(intent, "Share Link")); }
在此示例中,默認共享 UI 會自動顯示用於共享鏈接的應用程序列表,因此您只需幾行代碼即可在自己的應用程序中進行設置。
這些操作不是讓用戶在您的應用中選擇聯繫人和撰寫消息,而是委託給他們從共享對話框中選擇的應用。此外,將共享委託給其他應用程序意味著您不必向用戶詢問聯繫人權限,並允許用戶從他們所選應用程序中的擴展聯繫人列表中進行選擇。為了更好地促進社交分享,您可以將社交媒體預覽元數據添加到您的動態鏈接中,該鏈接將與主要社交渠道中的鏈接一起顯示。
但有時,僅發送一個沒有文本的裸鏈接不足以獲得引人注目的推薦。通過在鏈接中附上一條短消息,如果可能的話,更豐富的演示,用戶可以在收到推薦時理解推薦的價值主張:
iOS


安卓


儘管這比上一個示例更複雜,但方法或多或少是相同的。在這個屏幕上有一張大圖,上面有邀請的價值主張和用於分享到主要社交渠道的按鈕。此 UI 流程中存在一些冗餘 — 一些共享渠道單獨顯示以允許更多特定於渠道的消息自定義,例如向電子郵件邀請添加主題行。在這個邀請菜單中,我們:
- 顯示電子郵件、短信和復制鏈接共享按鈕,並適當地自定義他們的消息。電子郵件將包含一個主題,並且可以包含帶有換行符、圖像和空格的較長正文;文本應該包括一個較短的正文,有換行符,但空白很少,沒有圖像;鏈接複製應該只複製鏈接而不是其他任何東西。
- 將系統共享 UI 用於其他所有內容,包括伴隨鏈接的簡短邀請消息。
- 通過 URL 方案或通用鏈接的深層鏈接到另一個應用程序,該應用程序具有處理您應用程序邀請的特殊邏輯。這在您的組織與其他應用程序之間的合作夥伴關係之外不起作用,並且可能不是小型組織的選擇。也就是說,某些應用程序可能會公開記錄其通用/深度鏈接行為。我們將在我們的示例中實現一個虛擬版本。
首先,定義一個邀請內容類型,它只封裝邀請中的信息,不包含任何功能。這樣,您可以從數據類型入手,並根據代碼如何將數據組合在一起來考慮您的代碼。
迅速
/// The content within an invite, with optional fields to accommodate all presenters. /// This type could be modified to also include an image, for sending invites over email. struct InviteContent { /// The subject of the message. Not used for invites without subjects, like text message invites. var subject: String? /// The body of the message. Indispensable content should go here. var body: String? /// The URL containing the invite. In link-copy cases, only this field will be used. var link: URL }
目標-C
/// The content within an invite, with optional fields to accommodate all presenters. /// This type could be modified to also include an image, for sending invites over email. @interface InviteContent : NSObject <NSCopying> /// The subject of the message. Not used for invites without subjects, like text message invites. @property (nonatomic, readonly, nullable) NSString *subject; /// The body of the message. Indispensable content should go here. @property (nonatomic, readonly, nullable) NSString *body; /// The URL containing the invite. In link-copy cases, only this field will be used. @property (nonatomic, readonly) NSURL *link; - (instancetype)initWithSubject:(nullable NSString *)subject body:(nullable NSString *)body link:(NSURL *)link NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @end
Kotlin+KTX
/** * The content of an invitation, with optional fields to accommodate all presenters. * This type could be modified to also include an image, for sending invites over email. */ data class InviteContent( /** The subject of the message. Not used for invites without subjects, like SMS. */ val subject: String?, /** The body of the message. Indispensable content should go here. */ val body: String?, /** The URL containing the link to invite. In link-copy cases, only this field will be used. */ val link: Uri )
Java
/** * The content of an invitation, with optional fields to accommodate all presenters. * This type could be modified to also include an image, for sending invites over email. */ public class InviteContent { /** * The subject of the message. Not used for invites without subjects, like SMS. **/ @Nullable public final String subject; /** * The body of the message. Indispensable content should go here. **/ @Nullable public final String body; /** * The URL containing the link to invite. In link-copy cases, only this field will be used. **/ @NonNull public final Uri link; public InviteContent(@Nullable String subject, @Nullable String body, @NonNull Uri link) { // ... } }
此處唯一需要的數據是 URL,沒有它您就無法邀請用戶使用您的應用程序。其他數據的結構顯然是為了發送電子郵件,這使得它們在其他一些情況下有點尷尬——當通過文本發送邀請時,鏈接附帶的簡介可能讀起來類似於電子郵件主題,但當分享到社交媒體時文本附帶的鏈接可能更像是電子郵件正文。您必須自己對此進行試驗以找到最適合您的應用程序的平衡點,如果您不確定,您始終可以使用Remote Config之類的服務來允許您在應用程序啟動後更改文本值。
迅速
/// A type responsible for presenting an invite given using a specific method /// given the content of the invite. protocol InvitePresenter { /// The name of the presenter. User-visible. var name: String { get } /// An icon representing the invite method. User-visible. var icon: UIImage? { get } /// Whether or not the presenter's method is available. iOS devices that aren't phones /// may not be able to send texts, for example. var isAvailable: Bool { get } /// The content of the invite. Some of the content type's fields may be unused. var content: InviteContent { get } /// Designated initializer. init(content: InviteContent, presentingController: UIViewController) /// This method should cause the presenter to present the invite and then handle any actions /// required to complete the invite flow. func sendInvite() }
目標-C
/// A type responsible for presenting an invite given using a specific method /// given the content of the invite. @protocol InvitePresenter <NSObject> /// The name of the presenter. User-visible. @property (nonatomic, readonly) NSString *name; /// An icon representing the invite method. User-visible. @property (nonatomic, readonly, nullable) UIImage *icon; /// Whether or not the presenter's method is available. iOS devices that aren't phones /// may not be able to send texts, for example. @property (nonatomic, readonly) BOOL isAvailable; /// The content of the invite. Some of the content type's fields may be unused. @property (nonatomic, readonly) InviteContent *content; /// Designated initializer. - (instancetype)initWithContent:(InviteContent *)content presentingViewController:(UIViewController *)controller; /// This method should cause the presenter to present the invite and then handle any actions /// required to complete the invite flow. - (void)sendInvite; @end
Kotlin+KTX
/** * Presents the invite using a specific method, such as email or social. */ open class InvitePresenter( /** The user-visible name of the invite method, like 'Email' or 'SMS' */ val name: String, /** An icon representing the invite method. */ @param:DrawableRes @field:DrawableRes val icon: Int, /** Whether or not the method is available on this device. For example, SMS is phone only. */ val isAvailable: Boolean, /** The Content of the invitation */ val content: InviteContent ) { /** * Send the invitation using the specified method. */ open fun sendInvite(context: Context) { // ... } }
Java
/** * Presents the invite using a specific method, such as email or social. */ public class InvitePresenter { /** * The user-visible name of the invite method, like 'Email' or 'SMS' **/ public final String name; /** * An icon representing the invite method. **/ @DrawableRes public final int icon; /** * Whether or not the method is available on this device. For example, SMS is phone only. **/ public final boolean isAvailable; /** * The Content of the invitation **/ public final InviteContent content; public InvitePresenter(String name, @DrawableRes int icon, boolean isAvailable, InviteContent content) { // ... } /** * Send the invitation using the specified method. */ public void sendInvite(Context context) { // ... } }
現在剩下的就是將其插入到您選擇的 UI 組件中。有關此邀請流程的完整實施,請參閱 GitHub 上的iOS和Android示例。
這些都是讓你的用戶給好友發送邀請的方法,是最輕量級的邀請方案。許多流行的應用程序還通過自己的後端發送電子郵件來發送邀請,這需要集成郵件發送服務,但提供了許多其他方式無法提供的好處,只有一些小缺點。
優點:
- 啟用帶有復雜標記的電子郵件,您的用戶在發送前無法修改這些標記。
- 啟用更精細的跟踪/分析(即在後端發送成功和失敗)。
缺點:
- 電子郵件更有可能被標記為垃圾郵件
- 需要與電子郵件傳送服務集成
- 需要應用內聯繫人權限
通常,通過您自己的電子郵件傳送服務發送邀請以多功能性為代價提供了更一致且可能更豐富的邀請體驗。
在您的應用中打開鏈接的內容
最後,您需要接收傳遞到您的應用程序的鏈接,以便您可以向收件人顯示鏈接的內容。使用動態鏈接 SDK 很容易做到這一點:
iOS
在 iOS 上,您通過實施application:continueUserActivity:restorationHandler:
方法接收動態鏈接。在恢復處理程序中,您可以通過調用handleUniversalLink:completion:
獲取動態鏈接。如果動態鏈接已傳遞到您的應用程序,您可以從FIRDynamicLink
的url
屬性中獲取它。例如:
目標-C
[[FIRDynamicLinks dynamicLinks]
handleUniversalLink:userActivity.webpageURL
completion:^(FIRDynamicLink * _Nullable dynamicLink,
NSError * _Nullable error) {
NSString *link = dynamicLink.url;
BOOL strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong;
// ...
}];
迅速
FIRDynamicLinks.dynamicLinks()?.handleUniversalLink(userActivity.webpageURL!) { (dynamiclink, error) in
let link = dynamicLink.url
let strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong
// ...
}
此外,您必須在application:openURL:options:
方法中調用dynamicLinkFromCustomSchemeURL:
: 以接收作為自定義方案 URL 傳遞給您的應用的動態鏈接。例如:
目標-C
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
if (dynamicLink) {
NSString *link = dynamicLink.url;
BOOL strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong;
// ...
return YES;
}
迅速
let dynamicLink = FIRDynamicLinks.dynamicLinks()?.dynamicLinkFromCustomSchemeURL(url)
if let dynamicLink = dynamicLink {
let link = dynamicLink.url
let strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong
// ...
return true
}
現在您有了link
參數的值,您可以向接收者顯示鏈接的內容,或者以其他方式處理參數指定的數據。諸如JLRoutes之類的 URL 路由庫可以幫助完成此任務。
如果您收到的是針對特定收件人的鏈接,請確保動態鏈接的匹配置信度strong
,然後再運行任何用戶特定的邏輯。
安卓
在 Android 上,您使用getDynamicLink()
方法從動態鏈接獲取數據:
Kotlin+KTX
Firebase.dynamicLinks .getDynamicLink(intent) .addOnCompleteListener { task -> if (!task.isSuccessful) { // Handle error // ... } val invite = FirebaseAppInvite.getInvitation(task.result) if (invite != null) { // Handle invite // ... } }
Java
FirebaseDynamicLinks.getInstance() .getDynamicLink(getIntent()) .addOnCompleteListener(new OnCompleteListener<PendingDynamicLinkData>() { @Override public void onComplete(@NonNull Task<PendingDynamicLinkData> task) { if (!task.isSuccessful()) { // Handle error // ... } FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(task.getResult()); if (invite != null) { // Handle invite // ... } } });
現在您有了link
參數的值,您可以向接收者顯示鏈接的內容,或者以其他方式處理參數指定的數據。 URL 路由庫可以幫助完成這項任務。