获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

通过允许用户共享您的应用程序的内容来为您的应用程序获取新用户

让新用户安装您的应用程序的最有效方法之一是让您的用户与他们的朋友分享您的应用程序中的内容。使用动态链接,您可以创建出色的用户对用户共享体验:从朋友那里收到内容推荐的用户可以单击链接并直接转到您的应用程序中的共享内容,即使他们必须访问应用程序商店或 Google Play 商店先安装您的应用程序。

通过结合用户推荐的粘性和动态链接的持久性,您可以创建用户对用户的共享和推荐功能,通过将新用户直接吸引到您的应用程序的内容或提供使推荐人和被推荐人互惠互利的促销活动来吸引新用户.

主要优势

  • 首次打开您的应用程序的新用户将获得定制的首次运行体验,该体验根据他们的朋友想要与他们分享的内容进行情境化。例如,您可以显示与他们共享的内容,或自动将他们与邀请他们的朋友联系起来。
  • 让用户可以轻松地跨平台与他们的朋友分享内容,无论他们的朋友是否安装了您的应用。

以下是如何开始!

设置一个新的 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 Shorter API来生成看起来更友好的 URL。一个简短的动态链接类似于以下示例:

https://example.page.link/WXYZ

无论您使用哪个链接,当用户在其设备上打开动态链接时, apn参数(在 Android 上)或ibiisi参数(在 iOS 上)指定的应用程序会将用户带到 Play Store 或 App Store 以安装该应用程序如果尚未安装。然后,当应用程序安装并打开时,“链接”参数中指定的 URL 将被传递给应用程序。

首先,看一下这个基于房间的聊天应用程序的简单示例,例如 Hangouts,它会生成链接以邀请人们加入聊天室。

iOS

chat app screenshotchat app screenshot with share sheet

安卓

chat app screenshotchat app screenshot with share sheet

迅速

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
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
}

Objective-C

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
- (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;
}

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

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
}

获得动态链接后,您可以在 UI 中添加一个共享按钮,该按钮将启动标准平台共享流程:

迅速

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
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)
}

Objective-C

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
- (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];
}

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

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

在此示例中,默认共享 UI 会自动显示用于共享链接的应用列表,因此您只需几行代码即可在自己的应用中进行设置。

这些操作不是让用户选择联系人并在您的应用程序中撰写消息,而是委派给他们从共享对话框中选择的应用程序。此外,将共享委派给其他应用程序意味着您不必向用户询问联系人权限,并允许用户从他们选择的应用程序中的扩展联系人列表中进行选择。为了更好地促进社交分享,您可以将社交媒体预览元数据添加到您的动态链接中,该链接将与主要社交渠道中的链接一起显示。

但是,有时,仅仅发送一个没有文字的裸链接不足以进行引人入胜的推荐。通过在链接中附上一条短消息,如果可能的话,提供更丰富的演示文稿,用户可以在收到推荐人时了解推荐人的价值主张:

iOS

rewarded referral screenshotrewarded referral screenshot with share sheet

安卓

rewarded referral screenshotrewarded referral screenshot with share sheet

尽管这比上一个示例更复杂,但方法或多或少是相同的。在这个屏幕上,有一个带有邀请价值主张的大图形和用于分享到主要社交渠道的按钮。此 UI 流程中存在一些冗余 — 一些共享渠道是单独呈现的,以允许更多特定于渠道的消息自定义,例如向电子邮件邀请添加主题行。在这个邀请菜单中,我们:

  • 显示电子邮件、短信和复制链接共享按钮,并适当地自定义他们的消息。电子邮件将包含一个主题,并且可以包含带有换行符、图像和空格的较长正文;文本应包含较短的正文,带有换行符,但空格很少且没有图像;和链接复制应该只复制链接而不是别的。
  • 使用系统共享 UI 处理其他所有内容,包括链接附带的简短邀请消息。
  • 通过 URL 方案的深层链接或指向另一个应用程序的通用链接,该应用程序具有处理应用程序邀请的特殊逻辑。这在您的组织与其他应用程序之间的合作伙伴关系之外不起作用,并且对于较小的组织可能不是一个选项。也就是说,一些应用程序可能会公开记录它们的通用/深度链接行为。我们将在示例中实现它的虚拟版本。

