Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

通過允許用戶共享您的應用程序的內容來為您的應用程序獲取新用戶

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

讓新用戶安裝您的應用程序的最有效方法之一是讓您的用戶與他們的朋友分享您的應用程序中的內容。使用動態鏈接,您可以創建出色的用戶對用戶共享體驗:從朋友那裡收到內容推薦的用戶可以單擊鏈接並直接轉到您的應用程序中的共享內容,即使他們必須訪問應用程序商店或 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 路由庫可以幫助完成這項任務。