├── .DS_Store
├── RNBranch
├── .DS_Store
├── RNBranch
│ ├── .DS_Store
│ ├── RNBranch.h
│ └── RNBranch.m
└── RNBranch.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── kevinstumpf.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ ├── xcuserdata
│ └── kevinstumpf.xcuserdatad
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── RNBranch.xcscheme
│ └── project.pbxproj
├── Podfile
├── android
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── dispatcher
│ │ └── rnbranch
│ │ ├── RNBranchPackage.java
│ │ └── RNBranchModule.java
└── build.gradle
├── .gitignore
├── package.json
├── LICENSE
├── index.js
└── README.md
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinstumpf/react-native-branch/HEAD/.DS_Store
--------------------------------------------------------------------------------
/RNBranch/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinstumpf/react-native-branch/HEAD/RNBranch/.DS_Store
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '7.1'
2 |
3 | xcodeproj 'RNBranch/RNBranch.xcodeproj'
4 |
5 | pod 'Branch'
6 |
7 |
--------------------------------------------------------------------------------
/RNBranch/RNBranch/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinstumpf/react-native-branch/HEAD/RNBranch/RNBranch/.DS_Store
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/RNBranch/RNBranch.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RNBranch/RNBranch.xcodeproj/project.xcworkspace/xcuserdata/kevinstumpf.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinstumpf/react-native-branch/HEAD/RNBranch/RNBranch.xcodeproj/project.xcworkspace/xcuserdata/kevinstumpf.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.1"
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 22
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | }
14 |
15 | dependencies {
16 | compile 'com.facebook.react:react-native:0.20.+'
17 | compile 'io.branch.sdk.android:library:1.+'
18 | }
--------------------------------------------------------------------------------
/RNBranch/RNBranch/RNBranch.h:
--------------------------------------------------------------------------------
1 | //
2 | // RNBranch.h
3 | // RNBranch
4 | //
5 | // Created by Kevin Stumpf on 1/28/16.
6 | //
7 |
8 | #import
9 | #import "RCTBridgeModule.h"
10 |
11 | @interface RNBranch : NSObject
12 |
13 | + (void)initSessionWithLaunchOptions:(NSDictionary *)launchOptions isReferrable:(BOOL)isReferrable;
14 | + (BOOL)handleDeepLink:(NSURL *)url;
15 | + (BOOL)continueUserActivity:(NSUserActivity *)userActivity;
16 |
17 | @end
--------------------------------------------------------------------------------
/RNBranch/RNBranch.xcodeproj/xcuserdata/kevinstumpf.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | RNBranch.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | B3A3CC351C5B0A070016AC52
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | node_modules
28 |
29 | # Optional npm cache directory
30 | .npm
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
35 |
36 | android/build
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-branch",
3 | "version": "0.0.12",
4 | "description": "Native Wrapper around Branch Metrics native SDKs",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [
10 | "react-native",
11 | "react-component",
12 | "ios",
13 | "android",
14 | "branch",
15 | "metrics",
16 | "deeplink",
17 | "deep",
18 | "link"
19 | ],
20 | "author": "Kevin Stumpf",
21 | "license": "MIT",
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/DispatcherInc/react-native-branch.git"
25 | },
26 | "bugs": {
27 | "url": "https://github.com/DispatcherInc/react-native-branch/issues"
28 | },
29 | "homepage": "https://github.com/DispatcherInc/react-native-branch#readme"
30 | }
31 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dispatcher/rnbranch/RNBranchPackage.java:
--------------------------------------------------------------------------------
1 | package com.dispatcher.rnbranch;
2 |
3 | import java.util.*;
4 |
5 | import com.facebook.react.ReactPackage;
6 | import com.facebook.react.bridge.JavaScriptModule;
7 | import com.facebook.react.bridge.NativeModule;
8 | import com.facebook.react.bridge.ReactApplicationContext;
9 | import com.facebook.react.uimanager.ViewManager;
10 |
11 |
12 | public class RNBranchPackage implements ReactPackage {
13 | @Override
14 | public List createNativeModules(
15 | ReactApplicationContext reactContext) {
16 | List modules = new ArrayList<>();
17 |
18 | modules.add(new RNBranchModule(reactContext));
19 |
20 | return modules;
21 | }
22 |
23 | @Override
24 | public List> createJSModules() {
25 | return Collections.emptyList();
26 | }
27 |
28 | @Override
29 | public List createViewManagers(ReactApplicationContext reactContext) {
30 | return Collections.emptyList();
31 | }
32 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 DispatcherInc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/RNBranch/RNBranch.xcodeproj/xcuserdata/kevinstumpf.xcuserdatad/xcschemes/RNBranch.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var { NativeModules, NativeAppEventEmitter, DeviceEventEmitter, Platform } = require('react-native');
2 |
3 | var rnBranch = NativeModules.RNBranch;
4 | var _ = require('lodash');
5 |
6 | // According to the React Native docs from 0.21, NativeAppEventEmitter is used for native iOS modules to emit events. DeviceEventEmitter is used for native Android modules.
7 | // Both are technically supported on Android -- but I chose to follow the suggested route by the documentation to minimize the risk of this code breaking with a future release
8 | // in case NativeAppEventEmitter ever got deprecated on Android
9 | const nativeEventEmitter = Platform.OS === 'ios' ? NativeAppEventEmitter : DeviceEventEmitter;
10 |
11 | class Branch {
12 | constructor() {
13 | //We listen to the initialization event AND retrieve the result to account for both scenarios in which the results may already be available or be posted at a later point in time
14 | nativeEventEmitter.addListener('RNBranch.initSessionFinished', this._onReceivedInitSessionResult);
15 |
16 | this._getInitSessionResult((result) => {
17 | if(!result) { //Not available yet => will come through with the initSessionFinished event
18 | return;
19 | }
20 |
21 | this._onReceivedInitSessionResult(result);
22 | });
23 | this._patientInitSessionObservers = [];
24 | };
25 |
26 | _onReceivedInitSessionResult = (result) => {
27 | this._initSessionResult = result;
28 |
29 | this._patientInitSessionObservers.forEach((cb) => {
30 | cb(result);
31 | });
32 | this._patientInitSessionObservers = [];
33 | };
34 |
35 | _getInitSessionResult = (callback) => {
36 | rnBranch.getInitSessionResult(callback);
37 | };
38 |
39 | getInitSessionResultPatiently = (callback) => {
40 | if(this._initSessionResult) {
41 | return callback(this._initSessionResult);
42 | }
43 |
44 | this._patientInitSessionObservers.push(callback);
45 | };
46 |
47 | setDebug = () => {
48 | rnBranch.setDebug();
49 | };
50 |
51 | getLatestReferringParams = (callback) => {
52 | rnBranch.getLatestReferringParams(callback);
53 | };
54 |
55 | getFirstReferringParams = (callback) => {
56 | rnBranch.getFirstReferringParams(callback);
57 | };
58 |
59 | setIdentity = (identity) => {
60 | rnBranch.setIdentity(identity);
61 | };
62 |
63 | logout = () => {
64 | rnBranch.logout();
65 | };
66 |
67 | userCompletedAction = (event, state = {}) => {
68 | rnBranch.userCompletedAction(event, state);
69 | };
70 |
71 | showShareSheet = (shareOptions = {}, branchUniversalObject = {}, linkProperties = {}, callback) => {
72 | callback = callback || (() => {});
73 | _.defaults(shareOptions, {messageHeader: "Check this out!", messageBody: "Check this cool thing out: "});
74 | _.defaults(branchUniversalObject, {canonicalIdentifier: "RNBranchSharedObjectId", contentTitle: "Cool Content!", contentDescription: "Cool Content Description", contentImageUrl: ""});
75 | _.defaults(linkProperties, {feature: 'share', channel: 'RNApp'});
76 |
77 | rnBranch.showShareSheet(shareOptions, branchUniversalObject, linkProperties, ({channel, completed, error}) => callback({channel, completed, error}));
78 | };
79 | }
80 |
81 | module.exports = new Branch();
--------------------------------------------------------------------------------
/RNBranch/RNBranch/RNBranch.m:
--------------------------------------------------------------------------------
1 | //
2 | // RNBranch.m
3 | // RNBranch
4 | //
5 | // Created by Kevin Stumpf on 1/28/16.
6 | //
7 |
8 | #import "RNBranch.h"
9 | #import "RCTBridgeModule.h"
10 | #import "RCTBridge.h"
11 | #import "RCTEventDispatcher.h"
12 | #import
13 | #import "BranchLinkProperties.h"
14 | #import "BranchUniversalObject.h"
15 |
16 |
17 | @implementation RNBranch
18 |
19 | NSString * const initSessionWithLaunchOptionsFinishedEventName = @"initSessionWithLaunchOptionsFinished";
20 | static NSDictionary* initSessionWithLaunchOptionsResult;
21 |
22 | @synthesize bridge = _bridge;
23 |
24 | RCT_EXPORT_MODULE();
25 |
26 | //Called by AppDelegate.m -- stores initSession result in static variables and raises initSessionFinished event that's captured by the RNBranch instance to emit it to React Native
27 | + (void)initSessionWithLaunchOptions:(NSDictionary *)launchOptions isReferrable:(BOOL)isReferrable {
28 | [[Branch getInstance] initSessionWithLaunchOptions:launchOptions isReferrable:isReferrable andRegisterDeepLinkHandler:^(NSDictionary *params, NSError *error) {
29 | initSessionWithLaunchOptionsResult = @{@"params": params ? params : [NSNull null], @"error": error ? [error localizedDescription] : [NSNull null]};
30 | [[NSNotificationCenter defaultCenter] postNotificationName:initSessionWithLaunchOptionsFinishedEventName object:initSessionWithLaunchOptionsResult]; //Forward to RNBranch instance
31 | }];
32 | }
33 |
34 | + (BOOL)handleDeepLink:(NSURL *)url {
35 | return [[Branch getInstance] handleDeepLink:url];
36 | }
37 |
38 | + (BOOL)continueUserActivity:(NSUserActivity *)userActivity {
39 | return [[Branch getInstance] continueUserActivity:userActivity];
40 | }
41 |
42 | - (id)init {
43 | self = [super init];
44 |
45 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onInitSessionFinished:) name:initSessionWithLaunchOptionsFinishedEventName object:nil];
46 |
47 | return self;
48 | }
49 |
50 | - (void) dealloc {
51 | [[NSNotificationCenter defaultCenter] removeObserver:self];
52 | }
53 |
54 | - (void) onInitSessionFinished:(NSNotification*) notification {
55 | [self.bridge.eventDispatcher sendAppEventWithName:@"RNBranch.initSessionFinished" body:[notification object]];
56 | }
57 |
58 |
59 | RCT_EXPORT_METHOD(getInitSessionResult:(RCTResponseSenderBlock)callback) {
60 | callback(@[initSessionWithLaunchOptionsResult ? initSessionWithLaunchOptionsResult : [NSNull null]]);
61 | }
62 |
63 | RCT_EXPORT_METHOD(setDebug) {
64 | Branch *branch = [Branch getInstance];
65 | [branch setDebug];
66 | }
67 |
68 | RCT_EXPORT_METHOD(getLatestReferringParams:(RCTResponseSenderBlock)callback)
69 | {
70 | Branch *branch = [Branch getInstance];
71 | callback(@[[branch getLatestReferringParams]]);
72 | }
73 |
74 | RCT_EXPORT_METHOD(getFirstReferringParams:(RCTResponseSenderBlock)callback)
75 | {
76 | Branch *branch = [Branch getInstance];
77 | callback(@[[branch getFirstReferringParams]]);
78 | }
79 |
80 | RCT_EXPORT_METHOD(setIdentity:(NSString *)identity)
81 | {
82 | Branch *branch = [Branch getInstance];
83 | [branch setIdentity:identity];
84 | }
85 |
86 | RCT_EXPORT_METHOD(logout)
87 | {
88 | Branch *branch = [Branch getInstance];
89 | [branch logout];
90 | }
91 |
92 | RCT_EXPORT_METHOD(userCompletedAction:(NSString *)event withState:(NSDictionary *)appState)
93 | {
94 | Branch *branch = [Branch getInstance];
95 | [branch userCompletedAction:event withState:appState];
96 | }
97 |
98 | RCT_EXPORT_METHOD(showShareSheet:(NSDictionary *)shareOptionsMap withBranchUniversalObject:(NSDictionary *)branchUniversalObjectMap withLinkProperties:(NSDictionary *)linkPropertiesMap withCallback:(RCTResponseSenderBlock)callback)
99 | {
100 | dispatch_async(dispatch_get_main_queue(), ^(void){
101 | BranchUniversalObject *branchUniversalObject = [[BranchUniversalObject alloc] initWithCanonicalIdentifier:[branchUniversalObjectMap objectForKey:@"canonicalIdentifier"]];
102 | branchUniversalObject.title = [branchUniversalObjectMap objectForKey:@"contentTitle"];
103 | branchUniversalObject.contentDescription = [branchUniversalObjectMap objectForKey:@"contentDescription"];
104 | branchUniversalObject.imageUrl = [branchUniversalObjectMap objectForKey:@"contentImageUrl"];
105 |
106 | NSDictionary* metaData = [branchUniversalObjectMap objectForKey:@"metadata"];
107 | if(metaData) {
108 | NSEnumerator *enumerator = [metaData keyEnumerator];
109 | id metaDataKey;
110 | while((metaDataKey = [enumerator nextObject])) {
111 | [branchUniversalObject addMetadataKey:metaDataKey value:[metaData objectForKey:metaDataKey]];
112 | }
113 | }
114 |
115 | BranchLinkProperties *linkProperties = [[BranchLinkProperties alloc] init];
116 | linkProperties.channel = [linkPropertiesMap objectForKey:@"channel"];
117 | linkProperties.feature = [linkPropertiesMap objectForKey:@"feature"];
118 |
119 | [branchUniversalObject showShareSheetWithLinkProperties:linkProperties
120 | andShareText:[shareOptionsMap objectForKey:@"messageBody"]
121 | fromViewController:nil
122 | completion:^(NSString *activityType, BOOL completed){
123 | NSDictionary *result = @{
124 | @"channel" : activityType ? activityType : [NSNull null],
125 | @"completed" : [NSNumber numberWithBool:completed],
126 | @"error" : [NSNull null]
127 | };
128 |
129 | callback(@[result]);
130 | }];
131 | });
132 | }
133 |
134 | @end
135 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Update: This library is now maintained by the Branch Metrics Team: https://github.com/BranchMetrics/React-Native-Deferred-Deep-Linking-SDK
2 |
3 | # react-native-branch
4 | Native Wrapper around Branch Metrics native SDKs. Tested with React Native 0.21.0.
5 |
6 | Supports iOS and Android.
7 |
8 | ## Usage
9 |
10 | ```js
11 | var branch = require('react-native-branch');
12 |
13 | //Receives the initSession's result as soon as it becomes available
14 | branch.getInitSessionResultPatiently(({params, error}) => { });
15 |
16 | branch.setDebug();
17 | branch.getLatestReferringParams((params) => { });
18 | branch.getFirstReferringParams((params) => { });
19 | branch.setIdentity("Your User's ID");
20 | branch.userCompletedAction("Purchased Item", {item: 123});
21 |
22 | var shareOptions = {messageHeader: "Check this out!", messageBody: "Check this cool thing out: "};
23 | var branchUniversalObject = {metadata: {prop1: "test", prop2: "abc"}, canonicalIdentifier: "RNBranchSharedObjectId", contentTitle: "Cool Content!", contentDescription: "Cool Content Description", contentImageUrl: ""};
24 | var linkProperties = {feature: 'share', channel: 'RNApp'};
25 | branch.showShareSheet(shareOptions, branchUniversalObject, linkProperties, ({channel, completed, error}) => {});
26 |
27 | branch.logout();
28 | ```
29 |
30 | ## Installation
31 |
32 | ```sh
33 | npm install rnpm -g
34 | npm install --save react-native-branch
35 | rnpm link react-native-branch
36 | cd node_modules/react-native-branch
37 | pod install #Only required for iOS
38 | ```
39 |
40 | ### Android
41 |
42 | #### Step 0 - Verify Library Linking
43 |
44 | *Sometimes rnpm link creates incorrect relative paths, leading to compilation errors*
45 |
46 | *Ensure that the following files look as described and all linked paths are correct*
47 |
48 | ```gradle
49 | // file: android/settings.gradle
50 | ...
51 |
52 | include ':react-native-branch', ':app'
53 |
54 | // The relative path to the react-native-branch directory tends to often be prefixed with one too many "../"s
55 | project(':react-native-branch').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-branch/android')
56 | ```
57 |
58 | ```gradle
59 | // file: android/app/build.gradle
60 | ...
61 |
62 | dependencies {
63 | ...
64 | compile project(':react-native-branch')
65 | }
66 | ```
67 |
68 | #### Step 1 - Initialize the RNBranchModule
69 |
70 | ```java
71 | // file: android/app/src/main/java/com/xxx/MainActivity.java
72 |
73 | import android.content.Intent; // <-- import
74 | import com.dispatcher.rnbranch.*; // <-- import
75 |
76 | public class MainActivity extends ReactActivity {
77 | // ...
78 |
79 | @Override
80 | protected List getPackages() {
81 | return Arrays.asList(
82 | new MainReactPackage(),
83 | new RNBranchPackage() // <-- add this line, if not already there
84 | );
85 | }
86 |
87 | // Add onStart
88 | @Override
89 | public void onStart() {
90 | super.onStart();
91 |
92 | RNBranchModule.initSession(this.getIntent().getData(), this);
93 | }
94 |
95 | // Add onNewIntent
96 | @Override
97 | public void onNewIntent(Intent intent) {
98 | this.setIntent(intent);
99 | }
100 |
101 | // ...
102 | }
103 | ```
104 |
105 | #### Step 2 - Configure Manifest
106 |
107 | Please follow [these instructions] (https://dev.branch.io/getting-started/sdk-integration-guide/guide/android/#configure-manifest)
108 |
109 | #### Step 3 - Register for Google Play Install Referrer
110 |
111 | Please follow [these instructions](https://dev.branch.io/getting-started/sdk-integration-guide/guide/android/#register-for-google-play-install-referrer)
112 |
113 | Note: The "receiver" element needs to be added to the "application" node in AndroidManifest.xml
114 |
115 | #### Step 4 - Register a URI scheme
116 |
117 | Please follow [these instructions](https://dev.branch.io/getting-started/sdk-integration-guide/guide/android/#register-a-uri-scheme)
118 |
119 | Notes:
120 | - The "intent-filter" element needs to be added to the activity node, whose android:name is "com.yourAppName.MainActivity". This node is in the "application" node.
121 | - If you already have an intent-filter tag, this has to be added as an additional one.
122 | - Make sure to replace "yourApp" with the scheme you specified in the Branch dashboard.
123 |
124 | #### Step 5 - Enable Auto Session Management
125 |
126 | Please follow [these instructions](https://dev.branch.io/getting-started/sdk-integration-guide/guide/android/#enable-auto-session-management)
127 |
128 | Note: Just add the "android:name" attribute to your "application" node in your AndroidManifest.xml
129 |
130 | #### Step 6 - Enable App Links for Android M and above (Optional but Recommended)
131 |
132 | Please follow [these instructions](https://dev.branch.io/getting-started/universal-app-links/guide/android/)
133 |
134 | ### iOS
135 |
136 | #### Step 1 - Modifications to your React Native XCode Project
137 |
138 | - Drag and Drop /node_modules/react-native-branch/Pods/Pods.xcodeproj into the Libraries folder of your project in XCode (as described in Step 1 [here](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#content))
139 | - Drag and Drop the Pods.xcodeproj's Products's libBranch.a into your project's target's "Linked Frameworks and Libraries" section (as described in Step 2 [here](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#content))
140 |
141 |
142 |
143 | #### Step 2 - Modifications to AppDelegate.m
144 |
145 | Import RNBranch.h at the top
146 |
147 | ```objective-c
148 | #import "RNBranch.h"
149 | ```
150 |
151 |
152 | Initialize the Branch Session in didFinishLaunchingWithOptions
153 |
154 | ```objective-c
155 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
156 | {
157 | [RNBranch initSessionWithLaunchOptions:launchOptions isReferrable:YES];
158 |
159 | NSURL *jsCodeLocation;
160 | ///
161 | }
162 | ```
163 |
164 | Add the openURL and continueUserActivity functions
165 |
166 | ```objective-c
167 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
168 | if (![RNBranch handleDeepLink:url]) {
169 | // do other deep link routing for the Facebook SDK, Pinterest SDK, etc
170 | }
171 | return YES;
172 | }
173 |
174 | - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
175 | return [RNBranch continueUserActivity:userActivity];
176 | }
177 | ```
178 |
179 | #### Step 3 - Add the branch_key to your plist
180 |
181 | Add a String entry branch_key with your branch key to your plist (as described [here](https://dev.branch.io/references/ios_sdk/#add-your-branch-key-to-your-project))
182 |
183 | #### Step 4 - Register a URI Scheme for Direct Deep Linking (Optional but Recommended)
184 |
185 | Please follow these instructions [here](https://dev.branch.io/references/ios_sdk/#register-a-uri-scheme-direct-deep-linking-optional-but-recommended)
186 |
187 | #### Step 5 - Configure for Universal Linking
188 |
189 | Please follow these instructions [here](https://dev.branch.io/references/ios_sdk/#support-universal-linking-ios-9)
190 |
--------------------------------------------------------------------------------
/RNBranch/RNBranch.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | B3A3CC3A1C5B0A070016AC52 /* RNBranch.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B3A3CC391C5B0A070016AC52 /* RNBranch.h */; };
11 | B3A3CC3C1C5B0A070016AC52 /* RNBranch.m in Sources */ = {isa = PBXBuildFile; fileRef = B3A3CC3B1C5B0A070016AC52 /* RNBranch.m */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXCopyFilesBuildPhase section */
15 | B3A3CC341C5B0A070016AC52 /* CopyFiles */ = {
16 | isa = PBXCopyFilesBuildPhase;
17 | buildActionMask = 2147483647;
18 | dstPath = "include/$(PRODUCT_NAME)";
19 | dstSubfolderSpec = 16;
20 | files = (
21 | B3A3CC3A1C5B0A070016AC52 /* RNBranch.h in CopyFiles */,
22 | );
23 | runOnlyForDeploymentPostprocessing = 0;
24 | };
25 | /* End PBXCopyFilesBuildPhase section */
26 |
27 | /* Begin PBXFileReference section */
28 | B3A3CC361C5B0A070016AC52 /* libRNBranch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNBranch.a; sourceTree = BUILT_PRODUCTS_DIR; };
29 | B3A3CC391C5B0A070016AC52 /* RNBranch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNBranch.h; sourceTree = ""; };
30 | B3A3CC3B1C5B0A070016AC52 /* RNBranch.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNBranch.m; sourceTree = ""; };
31 | /* End PBXFileReference section */
32 |
33 | /* Begin PBXFrameworksBuildPhase section */
34 | B3A3CC331C5B0A070016AC52 /* Frameworks */ = {
35 | isa = PBXFrameworksBuildPhase;
36 | buildActionMask = 2147483647;
37 | files = (
38 | );
39 | runOnlyForDeploymentPostprocessing = 0;
40 | };
41 | /* End PBXFrameworksBuildPhase section */
42 |
43 | /* Begin PBXGroup section */
44 | B3A3CC2D1C5B0A070016AC52 = {
45 | isa = PBXGroup;
46 | children = (
47 | B3A3CC381C5B0A070016AC52 /* RNBranch */,
48 | B3A3CC371C5B0A070016AC52 /* Products */,
49 | );
50 | sourceTree = "";
51 | };
52 | B3A3CC371C5B0A070016AC52 /* Products */ = {
53 | isa = PBXGroup;
54 | children = (
55 | B3A3CC361C5B0A070016AC52 /* libRNBranch.a */,
56 | );
57 | name = Products;
58 | sourceTree = "";
59 | };
60 | B3A3CC381C5B0A070016AC52 /* RNBranch */ = {
61 | isa = PBXGroup;
62 | children = (
63 | B3A3CC391C5B0A070016AC52 /* RNBranch.h */,
64 | B3A3CC3B1C5B0A070016AC52 /* RNBranch.m */,
65 | );
66 | path = RNBranch;
67 | sourceTree = "";
68 | };
69 | /* End PBXGroup section */
70 |
71 | /* Begin PBXNativeTarget section */
72 | B3A3CC351C5B0A070016AC52 /* RNBranch */ = {
73 | isa = PBXNativeTarget;
74 | buildConfigurationList = B3A3CC3F1C5B0A070016AC52 /* Build configuration list for PBXNativeTarget "RNBranch" */;
75 | buildPhases = (
76 | B3A3CC321C5B0A070016AC52 /* Sources */,
77 | B3A3CC331C5B0A070016AC52 /* Frameworks */,
78 | B3A3CC341C5B0A070016AC52 /* CopyFiles */,
79 | );
80 | buildRules = (
81 | );
82 | dependencies = (
83 | );
84 | name = RNBranch;
85 | productName = RNBranch;
86 | productReference = B3A3CC361C5B0A070016AC52 /* libRNBranch.a */;
87 | productType = "com.apple.product-type.library.static";
88 | };
89 | /* End PBXNativeTarget section */
90 |
91 | /* Begin PBXProject section */
92 | B3A3CC2E1C5B0A070016AC52 /* Project object */ = {
93 | isa = PBXProject;
94 | attributes = {
95 | LastUpgradeCheck = 0720;
96 | ORGANIZATIONNAME = Dispatcher;
97 | TargetAttributes = {
98 | B3A3CC351C5B0A070016AC52 = {
99 | CreatedOnToolsVersion = 7.2;
100 | };
101 | };
102 | };
103 | buildConfigurationList = B3A3CC311C5B0A070016AC52 /* Build configuration list for PBXProject "RNBranch" */;
104 | compatibilityVersion = "Xcode 3.2";
105 | developmentRegion = English;
106 | hasScannedForEncodings = 0;
107 | knownRegions = (
108 | en,
109 | );
110 | mainGroup = B3A3CC2D1C5B0A070016AC52;
111 | productRefGroup = B3A3CC371C5B0A070016AC52 /* Products */;
112 | projectDirPath = "";
113 | projectRoot = "";
114 | targets = (
115 | B3A3CC351C5B0A070016AC52 /* RNBranch */,
116 | );
117 | };
118 | /* End PBXProject section */
119 |
120 | /* Begin PBXSourcesBuildPhase section */
121 | B3A3CC321C5B0A070016AC52 /* Sources */ = {
122 | isa = PBXSourcesBuildPhase;
123 | buildActionMask = 2147483647;
124 | files = (
125 | B3A3CC3C1C5B0A070016AC52 /* RNBranch.m in Sources */,
126 | );
127 | runOnlyForDeploymentPostprocessing = 0;
128 | };
129 | /* End PBXSourcesBuildPhase section */
130 |
131 | /* Begin XCBuildConfiguration section */
132 | B3A3CC3D1C5B0A070016AC52 /* Debug */ = {
133 | isa = XCBuildConfiguration;
134 | buildSettings = {
135 | ALWAYS_SEARCH_USER_PATHS = NO;
136 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
137 | CLANG_CXX_LIBRARY = "libc++";
138 | CLANG_ENABLE_MODULES = YES;
139 | CLANG_ENABLE_OBJC_ARC = YES;
140 | CLANG_WARN_BOOL_CONVERSION = YES;
141 | CLANG_WARN_CONSTANT_CONVERSION = YES;
142 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
143 | CLANG_WARN_EMPTY_BODY = YES;
144 | CLANG_WARN_ENUM_CONVERSION = YES;
145 | CLANG_WARN_INT_CONVERSION = YES;
146 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
147 | CLANG_WARN_UNREACHABLE_CODE = YES;
148 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
149 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
150 | COPY_PHASE_STRIP = NO;
151 | DEBUG_INFORMATION_FORMAT = dwarf;
152 | ENABLE_STRICT_OBJC_MSGSEND = YES;
153 | ENABLE_TESTABILITY = YES;
154 | GCC_C_LANGUAGE_STANDARD = gnu99;
155 | GCC_DYNAMIC_NO_PIC = NO;
156 | GCC_NO_COMMON_BLOCKS = YES;
157 | GCC_OPTIMIZATION_LEVEL = 0;
158 | GCC_PREPROCESSOR_DEFINITIONS = (
159 | "DEBUG=1",
160 | "$(inherited)",
161 | );
162 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
163 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
164 | GCC_WARN_UNDECLARED_SELECTOR = YES;
165 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
166 | GCC_WARN_UNUSED_FUNCTION = YES;
167 | GCC_WARN_UNUSED_VARIABLE = YES;
168 | HEADER_SEARCH_PATHS = (
169 | "$(inherited)",
170 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
171 | "$(SRCROOT)/../../react-native/React/**",
172 | );
173 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
174 | MTL_ENABLE_DEBUG_INFO = YES;
175 | ONLY_ACTIVE_ARCH = YES;
176 | SDKROOT = iphoneos;
177 | };
178 | name = Debug;
179 | };
180 | B3A3CC3E1C5B0A070016AC52 /* Release */ = {
181 | isa = XCBuildConfiguration;
182 | buildSettings = {
183 | ALWAYS_SEARCH_USER_PATHS = NO;
184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
185 | CLANG_CXX_LIBRARY = "libc++";
186 | CLANG_ENABLE_MODULES = YES;
187 | CLANG_ENABLE_OBJC_ARC = YES;
188 | CLANG_WARN_BOOL_CONVERSION = YES;
189 | CLANG_WARN_CONSTANT_CONVERSION = YES;
190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
191 | CLANG_WARN_EMPTY_BODY = YES;
192 | CLANG_WARN_ENUM_CONVERSION = YES;
193 | CLANG_WARN_INT_CONVERSION = YES;
194 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
195 | CLANG_WARN_UNREACHABLE_CODE = YES;
196 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
197 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
198 | COPY_PHASE_STRIP = NO;
199 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
200 | ENABLE_NS_ASSERTIONS = NO;
201 | ENABLE_STRICT_OBJC_MSGSEND = YES;
202 | GCC_C_LANGUAGE_STANDARD = gnu99;
203 | GCC_NO_COMMON_BLOCKS = YES;
204 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
205 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
206 | GCC_WARN_UNDECLARED_SELECTOR = YES;
207 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
208 | GCC_WARN_UNUSED_FUNCTION = YES;
209 | GCC_WARN_UNUSED_VARIABLE = YES;
210 | HEADER_SEARCH_PATHS = (
211 | "$(inherited)",
212 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
213 | "$(SRCROOT)/../../react-native/React/**",
214 | );
215 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
216 | MTL_ENABLE_DEBUG_INFO = NO;
217 | SDKROOT = iphoneos;
218 | VALIDATE_PRODUCT = YES;
219 | };
220 | name = Release;
221 | };
222 | B3A3CC401C5B0A070016AC52 /* Debug */ = {
223 | isa = XCBuildConfiguration;
224 | buildSettings = {
225 | HEADER_SEARCH_PATHS = (
226 | "$(inherited)",
227 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
228 | "$(SRCROOT)/../../react-native/React/**",
229 | "\"$(SRCROOT)/../../../ios/Pods/Headers/Public\"",
230 | "\"$(SRCROOT)/../../../ios/Pods/Headers/Public/Branch\"",
231 | );
232 | OTHER_LDFLAGS = "-ObjC";
233 | PRODUCT_NAME = "$(TARGET_NAME)";
234 | SKIP_INSTALL = YES;
235 | };
236 | name = Debug;
237 | };
238 | B3A3CC411C5B0A070016AC52 /* Release */ = {
239 | isa = XCBuildConfiguration;
240 | buildSettings = {
241 | HEADER_SEARCH_PATHS = (
242 | "$(inherited)",
243 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
244 | "$(SRCROOT)/../../react-native/React/**",
245 | "\"$(SRCROOT)/../../../ios/Pods/Headers/Public\"",
246 | "\"$(SRCROOT)/../../../ios/Pods/Headers/Public/Branch\"",
247 | );
248 | OTHER_LDFLAGS = "-ObjC";
249 | PRODUCT_NAME = "$(TARGET_NAME)";
250 | SKIP_INSTALL = YES;
251 | };
252 | name = Release;
253 | };
254 | /* End XCBuildConfiguration section */
255 |
256 | /* Begin XCConfigurationList section */
257 | B3A3CC311C5B0A070016AC52 /* Build configuration list for PBXProject "RNBranch" */ = {
258 | isa = XCConfigurationList;
259 | buildConfigurations = (
260 | B3A3CC3D1C5B0A070016AC52 /* Debug */,
261 | B3A3CC3E1C5B0A070016AC52 /* Release */,
262 | );
263 | defaultConfigurationIsVisible = 0;
264 | defaultConfigurationName = Release;
265 | };
266 | B3A3CC3F1C5B0A070016AC52 /* Build configuration list for PBXNativeTarget "RNBranch" */ = {
267 | isa = XCConfigurationList;
268 | buildConfigurations = (
269 | B3A3CC401C5B0A070016AC52 /* Debug */,
270 | B3A3CC411C5B0A070016AC52 /* Release */,
271 | );
272 | defaultConfigurationIsVisible = 0;
273 | defaultConfigurationName = Release;
274 | };
275 | /* End XCConfigurationList section */
276 | };
277 | rootObject = B3A3CC2E1C5B0A070016AC52 /* Project object */;
278 | }
279 |
--------------------------------------------------------------------------------
/android/src/main/java/com/dispatcher/rnbranch/RNBranchModule.java:
--------------------------------------------------------------------------------
1 | package com.dispatcher.rnbranch;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.IntentFilter;
6 | import android.content.BroadcastReceiver;
7 | import android.net.Uri;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.content.LocalBroadcastManager;
10 | import android.util.Log;
11 | import android.os.Handler;
12 |
13 | import com.facebook.react.ReactActivity;
14 | import com.facebook.react.bridge.*;
15 | import com.facebook.react.modules.core.*;
16 |
17 | import io.branch.referral.*;
18 | import io.branch.referral.util.*;
19 | import io.branch.indexing.BranchUniversalObject;
20 |
21 | import org.json.*;
22 | import java.util.*;
23 |
24 | public class RNBranchModule extends ReactContextBaseJavaModule {
25 | public static final String REACT_CLASS = "RNBranch";
26 | private static final String NATIVE_INIT_SESSION_FINISHED_EVENT = "onInitSessionFinished";
27 | private static final String RN_INIT_SESSION_FINISHED_EVENT = "RNBranch.initSessionFinished";
28 |
29 | private static JSONObject initSessionResult = null;
30 | private BroadcastReceiver mInitSessionEventReceiver = null;
31 |
32 | public static void initSession(Uri uri, ReactActivity reactActivity) {
33 | Branch branch = Branch.getInstance();
34 | branch.initSession(new Branch.BranchReferralInitListener(){
35 | private ReactActivity mActivity = null;
36 |
37 | @Override
38 | public void onInitFinished(JSONObject referringParams, BranchError error) {
39 | Log.d(REACT_CLASS, "onInitFinished");
40 | JSONObject result = new JSONObject();
41 | try{
42 | result.put("params", referringParams != null ? referringParams : JSONObject.NULL);
43 | result.put("error", error != null ? error.getMessage() : JSONObject.NULL);
44 | } catch(JSONException ex) {
45 | try {
46 | result.put("error", "Failed to convert result to JSONObject: " + ex.getMessage());
47 | } catch(JSONException k) {}
48 | }
49 | initSessionResult = result;
50 | LocalBroadcastManager.getInstance(mActivity).sendBroadcast(new Intent(NATIVE_INIT_SESSION_FINISHED_EVENT));
51 | }
52 |
53 | private Branch.BranchReferralInitListener init(ReactActivity activity) {
54 | mActivity = activity;
55 | return this;
56 | }
57 | }.init(reactActivity), uri, reactActivity);
58 | }
59 |
60 | public RNBranchModule(ReactApplicationContext reactContext) {
61 | super(reactContext);
62 |
63 | Log.d(REACT_CLASS, "ctor");
64 |
65 | forwardInitSessionFinishedEventToReactNative(reactContext);
66 | }
67 |
68 | private void forwardInitSessionFinishedEventToReactNative(ReactApplicationContext reactContext) {
69 | mInitSessionEventReceiver = new BroadcastReceiver() {
70 | RNBranchModule mBranchModule;
71 |
72 | @Override
73 | public void onReceive(Context context, Intent intent) {
74 | mBranchModule.sendRNEvent(RN_INIT_SESSION_FINISHED_EVENT, convertJsonToMap(initSessionResult));
75 | }
76 |
77 | private BroadcastReceiver init(RNBranchModule branchModule) {
78 | mBranchModule = branchModule;
79 | return this;
80 | }
81 | }.init(this);
82 |
83 | LocalBroadcastManager.getInstance(reactContext).registerReceiver(mInitSessionEventReceiver, new IntentFilter(NATIVE_INIT_SESSION_FINISHED_EVENT));
84 | }
85 |
86 | @Override
87 | public void onCatalystInstanceDestroy() {
88 | LocalBroadcastManager.getInstance(getReactApplicationContext()).unregisterReceiver(mInitSessionEventReceiver);
89 | }
90 |
91 | @Override
92 | public String getName() {
93 | return REACT_CLASS;
94 | }
95 |
96 | @ReactMethod
97 | public void getInitSessionResult(Callback cb) {
98 | cb.invoke(convertJsonToMap(initSessionResult));
99 | }
100 |
101 | @ReactMethod
102 | public void setDebug() {
103 | Branch branch = Branch.getInstance();
104 | branch.setDebug();
105 | }
106 |
107 | @ReactMethod
108 | public void getLatestReferringParams(Callback cb) {
109 | Branch branch = Branch.getInstance();
110 | cb.invoke(convertJsonToMap(branch.getLatestReferringParams()));
111 | }
112 |
113 | @ReactMethod
114 | public void getFirstReferringParams(Callback cb) {
115 | Branch branch = Branch.getInstance();
116 | cb.invoke(convertJsonToMap(branch.getFirstReferringParams()));
117 | }
118 |
119 | @ReactMethod
120 | public void setIdentity(String identity) {
121 | Branch branch = Branch.getInstance();
122 | branch.setIdentity(identity);
123 | }
124 |
125 | @ReactMethod
126 | public void logout() {
127 | Branch branch = Branch.getInstance();
128 | branch.logout();
129 | }
130 |
131 | @ReactMethod
132 | public void userCompletedAction(String event, ReadableMap appState) throws JSONException {
133 | Branch branch = Branch.getInstance();
134 | branch.userCompletedAction(event, convertMapToJson(appState));
135 | }
136 |
137 | @ReactMethod
138 | public void showShareSheet(ReadableMap shareOptionsMap, ReadableMap branchUniversalObjectMap, ReadableMap linkPropertiesMap, Callback cb) {
139 | Context context = getReactApplicationContext();
140 |
141 | Handler mainHandler = new Handler(context.getMainLooper());
142 |
143 | Runnable myRunnable = new Runnable() {
144 | Callback mCb;
145 | Context mContext;
146 | ReadableMap shareOptionsMap, branchUniversalObjectMap, linkPropertiesMap;
147 |
148 | private Runnable init(ReadableMap _shareOptionsMap, ReadableMap _branchUniversalObjectMap, ReadableMap _linkPropertiesMap, Callback cb, Context context) {
149 | mCb = cb;
150 | mContext = context;
151 | shareOptionsMap = _shareOptionsMap;
152 | branchUniversalObjectMap = _branchUniversalObjectMap;
153 | linkPropertiesMap = _linkPropertiesMap;
154 |
155 | return this;
156 | }
157 |
158 | @Override
159 | public void run() {
160 | ShareSheetStyle shareSheetStyle = new ShareSheetStyle(mContext, shareOptionsMap.getString("messageHeader"), shareOptionsMap.getString("messageBody"))
161 | .setCopyUrlStyle(mContext.getResources().getDrawable(android.R.drawable.ic_menu_send), "Copy", "Added to clipboard")
162 | .setMoreOptionStyle(mContext.getResources().getDrawable(android.R.drawable.ic_menu_search), "Show more")
163 | .addPreferredSharingOption(SharingHelper.SHARE_WITH.EMAIL)
164 | .addPreferredSharingOption(SharingHelper.SHARE_WITH.TWITTER)
165 | .addPreferredSharingOption(SharingHelper.SHARE_WITH.MESSAGE)
166 | .addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK);
167 |
168 | BranchUniversalObject branchUniversalObject = new BranchUniversalObject()
169 | // The identifier is what Branch will use to de-dupe the content across many different Universal Objects
170 | .setCanonicalIdentifier(branchUniversalObjectMap.getString("canonicalIdentifier"))
171 | // This is where you define the open graph structure and how the object will appear on Facebook or in a deepview
172 | .setTitle(branchUniversalObjectMap.getString("contentTitle"))
173 | .setContentDescription(branchUniversalObjectMap.getString("contentDescription"))
174 | .setContentImageUrl(branchUniversalObjectMap.getString("contentImageUrl"));
175 |
176 | if(branchUniversalObjectMap.hasKey("metadata")) {
177 | ReadableMap metadataMap = branchUniversalObjectMap.getMap("metadata");
178 | ReadableMapKeySetIterator iterator = metadataMap.keySetIterator();
179 | while (iterator.hasNextKey()) {
180 | String metadataKey = iterator.nextKey();
181 | Object metadataObject = getReadableMapObjectForKey(metadataMap, metadataKey);
182 | branchUniversalObject.addContentMetadata(metadataKey, metadataObject.toString());
183 | }
184 | }
185 |
186 | LinkProperties linkProperties = new LinkProperties()
187 | .setChannel(linkPropertiesMap.getString("channel"))
188 | .setFeature(linkPropertiesMap.getString("feature"));
189 |
190 | branchUniversalObject.showShareSheet(getCurrentActivity(),
191 | linkProperties,
192 | shareSheetStyle,
193 | new Branch.BranchLinkShareListener() {
194 | private Callback mCallback = null;
195 |
196 | @Override
197 | public void onShareLinkDialogLaunched() {
198 | }
199 | @Override
200 | public void onShareLinkDialogDismissed() {
201 | if(mCallback == null) {
202 | return;
203 | }
204 |
205 | WritableMap map = new WritableNativeMap();
206 | map.putString("channel", null);
207 | map.putBoolean("completed", false);
208 | map.putString("error", null);
209 | mCallback.invoke(map);
210 | mCallback = null;
211 | }
212 | @Override
213 | public void onLinkShareResponse(String sharedLink, String sharedChannel, BranchError error) {
214 | if(mCallback == null) {
215 | return;
216 | }
217 |
218 | WritableMap map = new WritableNativeMap();
219 | map.putString("channel", sharedChannel);
220 | map.putBoolean("completed", true);
221 | map.putString("error", (error != null ? error.getMessage() : null));
222 | mCallback.invoke(map);
223 | mCallback = null;
224 | }
225 | @Override
226 | public void onChannelSelected(String channelName) {
227 | }
228 |
229 | private Branch.BranchLinkShareListener init(Callback callback) {
230 | mCallback = callback;
231 | return this;
232 | }
233 | }.init(mCb));
234 | }
235 | }.init(shareOptionsMap, branchUniversalObjectMap, linkPropertiesMap, cb, context);
236 |
237 | mainHandler.post(myRunnable);
238 | }
239 |
240 | public void sendRNEvent(String eventName, @Nullable WritableMap params) {
241 | // This should avoid the crash in getJSModule() at startup
242 | // See also: https://github.com/walmartreact/react-native-orientation-listener/issues/8
243 |
244 | ReactApplicationContext context = getReactApplicationContext();
245 | Handler mainHandler = new Handler(context.getMainLooper());
246 |
247 | Runnable poller = new Runnable() {
248 |
249 | private Runnable init(ReactApplicationContext _context, Handler _mainHandler, String _eventName, WritableMap _params) {
250 | mMainHandler = _mainHandler;
251 | mEventName = _eventName;
252 | mContext = _context;
253 | mParams = _params;
254 | return this;
255 | }
256 |
257 | final int pollDelayInMs = 100;
258 | final int maxTries = 300;
259 |
260 | int tries = 1;
261 | String mEventName;
262 | WritableMap mParams;
263 | Handler mMainHandler;
264 | ReactApplicationContext mContext;
265 |
266 | @Override
267 | public void run() {
268 | try {
269 | Log.d(REACT_CLASS, "Catalyst instance poller try " + Integer.toString(tries));
270 | if (mContext.hasActiveCatalystInstance()) {
271 | Log.d(REACT_CLASS, "Catalyst instance active");
272 | mContext
273 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
274 | .emit(mEventName, mParams);
275 | } else {
276 | tries++;
277 | if (tries <= maxTries) {
278 | mMainHandler.postDelayed(this, pollDelayInMs);
279 | } else {
280 | Log.e(REACT_CLASS, "Could not get Catalyst instance");
281 | }
282 | }
283 | }
284 | catch (Exception e) {
285 | e.printStackTrace();
286 | }
287 | }
288 | }.init(context, mainHandler, eventName, params);
289 |
290 | Log.d(REACT_CLASS, "sendRNEvent");
291 |
292 | mainHandler.post(poller);
293 | }
294 |
295 | private static Object getReadableMapObjectForKey(ReadableMap readableMap, String key) {
296 | switch(readableMap.getType(key)) {
297 | case Null:
298 | return "Null";
299 | case Boolean:
300 | return readableMap.getBoolean(key);
301 | case Number:
302 | return readableMap.getDouble(key);
303 | case String:
304 | return readableMap.getString(key);
305 | default:
306 | return "Unsupported Type";
307 | }
308 | }
309 |
310 | private static JSONObject convertMapToJson(ReadableMap readableMap) throws JSONException {
311 | JSONObject object = new JSONObject();
312 | ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
313 | while (iterator.hasNextKey()) {
314 | String key = iterator.nextKey();
315 | switch (readableMap.getType(key)) {
316 | case Null:
317 | object.put(key, JSONObject.NULL);
318 | break;
319 | case Boolean:
320 | object.put(key, readableMap.getBoolean(key));
321 | break;
322 | case Number:
323 | object.put(key, readableMap.getDouble(key));
324 | break;
325 | case String:
326 | object.put(key, readableMap.getString(key));
327 | break;
328 | case Map:
329 | object.put(key, convertMapToJson(readableMap.getMap(key)));
330 | break;
331 | case Array:
332 | object.put(key, convertArrayToJson(readableMap.getArray(key)));
333 | break;
334 | }
335 | }
336 | return object;
337 | }
338 |
339 | private static JSONArray convertArrayToJson(ReadableArray readableArray) throws JSONException {
340 | JSONArray array = new JSONArray();
341 | for (int i = 0; i < readableArray.size(); i++) {
342 | switch (readableArray.getType(i)) {
343 | case Null:
344 | break;
345 | case Boolean:
346 | array.put(readableArray.getBoolean(i));
347 | break;
348 | case Number:
349 | array.put(readableArray.getDouble(i));
350 | break;
351 | case String:
352 | array.put(readableArray.getString(i));
353 | break;
354 | case Map:
355 | array.put(convertMapToJson(readableArray.getMap(i)));
356 | break;
357 | case Array:
358 | array.put(convertArrayToJson(readableArray.getArray(i)));
359 | break;
360 | }
361 | }
362 | return array;
363 | }
364 |
365 | private static WritableMap convertJsonToMap(JSONObject jsonObject) {
366 | if(jsonObject == null) {
367 | return null;
368 | }
369 |
370 | WritableMap map = new WritableNativeMap();
371 |
372 | try {
373 | Iterator iterator = jsonObject.keys();
374 | while (iterator.hasNext()) {
375 | String key = iterator.next();
376 | Object value = jsonObject.get(key);
377 | if (value instanceof JSONObject) {
378 | map.putMap(key, convertJsonToMap((JSONObject) value));
379 | } else if (value instanceof JSONArray) {
380 | map.putArray(key, convertJsonToArray((JSONArray) value));
381 | } else if (value instanceof Boolean) {
382 | map.putBoolean(key, (Boolean) value);
383 | } else if (value instanceof Integer) {
384 | map.putInt(key, (Integer) value);
385 | } else if (value instanceof Double) {
386 | map.putDouble(key, (Double) value);
387 | } else if (value instanceof String) {
388 | map.putString(key, (String) value);
389 | } else {
390 | map.putString(key, value.toString());
391 | }
392 | }
393 | } catch(JSONException ex) {
394 | map.putString("error", "Failed to convert JSONObject to WriteableMap: " + ex.getMessage());
395 | }
396 |
397 | return map;
398 | }
399 |
400 | private static WritableArray convertJsonToArray(JSONArray jsonArray) throws JSONException {
401 | WritableArray array = new WritableNativeArray();
402 |
403 | for (int i = 0; i < jsonArray.length(); i++) {
404 | Object value = jsonArray.get(i);
405 | if (value instanceof JSONObject) {
406 | array.pushMap(convertJsonToMap((JSONObject) value));
407 | } else if (value instanceof JSONArray) {
408 | array.pushArray(convertJsonToArray((JSONArray) value));
409 | } else if (value instanceof Boolean) {
410 | array.pushBoolean((Boolean) value);
411 | } else if (value instanceof Integer) {
412 | array.pushInt((Integer) value);
413 | } else if (value instanceof Double) {
414 | array.pushDouble((Double) value);
415 | } else if (value instanceof String) {
416 | array.pushString((String) value);
417 | } else {
418 | array.pushString(value.toString());
419 | }
420 | }
421 | return array;
422 | }
423 | }
424 |
--------------------------------------------------------------------------------