首先,定义一个邀请内容类型,它只封装邀请中的信息,不包含任何功能。这样,您可以从数据类型开始,并根据如何将数据组合在一起来考虑您的代码。

迅速

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
/// 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

}

Objective-C

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
/// 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

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) {
        // ...
    }

}

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
)

此处唯一需要的数据是 URL,没有它您将无法邀请用户使用您的应用程序。其他数据的结构清晰地用于发送电子邮件,这使得它们在其他一些情况下有点尴尬 - 当通过文本发送邀请时,链接随附的简介可能类似于电子邮件主题,但在共享到社交媒体时文本随附的链接可能更像是电子邮件正文。您必须自己进行试验才能为您的应用找到最佳平衡,如果您不确定,您可以随时使用Remote Config之类的服务来允许您在应用启动后更改文本值。

迅速

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
/// 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()

}

Objective-C

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
/// 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

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) {
        // ...
    }

}

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) {
        // ...
    }
}

现在剩下的就是将其插入您选择的 UI 组件中。有关此邀请流程的完整实施,请参阅适用于 iOSAndroid的 GitHub 上的示例。

这些都是使您的用户能够向他们的朋友发送邀请的方法,这是最轻量级的邀请解决方案。许多流行的应用程序还通过自己的后端发送电子邮件来传递邀请,这需要集成邮件发送服务,但提供了许多其他方式无法提供的好处,只有一些小缺点。

优点:

  • 启用具有复杂标记的电子邮件,您的用户在发送前无法修改。
  • 启用更精细的跟踪/分析(即在您的后端发送成功和失败)。

缺点:

  • 电子邮件更有可能被标记为垃圾邮件
  • 需要与电子邮件递送服务集成
  • 需要应用内联系人权限

通常,通过您自己的电子邮件递送服务发送邀请会以多功能性为代价提供更一致且可能更丰富的邀请体验。

在您的应用中打开链接的内容

最后,您需要接收传递给您的应用程序的链接,以便您可以将链接的内容显示给收件人。这很容易使用动态链接 SDK:

iOS

在 iOS 上,您通过实现application:continueUserActivity:restorationHandler:方法来接收动态链接。在恢复处理程序中,您可以通过调用handleUniversalLink:completion:来获取动态链接。如果动态链接已传递给您的应用,您可以从FIRDynamicLinkurl属性中获取它。例如:

Objective-C

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
[[FIRDynamicLinks dynamicLinks]
    handleUniversalLink:userActivity.webpageURL
             completion:^(FIRDynamicLink * _Nullable dynamicLink,
                          NSError * _Nullable error) {
      NSString *link = dynamicLink.url;
      BOOL strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong;
      // ...
    }];

迅速

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
FIRDynamicLinks.dynamicLinks()?.handleUniversalLink(userActivity.webpageURL!) { (dynamiclink, error) in
    let link = dynamicLink.url
    let strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong
    // ...
}

此外,您必须在application:openURL:options:方法中调用dynamicLinkFromCustomSchemeURL:以接收作为自定义方案 URL 传递给您的应用程序的动态链接。例如:

Objective-C

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
if (dynamicLink) {
  NSString *link = dynamicLink.url;
  BOOL strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong;
  // ...
  return YES;
}

迅速

注意:此 Firebase 产品不适用于 macOS、Mac Catalyst、tvOS 或 watchOS 目标。
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()方法从动态链接获取数据:

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

Kotlin+KTX

Firebase.dynamicLinks
        .getDynamicLink(intent)
        .addOnCompleteListener { task ->
            if (!task.isSuccessful) {
                // Handle error
                // ...
            }

            val invite = FirebaseAppInvite.getInvitation(task.result)
            if (invite != null) {
                // Handle invite
                // ...
            }
        }

现在您有了link参数的值,您可以将链接的内容显示给接收者,或者以其他方式处理参数指定的数据。 URL 路由库可以帮助完成这项任务。