One of the most effective ways to get new users to install your app is by enabling your users to share content from your app with their friends. With Dynamic Links, you can create a great user-to-user sharing experience: users who receive content recommendations from their friends can click a link and be taken directly to the shared content in your app, even if they have to go to the App Store or Google Play Store to install your app first.
By combining the stickiness of user referrals and the persistence of Dynamic Links, you can create user-to-user sharing and referral features that bring in new users by drawing them directly to your app's content or serving promotions that mutually benefit the referrer and the referred.
Key benefits
- New users opening your app for the first time get a customized first-run experience that is contextualized based on what their friend wanted to share with them. For example, you can display the content that was shared with them, or automatically connect them with the friend that invited them.
- Makes it easy for users to share content with their friends across platforms whether or not their friends have your app installed.
Here's how to get started!
Set up Firebase and the Dynamic Links SDK
Set up a new Firebase project and install the Dynamic Links SDK into your app.
Installing the Dynamic Links SDK allows Firebase to pass along data about the Dynamic Link to the app, including after the user installs the app.
Create Dynamic Links
Now it's time to set up the links that users can send to their friends. Don't worry if your users' friends don't have the app installed yet; Dynamic Links can take care of that for you.
For each element of content you want to be shareable, create a Dynamic Link.
When you create the Dynamic Link, you'll need to provide an HTTP or HTTPS URL as the
link
parameter that will be used to identify the content you're sharing. If
you have a website with equivalent content, you should use your website's URLs.
This will ensure that these links render correctly on a platform that
doesn't support Dynamic Links, such as a desktop browser. For example:
https://example.page.link/?link=https://www.example.com/content?item%3D1234&apn=com.example.android&ibi=com.example.ios&isi=12345
You can also add additional info to the data payload by adding URL-encoded parameters—for example, to indicate that the link is intended for a particular user, such as in a game invitation.
https://example.page.link/?link=https://www.example.com/invitation?gameid%3D1234%26referrer%3D555&apn=com.example.android&ibi=com.example.ios&isi=12345
Before you share these links, you might want to use the Firebase Dynamic Links URL shortener API to generate friendlier-looking URLs. A short Dynamic Link looks like the following example:
https://example.page.link/WXYZ
Whichever link you use, when users open the Dynamic Link on their device,
the app specified by the apn
parameter (on Android) or the ibi
and isi
parameters (on iOS) will take users to the Play Store or App Store to install
the app if it isn't already installed. Then, when the app is installed and
opened, the URL specified in the 'link' parameter gets passed to the app.
Add "Share" buttons that send Dynamic Links
First, take a look at this simple example of a room-based chat app like Hangouts that generates links to invite people to chat rooms.
iOS


Android


