├── .DS_Store ├── .gitignore ├── LICENSE ├── Podfile ├── README.md ├── RNBranch ├── .DS_Store ├── RNBranch.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── kevinstumpf.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── kevinstumpf.xcuserdatad │ │ └── xcschemes │ │ ├── RNBranch.xcscheme │ │ └── xcschememanagement.plist └── RNBranch │ ├── .DS_Store │ ├── RNBranch.h │ └── RNBranch.m ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── dispatcher │ └── rnbranch │ ├── RNBranchModule.java │ └── RNBranchPackage.java ├── index.js └── package.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinstumpf/react-native-branch/295e720218300705b8d295acd2690e8ae991b036/.DS_Store -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '7.1' 2 | 3 | xcodeproj 'RNBranch/RNBranch.xcodeproj' 4 | 5 | pod 'Branch' 6 | 7 | -------------------------------------------------------------------------------- /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/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinstumpf/react-native-branch/295e720218300705b8d295acd2690e8ae991b036/RNBranch/.DS_Store -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/295e720218300705b8d295acd2690e8ae991b036/RNBranch/RNBranch.xcodeproj/project.xcworkspace/xcuserdata/kevinstumpf.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /RNBranch/RNBranch/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinstumpf/react-native-branch/295e720218300705b8d295acd2690e8ae991b036/RNBranch/RNBranch/.DS_Store -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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(); -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------