Skip to content

Commit

Permalink
Add receiving dynamic link capability to Firebase Dynamic Links Plugin (
Browse files Browse the repository at this point in the history
flutter#596)

Add receiving dynamic link capability to Firebase Dynamic Links Plugin (flutter#596)
  • Loading branch information
bparrishMines authored Jun 25, 2018
1 parent 6e2c26c commit 1f610b7
Show file tree
Hide file tree
Showing 23 changed files with 611 additions and 294 deletions.
4 changes: 4 additions & 0 deletions packages/firebase_dynamic_links/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.0.5

* Added capability to receive dynamic links.

## 0.0.4

* Fixed dynamic link dartdoc generation.
Expand Down
79 changes: 72 additions & 7 deletions packages/firebase_dynamic_links/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

A Flutter plugin to use the [Google Dynamic Links for Firebase API](https://firebase.google.com/docs/dynamic-links/).

With Dynamic Links, your users get the best available experience for the platform they open your link on. If a user opens a Dynamic Link on iOS or Android, they can be taken directly to the linked content in your native app. If a user opens the same Dynamic Link in a desktop browser, they can be taken to the equivalent content on your website.

In addition, Dynamic Links work across app installs: if a user opens a Dynamic Link on iOS or Android and doesn't have your app installed, the user can be prompted to install it; then, after installation, your app starts and can access the link.

For Flutter plugins for other Firebase products, see [FlutterFire.md](https://github.com/flutter/plugins/blob/master/FlutterFire.md).

*Note*: This plugin is still under development, and some APIs might not be available yet. [Feedback](https://github.com/flutter/flutter/issues) and [Pull Requests](https://github.com/flutter/plugins/pulls) are most welcome!
Expand All @@ -14,16 +18,18 @@ To use this plugin, add `firebase_dynamic_links` as a [dependency in your pubspe

## Create Dynamic Links

You can create short or long Dynamic Links with the Firebase Dynamic Links Builder API. This API accepts either a long Dynamic Link or an object containing Dynamic Link parameters, and returns a URL like the following example:
You create a Dynamic Link either by using the Firebase console, using a REST API, iOS or Android Builder API, Flutter API, or by forming a URL by adding Dynamic Link parameters to a domain specific to your app. These parameters specify the links you want to open, depending on the user's platform and whether your app is installed.

Below are instructions to create Dynamic Links using Flutter with the Firebase Dynamic Links API. This API accepts either a long Dynamic Link or an object containing Dynamic Link parameters, and returns a URL like the following example:

```
https://abc123.app.goo.gl/WXYZ
```

You can create a Dynamic Link programmatically by setting the following parameters and getting the DynamicLinkParameters.url parameter.
You can create a Dynamic Link programmatically by setting the following parameters and using the `DynamicLinkParameters.buildUrl()` method.

```dart
final DynamicLinkParameters components = new DynamicLinkParameters(
final DynamicLinkParameters parameters = new DynamicLinkParameters(
domain: 'abc123.app.goo.gl',
link: Uri.parse('https://example.com/'),
androidParameters: new AndroidParameters(
Expand All @@ -50,13 +56,13 @@ final DynamicLinkParameters components = new DynamicLinkParameters(
),
);
final Uri dynamicLink = await components.buildUrl();
final Uri dynamicUrl = await parameters.buildUrl();
```

To create a short Dynamic Link, build DynamicLinkParameters the same way, but use the DynamicLinkParameters.shortUrl parameter.
To create a short Dynamic Link, build `DynamicLinkParameters` the same way, but use the `DynamicLinkParameters.buildShortLink()` method.

```dart
final ShortDynamicLink shortDynamicLink = await components.buildShortLink();
final ShortDynamicLink shortDynamicLink = await parameters.buildShortLink();
final Uri shortUrl = shortDynamicLink.shortUrl;
```

Expand All @@ -65,12 +71,71 @@ To shorten a long Dynamic Link, use the DynamicLinkParameters.shortenUrl method.
```dart
final ShortDynamicLink shortenedLink = await DynamicLinkParameters.shortenUrl(
Uri.parse('https://abc123.app.goo.gl/?link=https://example.com/&apn=com.example.android&ibn=com.example.ios'),
new DynamicLinkParametersOptions(ShortDynamicLinkPathLength.short),
new DynamicLinkParametersOptions(ShortDynamicLinkPathLength.unguessable),
);
final Uri shortUrl = shortenedLink.shortUrl;
```

## Handle Received Dynamic Links

You can receive a Dynamic Link containing a deep link that takes the user to specific content within your app:

1. In the [Firebase Console](https://console.firebase.google.com), open the Dynamic Links section.
- Accept the terms of service if you are prompted to do so.
- Take note of your project's Dynamic Links domain, which is displayed at the top of the Dynamic Links page. You need your project's Dynamic Links domain to programmatically create Dynamic Links. A Dynamic Links domain looks like `APP_CODE.app.goo.gl`.

Receiving dynamic links on *iOS* requires a couple more steps than *Android*. If you only want to receive dynamic links on *Android*, skip to step 4. You can also follow a video on the next two steps [here.](https://youtu.be/sFPo296OQqk?t=2m40s)

2. In the **Info** tab of your *iOS* app's Xcode project:
- Create a new **URL Type** to be used for Dynamic Links.
- Set the **Identifier** field to a unique value and the **URL Schemes** field to be your bundle identifier, which is the default URL scheme used by Dynamic Links.

3. In the **Capabilities** tab of your app's Xcode project, enable **Associated Domains** and add the following to the **Associated Domains** list:

```
applinks:YOUR_SUBDOMAIN.page.link
```

4. To receive a dynamic link, call the `retrieveDynamicLink()` method from `FirebaseDynamicLinks`:

```dart
void main() {
runApp(new MaterialApp(
title: 'Dynamic Links Example',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => new MyHomeWidget(), // Default home route
'/helloworld': (BuildContext context) => new MyHelloWorldWidget(),
},
));
}
class _MyHomeWidgetState extends State<_MyHomeWidget> {
.
.
.
@override
void initState() {
super.initState();
_retrieveDynamicLink();
}
Future<void> _retrieveDynamicLink() async {
final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.retrieveDynamicLink();
final Uri deepLink = data?.link;
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path); // '/helloworld'
}
}
.
.
.
}
```

If your app did not open from a dynamic link, `retrieveDynamicLink()` will return `null`.

## Getting Started

See the `example` directory for a complete sample app using Google Dynamic Links for Firebase.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.google.android.gms.tasks.Task;
import com.google.firebase.dynamiclinks.DynamicLink;
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
import com.google.firebase.dynamiclinks.PendingDynamicLinkData;
import com.google.firebase.dynamiclinks.ShortDynamicLink;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
Expand All @@ -19,10 +20,16 @@

/** FirebaseDynamicLinksPlugin */
public class FirebaseDynamicLinksPlugin implements MethodCallHandler {
private Registrar registrar;

private FirebaseDynamicLinksPlugin(Registrar registrar) {
this.registrar = registrar;
}

public static void registerWith(Registrar registrar) {
final MethodChannel channel =
new MethodChannel(registrar.messenger(), "plugins.flutter.io/firebase_dynamic_links");
channel.setMethodCallHandler(new FirebaseDynamicLinksPlugin());
channel.setMethodCallHandler(new FirebaseDynamicLinksPlugin(registrar));
}

@Override
Expand All @@ -43,12 +50,43 @@ public void onMethodCall(MethodCall call, Result result) {
builder.setLongLink(url);
buildShortDynamicLink(builder, call, createShortLinkListener(result));
break;
case "FirebaseDynamicLinks#retrieveDynamicLink":
handleRetrieveDynamicLink(result);
break;
default:
result.notImplemented();
break;
}
}

private void handleRetrieveDynamicLink(final Result result) {
FirebaseDynamicLinks.getInstance()
.getDynamicLink(registrar.activity().getIntent())
.addOnCompleteListener(
registrar.activity(),
new OnCompleteListener<PendingDynamicLinkData>() {
@Override
public void onComplete(@NonNull Task<PendingDynamicLinkData> task) {
if (task.isSuccessful()) {
PendingDynamicLinkData data = task.getResult();
if (data != null) {
Map<String, Object> dynamicLink = new HashMap<>();
dynamicLink.put("link", data.getLink().toString());

Map<String, Object> androidData = new HashMap<>();
androidData.put("clickTimestamp", data.getClickTimestamp());
androidData.put("minimumVersion", data.getMinimumAppVersion());

dynamicLink.put("android", androidData);
result.success(dynamicLink);
return;
}
}
result.success(null);
}
});
}

private OnCompleteListener<ShortDynamicLink> createShortLinkListener(final Result result) {
return new OnCompleteListener<ShortDynamicLink>() {
@Override
Expand Down
4 changes: 4 additions & 0 deletions packages/firebase_dynamic_links/example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Demonstrates how to use the firebase_dynamic_links plugin.

## *Important*

The example app for this plugin only receives links on Android. Xcode has signing requirements that must be configured with an iOS app developer team id. Check the `firebase_dynamic_links/README.md` for more details.

## Getting Started

For help getting started with Flutter, view our online
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
8F5FCAC62093AD310024C3FF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8F5FCAC52093AD310024C3FF /* GoogleService-Info.plist */; };
8FE60D1920C0959F00E3A541 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8FE60D1820C0959F00E3A541 /* GoogleService-Info.plist */; };
8FF283695FD42FAFAA6F2588 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BEA489D8A0A4C9E6F14F37D /* libPods-Runner.a */; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -50,7 +50,8 @@
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
8F5FCAC52093AD310024C3FF /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
8FE60D1820C0959F00E3A541 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
8FE60D1A20C0962300E3A541 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
Expand Down Expand Up @@ -112,8 +113,9 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
8F5FCAC52093AD310024C3FF /* GoogleService-Info.plist */,
8FE60D1A20C0962300E3A541 /* Runner.entitlements */,
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
8FE60D1820C0959F00E3A541 /* GoogleService-Info.plist */,
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
Expand Down Expand Up @@ -185,6 +187,12 @@
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.SafariKeychain = {
enabled = 0;
};
};
};
};
};
Expand Down Expand Up @@ -212,7 +220,7 @@
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
8F5FCAC62093AD310024C3FF /* GoogleService-Info.plist in Resources */,
8FE60D1920C0959F00E3A541 /* GoogleService-Info.plist in Resources */,
9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
Expand Down Expand Up @@ -432,7 +440,10 @@
buildSettings = {
ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -444,8 +455,10 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firebaseDynamicLinksExample;
PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseCppDynamicLinksTestApp.dev;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
Expand All @@ -456,7 +469,10 @@
buildSettings = {
ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -468,8 +484,10 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firebaseDynamicLinksExample;
PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseCppDynamicLinksTestApp.dev;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@
<key>AD_UNIT_ID_FOR_INTERSTITIAL_TEST</key>
<string>ca-app-pub-3940256099942544/4411468910</string>
<key>CLIENT_ID</key>
<string>479882132969-fsfeuulh0bsmulsui3snqsa22ah2hf2t.apps.googleusercontent.com</string>
<string>479882132969-pn2ancg65o0e7r5ikte1qiciuvdghqf9.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.479882132969-fsfeuulh0bsmulsui3snqsa22ah2hf2t</string>
<string>com.googleusercontent.apps.479882132969-pn2ancg65o0e7r5ikte1qiciuvdghqf9</string>
<key>ANDROID_CLIENT_ID</key>
<string>479882132969-32qusitiag53931ck80h121ajhlc5a7e.apps.googleusercontent.com</string>
<key>API_KEY</key>
<string>AIzaSyBECOwLTAN6PU4Aet1b2QLGIb3kRK8Xjew</string>
<key>GCM_SENDER_ID</key>
<string>479882132969</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>io.flutter.plugins.firebaseDynamicLinksExample</string>
<string>com.google.FirebaseCppDynamicLinksTestApp.dev</string>
<key>PROJECT_ID</key>
<string>my-flutter-proj</string>
<key>STORAGE_BUCKET</key>
Expand All @@ -27,13 +29,13 @@
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<false></false>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:479882132969:ios:e4a6acfe225eead9</string>
<string>1:479882132969:ios:36e157824ba4dd3d</string>
<key>DATABASE_URL</key>
<string>https://my-flutter-proj.firebaseio.com</string>
</dict>
Expand Down
13 changes: 13 additions & 0 deletions packages/firebase_dynamic_links/example/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>Bundle ID</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.google.FirebaseCppDynamicLinksTestApp.dev</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>
Loading

0 comments on commit 1f610b7

Please sign in to comment.