让新用户安装您的应用程序的最有效方法之一是让您的用户能够与他们的朋友分享您应用程序中的内容。使用动态链接,您可以创建出色的用户对用户共享体验:从朋友那里收到内容推荐的用户可以单击链接并直接转到您应用程序中的共享内容,即使他们必须转到应用程序商店或 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 路由库可以帮助完成这项任务。