Swift
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
- (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(); }
Once you have a dynamic link, you can add a share button to your UI that will launch the standard platform sharing flow:
Swift
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
- (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")); }
In this example, the default share UI automatically presents a list of apps for sharing the link, so it's something you can set up in your own app with just a few lines of code.
Rather than having the user select contacts and compose the message in your app, these actions are delegated to the app they choose from the share dialog. Additionally, delegating sharing to other apps means you don't have to ask the user for contacts permissions and allows users to select from an expanded contacts list within their chosen app. To better facilitate social sharing, you can add social media preview metadata to your dynamic link that will be displayed along with the link in major social channels.
Sometimes, though, just sending a bare link with no text isn't enough for a compelling referral. By accompanying the link with a short message and, if possible, a richer presentation, users can understand the referral's value proposition when they receive it:
iOS


Android


Though this is more complex than the last example, the approach will be more or less the same. On this screen there's a large graphic with the invite's value proposition and buttons for sharing to major social channels. There's some redundancy in this UI flow—some sharing channels are presented individually to allow for more channel-specific message customization, like adding a subject line to email invites. In this invite menu, we:
- Present email, text message, and copy link share buttons, and customize their messages appropriately. Email will include a subject and can include a longer body with line breaks, images, and whitespace; text should include a shorter body with line breaks but little whitespace and no images; and link copying should just copy the link and nothing else.
- Use the system share UI for everything else, including a short invitation message to accompany the link.
- Deep link via URL scheme or universal link to another app that has special logic for handling your app's invitations. This won't work outside of a partnership between your organization and the other app, and is likely not an option for smaller organizations. That said, some apps may publicly document their universal/deep linking behavior. We'll implement a dummy version of this in our sample.
First, define an invite content type, which encapsulates just the information in an invitation and contains no functionality. This way, you can start with the data types and think about your code in terms of how it pieces that data together.
Swift
/// 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
/// 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) { // ... } }
The only required piece of data here is the URL, without which you're unable to invite users to your app. The other pieces of data are clearly structured toward sending emails, which makes them a little awkward in some other cases--when sending an invite over text, the blurb accompanying the link might read similarly to an email subject, but when sharing to social media the link accompanying text might be more like an email body. You'll have to experiment with this yourself to find the best balance for your app, and if you're unsure, you can always use a service like Remote Config to allow you to change the text values after app launch.
Swift
/// 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
/// 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) { // ... } }
Now all that's left is plug this into a UI component of your choosing. For the full implementation of this invite flow, see the samples on GitHub for iOS and Android.
These are all methods for enabling your users to send invitations to their friends, which is the most lightweight invitation solution. Many popular apps also deliver invitations by sending emails through their own backend, which requires integrating a mail sending service, but offers a number of benefits that aren't otherwise available with only a few minor drawbacks.
Pros:
- Enables emails with complex markup that cannot be modified by your user before sending.
- Enables more granular tracking/analytics (i.e. send successes and failures on your backend).
Cons:
- Emails more likely to be flagged as spam
- Requires integration with an email delivery service
- Requires in-app contacts permissions
Generally, sending invitations through your own email delivery service provides a more consistent and potentially richer invitation experience at the cost of versatility.
Open the linked content in your app
Finally, you need to receive the link that's passed to your app so you can display the linked content to the recipient. This is easy using the Dynamic Links SDK:
iOS
On iOS, you receive the Dynamic Link by implementing the
application:continueUserActivity:restorationHandler:
method. In the
restoration handler, you can get the Dynamic Link by calling handleUniversalLink:completion:
.
If a Dynamic Link was passed to your app, you can get it from the url
property of
the FIRDynamicLink
.
For example:
Objective-C
[[FIRDynamicLinks dynamicLinks]
handleUniversalLink:userActivity.webpageURL
completion:^(FIRDynamicLink * _Nullable dynamicLink,
NSError * _Nullable error) {
NSString *link = dynamicLink.url;
BOOL strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong;
// ...
}];
Swift
FIRDynamicLinks.dynamicLinks()?.handleUniversalLink(userActivity.webpageURL!) { (dynamiclink, error) in
let link = dynamicLink.url
let strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong
// ...
}
Additionally, you must call dynamicLinkFromCustomSchemeURL:
in the application:openURL:options:
method to receive Dynamic Links passed to your
app as custom scheme URLs. For example:
Objective-C
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
if (dynamicLink) {
NSString *link = dynamicLink.url;
BOOL strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong;
// ...
return YES;
}
Swift
let dynamicLink = FIRDynamicLinks.dynamicLinks()?.dynamicLinkFromCustomSchemeURL(url)
if let dynamicLink = dynamicLink {
let link = dynamicLink.url
let strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong
// ...
return true
}
Now that you have the value of the link
parameter, you can display the linked
content to the recipient, or process the data specified by the parameter in some
other way. A URL-routing library such as JLRoutes
can help with this task.
If you're receiving a link intended for a specific recipient, ensure that the
Dynamic Link's match confidence is strong
before running any user-specific logic.
Android
On Android, you use the getDynamicLink()
method to get data from the Dynamic Link:
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 // ... } } });
Now that you have the value of the link
parameter, you can display the linked
content to the recipient, or process the data specified by the parameter in some
other way. A URL-routing library can help with this task.