Google is committed to advancing racial equity for Black communities. See how.
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

邀請用戶使用您的應用

吸引新用戶安裝您的應用程序的最有效方法之一是使您的用戶與朋友共享您的應用程序中的內容。借助動態鏈接,您可以創建出色的用戶間共享體驗:收到朋友推薦內容的用戶可以單擊鏈接,直接進入應用程序中的共享內容,即使他們必須轉到應用程序商店或Google Play商店先安裝您的應用。

通過結合用戶推薦的粘性和動態鏈接的持久性,您可以創建用戶到用戶的共享和推薦功能,從而通過將新用戶直接吸引到您的應用程序內容或提供促進互惠推薦人和被推薦人的促銷來吸引新用戶。

主要好處

  • 首次打開您的應用程序的新用戶將獲得定制化的首次運行體驗,該體驗根據他們的朋友想要與他們分享的內容進行上下文化。例如,您可以顯示與他們共享的內容,或自動將它們與邀請他們的朋友聯繫起來。
  • 無論用戶是否安裝了您的應用,都可以使用戶輕鬆地跨平台與朋友共享內容。

這是上手的方法!

設置一個新的Firebase項目並將Dynamic Links SDK安裝到您的應用程序中。

安裝Dynamic Links SDK後,Firebase可以將有關Dynamic Link的數據傳遞到應用程序,包括在用戶安裝應用程序之後。

現在是時候建立用戶可以發送給朋友的鏈接了。如果您的用戶朋友尚未安裝該應用程序,請不要擔心。動態鏈接可以為您解決這些問題。

對於要共享的內容的每個元素,創建一個動態鏈接

創建動態鏈接時,您需要提供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動態鏈接URL縮短程序API生成外觀更友好的URL。簡短的動態鏈接類似於以下示例:

https://example.page.link/WXYZ

無論您使用哪個鏈接,當用戶在設備上打開動態鏈接時,由apn參數(在Android上)或ibiisi參數(在iOS上)指定的應用會將用戶帶到Play商店或App Store安裝該應用如果尚未安裝。然後,在安裝並打開該應用程序後,“ link”參數中指定的URL會傳遞給該應用程序。

首先,請看一個基於聊天室的聊天應用程序(例如環聊)的簡單示例,該應用程序會生成邀請人們聊天室的鏈接。

的iOS

chat app screenshotchat app screenshot with share sheet

安卓系統

chat app screenshotchat app screenshot with share sheet

迅速

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

爪哇

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中,這將啟動標準平台共享流程:

迅速

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

物鏡

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

爪哇

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方案的深層鏈接或到另一個應用程序的通用鏈接,該應用程序具有處理您的應用程序邀請的特殊邏輯。在您的組織和其他應用之間的合作關係之外,這將無法工作,對於較小的組織,這可能不是一種選擇。也就是說,某些應用可能會公開記錄其通用/深層鏈接行為。我們將在示例中實現此虛擬版本。

首先,定義邀請內容類型,該類型僅將信息封裝在邀請中,不包含任何功能。這樣,您可以從數據類型開始,並根據如何將數據組合在一起來考慮代碼。

迅速

/// 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

}

物鏡

/// 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

爪哇

/**
 * 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唯一需要的數據就是URL,沒有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()

}

物鏡

/// 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

爪哇

/**
 * 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組件中。有關此邀請流程的完整實現,請參閱GitHub上的示例(適用於iOSAndroid)

這些都是使用戶能夠向他們的朋友發送邀請的方法,這是最輕巧的邀請解決方案。許多流行的應用程序還通過通過自己的後端發送電子郵件來發送邀請,這需要集成郵件發送服務,但具有許多其他好處,而這些好處是只有少數缺點才能實現的。

優點:

  • 啟用具有復雜標記的電子郵件,用戶在發送之前無法對其進行修改。
  • 啟用更詳細的跟踪/分析(即在後端發送成功和失敗)。

缺點:

  • 電子郵件更有可能被標記為垃圾郵件
  • 需要與電子郵件傳遞服務集成
  • 需要應用內聯繫人權限

通常,通過您自己的電子郵件傳遞服務發送邀請會以多功能性為代價提供更一致且可能更豐富的邀請體驗。

在您的應用中打開鏈接的內容

最後,您需要接收傳遞到您的應用程序的鏈接,以便可以將鏈接的內容顯示給收件人。使用動態鏈接SDK可以輕鬆實現:

的iOS

在iOS上,您可以通過實現application:continueUserActivity:restorationHandler:方法來接收動態鏈接。在恢復處理程序中,可以通過調用handleUniversalLink:completion:獲得動態鏈接。如果將動態鏈接傳遞到您的應用程序,則可以從FIRDynamicLinkurl屬性中獲取它。例如:

物鏡

[[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: application:openURL:options:以接收作為自定義方案URL傳遞到您的應用程序的動態鏈接。例如:

物鏡

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參數的值,您可以將鏈接的內容顯示給收件人,或以其他方式處理該參數指定的數據。 URL路由庫(例如JLRoutes)可以幫助完成此任務。

如果您收到的鏈接是針對特定收件人的,則在運行任何特定於用戶的邏輯之前,請確保動態鏈接的匹配置信度strong

安卓

在Android上,您可以使用getDynamicLink()方法從動態鏈接獲取數據:

爪哇

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路由庫可以幫助完成此任務。