Пригласите пользователей в свое приложение

Один из наиболее эффективных способов побудить новых пользователей установить ваше приложение — разрешить пользователям делиться контентом из вашего приложения со своими друзьями. С помощью динамических ссылок вы можете создать отличный опыт обмена между пользователями: пользователи, которые получают рекомендации контента от своих друзей, могут щелкнуть ссылку и перейти непосредственно к общему контенту в вашем приложении, даже если им нужно перейти в приложение. Store или Google Play Store, чтобы сначала установить приложение.

Сочетая прилипчивость рефералов пользователей и постоянство динамических ссылок, вы можете создавать функции обмена и рефералов между пользователями, которые привлекают новых пользователей, привлекая их непосредственно к содержимому вашего приложения или предоставляя рекламные акции, которые взаимно выгодны рефереру и рефералу. .

Ключевые преимущества

  • Новые пользователи, открывающие ваше приложение в первый раз, получают персонализированный опыт первого запуска, контекстуализированный в зависимости от того, чем их друг хотел поделиться с ними. Например, вы можете отобразить контент, которым с ними поделились, или автоматически связать их с другом, который их пригласил.
  • Позволяет пользователям легко обмениваться контентом со своими друзьями на разных платформах, независимо от того, установлено ли у их друзей ваше приложение.

Вот как начать!

Настройте новый проект Firebase и установите Dynamic Links SDK в свое приложение.

Установка Dynamic Links SDK позволяет Firebase передавать данные о Dynamic Link приложению, в том числе после того, как пользователь установит приложение.

Теперь пришло время настроить ссылки, которые пользователи могут отправлять своим друзьям. Не беспокойтесь, если друзья ваших пользователей еще не установили приложение; Динамические ссылки могут позаботиться об этом за вас.

Для каждого элемента контента, которым вы хотите поделиться, создайте динамическую ссылку .

При создании динамической ссылки вам потребуется указать URL-адрес HTTP или HTTPS в качестве параметра 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

Прежде чем делиться этими ссылками, вы можете использовать API-интерфейс сокращения URL-адресов динамических ссылок Firebase для создания более дружественных URL-адресов. Короткая динамическая ссылка выглядит следующим образом:

https://example.page.link/WXYZ

Какую бы ссылку вы ни использовали, когда пользователи открывают динамическую ссылку на своем устройстве, приложение, указанное параметром apn (на Android) или параметрами ibi и isi (на 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
}

Цель-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
}

Когда у вас есть динамическая ссылка, вы можете добавить кнопку «Поделиться» в свой пользовательский интерфейс, которая запустит стандартный процесс обмена платформой:

Быстрый

Примечание. Этот продукт 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)
}

Цель-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"))
}

В этом примере пользовательский интерфейс общего доступа по умолчанию автоматически представляет список приложений для обмена ссылкой, поэтому вы можете настроить его в своем собственном приложении с помощью всего нескольких строк кода.

Вместо того, чтобы пользователь выбирал контакты и составлял сообщение в вашем приложении, эти действия делегируются приложению, которое они выбирают в диалоговом окне общего доступа. Кроме того, делегирование общего доступа другим приложениям означает, что вам не нужно запрашивать у пользователя разрешения на контакты, и позволяет пользователям выбирать из расширенного списка контактов в выбранном ими приложении. Чтобы упростить обмен в социальных сетях, вы можете добавить метаданные предварительного просмотра в социальных сетях к своей динамической ссылке, которая будет отображаться вместе со ссылкой в ​​основных социальных сетях.

Иногда, однако, просто отправить голую ссылку без текста недостаточно для убедительного реферала. Сопроводив ссылку коротким сообщением и, если возможно, более подробной презентацией, пользователи могут понять ценностное предложение реферала, когда они его получат:

iOS

rewarded referral screenshotrewarded referral screenshot with share sheet

Андроид

rewarded referral screenshotrewarded referral screenshot with share sheet

Хотя это сложнее, чем в предыдущем примере, подход будет более или менее таким же. На этом экране есть большая графика с ценностным предложением приглашения и кнопками для публикации в основных социальных сетях. В этом потоке пользовательского интерфейса есть некоторая избыточность — некоторые каналы обмена представлены индивидуально, чтобы обеспечить дополнительную настройку сообщений для конкретных каналов, например добавление строки темы в приглашения по электронной почте. В этом меню приглашения мы:

  • Представьте кнопки электронной почты, текстовых сообщений и копирования ссылок и соответствующим образом настройте их сообщения. Электронное письмо будет включать тему и может включать более длинное тело с разрывами строк, изображениями и пробелами; текст должен включать в себя более короткое тело с разрывами строк, но с небольшим пробелом и без изображений; а копирование ссылок должно просто копировать ссылку и ничего больше.
  • Используйте системный общий пользовательский интерфейс для всего остального, включая короткое приглашение, сопровождающее ссылку.
  • Глубокая ссылка через схему 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

}

Цель-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()

}

Цель-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) {
        // ...
    }
}

Теперь все, что осталось, это подключить это к компоненту пользовательского интерфейса по вашему выбору. Полную реализацию этого потока приглашений см. в примерах на GitHub для iOS и Android .

Это все методы, позволяющие вашим пользователям отправлять приглашения своим друзьям, что является самым простым решением для приглашения. Многие популярные приложения также доставляют приглашения, отправляя электронные письма через свой собственный сервер, что требует интеграции службы отправки почты, но предлагает ряд преимуществ, которые в противном случае недоступны, за исключением нескольких незначительных недостатков.

Плюсы:

  • Включает электронные письма со сложной разметкой, которую пользователь не может изменить перед отправкой.
  • Обеспечивает более детальное отслеживание/аналитику (т. е. отправку сведений об успехах и неудачах на серверную часть).

Минусы:

  • Электронные письма с большей вероятностью будут помечены как спам
  • Требуется интеграция со службой доставки электронной почты
  • Требуются разрешения для контактов в приложении

Как правило, отправка приглашений через вашу собственную службу доставки электронной почты обеспечивает более последовательный и потенциально более богатый опыт приглашений за счет универсальности.

Откройте связанный контент в своем приложении.

Наконец, вам нужно получить ссылку, переданную вашему приложению, чтобы вы могли отобразить связанный контент получателю. Это легко сделать с помощью Dynamic Links SDK:

iOS

В iOS вы получаете динамическую ссылку, реализуя метод application:continueUserActivity:restorationHandler: В обработчике восстановления вы можете получить динамическую ссылку, вызвав handleUniversalLink:completion: . Если динамическая ссылка была передана вашему приложению, вы можете получить ее из свойства url FIRDynamicLink . Например:

Цель-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
    // ...
}

Кроме того, вы должны вызвать dynamicLinkFromCustomSchemeURL: в application:openURL:options: для получения динамических ссылок, передаваемых вашему приложению в виде URL-адресов пользовательской схемы. Например:

Цель-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 , вы можете отобразить связанное содержимое получателю или каким-либо другим образом обработать данные, указанные параметром. С этой задачей может помочь библиотека URL-маршрутизации, такая как JLRoutes .

Если вы получаете ссылку, предназначенную для определенного получателя, убедитесь, что достоверность соответствия динамической ссылки 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-маршрутизации.