├── .github ├── lock.yml ├── reaction.yml ├── release-drafter.yml └── stale.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── .travis ├── before_install.sh └── run.sh ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── idehub │ └── GoogleAnalyticsBridge │ ├── ConvertReadableToMap.java │ ├── ConvertToWritable.java │ ├── GoogleAnalyticsBridge.java │ ├── GoogleAnalyticsBridgePackage.java │ ├── GoogleAnalyticsPayload.java │ ├── GoogleAnalyticsSettings.java │ └── GoogleTagManagerBridge.java ├── examples └── rn57example │ ├── .babelrc │ ├── .buckconfig │ ├── .flowconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .watchmanconfig │ ├── App.js │ ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── keystores │ │ ├── BUCK │ │ └── debug.keystore.properties │ └── settings.gradle │ ├── app.json │ ├── index.js │ ├── ios │ ├── example-tvOS │ │ └── Info.plist │ ├── example-tvOSTests │ │ └── Info.plist │ ├── example.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── example-tvOS.xcscheme │ │ │ └── example.xcscheme │ ├── example │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── main.m │ └── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m │ ├── package.json │ ├── tsconfig.json │ └── yarn.lock ├── ios └── RCTGoogleAnalyticsBridge │ ├── RCTGoogleAnalyticsBridge.xcodeproj │ └── project.pbxproj │ ├── RCTGoogleAnalyticsBridge │ ├── GoogleAnalyticsPayload.h │ ├── GoogleAnalyticsPayload.m │ ├── RCTGoogleAnalyticsBridge.h │ ├── RCTGoogleAnalyticsBridge.m │ ├── RCTGoogleAnalyticsSettings.h │ ├── RCTGoogleAnalyticsSettings.m │ ├── RCTGoogleTagManagerBridge.h │ └── RCTGoogleTagManagerBridge.m │ └── google-analytics-lib │ ├── GAI.h │ ├── GAIDictionaryBuilder.h │ ├── GAIEcommerceFields.h │ ├── GAIEcommerceProduct.h │ ├── GAIEcommerceProductAction.h │ ├── GAIEcommercePromotion.h │ ├── GAIFields.h │ ├── GAILogger.h │ ├── GAITrackedViewController.h │ ├── GAITracker.h │ ├── TAGContainer.h │ ├── TAGContainerOpener.h │ ├── TAGDataLayer.h │ ├── TAGLogger.h │ ├── TAGManager.h │ ├── libAdIdAccess.a │ └── libGoogleAnalyticsServices.a ├── package.json ├── react-native-google-analytics-bridge.podspec ├── src ├── GoogleAnalyticsSettings.ts ├── GoogleAnalyticsTracker.ts ├── GoogleTagManager.ts ├── Helpers │ └── FunctionCallTagHandler │ │ ├── FunctionCallTagHandlerAndroid.ts │ │ ├── FunctionCallTagHandlerIOS.ts │ │ ├── index.ts │ │ └── models.ts ├── NativeBridges.ts ├── index.ts └── models │ ├── Analytics.ts │ ├── Custom.ts │ ├── DataLayerEvent.ts │ └── Product.ts ├── tsconfig.json └── yarn.lock /.github/lock.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before a closed issue or pull request is locked 2 | daysUntilLock: 365 3 | # Issues and pull requests with these labels will not be locked. Set to `[]` to disable 4 | exemptLabels: [] 5 | # Label to add before locking, such as `outdated`. Set to `false` to disable 6 | lockLabel: false 7 | # Comment to post before locking. Set to `false` to disable 8 | lockComment: > 9 | This thread has been automatically locked since there has not been 10 | any recent activity after it was closed. Please open a new issue for 11 | related bugs. 12 | # Assign `resolved` as the reason for locking. Set to `false` to disable 13 | setLockReason: true 14 | 15 | # Limit to only `issues` or `pulls` 16 | # only: issues 17 | 18 | # Optionally, specify configuration settings just for `issues` or `pulls` 19 | # issues: 20 | # exemptLabels: 21 | # - help-wanted 22 | # lockLabel: outdated 23 | 24 | # pulls: 25 | # daysUntilLock: 30 26 | 27 | # Repository to extend settings from 28 | # _extends: repo -------------------------------------------------------------------------------- /.github/reaction.yml: -------------------------------------------------------------------------------- 1 | # Issues and pull requests with these labels accept reaction comments. 2 | # Set to `[]` to disable 3 | exemptLabels: [] 4 | 5 | # Replace matching comments with this message, `{comment-author}` is an 6 | # optional placeholder. Set to `false` to disable 7 | reactionComment: > 8 | :wave: @{comment-author}, did you mean to use 9 | a [reaction](https://git.io/vhzhC) instead? 10 | 11 | # Limit to only `issues` or `pulls` 12 | # only: issues 13 | 14 | # Optionally, specify configuration settings just for `issues` or `pulls` 15 | # issues: 16 | # exemptLabels: 17 | # - party-parrot 18 | 19 | # pulls: 20 | # reactionComment: false 21 | 22 | # Repository to extend settings from 23 | # _extends: repo -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | template: | 2 | ## What’s Changed 3 | 4 | $CHANGES -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | !**/*.xcodeproj 3 | !**/*.pbxproj 4 | !**/*.xcworkspacedata 5 | !**/*.xcsettings 6 | !**/*.xcscheme 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.xcuserstate 22 | project.xcworkspace 23 | 24 | # Xcode, Gradle 25 | build/ 26 | 27 | # Android 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # Node 34 | node_modules 35 | *.log 36 | .nvm 37 | package-lock.json 38 | 39 | # OS X 40 | .DS_Store 41 | 42 | # Project 43 | dist -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # example project 2 | examples/ 3 | src/ 4 | # build 5 | .travis 6 | .travis.yml 7 | yarn.lock 8 | npm-debug.log 9 | yarn-error.log 10 | .DS_Store 11 | # Github stuff 12 | ISSUE_TEMPLATE.md 13 | .github 14 | # Xcode 15 | # 16 | build/ 17 | *.pbxuser 18 | !default.pbxuser 19 | *.mode1v3 20 | !default.mode1v3 21 | *.mode2v3 22 | !default.mode2v3 23 | *.perspectivev3 24 | !default.perspectivev3 25 | xcuserdata 26 | *.xccheckout 27 | *.moved-aside 28 | DerivedData 29 | *.hmap 30 | *.ipa 31 | *.xcuserstate 32 | project.xcworkspace 33 | # Android/IntelliJ 34 | # 35 | build/ 36 | .idea 37 | .gradle 38 | local.properties 39 | *.iml 40 | !android/src/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | - curl -o- -L https://yarnpkg.com/install.sh | bash 3 | - export PATH="$HOME/.yarn/bin:$PATH" 4 | 5 | matrix: 6 | include: 7 | - language: node_js 8 | node_js: 8 9 | env: LANE='node' 10 | cache: 11 | yarn: true 12 | script: .travis/run.sh 13 | - language: android 14 | sudo: required 15 | jdk: oraclejdk8 16 | before_cache: 17 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 18 | - rm -rf $HOME/.gradle/caches/*/plugin-resolution/ 19 | cache: 20 | directories: 21 | - $HOME/.yarn-cache 22 | - $HOME/.gradle/caches/ 23 | - $HOME/.gradle/wrapper/ 24 | before_install: 25 | - nvm install 8 26 | - .travis/before_install.sh 27 | android: 28 | components: 29 | - build-tools-27.0.3 30 | - android-27 31 | - extra-android-m2repository 32 | - extra-google-google_play_services 33 | - extra-google-m2repository 34 | - addon-google_apis-google-16 35 | script: .travis/run.sh 36 | - language: objective-c 37 | os: osx 38 | osx_image: xcode9.3 39 | node_js: 8 40 | cache: 41 | - bundler 42 | - yarn 43 | env: LANE='ios' 44 | before_install: 45 | - nvm install 8 46 | - .travis/before_install.sh 47 | script: .travis/run.sh 48 | 49 | notifications: 50 | slack: 51 | secure: qYpqErlZSfd5a88azsJqRkR0dKGXJknLoNsB/MuutbMbsX2HZzRfee9pDR1CDfcLlyBInRoN2lzv2B6xfrRDFnXWB/K9sRjTmzwXVPZbI1KDIi0CpQpgioZeNu2vaqJroX++1RdBCqUE9cHPRUmgo5/SqS4Qnl13uGTyGX4rYHrRK6HC1mXZ7/rVvLg6UnXn6Mb46DSRnFEmGRNlHbw/TmaYDZpFtjxqI9UyTfgrTNaMpXkSrfXlQzYwAfJUaWNClXtk87SFx54UIeu2elCwoPYZozeGDQMi1evPnGUFXwQ+vfnJTFzwt4klr6cdZ1/spmzxzewGheG3pmUnYaKxf3QTD3ppOyE+A44/9eEMib7qUxo2cIv2KOyYLE/pAu3293GhvP4SEdBELLzVHDh4brc6nNndNetJ+GKHeXqIOx48fe3hwfZ+UDCV3KzjUbeiSfh45eEUqEGRRkRLgkB3vC8sKx101vQj2N990h50/CW0+F0nz0S7sDEVfbN9M3RuCtDmpUikTlYCEb1Y6k2uCNbgLuhV99VBVWOrUSB8aBhLPui+xYd+tSUD476mjqqowxYy5O0fbTouf2VzKgGmpyZ3DFvRgG1jbLKKBJxIQJcGq9fITTlh/BouoCAt2BK0JPG1rd+T91lovyqfMrnzrgiGrVIBHzpbh8i3V1H/vN0= 52 | -------------------------------------------------------------------------------- /.travis/before_install.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | if [ "$LANE" = "ios" ]; then 3 | brew update 4 | brew install yarn 5 | brew outdated node || brew upgrade node 6 | brew outdated yarn || brew upgrade yarn 7 | elif [ "$LANE" = "android" ]; then 8 | node --version 9 | npm install -g yarn 10 | fi -------------------------------------------------------------------------------- /.travis/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | yarn install 5 | yarn run tsc 6 | cd examples/rn57example 7 | yarn install 8 | rm -rf node_modules/react-native-google-analytics-bridge/examples 9 | 10 | if [ "$LANE" = "node" ]; then 11 | yarn run tsc 12 | else 13 | npm install -g react-native-cli 14 | react-native -v 15 | 16 | if [ "$LANE" = "ios" ]; then 17 | xcodebuild -project ios/example.xcodeproj/ -configuration Debug -sdk iphonesimulator -scheme example CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO 18 | else 19 | cd android && ./gradlew assembleRelease 20 | fi 21 | fi -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 18 | 19 | ### Environment 20 | 21 | (Which react-native version? What version of this library? iOS, Android, or both?) 22 | 23 | ### Steps to Reproduce 24 | 25 | (Write your steps here.) 26 | 27 | ### Expected Behavior 28 | 29 | (Write what you thought would happen.) 30 | 31 | ### Actual Behavior 32 | 33 | (Write what happened.) 34 | 35 | ### Code sample/demo project 36 | 37 | (Write or link sample here.) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Idéhub 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 | 23 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def safeExtGet(prop, fallback) { 4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 5 | } 6 | 7 | android { 8 | compileSdkVersion safeExtGet('compileSdkVersion', 23) 9 | buildToolsVersion safeExtGet('buildToolsVersion', "23.0.1") 10 | 11 | defaultConfig { 12 | minSdkVersion safeExtGet('minSdkVersion', 16) 13 | targetSdkVersion safeExtGet('targetSdkVersion', 22) 14 | versionCode 1 15 | versionName "1.0" 16 | } 17 | 18 | lintOptions { 19 | warning 'InvalidPackage' // prevent error: https://github.com/square/okio/issues/58 20 | } 21 | } 22 | 23 | dependencies { 24 | implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" 25 | implementation "com.google.android.gms:play-services-analytics:${safeExtGet('googlePlayServicesVersion', '+')}" 26 | implementation "com.google.android.gms:play-services-tagmanager-v4-impl:${safeExtGet('googlePlayServicesVersion', '+')}" 27 | } 28 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/src/main/java/com/idehub/GoogleAnalyticsBridge/ConvertReadableToMap.java: -------------------------------------------------------------------------------- 1 | package com.idehub.GoogleAnalyticsBridge; 2 | 3 | import com.facebook.react.bridge.ReadableMap; 4 | import com.facebook.react.bridge.ReadableType; 5 | import com.facebook.react.bridge.ReadableArray; 6 | import com.facebook.react.bridge.ReadableMapKeySetIterator; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.List; 11 | import java.util.ArrayList; 12 | 13 | /** 14 | * Converts a ReadableMap to a HashMap 15 | */ 16 | public class ConvertReadableToMap { 17 | 18 | public static HashMap getMap(ReadableMap dictionary) { 19 | HashMap parsedMap = new HashMap(); 20 | ReadableMapKeySetIterator iterator = dictionary.keySetIterator(); 21 | while (iterator.hasNextKey()) { 22 | String key = iterator.nextKey(); 23 | ReadableType type = dictionary.getType(key); 24 | switch (type) { 25 | case Null: 26 | parsedMap.put(key, null); 27 | break; 28 | case Boolean: 29 | parsedMap.put(key, dictionary.getBoolean(key)); 30 | break; 31 | case Number: 32 | parsedMap.put(key, dictionary.getDouble(key)); 33 | break; 34 | case String: 35 | parsedMap.put(key, dictionary.getString(key)); 36 | break; 37 | case Map: 38 | parsedMap.put(key, ConvertReadableToMap.getMap(dictionary.getMap(key))); 39 | break; 40 | case Array: 41 | parsedMap.put(key, ConvertReadableToMap.getArray(dictionary.getArray(key))); 42 | break; 43 | default: 44 | throw new IllegalArgumentException("Could not convert of type: " + type + " and key: " + key + "."); 45 | } 46 | } 47 | return parsedMap; 48 | } 49 | 50 | private static List getArray(ReadableArray readableArray) { 51 | List parsedList = new ArrayList(readableArray.size()); 52 | for (int i = 0; i < readableArray.size(); i++) { 53 | ReadableType type = readableArray.getType(i); 54 | switch(type) { 55 | case Null: 56 | parsedList.add(i, null); 57 | break; 58 | case Boolean: 59 | parsedList.add(i, readableArray.getBoolean(i)); 60 | break; 61 | case Number: 62 | parsedList.add(i, readableArray.getDouble(i)); 63 | break; 64 | case String: 65 | parsedList.add(i, readableArray.getString(i)); 66 | break; 67 | case Map: 68 | parsedList.add(i, ConvertReadableToMap.getMap(readableArray.getMap(i))); 69 | break; 70 | case Array: 71 | parsedList.add(i, ConvertReadableToMap.getArray(readableArray.getArray(i))); 72 | break; 73 | default: 74 | throw new IllegalArgumentException("Could not convert of type: " + type + " and index: " + i + "."); 75 | } 76 | } 77 | return parsedList; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /android/src/main/java/com/idehub/GoogleAnalyticsBridge/ConvertToWritable.java: -------------------------------------------------------------------------------- 1 | package com.idehub.GoogleAnalyticsBridge; 2 | 3 | import com.facebook.react.bridge.Arguments; 4 | import com.facebook.react.bridge.WritableMap; 5 | import com.facebook.react.bridge.WritableArray; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.List; 10 | import java.util.Iterator; 11 | import java.util.ArrayList; 12 | 13 | /** 14 | * Converts a List to WritableList and Map to WritableList 15 | */ 16 | public class ConvertToWritable { 17 | public static WritableMap map(Map map) { 18 | WritableMap writableMap = Arguments.createMap(); 19 | for (Map.Entry entry : map.entrySet()) { 20 | String key = entry.getKey(); 21 | Object value = entry.getValue(); 22 | 23 | if (value == null) { 24 | writableMap.putNull(key); 25 | } else if (value instanceof Integer) { 26 | writableMap.putInt(key, (Integer) value); 27 | } else if (value instanceof Long) { 28 | writableMap.putDouble(key, (Double) value); 29 | } else if (value instanceof Double) { 30 | writableMap.putDouble(key, (Double) value); 31 | } else if (value instanceof List) { 32 | writableMap.putArray(key, ConvertToWritable.array((List) value)); 33 | } else if (value instanceof Map) { 34 | writableMap.putMap(key, ConvertToWritable.map((Map) value)); 35 | } else if (value instanceof String) { 36 | writableMap.putString(key, (String) value); 37 | } else { 38 | writableMap.putString(key, value.toString()); 39 | } 40 | } 41 | return writableMap; 42 | } 43 | 44 | public static WritableArray array(List list) { 45 | WritableArray writableArray = Arguments.createArray(); 46 | Iterator iterator = list.iterator(); 47 | while (iterator.hasNext()) { 48 | Object value = iterator.next(); 49 | if (value == null) { 50 | writableArray.pushNull(); 51 | } else if (value instanceof Integer) { 52 | writableArray.pushInt((Integer) value); 53 | } else if (value instanceof Long) { 54 | writableArray.pushDouble((Double) value); 55 | } else if (value instanceof Double) { 56 | writableArray.pushDouble((Double) value); 57 | } else if (value instanceof List) { 58 | writableArray.pushArray(ConvertToWritable.array((List) value)); 59 | } else if (value instanceof Map) { 60 | writableArray.pushMap(ConvertToWritable.map((Map) value)); 61 | } else if (value instanceof String) { 62 | writableArray.pushString((String) value); 63 | } else { 64 | writableArray.pushString(value.toString()); 65 | } 66 | } 67 | return writableArray; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /android/src/main/java/com/idehub/GoogleAnalyticsBridge/GoogleAnalyticsBridgePackage.java: -------------------------------------------------------------------------------- 1 | package com.idehub.GoogleAnalyticsBridge; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.bridge.JavaScriptModule; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | import com.google.android.gms.tagmanager.ContainerHolder; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class GoogleAnalyticsBridgePackage implements ReactPackage { 15 | 16 | public GoogleAnalyticsBridgePackage(ContainerHolder containerHolder, String trackingId) { 17 | _containerHolder = containerHolder; 18 | _trackingId = trackingId; 19 | } 20 | 21 | public GoogleAnalyticsBridgePackage(String trackingId) { 22 | this(null, trackingId); 23 | } 24 | 25 | public GoogleAnalyticsBridgePackage() { 26 | this(null, null); 27 | } 28 | 29 | private ContainerHolder _containerHolder = null; 30 | private String _trackingId; 31 | 32 | @Override 33 | public List createNativeModules( 34 | ReactApplicationContext reactContext) { 35 | List modules = new ArrayList<>(); 36 | 37 | modules.add(new GoogleAnalyticsBridge(reactContext, _trackingId)); 38 | modules.add(new GoogleAnalyticsSettings(reactContext)); 39 | modules.add(new GoogleTagManagerBridge(reactContext, _containerHolder)); 40 | 41 | return modules; 42 | } 43 | 44 | // Deprecated from RN 0.47.0 45 | public List> createJSModules() { 46 | return Collections.emptyList(); 47 | } 48 | 49 | @Override 50 | public List createViewManagers(ReactApplicationContext reactContext) { 51 | return Arrays.asList(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /android/src/main/java/com/idehub/GoogleAnalyticsBridge/GoogleAnalyticsSettings.java: -------------------------------------------------------------------------------- 1 | package com.idehub.GoogleAnalyticsBridge; 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext; 4 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 5 | import com.facebook.react.bridge.ReactMethod; 6 | import com.google.android.gms.analytics.GoogleAnalytics; 7 | 8 | public class GoogleAnalyticsSettings extends ReactContextBaseJavaModule { 9 | 10 | public GoogleAnalyticsSettings(ReactApplicationContext reactContext) { 11 | super(reactContext); 12 | } 13 | 14 | @Override 15 | public String getName() { return "GoogleAnalyticsSettings"; } 16 | 17 | synchronized GoogleAnalytics getAnalyticsInstance() { 18 | return GoogleAnalytics.getInstance(getReactApplicationContext()); 19 | } 20 | 21 | @ReactMethod 22 | public void setDryRun(Boolean enabled){ 23 | GoogleAnalytics analytics = getAnalyticsInstance(); 24 | 25 | if (analytics != null) 26 | { 27 | analytics.setDryRun(enabled); 28 | } 29 | } 30 | 31 | @ReactMethod 32 | public void setDispatchInterval(Integer intervalInSeconds){ 33 | GoogleAnalytics analytics = getAnalyticsInstance(); 34 | 35 | if (analytics != null) 36 | { 37 | analytics.setLocalDispatchPeriod(intervalInSeconds); 38 | } 39 | } 40 | 41 | @ReactMethod 42 | public void setOptOut(Boolean enabled){ 43 | GoogleAnalytics analytics = getAnalyticsInstance(); 44 | 45 | if (analytics != null) 46 | { 47 | analytics.setAppOptOut(enabled); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /android/src/main/java/com/idehub/GoogleAnalyticsBridge/GoogleTagManagerBridge.java: -------------------------------------------------------------------------------- 1 | package com.idehub.GoogleAnalyticsBridge; 2 | 3 | import android.content.Context; 4 | 5 | import com.facebook.react.bridge.Promise; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 8 | import com.facebook.react.bridge.ReactMethod; 9 | import com.facebook.react.bridge.ReadableMap; 10 | import com.facebook.react.bridge.ReadableMapKeySetIterator; 11 | import com.facebook.react.bridge.WritableMap; 12 | import com.facebook.react.modules.core.DeviceEventManagerModule; 13 | import com.google.android.gms.common.api.PendingResult; 14 | import com.google.android.gms.common.api.ResultCallback; 15 | import com.google.android.gms.tagmanager.ContainerHolder; 16 | import com.google.android.gms.tagmanager.Container.FunctionCallTagCallback; 17 | import com.google.android.gms.tagmanager.DataLayer; 18 | import com.google.android.gms.tagmanager.TagManager; 19 | 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class GoogleTagManagerBridge extends ReactContextBaseJavaModule { 25 | 26 | public GoogleTagManagerBridge(ReactApplicationContext reactContext, ContainerHolder containerHolder) { 27 | super(reactContext); 28 | mContainerHolder = containerHolder; 29 | } 30 | 31 | 32 | private final String E_CONTAINER_ALREADY_OPEN = "E_CONTAINER_ALREADY_OPEN"; 33 | private final String E_ONGOING_OPEN_OPERATION = "E_ONGOING_OPEN_OPERATION"; 34 | private final String E_CONTAINER_NOT_OPENED = "E_CONTAINER_NOT_OPENED"; 35 | private final String E_OPEN_CONTAINER_FAILED = "E_OPEN_CONTAINER_FAILED"; 36 | private final String E_PUSH_EVENT_FAILED = "E_PUSH_EVENT_FAILED"; 37 | private final String E_FUNCTION_CALL_REGISTRATION_FAILED = "E_FUNCTION_CALL_REGISTRATION_FAILED"; 38 | 39 | private final String FUNCTION_CALL_TAG_EVENT_PREFIX = "GTM_FUNCTION_CALL_TAG_"; 40 | 41 | private ContainerHolder mContainerHolder; 42 | private Boolean openOperationInProgress = false; 43 | private DataLayer mDatalayer; 44 | 45 | @Override 46 | public String getName() { 47 | return "GoogleTagManagerBridge"; 48 | } 49 | 50 | @ReactMethod 51 | public void openContainerWithId(final String containerId, final Promise promise){ 52 | if (mContainerHolder != null) { 53 | promise.reject(E_CONTAINER_ALREADY_OPEN, new Throwable("The container is already open.")); 54 | return; 55 | } 56 | 57 | if (openOperationInProgress) { 58 | promise.reject(E_ONGOING_OPEN_OPERATION, new Throwable("Container open-operation already in progress.")); 59 | return; 60 | } 61 | 62 | TagManager mTagManager = TagManager.getInstance(getReactApplicationContext()); 63 | 64 | openOperationInProgress = true; 65 | 66 | final int containerResourceId = getDefaultContainerResourceId(containerId); 67 | 68 | PendingResult pending = mTagManager.loadContainerPreferFresh(containerId, containerResourceId); 69 | pending.setResultCallback(new ResultCallback() { 70 | @Override 71 | public void onResult(ContainerHolder containerHolder) { 72 | if (containerHolder != null && containerHolder.getStatus().isSuccess()) { 73 | mContainerHolder = containerHolder; 74 | promise.resolve(true); 75 | } else { 76 | promise.reject(E_OPEN_CONTAINER_FAILED, new Throwable(String.format("Failed to open container. Does container with id %s exist?", containerId))); 77 | } 78 | openOperationInProgress = false; 79 | } 80 | }, 2000, TimeUnit.MILLISECONDS); 81 | } 82 | 83 | @ReactMethod 84 | public void refreshContainer(final Promise promise){ 85 | if (mContainerHolder != null && mContainerHolder.getContainer() != null) { 86 | mContainerHolder.refresh(); 87 | promise.resolve(true); 88 | } else { 89 | promise.reject(E_CONTAINER_NOT_OPENED, new Throwable("The container has not been opened. You must call openContainerWithId(..)")); 90 | } 91 | } 92 | 93 | @ReactMethod 94 | public void booleanForKey(final String key, final Promise promise){ 95 | if (mContainerHolder != null && mContainerHolder.getContainer() != null) { 96 | promise.resolve(mContainerHolder.getContainer().getBoolean(key)); 97 | } else { 98 | promise.reject(E_CONTAINER_NOT_OPENED, new Throwable("The container has not been opened. You must call openContainerWithId(..)")); 99 | } 100 | } 101 | 102 | @ReactMethod 103 | public void stringForKey(final String key, final Promise promise){ 104 | if (mContainerHolder != null && mContainerHolder.getContainer() != null) { 105 | promise.resolve(mContainerHolder.getContainer().getString(key)); 106 | } else { 107 | promise.reject(E_CONTAINER_NOT_OPENED, new Throwable("The container has not been opened. You must call openContainerWithId(..)")); 108 | } 109 | } 110 | 111 | @ReactMethod 112 | public void doubleForKey(final String key, final Promise promise){ 113 | if (mContainerHolder != null && mContainerHolder.getContainer() != null) { 114 | promise.resolve(mContainerHolder.getContainer().getDouble(key)); 115 | } else { 116 | promise.reject(E_CONTAINER_NOT_OPENED, new Throwable("The container has not been opened. You must call openContainerWithId(..)")); 117 | } 118 | } 119 | 120 | @ReactMethod 121 | public void pushDataLayerEvent(ReadableMap dictionary, final Promise promise){ 122 | 123 | if (mContainerHolder != null && isValidMapToPushEvent(dictionary)) { 124 | getDataLayer().push(ConvertReadableToMap.getMap(dictionary)); 125 | promise.resolve(true); 126 | } else { 127 | if (mContainerHolder == null) { 128 | promise.reject(E_CONTAINER_NOT_OPENED, new Throwable("The container has not been opened. You must call openContainerWithId(..)")); 129 | } else { 130 | promise.reject(E_PUSH_EVENT_FAILED, new Throwable("Validation error, data must have a key \"event\" with valid event name")); 131 | } 132 | } 133 | } 134 | 135 | @ReactMethod 136 | public void registerFunctionCallTagHandler(String functionName, final Promise promise){ 137 | 138 | if (mContainerHolder != null && functionName != null) { 139 | mContainerHolder.getContainer().registerFunctionCallTagCallback(functionName, new FunctionCallTagCallback() { 140 | @Override 141 | public void execute(String functionName, Map parameters) { 142 | 143 | // eventName is prefixed to prevent event collision with other modules 144 | String eventName = generateFunctionCallTagEventName(functionName); 145 | 146 | WritableMap params = ConvertToWritable.map(parameters); 147 | getReactApplicationContext() 148 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 149 | .emit(eventName, params); 150 | } 151 | }); 152 | promise.resolve(true); 153 | } else { 154 | if (mContainerHolder == null) { 155 | promise.reject(E_CONTAINER_NOT_OPENED, new Throwable("The container has not been opened. You must call openContainerWithId(..)")); 156 | } else { 157 | promise.reject(E_FUNCTION_CALL_REGISTRATION_FAILED, new Throwable("Function name of the tag is not provided")); 158 | } 159 | } 160 | } 161 | 162 | @ReactMethod 163 | public void setVerboseLoggingEnabled(final Boolean enabled, final Promise promise){ 164 | TagManager mTagManager = TagManager.getInstance(getReactApplicationContext()); 165 | mTagManager.setVerboseLoggingEnabled(enabled); 166 | promise.resolve(true); 167 | } 168 | 169 | private boolean isValidMapToPushEvent(ReadableMap dictionary) { 170 | return (dictionary != null && dictionary.getString("event") != null 171 | && dictionary.getString("event").length() > 0); 172 | } 173 | 174 | private DataLayer getDataLayer() { 175 | if (mDatalayer == null) { 176 | TagManager tagManager = TagManager.getInstance(getReactApplicationContext()); 177 | mDatalayer = tagManager.getDataLayer(); 178 | } 179 | return mDatalayer; 180 | } 181 | 182 | private String generateFunctionCallTagEventName(String functionName) { 183 | return FUNCTION_CALL_TAG_EVENT_PREFIX + functionName; 184 | } 185 | 186 | private int getDefaultContainerResourceId(String containerId) { 187 | Context ctx = getReactApplicationContext().getApplicationContext(); 188 | final String resName = containerId.replaceAll("-", "_").toLowerCase(); 189 | final int resId = ctx.getResources().getIdentifier(resName, "raw", ctx.getPackageName()); 190 | if (resId == 0) return -1; 191 | return resId; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /examples/rn57example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["module:metro-react-native-babel-preset"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/rn57example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /examples/rn57example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | ; Ignore metro 20 | .*/node_modules/metro/.* 21 | 22 | [include] 23 | 24 | [libs] 25 | node_modules/react-native/Libraries/react-native/react-native-interface.js 26 | node_modules/react-native/flow/ 27 | node_modules/react-native/flow-github/ 28 | 29 | [options] 30 | emoji=true 31 | 32 | esproposal.optional_chaining=enable 33 | esproposal.nullish_coalescing=enable 34 | 35 | module.system=haste 36 | module.system.haste.use_name_reducers=true 37 | # get basename 38 | module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' 39 | # strip .js or .js.flow suffix 40 | module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' 41 | # strip .ios suffix 42 | module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' 43 | module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' 44 | module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' 45 | module.system.haste.paths.blacklist=.*/__tests__/.* 46 | module.system.haste.paths.blacklist=.*/__mocks__/.* 47 | module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* 48 | module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* 49 | 50 | munge_underscores=true 51 | 52 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 53 | 54 | module.file_ext=.js 55 | module.file_ext=.jsx 56 | module.file_ext=.json 57 | module.file_ext=.native.js 58 | 59 | suppress_type=$FlowIssue 60 | suppress_type=$FlowFixMe 61 | suppress_type=$FlowFixMeProps 62 | suppress_type=$FlowFixMeState 63 | 64 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 65 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 66 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 67 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 68 | 69 | [version] 70 | ^0.78.0 71 | -------------------------------------------------------------------------------- /examples/rn57example/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /examples/rn57example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | -------------------------------------------------------------------------------- /examples/rn57example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /examples/rn57example/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { AppRegistry, StyleSheet, Text, View } from "react-native"; 3 | 4 | import { 5 | GoogleAnalyticsTracker, 6 | GoogleAnalyticsSettings, 7 | GoogleTagManager 8 | } from "react-native-google-analytics-bridge"; 9 | export default class App extends Component { 10 | render() { 11 | // Recommend you set this much higher in real app! 30 seconds+ 12 | // GoogleAnalyticsSettings has static methods and is applied 13 | // for all trackers 14 | GoogleAnalyticsSettings.setDispatchInterval(2); 15 | //GoogleAnalyticsSettings.setDryRun(true); 16 | //GoogleAnalyticsSettings.setOptOut(true); 17 | 18 | // The tracker is constructed 19 | let tracker = new GoogleAnalyticsTracker("UA-12345-3"); 20 | // You can have multiple trackers 21 | //let tracker2 = new GoogleAnalyticsTracker("UA-12345-3", { demo: 1 }); 22 | 23 | //tracker2.trackScreenViewWithCustomDimensionValues("Home", { demo: "Yes" }); 24 | 25 | tracker.trackEvent("testcategory", "Hello iOS"); 26 | 27 | tracker.trackScreenView("Home"); 28 | 29 | tracker.trackEvent("testcategory", "Hello iOS", { 30 | label: "notdry", 31 | value: 1 32 | }); 33 | 34 | tracker.trackTiming("testcategory", 13000, { 35 | label: "notdry", 36 | name: "testduration" 37 | }); 38 | 39 | tracker.setTrackUncaughtExceptions(true); 40 | 41 | tracker.setUser("12345678"); 42 | 43 | tracker.allowIDFA(true); 44 | 45 | tracker.setAnonymizeIp(true); 46 | 47 | tracker.trackScreenView("Hello"); 48 | 49 | GoogleTagManager.openContainerWithId("GTM-NZT48") 50 | .then(() => { 51 | return GoogleTagManager.registerFunctionCallTagHandler( 52 | "awzm_tag", 53 | (fn, payload) => { 54 | console.log("test", fn, payload); 55 | } 56 | ); 57 | }) 58 | .then(() => { 59 | return GoogleTagManager.registerFunctionCallTagHandler( 60 | "some_other_tag", 61 | (fn, payload) => { 62 | console.log("test2", fn, payload); 63 | } 64 | ); 65 | }) 66 | .then(reg => { 67 | console.log("Push?: ", reg); 68 | return GoogleTagManager.pushDataLayerEvent({ 69 | event: "some_event", 70 | id: 1 71 | }); 72 | }) 73 | .then(db => { 74 | console.log("db: ", db); 75 | return GoogleTagManager.doubleForKey("db"); 76 | }) 77 | .catch(err => { 78 | console.log(err); 79 | }); 80 | 81 | return ( 82 | 83 | 84 | Welcome to React Native Google Analytics Bridge! 85 | 86 | 87 | ); 88 | } 89 | } 90 | 91 | const styles = StyleSheet.create({ 92 | container: { 93 | flex: 1, 94 | justifyContent: "center", 95 | alignItems: "center", 96 | backgroundColor: "#F5FCFF" 97 | }, 98 | welcome: { 99 | fontSize: 20, 100 | textAlign: "center", 101 | margin: 10 102 | } 103 | }); 104 | -------------------------------------------------------------------------------- /examples/rn57example/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | lib_deps = [] 12 | 13 | for jarfile in glob(['libs/*.jar']): 14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] 15 | lib_deps.append(':' + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | 21 | for aarfile in glob(['libs/*.aar']): 22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] 23 | lib_deps.append(':' + name) 24 | android_prebuilt_aar( 25 | name = name, 26 | aar = aarfile, 27 | ) 28 | 29 | android_library( 30 | name = "all-libs", 31 | exported_deps = lib_deps, 32 | ) 33 | 34 | android_library( 35 | name = "app-code", 36 | srcs = glob([ 37 | "src/main/java/**/*.java", 38 | ]), 39 | deps = [ 40 | ":all-libs", 41 | ":build_config", 42 | ":res", 43 | ], 44 | ) 45 | 46 | android_build_config( 47 | name = "build_config", 48 | package = "com.example", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.example", 54 | res = "src/main/res", 55 | ) 56 | 57 | android_binary( 58 | name = "app", 59 | keystore = "//android/keystores:debug", 60 | manifest = "src/main/AndroidManifest.xml", 61 | package_type = "debug", 62 | deps = [ 63 | ":app-code", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /examples/rn57example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 37 | * // for example: to disable dev mode in the staging build type (if configured) 38 | * devDisabledInStaging: true, 39 | * // The configuration property can be in the following formats 40 | * // 'devDisabledIn${productFlavor}${buildType}' 41 | * // 'devDisabledIn${buildType}' 42 | * 43 | * // the root of your project, i.e. where "package.json" lives 44 | * root: "../../", 45 | * 46 | * // where to put the JS bundle asset in debug mode 47 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 48 | * 49 | * // where to put the JS bundle asset in release mode 50 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 51 | * 52 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 53 | * // require('./image.png')), in debug mode 54 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 55 | * 56 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 57 | * // require('./image.png')), in release mode 58 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 59 | * 60 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 61 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 62 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 63 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 64 | * // for example, you might want to remove it from here. 65 | * inputExcludes: ["android/**", "ios/**"], 66 | * 67 | * // override which node gets called and with what additional arguments 68 | * nodeExecutableAndArgs: ["node"], 69 | * 70 | * // supply additional arguments to the packager 71 | * extraPackagerArgs: [] 72 | * ] 73 | */ 74 | 75 | project.ext.react = [ 76 | entryFile: "index.js" 77 | ] 78 | 79 | apply from: "../../node_modules/react-native/react.gradle" 80 | 81 | /** 82 | * Set this to true to create two separate APKs instead of one: 83 | * - An APK that only works on ARM devices 84 | * - An APK that only works on x86 devices 85 | * The advantage is the size of the APK is reduced by about 4MB. 86 | * Upload all the APKs to the Play Store and people will download 87 | * the correct one based on the CPU architecture of their device. 88 | */ 89 | def enableSeparateBuildPerCPUArchitecture = false 90 | 91 | /** 92 | * Run Proguard to shrink the Java bytecode in release builds. 93 | */ 94 | def enableProguardInReleaseBuilds = false 95 | 96 | android { 97 | compileSdkVersion rootProject.ext.compileSdkVersion 98 | buildToolsVersion rootProject.ext.buildToolsVersion 99 | 100 | defaultConfig { 101 | applicationId "com.example" 102 | minSdkVersion rootProject.ext.minSdkVersion 103 | targetSdkVersion rootProject.ext.targetSdkVersion 104 | versionCode 1 105 | versionName "1.0" 106 | ndk { 107 | abiFilters "armeabi-v7a", "x86" 108 | } 109 | } 110 | splits { 111 | abi { 112 | reset() 113 | enable enableSeparateBuildPerCPUArchitecture 114 | universalApk false // If true, also generate a universal APK 115 | include "armeabi-v7a", "x86" 116 | } 117 | } 118 | buildTypes { 119 | release { 120 | minifyEnabled enableProguardInReleaseBuilds 121 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 122 | } 123 | } 124 | // applicationVariants are e.g. debug, release 125 | applicationVariants.all { variant -> 126 | variant.outputs.each { output -> 127 | // For each separate APK per architecture, set a unique version code as described here: 128 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 129 | def versionCodes = ["armeabi-v7a":1, "x86":2] 130 | def abi = output.getFilter(OutputFile.ABI) 131 | if (abi != null) { // null for the universal-debug, universal-release variants 132 | output.versionCodeOverride = 133 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 134 | } 135 | } 136 | } 137 | } 138 | 139 | dependencies { 140 | implementation project(':react-native-google-analytics-bridge') 141 | implementation fileTree(dir: "libs", include: ["*.jar"]) 142 | implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" 143 | implementation "com.facebook.react:react-native:+" // From node_modules 144 | } 145 | 146 | // Run this once to be able to run the application with BUCK 147 | // puts all compile dependencies into folder libs for BUCK to use 148 | task copyDownloadableDepsToLibs(type: Copy) { 149 | from configurations.compile 150 | into 'libs' 151 | } 152 | -------------------------------------------------------------------------------- /examples/rn57example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/java/com/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.idehub.GoogleAnalyticsBridge.GoogleAnalyticsBridgePackage; 7 | import com.facebook.react.ReactNativeHost; 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.shell.MainReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | public class MainApplication extends Application implements ReactApplication { 16 | 17 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | return Arrays.asList( 26 | new MainReactPackage(), 27 | new GoogleAnalyticsBridgePackage() 28 | ); 29 | } 30 | 31 | @Override 32 | protected String getJSMainModuleName() { 33 | return "index"; 34 | } 35 | }; 36 | 37 | @Override 38 | public ReactNativeHost getReactNativeHost() { 39 | return mReactNativeHost; 40 | } 41 | 42 | @Override 43 | public void onCreate() { 44 | super.onCreate(); 45 | SoLoader.init(this, /* native exopackage */ false); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | example 3 | 4 | -------------------------------------------------------------------------------- /examples/rn57example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/rn57example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | googlePlayServicesVersion = "16.0.3" 6 | buildToolsVersion = "27.0.3" 7 | minSdkVersion = 16 8 | compileSdkVersion = 27 9 | targetSdkVersion = 26 10 | supportLibVersion = "27.1.1" 11 | } 12 | repositories { 13 | google() 14 | jcenter() 15 | } 16 | dependencies { 17 | classpath 'com.android.tools.build:gradle:3.1.4' 18 | 19 | // NOTE: Do not place your application dependencies here; they belong 20 | // in the individual module build.gradle files 21 | } 22 | } 23 | 24 | allprojects { 25 | repositories { 26 | google() 27 | mavenLocal() 28 | jcenter() 29 | maven { 30 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 31 | url "$rootDir/../node_modules/react-native/android" 32 | } 33 | } 34 | } 35 | 36 | 37 | task wrapper(type: Wrapper) { 38 | gradleVersion = '4.4' 39 | distributionUrl = distributionUrl.replace("bin", "all") 40 | } -------------------------------------------------------------------------------- /examples/rn57example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | -------------------------------------------------------------------------------- /examples/rn57example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/examples/rn57example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/rn57example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 6 | -------------------------------------------------------------------------------- /examples/rn57example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /examples/rn57example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /examples/rn57example/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /examples/rn57example/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /examples/rn57example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | include ':react-native-google-analytics-bridge' 3 | project(':react-native-google-analytics-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-google-analytics-bridge/android') 4 | 5 | include ':app' 6 | -------------------------------------------------------------------------------- /examples/rn57example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "displayName": "example" 4 | } -------------------------------------------------------------------------------- /examples/rn57example/index.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import {AppRegistry} from 'react-native'; 4 | import App from './App'; 5 | import {name as appName} from './app.json'; 6 | 7 | AppRegistry.registerComponent(appName, () => App); 8 | -------------------------------------------------------------------------------- /examples/rn57example/ios/example-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /examples/rn57example/ios/example-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/rn57example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /examples/rn57example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /examples/rn57example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (nonatomic, strong) UIWindow *window; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /examples/rn57example/ios/example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import "AppDelegate.h" 9 | 10 | #import 11 | #import 12 | 13 | @implementation AppDelegate 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 16 | { 17 | NSURL *jsCodeLocation; 18 | 19 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 20 | 21 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 22 | moduleName:@"example" 23 | initialProperties:nil 24 | launchOptions:launchOptions]; 25 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 26 | 27 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 28 | UIViewController *rootViewController = [UIViewController new]; 29 | rootViewController.view = rootView; 30 | self.window.rootViewController = rootViewController; 31 | [self.window makeKeyAndVisible]; 32 | return YES; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /examples/rn57example/ios/example/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/rn57example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /examples/rn57example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/rn57example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSLocationWhenInUseUsageDescription 28 | 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UIViewControllerBasedStatusBarAppearance 42 | 43 | NSLocationWhenInUseUsageDescription 44 | 45 | NSAppTransportSecurity 46 | 47 | 48 | NSAllowsArbitraryLoads 49 | 50 | NSExceptionDomains 51 | 52 | localhost 53 | 54 | NSExceptionAllowsInsecureHTTPLoads 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/rn57example/ios/example/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/rn57example/ios/exampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/rn57example/ios/exampleTests/exampleTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | #import 12 | #import 13 | 14 | #define TIMEOUT_SECONDS 600 15 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 16 | 17 | @interface exampleTests : XCTestCase 18 | 19 | @end 20 | 21 | @implementation exampleTests 22 | 23 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 24 | { 25 | if (test(view)) { 26 | return YES; 27 | } 28 | for (UIView *subview in [view subviews]) { 29 | if ([self findSubviewInView:subview matching:test]) { 30 | return YES; 31 | } 32 | } 33 | return NO; 34 | } 35 | 36 | - (void)testRendersWelcomeScreen 37 | { 38 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 39 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 40 | BOOL foundElement = NO; 41 | 42 | __block NSString *redboxError = nil; 43 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 44 | if (level >= RCTLogLevelError) { 45 | redboxError = message; 46 | } 47 | }); 48 | 49 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 50 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 51 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 52 | 53 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 54 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 55 | return YES; 56 | } 57 | return NO; 58 | }]; 59 | } 60 | 61 | RCTSetLogFunction(RCTDefaultLogFunction); 62 | 63 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 64 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 65 | } 66 | 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /examples/rn57example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "react": "16.6.0-alpha.8af6728", 11 | "react-native": "0.57.4", 12 | "react-native-google-analytics-bridge": "file:../../" 13 | }, 14 | "devDependencies": { 15 | "@types/react": "^16.7.2", 16 | "@types/react-native": "^0.57.7", 17 | "babel-jest": "23.6.0", 18 | "jest": "23.6.0", 19 | "metro-react-native-babel-preset": "0.49.0", 20 | "react-test-renderer": "16.6.0-alpha.8af6728", 21 | "typescript": "^3.1.6" 22 | }, 23 | "jest": { 24 | "preset": "react-native" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/rn57example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "module": "es2015", 5 | "moduleResolution": "node", 6 | "allowJs": true, 7 | "checkJs": true, 8 | "noEmit": true, 9 | "jsx": "react-native", 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "allowSyntheticDefaultImports": true, 13 | "resolveJsonModule": true 14 | }, 15 | "exclude": ["node_modules"] 16 | } 17 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge/GoogleAnalyticsPayload.h: -------------------------------------------------------------------------------- 1 | #import "GAIDictionaryBuilder.h" 2 | 3 | @interface GoogleAnalyticsPayload : NSObject 4 | + (void)addBuilderPayload:(GAIDictionaryBuilder*)builder payload:(NSDictionary *)payload; 5 | @end 6 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge/GoogleAnalyticsPayload.m: -------------------------------------------------------------------------------- 1 | #import "GoogleAnalyticsPayload.h" 2 | #import 3 | #import 4 | #import "GAI.h" 5 | #import "GAIFields.h" 6 | #import "GAIDictionaryBuilder.h" 7 | #import "GAIEcommerceProduct.h" 8 | #import "GAIEcommerceProductAction.h" 9 | #import "GAIEcommerceFields.h" 10 | 11 | @implementation GoogleAnalyticsPayload { 12 | 13 | } 14 | 15 | typedef enum { 16 | Detail = 1, 17 | Click = 2, 18 | Add = 3, 19 | Remove = 4, 20 | Checkout = 5, 21 | CheckoutOption = 6, 22 | Purchase = 7, 23 | Refund = 8 24 | } ProductActionEnum; 25 | 26 | + (void)addBuilderPayload:(GAIDictionaryBuilder*)builder payload:(NSDictionary*)payload 27 | { 28 | NSArray* products = payload[@"products"]; 29 | if (products) { 30 | NSArray* ecommerceProducts = getEcommerceProducts(products); 31 | for (GAIEcommerceProduct* product in ecommerceProducts) { 32 | [builder addProduct:product]; 33 | } 34 | } 35 | 36 | NSArray* impressionProducts = payload[@"impressionProducts"]; 37 | if (impressionProducts) { 38 | NSArray* ecommerceProducts = getEcommerceProducts(impressionProducts); 39 | for (GAIEcommerceProduct* product in ecommerceProducts) { 40 | [builder addProductImpression:product 41 | impressionList:payload[@"impressionList"] 42 | impressionSource:payload[@"impressionSource"]]; 43 | } 44 | } 45 | 46 | NSDictionary* productActionDict = payload[@"productAction"]; 47 | if (productActionDict) { 48 | [builder setProductAction:getEcommerceProductAction(productActionDict)]; 49 | } 50 | 51 | NSDictionary* customDimensions = payload[@"customDimensions"]; 52 | if (customDimensions) { 53 | for (NSString *dimensionIndex in customDimensions){ 54 | [builder set:[customDimensions objectForKey:dimensionIndex] forKey:[GAIFields customDimensionForIndex:[dimensionIndex intValue]]]; 55 | } 56 | } 57 | 58 | NSDictionary* customMetrics = payload[@"customMetrics"]; 59 | if (customMetrics) { 60 | for (NSString *metricIndex in customMetrics) { 61 | [builder set:[customMetrics objectForKey:metricIndex] forKey:[GAIFields customMetricForIndex:[metricIndex intValue]]]; 62 | } 63 | } 64 | 65 | NSString* utmCampaignUrl = payload[@"utmCampaignUrl"]; 66 | if (utmCampaignUrl) { 67 | [builder setAll:getUtmCampaignUrlHitParams(utmCampaignUrl)]; 68 | } 69 | 70 | NSString* sessionState = payload[@"session"]; 71 | if (sessionState != nil) { 72 | [builder set:sessionState forKey:kGAISessionControl]; 73 | } 74 | } 75 | 76 | static NSArray* getEcommerceProducts(NSArray* products) 77 | { 78 | NSMutableArray* ecommerceProducts = [NSMutableArray new]; 79 | 80 | for (NSDictionary* product in products) 81 | { 82 | GAIEcommerceProduct *ecommerceProduct = [[GAIEcommerceProduct alloc] init]; 83 | 84 | [ecommerceProduct setId:[RCTConvert NSString:product[@"id"]]]; 85 | [ecommerceProduct setName:[RCTConvert NSString:product[@"name"]]]; 86 | [ecommerceProduct setCategory:[RCTConvert NSString:product[@"category"]]]; 87 | [ecommerceProduct setBrand:[RCTConvert NSString:product[@"brand"]]]; 88 | [ecommerceProduct setVariant:[RCTConvert NSString:product[@"variant"]]]; 89 | [ecommerceProduct setPrice:[RCTConvert NSNumber:product[@"price"]]]; 90 | [ecommerceProduct setCouponCode:[RCTConvert NSString:product[@"couponCode"]]]; 91 | [ecommerceProduct setQuantity:[RCTConvert NSNumber:product[@"quantity"]]]; 92 | 93 | [ecommerceProducts addObject:ecommerceProduct]; 94 | } 95 | 96 | return ecommerceProducts; 97 | } 98 | 99 | static GAIEcommerceProductAction* getEcommerceProductAction(NSDictionary* productActionDict) 100 | { 101 | if (!productActionDict) return NULL; 102 | 103 | NSString* action = getProductAction([RCTConvert NSNumber:productActionDict[@"action"]]); 104 | 105 | GAIEcommerceProductAction* productAction = [[GAIEcommerceProductAction alloc] init]; 106 | 107 | [productAction setAction:action]; 108 | 109 | NSDictionary* transaction = productActionDict[@"transaction"]; 110 | if (transaction) { 111 | [productAction setTransactionId:[RCTConvert NSString:transaction[@"id"]]]; 112 | [productAction setAffiliation: [RCTConvert NSString:transaction[@"affiliation"]]]; 113 | [productAction setRevenue:[RCTConvert NSNumber:transaction[@"revenue"]]]; 114 | [productAction setTax:[RCTConvert NSNumber:transaction[@"tax"]]]; 115 | [productAction setShipping:[RCTConvert NSNumber:transaction[@"shipping"]]]; 116 | [productAction setCouponCode:[RCTConvert NSString:transaction[@"couponCode"]]]; 117 | } 118 | 119 | // Sets the option associated with the checkout. This value is used for kGAICheckout and kGAICheckoutOptions product actions. 120 | NSNumber* checkoutStep = [RCTConvert NSNumber:productActionDict[@"checkoutStep"]]; 121 | if (checkoutStep) 122 | [productAction setCheckoutStep:checkoutStep]; 123 | 124 | // Sets the option associated with the checkout. This value is used for kGAICheckout and kGAICheckoutOptions product actions. 125 | NSString* checkoutOption = [RCTConvert NSString:productActionDict[@"checkoutOption"]]; 126 | if (checkoutOption) 127 | [productAction setCheckoutOption:checkoutOption]; 128 | 129 | // Sets the list name associated with the products in Google Analytics beacons. This value is used in kGAIPADetail and kGAIPAClick product actions. 130 | NSString* productActionList = [RCTConvert NSString:productActionDict[@"productActionList"]]; 131 | if (productActionList) 132 | [productAction setProductActionList:productActionList]; 133 | 134 | // Sets the list source name associated with the products in Google Analytics beacons. This value is used in kGAIPADetail and kGAIPAClick product actions. 135 | NSString* productListSource = [RCTConvert NSString:productActionDict[@"productListSource"]]; 136 | if (productListSource) 137 | [productAction setProductListSource:productListSource]; 138 | 139 | return productAction; 140 | } 141 | 142 | static NSString* getProductAction(NSNumber* action) 143 | { 144 | switch ([action intValue]) { 145 | case Click: 146 | return kGAIPAClick; 147 | case Detail: 148 | return kGAIPADetail; 149 | case Add: 150 | return kGAIPAAdd; 151 | case Remove: 152 | return kGAIPARemove; 153 | case Checkout: 154 | return kGAIPACheckout; 155 | case Refund: 156 | return kGAIPARefund; 157 | default: 158 | case Purchase: 159 | return kGAIPAPurchase; 160 | } 161 | } 162 | 163 | static NSDictionary* getUtmCampaignUrlHitParams(NSString* urlString) 164 | { 165 | GAIDictionaryBuilder *hitParams = [[GAIDictionaryBuilder alloc] init]; 166 | 167 | // setCampaignParametersFromUrl: parses Google Analytics campaign ("UTM") 168 | // parameters from a string url into a Map that can be set on a Tracker. 169 | [hitParams setCampaignParametersFromUrl:urlString]; 170 | 171 | // kGAICampaignSource is required. Use host if not set by previous call. 172 | NSURL *url = [NSURL URLWithString:urlString]; 173 | if(![hitParams get:kGAICampaignSource] && [url host].length !=0) { 174 | [hitParams set:@"referrer" forKey:kGAICampaignMedium]; 175 | [hitParams set:[url host] forKey:kGAICampaignSource]; 176 | } 177 | 178 | return [hitParams build]; 179 | } 180 | 181 | @end 182 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RCTGoogleAnalyticsBridge : NSObject 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge.m: -------------------------------------------------------------------------------- 1 | #import "RCTGoogleAnalyticsBridge.h" 2 | #import "GoogleAnalyticsPayload.h" 3 | #import 4 | #import 5 | #import 6 | #import "GAI.h" 7 | #import "GAIFields.h" 8 | #import "GAIDictionaryBuilder.h" 9 | #import "GAIEcommerceProduct.h" 10 | #import "GAIEcommerceProductAction.h" 11 | #import "GAIEcommerceFields.h" 12 | 13 | @implementation RCTGoogleAnalyticsBridge { 14 | 15 | } 16 | 17 | - (instancetype)init 18 | { 19 | if ((self = [super init])) { 20 | [GAI sharedInstance].trackUncaughtExceptions = YES; 21 | [GAI sharedInstance].dispatchInterval = 20; 22 | 23 | NSString *logLevel = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"GAILogLevel"]; 24 | if (logLevel != nil) { 25 | [[GAI sharedInstance].logger setLogLevel:[logLevel intValue]]; 26 | } 27 | } 28 | return self; 29 | } 30 | 31 | RCT_EXPORT_MODULE(); 32 | 33 | RCT_EXPORT_METHOD(trackScreenView:(nonnull NSString *)trackerId 34 | screenName:(nonnull NSString *)screenName 35 | payload:(NSDictionary *)payload) 36 | { 37 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 38 | [tracker set:kGAIScreenName 39 | value:screenName]; 40 | GAIDictionaryBuilder *builder = [GAIDictionaryBuilder createScreenView]; 41 | 42 | if (payload) { 43 | [GoogleAnalyticsPayload addBuilderPayload:builder payload:payload]; 44 | } 45 | 46 | [tracker send:[builder build]]; 47 | } 48 | 49 | RCT_EXPORT_METHOD(trackEvent:(nonnull NSString *)trackerId 50 | category:(nonnull NSString *)category 51 | action:(nonnull NSString *)action 52 | label:(NSString *)label 53 | value:(NSString *)value 54 | payload:(NSDictionary *)payload) 55 | { 56 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 57 | 58 | NSNumber* valueNumber = nil; 59 | if (value) { 60 | valueNumber = @([value intValue]); 61 | } 62 | 63 | GAIDictionaryBuilder *builder = [GAIDictionaryBuilder createEventWithCategory:category 64 | action:action 65 | label:label 66 | value:valueNumber]; 67 | 68 | if (payload) { 69 | [GoogleAnalyticsPayload addBuilderPayload:builder payload:payload]; 70 | } 71 | 72 | // Non-interaction event. Enable sending events without affecting bounce 73 | NSString* nonInteraction = payload[@"nonInteraction"]; 74 | if (nonInteraction) { 75 | [builder set:nonInteraction ? @"1" : @"0" forKey:kGAINonInteraction]; 76 | } 77 | 78 | [tracker send:[builder build]]; 79 | } 80 | 81 | RCT_EXPORT_METHOD(trackTiming:(nonnull NSString *)trackerId 82 | category:(nonnull NSString *)category 83 | interval:(nonnull NSNumber *)interval 84 | name:(nonnull NSString *)name 85 | label:(NSString *)label 86 | payload:(NSDictionary *)payload) 87 | { 88 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 89 | 90 | GAIDictionaryBuilder *builder = [GAIDictionaryBuilder createTimingWithCategory:category 91 | interval:interval 92 | name:name 93 | label:label]; 94 | 95 | if (payload) { 96 | [GoogleAnalyticsPayload addBuilderPayload:builder payload:payload]; 97 | } 98 | 99 | [tracker send:[builder build]]; 100 | } 101 | 102 | RCT_EXPORT_METHOD(trackException:(nonnull NSString *)trackerId 103 | error:(nonnull NSString *)error 104 | fatal:(BOOL)fatal 105 | payload:(NSDictionary *)payload) 106 | { 107 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 108 | 109 | GAIDictionaryBuilder *builder = [GAIDictionaryBuilder createExceptionWithDescription:error 110 | withFatal:[NSNumber numberWithBool:fatal]]; 111 | 112 | if (payload) { 113 | [GoogleAnalyticsPayload addBuilderPayload:builder payload:payload]; 114 | } 115 | 116 | [tracker send:[builder build]]; 117 | } 118 | 119 | RCT_EXPORT_METHOD(trackSocialInteraction:(nonnull NSString *)trackerId 120 | network:(nonnull NSString *)network 121 | action:(nonnull NSString *)action 122 | targetUrl:(nonnull NSString *)targetUrl 123 | payload:(NSDictionary *)payload) 124 | { 125 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 126 | 127 | GAIDictionaryBuilder *builder =[GAIDictionaryBuilder createSocialWithNetwork:network 128 | action:action 129 | target:targetUrl]; 130 | 131 | if (payload) { 132 | [GoogleAnalyticsPayload addBuilderPayload:builder payload:payload]; 133 | } 134 | 135 | [tracker send:[builder build]]; 136 | } 137 | 138 | RCT_EXPORT_METHOD(setUser:(nonnull NSString *)trackerId userId:(nonnull NSString *)userId) 139 | { 140 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 141 | [tracker set:kGAIUserId 142 | value:userId]; 143 | } 144 | 145 | RCT_EXPORT_METHOD(setClient:(nonnull NSString *)trackerId clientId:(nonnull NSString *)clientId) 146 | { 147 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 148 | [tracker set:kGAIClientId 149 | value:clientId]; 150 | } 151 | 152 | RCT_EXPORT_METHOD(getClientId:(NSString *)trackerId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) 153 | { 154 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 155 | NSString *clientId = [tracker get:kGAIClientId]; 156 | if (clientId != NULL) 157 | resolve(clientId); 158 | else 159 | reject(@"CLIENTID_FAILED", nil, RCTErrorWithMessage(@"Unable to fetch client id")); 160 | } 161 | 162 | RCT_EXPORT_METHOD(allowIDFA:(nonnull NSString *)trackerId enabled:(BOOL)enabled) 163 | { 164 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 165 | tracker.allowIDFACollection = enabled; 166 | } 167 | 168 | RCT_EXPORT_METHOD(setSamplingRate:(nonnull NSString *)trackerId sampleRate:(nonnull NSNumber *)sampleRate) 169 | { 170 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 171 | [tracker set:kGAISampleRate value:[sampleRate stringValue]]; 172 | } 173 | 174 | RCT_EXPORT_METHOD(setAnonymizeIp:(nonnull NSString *)trackerId enabled:(BOOL)enabled) 175 | { 176 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 177 | [tracker set:kGAIAnonymizeIp value:enabled ? @"1" : @"0"]; 178 | } 179 | 180 | RCT_EXPORT_METHOD(setAppName:(nonnull NSString *)trackerId appName:(nonnull NSString *)appName) 181 | { 182 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 183 | [tracker set:kGAIAppName value:appName]; 184 | } 185 | 186 | RCT_EXPORT_METHOD(setAppVersion:(nonnull NSString *)trackerId appVersion:(nonnull NSString *)appVersion) 187 | { 188 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 189 | [tracker set:kGAIAppVersion value:appVersion]; 190 | } 191 | 192 | RCT_EXPORT_METHOD(setCurrency:(nonnull NSString *)trackerId currencyCode:(nonnull NSString *)currencyCode) 193 | { 194 | id tracker = [[GAI sharedInstance] trackerWithTrackingId:trackerId]; 195 | [tracker set:kGAICurrencyCode 196 | value:currencyCode]; 197 | } 198 | 199 | // A special case. For iOS this is set on all trackers. On Android it is on each tracker. 200 | RCT_EXPORT_METHOD(setTrackUncaughtExceptions:(nonnull NSString *)trackerId enabled:(BOOL)enabled) 201 | { 202 | [GAI sharedInstance].trackUncaughtExceptions = enabled; 203 | } 204 | 205 | RCT_EXPORT_METHOD(dispatch:(RCTPromiseResolveBlock)resolve 206 | reject:(RCTPromiseRejectBlock)reject) { 207 | [[GAI sharedInstance] dispatchWithCompletionHandler:^void(GAIDispatchResult result){ 208 | if (result != kGAIDispatchError) { 209 | resolve(@YES); 210 | } else { 211 | reject(@"DISPATCH_FAILED", nil, RCTErrorWithMessage(@"Dispatch failed")); 212 | } 213 | }]; 214 | } 215 | 216 | + (BOOL)requiresMainQueueSetup 217 | { 218 | return YES; 219 | } 220 | 221 | @end 222 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsSettings.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RCTGoogleAnalyticsSettings : NSObject 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsSettings.m: -------------------------------------------------------------------------------- 1 | #import "RCTGoogleAnalyticsSettings.h" 2 | #import "GAI.h" 3 | 4 | @implementation RCTGoogleAnalyticsSettings { 5 | 6 | } 7 | 8 | RCT_EXPORT_MODULE(); 9 | 10 | RCT_EXPORT_METHOD(setDryRun:(BOOL)enabled) 11 | { 12 | [GAI sharedInstance].dryRun = enabled; 13 | } 14 | 15 | RCT_EXPORT_METHOD(setDispatchInterval:(NSInteger)intervalInSeconds) 16 | { 17 | [GAI sharedInstance].dispatchInterval = intervalInSeconds; 18 | } 19 | 20 | RCT_EXPORT_METHOD(setOptOut:(BOOL)enabled) 21 | { 22 | [GAI sharedInstance].optOut = enabled; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge/RCTGoogleTagManagerBridge.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "TAGContainer.h" 4 | #import "TagContainerOpener.h" 5 | #import "TAGManager.h" 6 | #import 7 | 8 | @interface RCTGoogleTagManagerBridge : RCTEventEmitter 9 | 10 | @property (nonatomic, strong) TAGManager *tagManager; 11 | @property (nonatomic, strong) TAGContainer *container; 12 | @property (nonatomic, copy) RCTPromiseResolveBlock openContainerResolver; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge/RCTGoogleTagManagerBridge.m: -------------------------------------------------------------------------------- 1 | #import "RCTGoogleTagManagerBridge.h" 2 | #import "TAGContainer.h" 3 | #import "TagContainerOpener.h" 4 | #import "TAGDataLayer.h" 5 | 6 | @interface RCTGoogleTagManagerBridge () 7 | @end 8 | 9 | @implementation RCTGoogleTagManagerBridge 10 | 11 | RCT_EXPORT_MODULE(); 12 | 13 | @synthesize methodQueue = _methodQueue; 14 | 15 | NSString *const E_CONTAINER_ALREADY_OPEN = @"E_CONTAINER_ALREADY_OPEN"; 16 | NSString *const E_ONGOING_OPEN_OPERATION = @"E_ONGOING_OPEN_OPERATION"; 17 | NSString *const E_CONTAINER_NOT_OPENED = @"E_CONTAINER_NOT_OPENED"; 18 | NSString *const E_PUSH_EVENT_FAILED = @"E_PUSH_EVENT_FAILED"; 19 | NSString *const E_FUNCTION_CALL_REGISTRATION_FAILED = @"E_FUNCTION_CALL_REGISTRATION_FAILED"; 20 | 21 | NSString *const GTM_FUNCTION_CALL_TAG_EVENT = @"GTM_FUNCTION_CALL_TAG"; 22 | 23 | RCT_EXPORT_METHOD(openContainerWithId:(NSString *)containerId 24 | resolver:(RCTPromiseResolveBlock)resolve 25 | rejecter:(RCTPromiseRejectBlock)reject) 26 | { 27 | if (self.container != nil) { 28 | reject(E_CONTAINER_ALREADY_OPEN, nil, RCTErrorWithMessage(@"The container is already open.")); 29 | return; 30 | } 31 | 32 | if (self.openContainerResolver) { 33 | reject(E_ONGOING_OPEN_OPERATION, nil, RCTErrorWithMessage(@"Container open-operation already in progress.")); 34 | return; 35 | } 36 | 37 | if (self.tagManager == nil) { 38 | self.tagManager = [TAGManager instance]; 39 | } 40 | 41 | self.openContainerResolver = resolve; 42 | 43 | [TAGContainerOpener openContainerWithId:containerId 44 | tagManager:self.tagManager 45 | openType:kTAGOpenTypePreferFresh 46 | timeout:nil 47 | notifier:self]; 48 | } 49 | 50 | RCT_REMAP_METHOD(refreshContainer, 51 | refreshContainerWithResolver:(RCTPromiseResolveBlock)resolve 52 | rejecter:(RCTPromiseRejectBlock)reject) 53 | { 54 | if (self.container != nil) { 55 | [self.container refresh]; 56 | resolve(@YES); 57 | } else { 58 | reject(E_CONTAINER_NOT_OPENED, nil, RCTErrorWithMessage(@"The container has not been opened. You must call openContainerWithId(..)")); 59 | } 60 | } 61 | 62 | RCT_EXPORT_METHOD(stringForKey:(NSString *)key 63 | resolver:(RCTPromiseResolveBlock)resolve 64 | rejecter:(RCTPromiseRejectBlock)reject) 65 | { 66 | if (self.container != nil) { 67 | resolve([self.container stringForKey:key]); 68 | } else { 69 | reject(E_CONTAINER_NOT_OPENED, nil, RCTErrorWithMessage(@"The container has not been opened. You must call openContainerWithId(..)")); 70 | } 71 | } 72 | 73 | RCT_EXPORT_METHOD(booleanForKey:(NSString*)key 74 | resolver:(RCTPromiseResolveBlock)resolve 75 | rejecter:(RCTPromiseRejectBlock)reject){ 76 | if (self.container != nil) { 77 | resolve([NSNumber numberWithBool:[self.container booleanForKey:key]]); 78 | } else { 79 | reject(E_CONTAINER_NOT_OPENED, nil, RCTErrorWithMessage(@"The container has not been opened. You must call openContainerWithId(..)")); 80 | } 81 | } 82 | 83 | RCT_EXPORT_METHOD(doubleForKey:(NSString*)key 84 | resolver:(RCTPromiseResolveBlock)resolve 85 | rejecter:(RCTPromiseRejectBlock)reject){ 86 | if (self.container != nil) { 87 | resolve([NSNumber numberWithDouble:[self.container doubleForKey:key]]); 88 | } else { 89 | reject(E_CONTAINER_NOT_OPENED, nil, RCTErrorWithMessage(@"The container has not been opened. You must call openContainerWithId(..)")); 90 | } 91 | } 92 | 93 | RCT_EXPORT_METHOD(pushDataLayerEvent:(NSDictionary*)dictionary 94 | resolver:(RCTPromiseResolveBlock)resolve 95 | rejecter:(RCTPromiseRejectBlock)reject){ 96 | if (self.container != nil && [[dictionary allKeys] containsObject:@"event"]) { 97 | [[TAGManager instance].dataLayer push:dictionary]; 98 | [[TAGManager instance] dispatch]; 99 | resolve(@YES); 100 | } else { 101 | if (self.container == nil) { 102 | reject(E_CONTAINER_NOT_OPENED, nil, RCTErrorWithMessage(@"The container has not been opened. You must call openContainerWithId(..)")); 103 | } else { 104 | reject(E_PUSH_EVENT_FAILED, nil, RCTErrorWithMessage(@"Validation error, data must have a key \"event\" with valid event name")); 105 | } 106 | } 107 | } 108 | 109 | RCT_EXPORT_METHOD(registerFunctionCallTagHandler:(NSString*)functionName 110 | resolver:(RCTPromiseResolveBlock)resolve 111 | rejecter:(RCTPromiseRejectBlock)reject){ 112 | if (self.container != nil && functionName != nil) { 113 | [self.container registerFunctionCallTagHandler:self forTag:functionName]; 114 | resolve(@YES); 115 | } else { 116 | if (self.container == nil) { 117 | reject(E_CONTAINER_NOT_OPENED, nil, RCTErrorWithMessage(@"The container has not been opened. You must call openContainerWithId(..)")); 118 | } else { 119 | reject(E_FUNCTION_CALL_REGISTRATION_FAILED, nil, RCTErrorWithMessage(@"Function name of the tag is not provided")); 120 | } 121 | } 122 | } 123 | 124 | RCT_EXPORT_METHOD(setVerboseLoggingEnabled:(BOOL)enabled 125 | resolver:(RCTPromiseResolveBlock)resolve 126 | rejecter:(RCTPromiseRejectBlock)reject){ 127 | if (enabled) { 128 | [[TAGManager instance].logger setLogLevel:kTAGLoggerLogLevelVerbose]; 129 | } else { 130 | [[TAGManager instance].logger setLogLevel:kTAGLoggerLogLevelWarning]; 131 | } 132 | resolve(@YES); 133 | } 134 | 135 | - (void)containerAvailable:(TAGContainer *)container { 136 | dispatch_async(_methodQueue, ^{ 137 | self.container = container; 138 | if (self.openContainerResolver) { 139 | self.openContainerResolver(@YES); 140 | self.openContainerResolver = nil; 141 | } 142 | }); 143 | } 144 | 145 | - (void)emitFunctionTag:(NSString *)functionName 146 | withPayload:(NSDictionary *)payload { 147 | dispatch_async(_methodQueue, ^{ 148 | NSDictionary *eventBody = @{@"payload": payload, @"_fn":functionName}; 149 | [self sendEventWithName:GTM_FUNCTION_CALL_TAG_EVENT body:eventBody]; 150 | }); 151 | } 152 | 153 | /* TAGFunctionCallTagHandler Inteface Implementation */ 154 | 155 | - (void)execute:(NSString *)functionName 156 | parameters:(NSDictionary *)parameters { 157 | [self emitFunctionTag:functionName withPayload:parameters]; 158 | } 159 | 160 | /* RCTEventEmitter Overrides */ 161 | 162 | - (NSArray *)supportedEvents 163 | { 164 | return @[GTM_FUNCTION_CALL_TAG_EVENT]; 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/GAI.h: -------------------------------------------------------------------------------- 1 | /*! 2 | @header GAI.h 3 | @abstract Google Analytics iOS SDK Header 4 | @version 3.17 5 | @copyright Copyright 2015 Google Inc. All rights reserved. 6 | */ 7 | 8 | #import 9 | 10 | #import "GAILogger.h" 11 | #import "GAITrackedViewController.h" 12 | #import "GAITracker.h" 13 | 14 | typedef NS_ENUM(NSUInteger, GAIDispatchResult) { 15 | kGAIDispatchNoData, 16 | kGAIDispatchGood, 17 | kGAIDispatchError 18 | }; 19 | 20 | /*! Google Analytics product string. */ 21 | extern NSString *const kGAIProduct; 22 | 23 | /*! Google Analytics version string. */ 24 | extern NSString *const kGAIVersion; 25 | 26 | /*! 27 | NSError objects returned by the Google Analytics SDK may have this error domain 28 | to indicate that the error originated in the Google Analytics SDK. 29 | */ 30 | extern NSString *const kGAIErrorDomain; 31 | 32 | /*! Google Analytics error codes. */ 33 | typedef enum { 34 | // This error code indicates that there was no error. Never used. 35 | kGAINoError = 0, 36 | 37 | // This error code indicates that there was a database-related error. 38 | kGAIDatabaseError, 39 | 40 | // This error code indicates that there was a network-related error. 41 | kGAINetworkError, 42 | } GAIErrorCode; 43 | 44 | /*! 45 | Google Analytics iOS top-level class. Provides facilities to create trackers 46 | and set behaviorial flags. 47 | */ 48 | @interface GAI : NSObject 49 | 50 | /*! 51 | For convenience, this class exposes a default tracker instance. 52 | This is initialized to `nil` and will be set to the first tracker that is 53 | instantiated in trackerWithTrackingId:. It may be overridden as desired. 54 | 55 | The GAITrackedViewController class will, by default, use this tracker instance. 56 | */ 57 | @property(nonatomic, assign) id defaultTracker; 58 | 59 | /*! 60 | The GAILogger to use. 61 | */ 62 | @property(nonatomic, retain) id logger; 63 | 64 | /*! 65 | When this is true, no tracking information will be gathered; tracking calls 66 | will effectively become no-ops. When set to true, all tracking information that 67 | has not yet been submitted. The value of this flag will be persisted 68 | automatically by the SDK. Developers can optionally use this flag to implement 69 | an opt-out setting in the app to allows users to opt out of Google Analytics 70 | tracking. 71 | 72 | This is set to `NO` the first time the Google Analytics SDK is used on a 73 | device, and is persisted thereafter. 74 | */ 75 | @property(nonatomic, assign) BOOL optOut; 76 | 77 | /*! 78 | If this value is positive, tracking information will be automatically 79 | dispatched every dispatchInterval seconds. Otherwise, tracking information must 80 | be sent manually by calling dispatch. 81 | 82 | By default, this is set to `120`, which indicates tracking information should 83 | be dispatched automatically every 120 seconds. 84 | */ 85 | @property(nonatomic, assign) NSTimeInterval dispatchInterval; 86 | 87 | /*! 88 | When set to true, the SDK will record the currently registered uncaught 89 | exception handler, and then register an uncaught exception handler which tracks 90 | the exceptions that occurred using defaultTracker. If defaultTracker is not 91 | `nil`, this function will track the exception on the tracker and attempt to 92 | dispatch any outstanding tracking information for 5 seconds. It will then call 93 | the previously registered exception handler, if any. When set back to false, 94 | the previously registered uncaught exception handler will be restored. 95 | */ 96 | @property(nonatomic, assign) BOOL trackUncaughtExceptions; 97 | 98 | /*! 99 | When this is 'YES', no tracking information will be sent. Defaults to 'NO'. 100 | */ 101 | @property(nonatomic, assign) BOOL dryRun; 102 | 103 | /*! Get the shared instance of the Google Analytics for iOS class. */ 104 | + (GAI *)sharedInstance; 105 | 106 | /*! 107 | Creates or retrieves a GAITracker implementation with the specified name and 108 | tracking ID. If the tracker for the specified name does not already exist, then 109 | it will be created and returned; otherwise, the existing tracker will be 110 | returned. If the existing tracker for the respective name has a different 111 | tracking ID, that tracking ID is not changed by this method. If defaultTracker 112 | is not set, it will be set to the tracker instance returned here. 113 | 114 | @param name The name of this tracker. Must not be `nil` or empty. 115 | 116 | @param trackingID The tracking ID to use for this tracker. It should be of 117 | the form `UA-xxxxx-y`. 118 | 119 | @return A GAITracker associated with the specified name. The tracker 120 | can be used to send tracking data to Google Analytics. The first time this 121 | method is called with a particular name, the tracker for that name will be 122 | returned, and subsequent calls with the same name will return the same 123 | instance. It is not necessary to retain the tracker because the tracker will be 124 | retained internally by the library. 125 | 126 | If an error occurs or the name is not valid, this method will return 127 | `nil`. 128 | */ 129 | - (id)trackerWithName:(NSString *)name 130 | trackingId:(NSString *)trackingId; 131 | 132 | /*! 133 | Creates or retrieves a GAITracker implementation with name equal to 134 | the specified tracking ID. If the tracker for the respective name does not 135 | already exist, it is created, has it's tracking ID set to |trackingId|, 136 | and is returned; otherwise, the existing tracker is returned. If the existing 137 | tracker for the respective name has a different tracking ID, that tracking ID 138 | is not changed by this method. If defaultTracker is not set, it is set to the 139 | tracker instance returned here. 140 | 141 | @param trackingID The tracking ID to use for this tracker. It should be of 142 | the form `UA-xxxxx-y`. The name of the tracker will be the same as trackingID. 143 | 144 | @return A GAITracker associated with the specified trackingID. The tracker 145 | can be used to send tracking data to Google Analytics. The first time this 146 | method is called with a particular trackingID, the tracker for the respective 147 | name will be returned, and subsequent calls with the same trackingID 148 | will return the same instance. It is not necessary to retain the tracker 149 | because the tracker will be retained internally by the library. 150 | 151 | If an error occurs or the trackingId is not valid, this method will return 152 | `nil`. 153 | */ 154 | - (id)trackerWithTrackingId:(NSString *)trackingId; 155 | 156 | /*! 157 | Remove a tracker from the trackers dictionary. If it is the default tracker, 158 | clears the default tracker as well. 159 | 160 | @param name The name of the tracker. 161 | */ 162 | - (void)removeTrackerByName:(NSString *)name; 163 | 164 | /*! 165 | Dispatches any pending tracking information. 166 | 167 | Note that this does not have any effect on dispatchInterval, and can be used in 168 | conjunction with periodic dispatch. */ 169 | - (void)dispatch; 170 | 171 | /*! 172 | Dispatches the next tracking beacon in the queue, calling completionHandler when 173 | the tracking beacon has either been sent (returning kGAIDispatchGood) or an error has resulted 174 | (returning kGAIDispatchError). If there is no network connection or there is no data to send, 175 | kGAIDispatchNoData is returned. 176 | 177 | Note that calling this method with a non-nil completionHandler disables periodic dispatch. 178 | Periodic dispatch can be reenabled by setting the dispatchInterval to a positive number when 179 | the app resumes from the background. 180 | 181 | Calling this method with a nil completionHandler is the same as calling the dispatch 182 | above. 183 | 184 | This method can be used for background data fetching in iOS 7.0 or later. It would be wise to 185 | call this when the application is exiting to initiate the submission of any unsubmitted 186 | tracking information. 187 | 188 | @param completionHandler The block to run after a single dispatch request. The GAIDispatchResult 189 | param indicates whether the dispatch succeeded, had an error, or had no hits to dispatch. 190 | */ 191 | - (void)dispatchWithCompletionHandler:(void (^)(GAIDispatchResult result))completionHandler; 192 | @end 193 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/GAIDictionaryBuilder.h: -------------------------------------------------------------------------------- 1 | /*! 2 | @header GAIDictionaryBuilder.h 3 | @abstract Google Analytics iOS SDK Hit Format Header 4 | @copyright Copyright 2013 Google Inc. All rights reserved. 5 | */ 6 | 7 | #import 8 | 9 | #import "GAIEcommerceProduct.h" 10 | #import "GAIEcommerceProductAction.h" 11 | #import "GAIEcommercePromotion.h" 12 | 13 | /*! 14 | * Helper class to build a dictionary of hit parameters and values. 15 | *
16 | * Examples: 17 | * 18 | * id t = // get a tracker. 19 | * [t send:[[[GAIDictionaryBuilder createEventWithCategory:@"EventCategory" 20 | * action:@"EventAction" 21 | * label:nil 22 | * value:nil] 23 | * set:@"dimension1" forKey:[GAIFields customDimensionForIndex:1]] build]]; 24 | * 25 | * This will send an event hit type with the specified parameters 26 | * and a custom dimension parameter. 27 | *
28 | * If you want to send a parameter with all hits, set it on GAITracker directly. 29 | * 30 | * [t set:kGAIScreenName value:@"Home"]; 31 | * [t send:[[GAIDictionaryBuilder createSocialWithNetwork:@"Google+" 32 | * action:@"PlusOne" 33 | * target:@"SOME_URL"] build]]; 34 | * [t send:[[GAIDictionaryBuilder createSocialWithNetwork:@"Google+" 35 | * action:@"Share" 36 | * target:@"SOME_POST"] build]]; 37 | * [t send:[[GAIDictionaryBuilder createSocialWithNetwork:@"Google+" 38 | * action:@"HangOut" 39 | * target:@"SOME_CIRCLE"] 40 | * build]]; 41 | * 42 | * You can override a value set on the tracker by adding it to the dictionary. 43 | * 44 | * [t set:kGAIScreenName value:@"Home"]; 45 | * [t send:...]; 46 | * [t send[[[GAIDictionaryBuilder createEventWithCategory:@"click" 47 | * action:@"popup" 48 | * label:nil 49 | * value:nil] 50 | * set:@"popup title" forKey:kGAIScreenName] build]]; 51 | * 52 | * The values set via [GAIDictionaryBuilder set] or 53 | * [GAIDictionaryBuilder setAll] will override any existing values in the 54 | * GAIDictionaryBuilder object (i.e. initialized by 55 | * [GAIDictionaryBuilder createXYZ]). e.g. 56 | * 57 | * GAIDictionaryBuilder *m = 58 | * GAIDictionaryBuilder createTimingWithCategory:@"category" 59 | * interval:@0 60 | * name:@"name" 61 | * label:nil]; 62 | * [t send:[m.set:@"10" forKey:kGAITimingVar] build]; 63 | * [t send:[m.set:@"20" forKey:kGAITimingVar] build]; 64 | * 65 | */ 66 | @interface GAIDictionaryBuilder : NSObject 67 | 68 | - (GAIDictionaryBuilder *)set:(NSString *)value 69 | forKey:(NSString *)key; 70 | 71 | /*! 72 | * Copies all the name-value pairs from params into this object, ignoring any 73 | * keys that are not NSString and any values that are neither NSString or 74 | * NSNull. 75 | */ 76 | - (GAIDictionaryBuilder *)setAll:(NSDictionary *)params; 77 | 78 | /*! 79 | * Returns the value for the input parameter paramName, or nil if paramName 80 | * is not present. 81 | */ 82 | - (NSString *)get:(NSString *)paramName; 83 | 84 | /*! 85 | * Return an NSMutableDictionary object with all the parameters set in this 86 | */ 87 | - (NSMutableDictionary *)build; 88 | 89 | /*! 90 | * Parses and translates utm campaign parameters to analytics campaign param 91 | * and returns them as a map. 92 | * 93 | * @param params url containing utm campaign parameters. 94 | * 95 | * Valid campaign parameters are: 96 | *
    97 | *
  • utm_id
  • 98 | *
  • utm_campaign
  • 99 | *
  • utm_content
  • 100 | *
  • utm_medium
  • 101 | *
  • utm_source
  • 102 | *
  • utm_term
  • 103 | *
  • dclid
  • 104 | *
  • gclid
  • 105 | *
  • gmob_t
  • 106 | *
  • aclid
  • 107 | *
  • anid
  • 108 | *
109 | *

110 | * Example: 111 | * http://my.site.com/index.html?utm_campaign=wow&utm_source=source 112 | * utm_campaign=wow&utm_source=source. 113 | *

114 | * For more information on manual and auto-tagging, see 115 | * https://support.google.com/analytics/answer/1733663?hl=en 116 | */ 117 | - (GAIDictionaryBuilder *)setCampaignParametersFromUrl:(NSString *)urlString; 118 | 119 | /*! 120 | Returns a GAIDictionaryBuilder object with parameters specific to an appview 121 | hit. 122 | 123 | Note that using this method will not set the screen name for followon hits. To 124 | do that you need to call set:kGAIDescription value: on the 125 | GAITracker instance. 126 | 127 | This method is deprecated. Use createScreenView instead. 128 | */ 129 | + (GAIDictionaryBuilder *)createAppView DEPRECATED_MSG_ATTRIBUTE("Use createScreenView instead."); 130 | 131 | /*! 132 | Returns a GAIDictionaryBuilder object with parameters specific to a screenview 133 | hit. 134 | 135 | Note that using this method will not set the screen name for followon hits. To 136 | do that you need to call set:kGAIDescription value: on the 137 | GAITracker instance. 138 | */ 139 | + (GAIDictionaryBuilder *)createScreenView; 140 | 141 | /*! 142 | Returns a GAIDictionaryBuilder object with parameters specific to an event hit. 143 | */ 144 | + (GAIDictionaryBuilder *)createEventWithCategory:(NSString *)category 145 | action:(NSString *)action 146 | label:(NSString *)label 147 | value:(NSNumber *)value; 148 | 149 | /*! 150 | Returns a GAIDictionaryBuilder object with parameters specific to an exception 151 | hit. 152 | */ 153 | + (GAIDictionaryBuilder *)createExceptionWithDescription:(NSString *)description 154 | withFatal:(NSNumber *)fatal; 155 | 156 | /*! 157 | Returns a GAIDictionaryBuilder object with parameters specific to an item hit. 158 | */ 159 | + (GAIDictionaryBuilder *)createItemWithTransactionId:(NSString *)transactionId 160 | name:(NSString *)name 161 | sku:(NSString *)sku 162 | category:(NSString *)category 163 | price:(NSNumber *)price 164 | quantity:(NSNumber *)quantity 165 | currencyCode:(NSString *)currencyCode; 166 | 167 | /*! 168 | Returns a GAIDictionaryBuilder object with parameters specific to a social hit. 169 | */ 170 | + (GAIDictionaryBuilder *)createSocialWithNetwork:(NSString *)network 171 | action:(NSString *)action 172 | target:(NSString *)target; 173 | 174 | /*! 175 | Returns a GAIDictionaryBuilder object with parameters specific to a timing hit. 176 | */ 177 | + (GAIDictionaryBuilder *)createTimingWithCategory:(NSString *)category 178 | interval:(NSNumber *)intervalMillis 179 | name:(NSString *)name 180 | label:(NSString *)label; 181 | 182 | /*! 183 | Returns a GAIDictionaryBuilder object with parameters specific to a transaction 184 | hit. 185 | */ 186 | + (GAIDictionaryBuilder *)createTransactionWithId:(NSString *)transactionId 187 | affiliation:(NSString *)affiliation 188 | revenue:(NSNumber *)revenue 189 | tax:(NSNumber *)tax 190 | shipping:(NSNumber *)shipping 191 | currencyCode:(NSString *)currencyCode; 192 | 193 | /*! 194 | Set the product action field for this hit. 195 | */ 196 | - (GAIDictionaryBuilder *)setProductAction:(GAIEcommerceProductAction *)productAction; 197 | 198 | /*! 199 | Adds a product to this hit. 200 | */ 201 | - (GAIDictionaryBuilder *)addProduct:(GAIEcommerceProduct *)product; 202 | 203 | /*! 204 | Add a product impression to this hit. 205 | */ 206 | - (GAIDictionaryBuilder *)addProductImpression:(GAIEcommerceProduct *)product 207 | impressionList:(NSString *)name 208 | impressionSource:(NSString *)source; 209 | 210 | /*! 211 | Add a promotion to this hit. 212 | */ 213 | - (GAIDictionaryBuilder *)addPromotion:(GAIEcommercePromotion *)promotion; 214 | @end 215 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/GAIEcommerceFields.h: -------------------------------------------------------------------------------- 1 | /*! 2 | @header GAIEcommerceFields.h 3 | @abstract Google Analytics iOS SDK Ecommerce Hit Format Header 4 | @copyright Copyright 2014 Google Inc. All rights reserved. 5 | */ 6 | 7 | #import 8 | 9 | /*! 10 | This class provides several fields and methods useful as wire format parameters for 11 | Enhanced Ecommerce. See the online developer guides for Enhanced Ecommerce for details 12 | on how to use the Enhanced Ecommerce features. 13 | */ 14 | 15 | // Enhanced Ecommerce Product fields 16 | extern NSString *const kGAIProductId; 17 | extern NSString *const kGAIProductName; 18 | extern NSString *const kGAIProductBrand; 19 | extern NSString *const kGAIProductCategory; 20 | extern NSString *const kGAIProductVariant; 21 | extern NSString *const kGAIProductPrice; 22 | extern NSString *const kGAIProductQuantity; 23 | extern NSString *const kGAIProductCouponCode; 24 | extern NSString *const kGAIProductPosition; 25 | 26 | extern NSString *const kGAIProductAction; 27 | 28 | // product action values 29 | extern NSString *const kGAIPADetail; 30 | extern NSString *const kGAIPAClick; 31 | extern NSString *const kGAIPAAdd; 32 | extern NSString *const kGAIPARemove; 33 | extern NSString *const kGAIPACheckout; 34 | extern NSString *const kGAIPACheckoutOption; 35 | extern NSString *const kGAIPAPurchase; 36 | extern NSString *const kGAIPARefund; 37 | 38 | // product action fields 39 | // used for 'purchase' and 'refund' actions 40 | extern NSString *const kGAIPATransactionId; 41 | extern NSString *const kGAIPAAffiliation; 42 | extern NSString *const kGAIPARevenue; 43 | extern NSString *const kGAIPATax; 44 | extern NSString *const kGAIPAShipping; 45 | extern NSString *const kGAIPACouponCode; 46 | // used for 'checkout' action 47 | extern NSString *const kGAICheckoutStep; 48 | extern NSString *const kGAICheckoutOption; 49 | // used for 'detail' and 'click' actions 50 | extern NSString *const kGAIProductActionList; 51 | extern NSString *const kGAIProductListSource; 52 | 53 | // Enhanced Ecommerce Impressions fields 54 | extern NSString *const kGAIImpressionName; 55 | extern NSString *const kGAIImpressionListSource; 56 | extern NSString *const kGAIImpressionProduct; 57 | extern NSString *const kGAIImpressionProductId; 58 | extern NSString *const kGAIImpressionProductName; 59 | extern NSString *const kGAIImpressionProductBrand; 60 | extern NSString *const kGAIImpressionProductCategory; 61 | extern NSString *const kGAIImpressionProductVariant; 62 | extern NSString *const kGAIImpressionProductPosition; 63 | extern NSString *const kGAIImpressionProductPrice; 64 | 65 | // Enhanced Ecommerce Promotions fields 66 | extern NSString *const kGAIPromotionId; 67 | extern NSString *const kGAIPromotionName; 68 | extern NSString *const kGAIPromotionCreative; 69 | extern NSString *const kGAIPromotionPosition; 70 | 71 | // Promotion actions 72 | extern NSString *const kGAIPromotionAction; 73 | extern NSString *const kGAIPromotionView; 74 | extern NSString *const kGAIPromotionClick; 75 | 76 | @interface GAIEcommerceFields : NSObject 77 | 78 | /*! 79 | Generates an enhanced ecommerce product field. Note that field names generated by 80 | customDimensionForIndex and customMetricForIndex can be used as suffixes. 81 | 82 | @param index the index of the product 83 | @param suffix the product field suffix (such as kGAIProductPrice). 84 | 85 | @return an NSString representing the product field parameter 86 | */ 87 | + (NSString *)productFieldForIndex:(NSUInteger)index suffix:(NSString *)suffix; 88 | 89 | /*! 90 | Genrates an enhanced ecommerce impression list field name with an index. The return value of 91 | this method should also be used as input to the productImpressionForList method below. 92 | 93 | @param index the index of the impression list 94 | 95 | @return an NSString representing the impression list parameter 96 | */ 97 | + (NSString *)impressionListForIndex:(NSUInteger)index; 98 | 99 | /*! 100 | Generates an enhanced ecommerce product impression field with the impression list, product index 101 | and product suffix as parameters. The output of the method impressionListForIndex above should be 102 | used as the input list for this method. The output of customDimensionForIndex and 103 | customMetricForIndex can be used as suffixes. 104 | 105 | @param list the impression list for this product impression 106 | @param index the index of this product in the impression list 107 | @param suffix the product impression suffix for this field 108 | 109 | @return an NSString representing this product impression field parameter 110 | */ 111 | + (NSString *)productImpressionForList:(NSString *)list 112 | index:(NSUInteger)index 113 | suffix:(NSString *)Suffix; 114 | 115 | /*! 116 | Generates an enhanced ecommerce promotion field with an index and suffix. 117 | 118 | @param index the index of the promotion 119 | @param suffix the promotion suffix (such as kGAIPromotionId) 120 | 121 | @return an NSString representing this promotion field paramter 122 | */ 123 | + (NSString *)promotionForIndex:(NSUInteger)index suffix:(NSString *)suffix; 124 | @end 125 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/GAIEcommerceProduct.h: -------------------------------------------------------------------------------- 1 | /*! 2 | @header GAIEcommerceProduct.h 3 | @abstract Google Analytics iOS SDK Hit Format Header 4 | @copyright Copyright 2014 Google Inc. All rights reserved. 5 | */ 6 | 7 | #import 8 | 9 | /*! 10 | * Class to construct product related information for a Google Analytics beacon. Use this class to 11 | * report information about products sold by merchants or impressions of products seen by users. 12 | * Instances of this class can be associated with both Product Actions and Product 13 | * Impression Lists. 14 | *
15 | * Typical usage: 16 | * 17 | * [tracker set:kGAIScreenName value:@"MyScreen"]; 18 | * GAIDictionaryBuilder *builder = [GAIDictionaryBuilder createScreenView]; 19 | * GAIEcommerceProduct *product = [[GAIEcommerceProduct alloc] init]; 20 | * [product setId:@""PID-1234""]; 21 | * [product setName:@"Space Monkeys!"]; 22 | * [product setPrice:@100]; 23 | * [product setQuantity:@2]; 24 | * [builder addProductImpression:product impressionList:@"listName"]; 25 | * [tracker send:[builder build]]; 26 | * 27 | */ 28 | @interface GAIEcommerceProduct : NSObject 29 | 30 | /*! 31 | Sets the id that is used to identify a product in GA reports. 32 | */ 33 | - (GAIEcommerceProduct *)setId:(NSString *)productId; 34 | 35 | /*! 36 | Sets the name that is used to indentify the product in GA reports. 37 | */ 38 | - (GAIEcommerceProduct *)setName:(NSString *)productName; 39 | 40 | /*! 41 | Sets the brand associated with the product in GA reports. 42 | */ 43 | - (GAIEcommerceProduct *)setBrand:(NSString *)productBrand; 44 | 45 | /*! 46 | Sets the category associated with the product in GA reports. 47 | */ 48 | - (GAIEcommerceProduct *)setCategory:(NSString *)productCategory; 49 | 50 | /*! 51 | Sets the variant of the product. 52 | */ 53 | - (GAIEcommerceProduct *)setVariant:(NSString *)productVariant; 54 | 55 | /*! 56 | Sets the price of the product. 57 | */ 58 | - (GAIEcommerceProduct *)setPrice:(NSNumber *)productPrice; 59 | 60 | /*! 61 | Sets the quantity of the product. This field is usually not used with product impressions. 62 | */ 63 | - (GAIEcommerceProduct *)setQuantity:(NSNumber *)productQuantity; 64 | 65 | /*! 66 | Sets the coupon code associated with the product. This field is usually not used with product 67 | impressions. 68 | */ 69 | - (GAIEcommerceProduct *)setCouponCode:(NSString *)productCouponCode; 70 | 71 | /*! 72 | Sets the position of the product on the screen/product impression list, etc. 73 | */ 74 | - (GAIEcommerceProduct *)setPosition:(NSNumber *)productPosition; 75 | 76 | /*! 77 | Sets the custom dimension associated with this product. 78 | */ 79 | - (GAIEcommerceProduct *)setCustomDimension:(NSUInteger)index value:(NSString *)value; 80 | 81 | /*! 82 | Sets the custom metric associated with this product. 83 | */ 84 | - (GAIEcommerceProduct *)setCustomMetric:(NSUInteger)index value:(NSNumber *)value; 85 | 86 | /*! 87 | Builds an NSDictionary of fields stored in this instance suitable for a product action. The 88 | index parameter is the index of this product in the product action list. 89 |
90 | Normally, users will have no need to call this method. 91 | */ 92 | - (NSDictionary *)buildWithIndex:(NSUInteger)index; 93 | 94 | /*! 95 | Builds an NSDictionary of fields stored in this instance suitable for an impression list. The 96 | lIndex parameter is the index of the product impression list while the index parameter is the 97 | index of this product in that impression list. 98 |
99 | Normally, users will have no need to call this method. 100 | */ 101 | - (NSDictionary *)buildWithListIndex:(NSUInteger)lIndex index:(NSUInteger)index; 102 | @end 103 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/GAIEcommerceProductAction.h: -------------------------------------------------------------------------------- 1 | /*! 2 | @header GAIProductAction.h 3 | @abstract Google Analytics iOS SDK Hit Format Header 4 | @copyright Copyright 2014 Google Inc. All rights reserved. 5 | */ 6 | 7 | #import 8 | 9 | /*! 10 | * Class to construct transaction/checkout or other product interaction related information for a 11 | * Google Analytics hit. Use this class to report information about products sold, viewed or 12 | * refunded. This class is intended to be used with GAIDictionaryBuilder. 13 | *
14 | * Typical usage: 15 | * 16 | * [tracker set:kGAIScreenName value:@"MyScreen"]; 17 | * GAIDictionaryBuilder *builder = [GAIDictionaryBuilder createScreenView]; 18 | * GAIEcommerceProductAction *action = [[GAIEcommerceProductAction alloc] init]; 19 | * [action setAction:kGAIPAPurchase]; 20 | * [action setTransactionId:@"TT-1234"]; 21 | * [action setRevenue:@3.14]; 22 | * [action setCouponCode:@"EXTRA100"]; 23 | * [builder setProductAction:action]; 24 | * GAIEcommerceProduct *product = [[GAIEcommerceProduct alloc] init]; 25 | * [product setId:@""PID-1234""]; 26 | * [product setName:@"Space Monkeys!"]; 27 | * [product setPrice:@100]; 28 | * [product setQuantity:@2]; 29 | * [builder addProduct:product]; 30 | * [tracker send:[builder build]]; 31 | * 32 | */ 33 | @interface GAIEcommerceProductAction : NSObject 34 | 35 | /*! 36 | Sets the product action field for this product action. Valid values can be found in 37 | GAIEcommerceFields.h under "product action values". 38 | */ 39 | - (GAIEcommerceProductAction *)setAction:(NSString *)productAction; 40 | 41 | /*! 42 | The unique id associated with the transaction. This value is used for kGAIPAPurchase and 43 | kGAIPARefund product actions. 44 | */ 45 | - (GAIEcommerceProductAction *)setTransactionId:(NSString *)transactionId; 46 | 47 | /*! 48 | Sets the transaction's affiliation value. This value is used for kGAIPAPurchase and 49 | kGAIPARefund product actions. 50 | */ 51 | - (GAIEcommerceProductAction *)setAffiliation:(NSString *)affiliation; 52 | 53 | /*! 54 | Sets the transaction's total revenue. This value is used for kGAIPAPurchase and kGAIPARefund 55 | product actions. 56 | */ 57 | - (GAIEcommerceProductAction *)setRevenue:(NSNumber *)revenue; 58 | 59 | /*! 60 | Sets the transaction's total tax. This value is used for kGAIPAPurchase and kGAIPARefund 61 | product actions. 62 | */ 63 | - (GAIEcommerceProductAction *)setTax:(NSNumber *)tax; 64 | 65 | /*! 66 | Sets the transaction's total shipping costs. This value is used for kGAIPAPurchase and 67 | kGAIPARefund product actions. 68 | */ 69 | - (GAIEcommerceProductAction *)setShipping:(NSNumber *)shipping; 70 | 71 | /*! 72 | Sets the coupon code used in this transaction. This value is used for kGAIPAPurchase and 73 | kGAIPARefund product actions. 74 | */ 75 | - (GAIEcommerceProductAction *)setCouponCode:(NSString *)couponCode; 76 | 77 | /*! 78 | Sets the checkout process's progress. This value is used for kGAICheckout and 79 | kGAICheckoutOptions product actions. 80 | */ 81 | - (GAIEcommerceProductAction *)setCheckoutStep:(NSNumber *)checkoutStep; 82 | 83 | /*! 84 | Sets the option associated with the checkout. This value is used for kGAICheckout and 85 | kGAICheckoutOptions product actions. 86 | */ 87 | - (GAIEcommerceProductAction *)setCheckoutOption:(NSString *)checkoutOption; 88 | 89 | /*! 90 | Sets the list name associated with the products in Google Analytics beacons. This value is 91 | used in kGAIPADetail and kGAIPAClick product actions. 92 | */ 93 | - (GAIEcommerceProductAction *)setProductActionList:(NSString *)productActionList; 94 | 95 | /*! 96 | Sets the list source name associated with the products in Google Analytics beacons. This value 97 | is used in kGAIPADetail and kGAIPAClick product actions. 98 | */ 99 | - (GAIEcommerceProductAction *)setProductListSource:(NSString *)productListSource; 100 | 101 | /*! 102 | Builds an NSDictionary of fields stored in this instance representing this product action. 103 |
104 | Normally, users will have no need to call this method. 105 | */ 106 | - (NSDictionary *)build; 107 | @end 108 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/GAIEcommercePromotion.h: -------------------------------------------------------------------------------- 1 | /*! 2 | @header GAIEcommercePromotion.h 3 | @abstract Google Analytics iOS SDK Hit Format Header 4 | @copyright Copyright 2014 Google Inc. All rights reserved. 5 | */ 6 | 7 | #import 8 | 9 | /*! 10 | * Class to construct promotion related fields for Google Analytics hits. The fields from this class 11 | * can be used to represent internal promotions that run within an app, such as banners, banner ads 12 | * etc. 13 | * 14 | * Typical usage: 15 | * 16 | * GAIDictionaryBuilder *builder = [GAIDictionaryBuilder createScreenView]; 17 | * GAIEcommercePromotion *promotion = [[GAIEcommercePromotion alloc] init]; 18 | * [promotion setId:@"PROMO-ID1234"]; 19 | * [promotion setName:@"Home screen banner"]; 20 | * [builder set:kGAIPromotionClick forKey:kGAIPromotionAction]; 21 | * [builder addPromotion:promotion]; 22 | * [tracker send:builder.build]]; 23 | * 24 | */ 25 | @interface GAIEcommercePromotion : NSObject 26 | 27 | /*! 28 | Sets the id that is used to identify a promotion in GA reports. 29 | */ 30 | - (GAIEcommercePromotion *)setId:(NSString *)pid; 31 | 32 | /*! 33 | Sets the name that is used to identify a promotion in GA reports. 34 | */ 35 | - (GAIEcommercePromotion *)setName:(NSString *)name; 36 | 37 | /*! 38 | Sets the name of the creative associated with the promotion. 39 | */ 40 | - (GAIEcommercePromotion *)setCreative:(NSString *)creative; 41 | 42 | /*! 43 | Sets the position of the promotion. 44 | */ 45 | - (GAIEcommercePromotion *)setPosition:(NSString *)position; 46 | 47 | /*! 48 | Builds an NSDictionary of fields stored in this instance. The index parameter is the 49 | index of this promotion in that promotion list. 50 |
51 | Normally, users will have no need to call this method. 52 | */ 53 | - (NSDictionary *)buildWithIndex:(NSUInteger)index; 54 | @end 55 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/GAIFields.h: -------------------------------------------------------------------------------- 1 | /*! 2 | @header GAIFields.h 3 | @abstract Google Analytics iOS SDK Hit Format Header 4 | @copyright Copyright 2013 Google Inc. All rights reserved. 5 | */ 6 | 7 | #import 8 | 9 | /*! 10 | These fields can be used for the wire format parameter names required by 11 | the |GAITracker| get, set and send methods as well as the set methods in the 12 | |GAIDictionaryBuilder| class. 13 | */ 14 | extern NSString *const kGAIUseSecure; 15 | 16 | extern NSString *const kGAIHitType; 17 | extern NSString *const kGAITrackingId; 18 | extern NSString *const kGAIClientId; 19 | extern NSString *const kGAIDataSource; 20 | extern NSString *const kGAIAnonymizeIp; 21 | extern NSString *const kGAISessionControl; 22 | extern NSString *const kGAIDeviceModelVersion; 23 | extern NSString *const kGAIScreenResolution; 24 | extern NSString *const kGAIViewportSize; 25 | extern NSString *const kGAIEncoding; 26 | extern NSString *const kGAIScreenColors; 27 | extern NSString *const kGAILanguage; 28 | extern NSString *const kGAIJavaEnabled; 29 | extern NSString *const kGAIFlashVersion; 30 | extern NSString *const kGAINonInteraction; 31 | extern NSString *const kGAIReferrer; 32 | extern NSString *const kGAILocation; 33 | extern NSString *const kGAIHostname; 34 | extern NSString *const kGAIPage; 35 | extern NSString *const kGAIDescription; // synonym for kGAIScreenName 36 | extern NSString *const kGAIScreenName; // synonym for kGAIDescription 37 | extern NSString *const kGAITitle; 38 | extern NSString *const kGAIAdMobHitId; 39 | extern NSString *const kGAIAppName; 40 | extern NSString *const kGAIAppVersion; 41 | extern NSString *const kGAIAppId; 42 | extern NSString *const kGAIAppInstallerId; 43 | extern NSString *const kGAIUserId; 44 | 45 | extern NSString *const kGAIEventCategory; 46 | extern NSString *const kGAIEventAction; 47 | extern NSString *const kGAIEventLabel; 48 | extern NSString *const kGAIEventValue; 49 | 50 | extern NSString *const kGAISocialNetwork; 51 | extern NSString *const kGAISocialAction; 52 | extern NSString *const kGAISocialTarget; 53 | 54 | extern NSString *const kGAITransactionId; 55 | extern NSString *const kGAITransactionAffiliation; 56 | extern NSString *const kGAITransactionRevenue; 57 | extern NSString *const kGAITransactionShipping; 58 | extern NSString *const kGAITransactionTax; 59 | extern NSString *const kGAICurrencyCode; 60 | 61 | extern NSString *const kGAIItemPrice; 62 | extern NSString *const kGAIItemQuantity; 63 | extern NSString *const kGAIItemSku; 64 | extern NSString *const kGAIItemName; 65 | extern NSString *const kGAIItemCategory; 66 | 67 | extern NSString *const kGAICampaignSource; 68 | extern NSString *const kGAICampaignMedium; 69 | extern NSString *const kGAICampaignName; 70 | extern NSString *const kGAICampaignKeyword; 71 | extern NSString *const kGAICampaignContent; 72 | extern NSString *const kGAICampaignId; 73 | extern NSString *const kGAICampaignAdNetworkClickId; 74 | extern NSString *const kGAICampaignAdNetworkId; 75 | 76 | extern NSString *const kGAITimingCategory; 77 | extern NSString *const kGAITimingVar; 78 | extern NSString *const kGAITimingValue; 79 | extern NSString *const kGAITimingLabel; 80 | 81 | extern NSString *const kGAIExDescription; 82 | extern NSString *const kGAIExFatal; 83 | 84 | extern NSString *const kGAISampleRate; 85 | 86 | extern NSString *const kGAIIdfa; 87 | extern NSString *const kGAIAdTargetingEnabled; 88 | 89 | // hit types 90 | extern NSString *const kGAIAppView DEPRECATED_MSG_ATTRIBUTE("Use kGAIScreenView instead."); 91 | extern NSString *const kGAIScreenView; 92 | extern NSString *const kGAIEvent; 93 | extern NSString *const kGAISocial; 94 | extern NSString *const kGAITransaction; 95 | extern NSString *const kGAIItem; 96 | extern NSString *const kGAIException; 97 | extern NSString *const kGAITiming; 98 | 99 | /*! 100 | This class provides several fields and methods useful as wire format parameter 101 | names. The methods are used for wire format parameter names that are indexed. 102 | */ 103 | 104 | @interface GAIFields : NSObject 105 | 106 | /*! 107 | Generates the correct parameter name for a content group with an index. 108 | 109 | @param index the index of the content group. 110 | 111 | @return an NSString representing the content group parameter for the index. 112 | */ 113 | + (NSString *)contentGroupForIndex:(NSUInteger)index; 114 | 115 | /*! 116 | Generates the correct parameter name for a custon dimension with an index. 117 | 118 | @param index the index of the custom dimension. 119 | 120 | @return an NSString representing the custom dimension parameter for the index. 121 | */ 122 | + (NSString *)customDimensionForIndex:(NSUInteger)index; 123 | 124 | /*! 125 | Generates the correct parameter name for a custom metric with an index. 126 | 127 | @param index the index of the custom metric. 128 | 129 | @return an NSString representing the custom metric parameter for the index. 130 | */ 131 | + (NSString *)customMetricForIndex:(NSUInteger)index; 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/GAILogger.h: -------------------------------------------------------------------------------- 1 | /*! 2 | @header GAILogger.h 3 | @abstract Google Analytics iOS SDK Source 4 | @copyright Copyright 2011 Google Inc. All rights reserved. 5 | */ 6 | 7 | #import 8 | 9 | typedef NS_ENUM(NSUInteger, GAILogLevel) { 10 | kGAILogLevelNone = 0, 11 | kGAILogLevelError = 1, 12 | kGAILogLevelWarning = 2, 13 | kGAILogLevelInfo = 3, 14 | kGAILogLevelVerbose = 4 15 | }; 16 | 17 | /*! 18 | Protocol to be used for logging debug and informational messages from the SDK. 19 | Implementations of this protocol can be provided to the |GAI| class, 20 | to be used as the logger by the SDK. See the |logger| property in GAI.h. 21 | */ 22 | @protocol GAILogger 23 | @required 24 | 25 | /*! 26 | Only messages of |logLevel| and below are logged. 27 | */ 28 | @property (nonatomic, assign) GAILogLevel logLevel; 29 | 30 | /*! 31 | Logs message with log level |kGAILogLevelVerbose|. 32 | */ 33 | - (void)verbose:(NSString *)message; 34 | 35 | /*! 36 | Logs message with log level |kGAILogLevelInfo|. 37 | */ 38 | - (void)info:(NSString *)message; 39 | 40 | /*! 41 | Logs message with log level |kGAILogLevelWarning|. 42 | */ 43 | - (void)warning:(NSString *)message; 44 | 45 | /*! 46 | Logs message with log level |kGAILogLevelError|. 47 | */ 48 | - (void)error:(NSString *)message; 49 | @end 50 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/GAITrackedViewController.h: -------------------------------------------------------------------------------- 1 | /*! 2 | @header GAITrackedViewController.h 3 | @abstract Google Analytics for iOS Tracked View Controller Header 4 | @copyright Copyright 2012 Google Inc. All rights reserved. 5 | */ 6 | 7 | #import 8 | #import 9 | 10 | @protocol GAITracker; 11 | 12 | /*! 13 | Extends UIViewController to generate Google Analytics screenview calls 14 | whenever the view appears; this is done by overriding the `viewDidAppear:` 15 | method. The screen name must be set for any tracking calls to be made. 16 | 17 | By default, this will use [GAI defaultTracker] for tracking calls, but one can 18 | override this by setting the tracker property. 19 | */ 20 | @interface GAITrackedViewController : UIViewController 21 | 22 | /*! 23 | The tracker on which view tracking calls are be made, or `nil`, in which case 24 | [GAI defaultTracker] will be used. 25 | */ 26 | @property(nonatomic, assign) id tracker; 27 | /*! 28 | The screen name, for purposes of Google Analytics tracking. If this is `nil`, 29 | no tracking calls will be made. 30 | */ 31 | @property(nonatomic, copy) NSString *screenName; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/GAITracker.h: -------------------------------------------------------------------------------- 1 | /*! 2 | @header GAITracker.h 3 | @abstract Google Analytics iOS SDK Tracker Header 4 | @copyright Copyright 2013 Google Inc. All rights reserved. 5 | */ 6 | 7 | #import 8 | 9 | /*! 10 | Google Analytics tracking interface. Obtain instances of this interface from 11 | [GAI trackerWithTrackingId:] to track screens, events, transactions, timing, 12 | and exceptions. The implementation of this interface is thread-safe, and no 13 | calls are expected to block or take a long time. All network and disk activity 14 | will take place in the background. 15 | */ 16 | @protocol GAITracker 17 | 18 | /*! 19 | Name of this tracker. 20 | */ 21 | @property(nonatomic, readonly) NSString *name; 22 | 23 | /*! 24 | Allow collection of IDFA and related fields if set to true. Default is false. 25 | */ 26 | @property(nonatomic) BOOL allowIDFACollection; 27 | 28 | /*! 29 | Set a tracking parameter. 30 | 31 | @param parameterName The parameter name. 32 | 33 | @param value The value to set for the parameter. If this is nil, the 34 | value for the parameter will be cleared. 35 | */ 36 | - (void)set:(NSString *)parameterName 37 | value:(NSString *)value; 38 | 39 | /*! 40 | Get a tracking parameter. 41 | 42 | @param parameterName The parameter name. 43 | 44 | @returns The parameter value, or nil if no value for the given parameter is 45 | set. 46 | */ 47 | - (NSString *)get:(NSString *)parameterName; 48 | 49 | /*! 50 | Queue tracking information with the given parameter values. 51 | 52 | @param parameters A map from parameter names to parameter values which will be 53 | set just for this piece of tracking information, or nil for none. 54 | */ 55 | - (void)send:(NSDictionary *)parameters; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/TAGContainer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | /** @file */ 3 | 4 | #import 5 | 6 | @class TAGContainer; 7 | 8 | /** 9 | * Refresh types for container callback. 10 | */ 11 | typedef enum { 12 | /** Refresh from a saved container. */ 13 | kTAGContainerCallbackRefreshTypeSaved, 14 | 15 | /** Refresh from the network. */ 16 | kTAGContainerCallbackRefreshTypeNetwork, 17 | } TAGContainerCallbackRefreshType; 18 | 19 | /** 20 | * Ways in which a refresh can fail. 21 | */ 22 | typedef enum { 23 | /** There is no saved container. */ 24 | kTAGContainerCallbackRefreshFailureNoSavedContainer, 25 | 26 | /** An I/O error prevented refreshing the container. */ 27 | kTAGContainerCallbackRefreshFailureIoError, 28 | 29 | /** No network is available. */ 30 | kTAGContainerCallbackRefreshFailureNoNetwork, 31 | 32 | /** A network error has occurred. */ 33 | kTAGContainerCallbackRefreshFailureNetworkError, 34 | 35 | /** An error on the server. */ 36 | kTAGContainerCallbackRefreshFailureServerError, 37 | 38 | /** An error that cannot be categorized. */ 39 | kTAGContainerCallbackRefreshFailureUnknownError 40 | } TAGContainerCallbackRefreshFailure; 41 | 42 | /** 43 | * A protocol implemented by the application to execute a custom tag. 44 | */ 45 | @protocol TAGFunctionCallTagHandler 46 | /** 47 | * Handler is given the tag name and a dictionary of named parameters. 48 | * 49 | * @param tagName The same name by which the handler was registered. It 50 | * is provided as a convenience to allow a single handler to be registered 51 | * for multiple function call tags. 52 | * @param parameters The named parameters for the function call. The 53 | * dictionary may contain NSString, NSNumber 54 | * (double, int, or boolean), NSDictionary, or 55 | * NSArray. 56 | * @return The evaluated result, which can be an NSString or 57 | * NSNumber. 58 | */ 59 | - (void)execute:(NSString *)tagName parameters:(NSDictionary *)parameters; 60 | 61 | @end 62 | 63 | /** 64 | * A protocol implemented by the application to calculate the value of a 65 | * function call macro. 66 | */ 67 | @protocol TAGFunctionCallMacroHandler 68 | /** 69 | * Returns an object which is the calculated value of the macro. 70 | * Handler is given the macro name and a dictionary of named parameters. 71 | * 72 | * @param macroName The same name by which the handler was registered. It 73 | * is provided as a convenience to allow a single handler to be registered 74 | * for multiple function call macros. 75 | * @param parameters The named parameters for the function call. The 76 | * dictionary may contain NSString, NSNumber 77 | * (double, int, or boolean), NSDictionary, or 78 | * NSArray. 79 | * @return The evaluated result, which can be an NSString or 80 | * NSNumber. 81 | */ 82 | - (id)valueForMacro:(NSString *)macroName parameters:(NSDictionary *)parameters; 83 | 84 | @end 85 | 86 | /** 87 | * A protocol that a client may implement to receive 88 | * information when the contents of the container has been successfully 89 | * loaded or failed to load. 90 | * 91 | * You may rely on the fact that 92 | * TAGContainerCallback::containerRefreshBegin:refreshType: 93 | * will be called for a given @ref TAGContainerCallbackRefreshType before its 94 | * associated TAGContainerCallback::containerRefreshSuccess:refreshType: or 95 | * TAGContainerCallback::containerRefreshFailure:failure:refreshType:, but 96 | * shouldn't make any other assumptions about ordering. In particular, there 97 | * may be two refreshes outstanding at once 98 | * (both @ref kTAGContainerCallbackRefreshTypeSaved and 99 | * @ref kTAGContainerCallbackRefreshTypeNetwork), or a 100 | * @ref kTAGContainerCallbackRefreshTypeSaved refresh 101 | * may occur before a @ref kTAGContainerCallbackRefreshTypeNetwork refresh. 102 | */ 103 | @protocol TAGContainerCallback 104 | 105 | /** 106 | * Called before the refresh is about to begin. 107 | * 108 | * @param container The container being refreshed. 109 | * @param refreshType The type of refresh which is starting. 110 | */ 111 | - (void)containerRefreshBegin:(TAGContainer *)container 112 | refreshType:(TAGContainerCallbackRefreshType)refreshType; 113 | 114 | /** 115 | * Called when a refresh has successfully completed for the given refresh type. 116 | * 117 | * @param container The container being refreshed. 118 | * @param refreshType The type of refresh which completed successfully. 119 | */ 120 | - (void)containerRefreshSuccess:(TAGContainer *)container 121 | refreshType:(TAGContainerCallbackRefreshType)refreshType; 122 | 123 | /** 124 | * Called when a refresh has failed to complete for the given refresh type. 125 | * 126 | * @param container The container being refreshed. 127 | * @param failure The reason for the refresh failure. 128 | * @param refreshType The type of refresh which failed. 129 | */ 130 | - (void)containerRefreshFailure:(TAGContainer *)container 131 | failure:(TAGContainerCallbackRefreshFailure)failure 132 | refreshType:(TAGContainerCallbackRefreshType)refreshType; 133 | 134 | @end 135 | 136 | /** 137 | * A class that provides access to container values. 138 | * Container objects must be created via @ref TAGManager. 139 | * Once a container is created, it can be queried for key values which 140 | * may depend on rules established for the container. 141 | * A container is automatically refreshed periodically (every 12 hours), but 142 | * can also be manually refreshed with TAGContainer::refresh. 143 | */ 144 | @interface TAGContainer : NSObject 145 | 146 | /** 147 | * The ID for this container. 148 | */ 149 | @property(readonly, nonatomic, copy) NSString *containerId; 150 | 151 | /** 152 | * The last time (in milliseconds since midnight Jan 1, 1970 UTC) that this 153 | * container was refreshed from the network. 154 | */ 155 | @property(atomic, readonly) double lastRefreshTime; 156 | 157 | // @cond 158 | /** 159 | * Containers should be instantiated through TAGManager or TAGContainerOpener. 160 | */ 161 | - (id)init __attribute__((unavailable)); 162 | // @endcond 163 | 164 | /** 165 | * Returns a BOOL representing the configuration value for the 166 | * given key. If the container has no value for this key, NO will be returned. 167 | * 168 | * @param key The key to lookup for the configuration value. 169 | */ 170 | - (BOOL)booleanForKey:(NSString *)key; 171 | 172 | /** 173 | * Returns a double representing the configuration value for the 174 | * given key. If the container has no value for this key, 0.0 will be returned. 175 | * 176 | * @param key The key to lookup for the configuration value. 177 | */ 178 | - (double)doubleForKey:(NSString *)key; 179 | 180 | /** 181 | * Returns an int64_t representing the configuration value for the 182 | * given key. If the container has no value for this key, 0 will be returned. 183 | * 184 | * @param key The key to lookup for the configuration value. 185 | */ 186 | - (int64_t)int64ForKey:(NSString *)key; 187 | 188 | /** 189 | * Returns an NSString to represent the configuration value for the 190 | * given key. If the container has no value for this key, an empty string 191 | * will be returned. 192 | * 193 | * @param key The key to lookup for the configuration value. 194 | */ 195 | - (NSString *)stringForKey:(NSString *)key; 196 | 197 | /** 198 | * Requests that this container be refreshed from the network. 199 | * This call is asynchronous, so the refresh may take place on another thread. 200 | */ 201 | - (void)refresh; 202 | 203 | /** 204 | * Closes this container so that it will no longer be refreshed. 205 | * After closing, don't make any other calls to the container. 206 | */ 207 | - (void)close; 208 | 209 | /** 210 | * Returns whether this is a default container, or one refreshed from the 211 | * server. 212 | */ 213 | - (BOOL)isDefault; 214 | 215 | /** 216 | * Registers the given macro handler to handle a given function call macro. 217 | * 218 | * @param handler The handler for the macro. If the parameter is 219 | * nil, the method unregisters any existing handler for that macro. 220 | * @param macroName The name of the macro which is being registered. 221 | */ 222 | - (void)registerFunctionCallMacroHandler:(id)handler 223 | forMacro:(NSString *)macroName; 224 | 225 | /** 226 | * Given the name of a function call macro, returns the handler registered for 227 | * the macro. 228 | * 229 | * @return The handler registered for the macro. 230 | */ 231 | - (id) 232 | functionCallMacroHandlerForMacro:(NSString *)functionCallMacroName; 233 | 234 | /** 235 | * Registers the given tag handler to handle a given function call tag. 236 | * 237 | * @param handler The handler for the tag. If the parameter is 238 | * nil, the method unregisters any existing handler for that tag. 239 | * @param tagName The name of the tag which is being registered. 240 | */ 241 | - (void)registerFunctionCallTagHandler:(id)handler 242 | forTag:(NSString *)tagName; 243 | 244 | /** 245 | * Given the name of a function call tag, returns the handler registered for 246 | * the tag. 247 | * 248 | * @return The handler registered for the tag. 249 | */ 250 | - (id)functionCallTagHandlerForTag:(NSString *)functionCallTagName; 251 | 252 | @end 253 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/TAGDataLayer.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | /** 4 | * Represents an object missing in an NSArray. If the data layer currently 5 | * contains: 6 |

  7 |  *   {
  8 |  *     myArray: [1, 2, 3]
  9 |  *   }
 10 | 
11 | * and you want to update the third element to 5, while leaving other elements 12 | * alone, you'd use kTAGDataLayerNotPresent as the 1st and 2nd 13 | * elements (if you were to use a NSNull object, 14 | * the 1st and 2nd elements would be replaced). 15 | *

16 | * So, after executing: 17 |

 18 |  *  [dataLayer push:@{@"myArray",
 19 |  *      @[kTAGDataLayerObjectNotPresent, kTAGDataLayerObjectNotPresent, @5}];
 20 | 
21 | * then, the data layer will contain: 22 |
 23 |  *   {
 24 |  *     myArray: [1, 2, 5]
 25 |  *   }
 26 | 
27 | */ 28 | extern id kTAGDataLayerObjectNotPresent; 29 | 30 | /** 31 | * The data layer is a dictionary holding generic information about the 32 | * application. It uses a standard set of keys so it can be read by any party 33 | * that understands the specification. The data layer state is updated 34 | * through its API. For example, an app might start with the following 35 | * dataLayer: 36 |
 37 |  *   {
 38 |  *     title: "Original screen title"
 39 |  *   }
 40 | 
41 | * 42 | * As the state/data of an app can change, the app can update the dataLayer with a call such as: 43 |
 44 |  *   [dataLayer push:@{@"title": @"New screen title"}];
 45 | 
46 | * Now the data layer contains: 47 |
 48 |  *   {
 49 |  *     title: "New screen title"
 50 |  *   }
 51 | 
52 | * After another push happens: 53 |
 54 |  *   [dataLayer push:@{@"xyz": @3}];
 55 | 
56 | * The dataLayer contains: 57 |
 58 |  *   {
 59 |  *     "title": "New screen title",
 60 |  *     "xyz": 3
 61 |  *   }
 62 | 
63 | * The following example demonstrates how array and map merging works. If the 64 | * original dataLayer contains: 65 |
 66 |  *   {
 67 |  *     "items": @[@"item1", [NSNull null], @"item2", @{@"a": @"aValue", @"b": @"bValue"}]
 68 |  *   }
 69 | 
70 | * After this push happens: 71 |
 72 |  *   [dataLayer push:@{@"items":
 73 |  *       @[[NSNull null], @"item6", kTAGDataLayerObjectNotPresent, @{@"a": [NSNull null]}]}
 74 | 
75 | * The dataLayer contains: 76 |
 77 |  *   {
 78 |  *     "items": @[[NSNull null], @"item6", @"item2", @{@"a": [NSNull null], @"b": @"bValue"}]}
 79 |  *   }
 80 | 
81 | *

Pushes happen synchronously; after the push, changes have been reflected 82 | * in the model. 83 | *

When an event key is pushed to the data layer, rules for tags 84 | * are evaluated and any tags matching this event will fire. 85 | * For example, given a container with a tag whose firing rules is that "event" 86 | * is equal to "openScreen", after this push: 87 |

 88 |  *   [dataLayer push:@{@"event", @"openScreen"}];
 89 | 
90 | * that tag will fire. 91 | */ 92 | @interface TAGDataLayer : NSObject 93 | 94 | @property(readonly, nonatomic) NSDictionary *dataLayer; 95 | 96 | /** 97 | * Pushes a key/value pair to the data layer. This is just a convenience 98 | * method that calls push:@{key: value}. 99 | */ 100 | - (void)pushValue:(id)value forKey:(id)key; 101 | 102 | /** 103 | * Merges the given update object into the existing data model, 104 | * calling any listeners with the update (after the merge occurs). 105 | * 106 | *

It's valid for values in the dictionary (or embedded Arrays) to be 107 | * of type NSNull. 108 | * If you want to represent a missing value (like an empty index in a List), 109 | * use the kTAGDataLayerObjectNotPresent object. 110 | * 111 | *

This is normally a synchronous call. 112 | * However, if, while the thread is executing the push, another push happens 113 | * from the same thread, then that second push is asynchronous (the second push 114 | * will return before changes have been made to the data layer). This second 115 | * push from the same thread can occur, for example, if a data layer push is 116 | * made in response to a tag firing. However, all updates will be processed 117 | * before the outermost push returns. 118 | *

If the update contains the key event, rules 119 | * will be evaluated and matching tags will fire. 120 | * 121 | * @param update The update object to process 122 | */ 123 | - (void)push:(NSDictionary*)update; 124 | 125 | /** 126 | * Returns the object in the model associated with the given key. 127 | * If key isn't present, returns nil. If 128 | * key is present, but NSNull, returns 129 | * NSNull. 130 | * 131 | *

The key can can have embedded periods. For example: 132 | * a key of "a.b.c" returns the value of the "c" key in 133 | * the dictionary with key "b" in the dictionary with key "a" in the model. 134 | */ 135 | - (id)get:(NSString*)key; 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/TAGLogger.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | /** @file */ 3 | 4 | #import 5 | 6 | /** 7 | * Log Level setting. 8 | */ 9 | typedef enum { 10 | /** Log level of Verbose. */ 11 | kTAGLoggerLogLevelVerbose, 12 | 13 | /** Log level of Debug. */ 14 | kTAGLoggerLogLevelDebug, 15 | 16 | /** Log level of Info. */ 17 | kTAGLoggerLogLevelInfo, 18 | 19 | /** Log level of Warning. */ 20 | kTAGLoggerLogLevelWarning, 21 | 22 | /** Log level of Error. */ 23 | kTAGLoggerLogLevelError, 24 | 25 | /** Log level of None. */ 26 | kTAGLoggerLogLevelNone 27 | } TAGLoggerLogLevelType; 28 | 29 | /** 30 | * A protocol for error/warning/info/debug/verbose logging. 31 | * 32 | * By default, Google Tag Manager logs error/warning messages and 33 | * ignores info/debug/verbose messages. You can install your own logger 34 | * by setting the TAGManager::logger property. 35 | */ 36 | @protocol TAGLogger 37 | 38 | /** 39 | * Logs an error message. 40 | * 41 | * @param message The error message to be logged. 42 | */ 43 | - (void)error:(NSString *)message; 44 | 45 | /** 46 | * Logs a warning message. 47 | * 48 | * @param message The warning message to be logged. 49 | */ 50 | - (void)warning:(NSString *)message; 51 | 52 | /** 53 | * Logs an info message. 54 | * 55 | * @param message The info message to be logged. 56 | */ 57 | - (void)info:(NSString *)message; 58 | 59 | /** 60 | * Logs a debug message. 61 | * 62 | * @param message The debug message to be logged. 63 | */ 64 | - (void)debug:(NSString *)message; 65 | 66 | /** 67 | * Logs a verbose message. 68 | * 69 | * @param message The verbose message to be logged. 70 | */ 71 | - (void)verbose:(NSString *)message; 72 | 73 | /** 74 | * Sets the log level. It is up to the implementation how the log level is used, 75 | * but log messages outside the log level should not be output. 76 | */ 77 | - (void)setLogLevel:(TAGLoggerLogLevelType)logLevel; 78 | 79 | /** 80 | * Returns the current log level. 81 | */ 82 | - (TAGLoggerLogLevelType)logLevel; 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/TAGManager.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All rights reserved. 2 | /** @file */ 3 | 4 | #import 5 | 6 | #import "TAGContainer.h" 7 | #import "TAGLogger.h" 8 | 9 | @class TAGDataLayer; 10 | 11 | /** 12 | * Mode for refreshing the container. 13 | */ 14 | typedef enum { 15 | /** 16 | * In this mode, containers are automatically refreshed from network every 12 17 | * hours. Developers can also call TAGContainer::refresh to manually 18 | * refresh containers. 19 | * 20 | * This is the default mode. 21 | */ 22 | kTAGRefreshModeStandard, 23 | /** 24 | * In this mode, the default container is always used (the container will not load a version 25 | * from network, or from disk). 26 | * 27 | * This mode allows developers to add new key/value pairs locally and 28 | * then to use the Plist or JSON default container to test them quickly 29 | * without having to add those key/value pairs to the container using 30 | * the GTM UI. 31 | * 32 | * This mode is intended for development only and not for shipping code. 33 | */ 34 | kTAGRefreshModeDefaultContainer, 35 | } TAGRefreshMode; 36 | 37 | 38 | typedef NS_ENUM(NSUInteger, TAGDispatchResult) { 39 | kTAGDispatchNoData, 40 | kTAGDispatchGood, 41 | kTAGDispatchError 42 | }; 43 | 44 | /** 45 | * A class that is the mobile implementation of Google Tag Manager (GTM). 46 | * 47 | * Sample usage: 48 |

 49 |  *      TAGContainer *container =
 50 |  *          [[TAGManager instance] openContainerById:myContainerId];
 51 |  *      NSString *value = [container stringForKey:@"myKey"];
 52 |  *
 53 |  *      TAGDataLayer *dataLayer =
 54 |  *          [[TAGManager instance] dataLayer];
 55 |  *      [dataLayer push:@{@"event": @"openScreen",
 56 |  *                        @"screenName": @"Main Page"}];
 57 | 
58 | * 59 | * A container is a collection of macros, rules, and tags. It is created within 60 | * the GTM application (http://www.google.com/tagmanager), and is assigned 61 | * a container ID. This container ID is the one used within this API. 62 | * 63 | * The TAGContainer class provides methods for retrieving 64 | * macro values given the macro name. The routines 65 | * TAGContainer::booleanForKey:, TAGContainer::doubleForKey:, 66 | * TAGContainer::int64ForKey:, TAGContainer::stringForKey: return the current 67 | * value for the value collection macro name, depending on the rules associated with that macro 68 | * in the container. 69 | * 70 | * As an example, if your container has a value collection macro with a key "speed" whose 71 | * value is 32, and the enabling rule is Language is "en"; and another value collection macro 72 | * with a key "speed" whose value is 45, and the enabling rule is Language is not "en", 73 | * then making the following call: 74 | * 75 |
 76 |  *     [container longForKey:\@"speed"]
 77 |  
78 | * 79 | * will return either 32 if the current language of the device is English, or 80 | * 45 otherwise. 81 | 82 | * The data layer is a map holding generic information about the application. 83 | * The TAGDataLayer class provides methods to push and retrieve data from 84 | * the data layer. Pushing an event key to the data layer will 85 | * cause tags that match this event to fire. 86 | * 87 | * An initial version of the container is bundled with the application. It 88 | * should be placed as a resource in the bundle with name 89 | * containerId where containerId is the same container 90 | * ID you will use within this API. When you call 91 | * TAGManager::openContainerById:callback:, the container will be 92 | * returned with those bundled rules/tags/macros. You will create the container 93 | * in the UI and use the Download button to download it. 94 | * 95 | * You can modify the container in the UI and publish a new version. In that 96 | * case, the next time the mobile app refreshes the container from the network 97 | * (currently every 12 hours), 98 | * it will get that new version. When you call one of the get... routines, the 99 | * value will be computed using the most recent rules. 100 | * 101 | * The downloaded container is saved locally. When you call 102 | * TAGManager::openContainerById:callback:, it will first load the default 103 | * container, and will then asynchronously load any saved container. If none is 104 | * found or if it is older than 12 hours, it will try to retrieve a newer version 105 | * from the network. You can find the status of those asynchronous loads by 106 | * passing a TAGContainerCallback to TAGManager::openContainerById:callback:. 107 | * 108 | * Sometimes you may want to block until either a non-default container is 109 | * available, or until a recent fresh container is available. You can do that 110 | * by using the callbacks in TAGManager::openContainerById:callback: or use 111 | * TAGContainerOpener. 112 | * 113 | * When you are finished with a container, call TAGContainer::close. 114 | */ 115 | @interface TAGManager : NSObject 116 | 117 | /** 118 | * The logger to use for Google Tag Manager SDK. By default, Google Tag Manager 119 | * logs error/warning messages and ignores info/debug/verbose messages. 120 | * You can use your own customized logger by setting this property. 121 | */ 122 | @property(nonatomic, strong) id logger; 123 | 124 | /** 125 | * The refresh mode used for Google Tag Manager SDK. Setting this to 126 | * @ref kTAGRefreshModeDefaultContainer allows the refresh 127 | * method to use only the default container for development purposes. Default is 128 | * @ref kTAGRefreshModeStandard. 129 | */ 130 | @property(nonatomic) TAGRefreshMode refreshMode; 131 | 132 | /** 133 | * Call TAGDataLayer::push: method to push events and other data. 134 | */ 135 | @property(nonatomic, readonly, strong) TAGDataLayer *dataLayer; 136 | 137 | /** 138 | * Returns a container. 139 | * Usually the returned container will be empty, but the loading will happen 140 | * asynchronously, so the returned container may be refreshed before it 141 | * is returned, after it is returned, or may never be refreshed if, for example, 142 | * there is no network connection during the lifetime of the container. 143 | * 144 | * Callback will be called as various things happen for 145 | * the container. At a minimum, TAGManager::openContainerById:callback: will 146 | * attempt to load a saved version of the container. If there is no saved 147 | * version, or if the saved version is out of date, attempt will be made to 148 | * load from the network. 149 | * 150 | * If TAGManager::openContainerById:callback: is called a second time for a 151 | * given containerId, nil will be returned unless 152 | * the previous opened container has already been closed. 153 | * 154 | * @param containerId The ID of the container to open. 155 | * @param callback An object whose various methods will be called during the 156 | * loading process. Note that the methods may be called from different 157 | * threads. In addition, they may be called before 158 | * TAGManager::openContainerById:callback: returns. 159 | * @return The opened container. 160 | */ 161 | - (TAGContainer *)openContainerById:(NSString *)containerId 162 | callback:(id )callback; 163 | 164 | /** 165 | * Returns the container associated with the given containerId; 166 | * returns nil if the container is not already open. 167 | */ 168 | - (TAGContainer *)getContainerById:(NSString *)containerId; 169 | 170 | /** 171 | * Previews the app with the input url. 172 | * 173 | * The valid url must start with the following: 174 |
175 |  * tagmanager.c.\://preview/p?id=
176 | 
177 | * where <app_name> is the application name. 178 | * 179 | * @param url The preview url. 180 | * @return YES if the url is a valid tagmanager preview url. 181 | */ 182 | - (BOOL)previewWithUrl:(NSURL *)url; 183 | 184 | /** 185 | * Gets the singleton instance of the TAGManager class, creating it if 186 | * necessary. 187 | * 188 | * @return The singleton instance of TAGManager. 189 | */ 190 | + (TAGManager *)instance; 191 | 192 | 193 | /** 194 | * Dispatches any pending network traffic generated by tags (arbitrary pixels, analytics beacons, 195 | * etc). 196 | */ 197 | - (void)dispatch; 198 | 199 | /** 200 | * If this value is positive, tracking information will be automatically 201 | * dispatched every dispatchInterval seconds. 202 | * Otherwise, tracking information must be sent manually by 203 | * calling dispatch. 204 | * 205 | * By default, this is set to `120`, which indicates tracking information will 206 | * be dispatched automatically every 120 seconds. 207 | */ 208 | @property(nonatomic, assign) NSTimeInterval dispatchInterval; 209 | 210 | /** 211 | * Dispatches the next pending network traffic in the queue, calling completionHandler when 212 | * the request has either been sent (returning kTAGDispatchGood) or an error has resulted 213 | * (returning kTAGDispatchError). If there is no network connection or there is no data to send, 214 | * kTAGDispatchNoData is returned. 215 | * 216 | * Calling this method with a nil completionHandler is the same as calling dispatch. 217 | * 218 | * This method can be used for background data fetching in iOS 7.0 or later. 219 | * 220 | * It would be wise to call this when the application is exiting to initiate the submission of any 221 | * unsubmitted tracking information. 222 | */ 223 | - (void)dispatchWithCompletionHandler:(void (^)(TAGDispatchResult))completionHandler; 224 | 225 | @end 226 | -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/libAdIdAccess.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/ios/RCTGoogleAnalyticsBridge/google-analytics-lib/libAdIdAccess.a -------------------------------------------------------------------------------- /ios/RCTGoogleAnalyticsBridge/google-analytics-lib/libGoogleAnalyticsServices.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idehub/react-native-google-analytics-bridge/e6623b18a54cd7bb9166cd41a0b370efd7e0687b/ios/RCTGoogleAnalyticsBridge/google-analytics-lib/libGoogleAnalyticsServices.a -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-google-analytics-bridge", 3 | "version": "7.1.0", 4 | "description": "React Native bridge for using native Google Analytics libraries on iOS and Android", 5 | "main": "./dist/index.js", 6 | "types": "./dist/index.d.ts", 7 | "scripts": { 8 | "prepublishOnly": "yarn run build-all", 9 | "test": "echo \"Error: no test yet!\" && exit 0", 10 | "build": "tsc", 11 | "build-watch": "tsc --watch", 12 | "docs": "documentation readme dist/** --section=\"JavaScript API\"", 13 | "build-all": "rm -rf dist && yarn run build && yarn run docs", 14 | "precommit": "yarn run build" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/idehub/react-native-google-analytics-bridge.git" 19 | }, 20 | "author": "Idéhub AS", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/idehub/react-native-google-analytics-bridge/issues" 24 | }, 25 | "homepage": "https://github.com/idehub/react-native-google-analytics-bridge#readme", 26 | "keywords": [ 27 | "react", 28 | "native", 29 | "google", 30 | "analytics", 31 | "tracking", 32 | "native", 33 | "bridge", 34 | "events", 35 | "screen", 36 | "hits", 37 | "react-native", 38 | "ios", 39 | "android" 40 | ], 41 | "rnpm": { 42 | "ios": { 43 | "sharedLibraries": [ 44 | "CoreData.framework", 45 | "SystemConfiguration.framework", 46 | "libz.tbd", 47 | "libsqlite3.0.tbd" 48 | ] 49 | } 50 | }, 51 | "devDependencies": { 52 | "@types/react": "16.3.1", 53 | "@types/react-native": "0.55.4", 54 | "documentation": "^8.0.0", 55 | "husky": "^0.14.3", 56 | "prettier": "^1.6.1", 57 | "typescript": "^2.5.2" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /react-native-google-analytics-bridge.podspec: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 4 | ios_root = 'ios/RCTGoogleAnalyticsBridge' 5 | galib_root = ios_root+'/google-analytics-lib' 6 | 7 | Pod::Spec.new do |s| 8 | s.name = "react-native-google-analytics-bridge" 9 | s.version = package["version"] 10 | s.summary = package["description"] 11 | s.author = "Idéhub AS" 12 | 13 | s.homepage = package["homepage"] 14 | 15 | s.license = package["license"] 16 | s.platform = :ios, "7.0" 17 | 18 | s.source = { :git => "https://github.com/idehub/react-native-google-analytics-bridge", :tag => "v#{s.version}" } 19 | s.default_subspec = 'Core' 20 | 21 | s.subspec 'Core' do |ss| 22 | ss.dependency 'React' 23 | ss.frameworks = 'CoreData', 'SystemConfiguration' 24 | ss.libraries = 'z', 'sqlite3.0','GoogleAnalyticsServices' 25 | 26 | ss.vendored_libraries = 27 | galib_root+'/libGoogleAnalyticsServices.a' 28 | 29 | ss.source_files = 30 | galib_root+'/*.{h}', 31 | ios_root+'/RCTGoogleAnalyticsBridge/*.{h,m}' 32 | end 33 | 34 | s.subspec 'adSupport' do |ss| 35 | ss.dependency 'react-native-google-analytics-bridge/Core' 36 | ss.frameworks = 'AdSupport' 37 | ss.libraries = 'AdIdAccess' 38 | ss.vendored_libraries = 39 | galib_root+'/libAdIdAccess.a' 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /src/GoogleAnalyticsSettings.ts: -------------------------------------------------------------------------------- 1 | import { AnalyticsSettings } from "./NativeBridges"; 2 | 3 | /** 4 | * Settings which are applied across all trackers. 5 | * @name GoogleAnalyticsSettings 6 | */ 7 | class GoogleAnalyticsSettings { 8 | /** 9 | * Sets if OptOut is active and disables Google Analytics. This is disabled by default. Note: This has to be set each time the App starts. 10 | * @example GoogleAnalyticsSettings.setOptOut(true); 11 | * @param {boolean} enabled 12 | */ 13 | static setOptOut(enabled: boolean): void { 14 | AnalyticsSettings.setOptOut(enabled); 15 | } 16 | 17 | /** 18 | * Sets the trackers dispatch interval. 19 | * Events, screen views, etc, are sent in batches to your tracker. This function allows you to configure how often (in seconds) the batches are sent to your tracker. Recommended to keep this around 20-120 seconds to preserve battery and network traffic. This is set to 20 seconds by default. 20 | * @example GoogleAnalyticsSettings.setDispatchInterval(30); 21 | * @param {number} intervalInSeconds 22 | */ 23 | static setDispatchInterval(intervalInSeconds: number): void { 24 | AnalyticsSettings.setDispatchInterval(intervalInSeconds); 25 | } 26 | 27 | /** 28 | * When enabled the native library prevents any data from being sent to Google Analytics. This allows you to test or debug the implementation, without your test data appearing in your Google Analytics reports. 29 | * @example GoogleAnalyticsSettings.setDryRun(true); 30 | * @param {boolean} enabled 31 | */ 32 | static setDryRun(enabled: boolean): void { 33 | AnalyticsSettings.setDryRun(enabled); 34 | } 35 | } 36 | 37 | export default GoogleAnalyticsSettings; 38 | -------------------------------------------------------------------------------- /src/GoogleTagManager.ts: -------------------------------------------------------------------------------- 1 | import DataLayerEvent from "./models/DataLayerEvent"; 2 | import { TagManagerBridge } from "./NativeBridges"; 3 | import FunctionCallTagHandler from "./Helpers/FunctionCallTagHandler"; 4 | 5 | /** 6 | * Can only be used with one container. All functions returns a Promise. 7 | * 8 | * @name GoogleTagManager 9 | * @example 10 | * import { GoogleTagManager } from "react-native-google-analytics-bridge"; 11 | * GoogleTagManager.openContainerWithId("GT-NZT48") 12 | * .then(() => GoogleTagManager.stringForKey("pack")) 13 | * .then(str => console.log("Pack: ", str)); 14 | */ 15 | class GoogleTagManager { 16 | /** 17 | * Call once to open the container for all subsequent static calls. 18 | * @example 19 | * GoogleTagManager.openContainerWithId('GT-NZT48').then((..) => ..) 20 | * @param {string} containerId 21 | * @returns {Promise} 22 | */ 23 | static openContainerWithId(containerId: string): Promise { 24 | return TagManagerBridge.openContainerWithId(containerId); 25 | } 26 | 27 | /** 28 | * Refreshes the GTM container. 29 | * According to Tag Manager documentations for Android can be called once every 15 minutes. 30 | * No such limitations has been mentioned for iOS containers, though. 31 | * @example 32 | * GoogleTagManager.refreshContainer().then((..) => ..) 33 | * @returns {Promise} 34 | */ 35 | static refreshContainer(): Promise { 36 | return TagManagerBridge.refreshContainer(); 37 | } 38 | 39 | /** 40 | * Retrieves a boolean value with the given key from the opened container. 41 | * @example GoogleTagManager.boolForKey("key").then(val => console.log(val)); 42 | * @param {string} key 43 | * @returns {Promise} 44 | */ 45 | static boolForKey(key: string): Promise { 46 | return TagManagerBridge.booleanForKey(key); 47 | } 48 | 49 | /** 50 | * Retrieves a string with the given key from the opened container. 51 | * @example GoogleTagManager.stringForKey("key").then(val => console.log(val)); 52 | * @param {string} key 53 | * @returns {Promise} 54 | */ 55 | static stringForKey(key: string): Promise { 56 | return TagManagerBridge.stringForKey(key); 57 | } 58 | 59 | /** 60 | * Retrieves a number with the given key from the opened container. 61 | * @example GoogleTagManager.doubleForKey("key").then(val => console.log(val)); 62 | * @param {string} key 63 | * @returns {Promise} 64 | */ 65 | static doubleForKey(key): Promise { 66 | return TagManagerBridge.doubleForKey(key); 67 | } 68 | 69 | /** 70 | * Push a datalayer event for Google Analytics through Google Tag Manager. The event must have at least one key "event" with event name. 71 | * @example 72 | * GoogleTagManager.pushDataLayerEvent({ 73 | * event: "eventName", 74 | * pageId: "/home" 75 | * }).then(success => console.log(success)); 76 | * @param {DataLayerEvent} event An Map containing key and value pairs. It must have at least one key "event" with event name 77 | * @returns {Promise} 78 | */ 79 | static pushDataLayerEvent(event: DataLayerEvent): Promise { 80 | return TagManagerBridge.pushDataLayerEvent(event); 81 | } 82 | 83 | /** 84 | * Register Function Call tag handler 85 | * @param {String} functionName 86 | * @param {Function} handler 87 | */ 88 | static registerFunctionCallTagHandler(functionName, handler) { 89 | return FunctionCallTagHandler(functionName, handler); 90 | } 91 | 92 | /** 93 | * Sets logger to verbose, default is warning 94 | * @param {boolean} enabled 95 | */ 96 | static setVerboseLoggingEnabled(enabled: boolean): Promise { 97 | return TagManagerBridge.setVerboseLoggingEnabled(enabled); 98 | } 99 | } 100 | 101 | export default GoogleTagManager; 102 | -------------------------------------------------------------------------------- /src/Helpers/FunctionCallTagHandler/FunctionCallTagHandlerAndroid.ts: -------------------------------------------------------------------------------- 1 | import { DeviceEventEmitter } from "react-native"; 2 | import { TagManagerBridge } from "../../NativeBridges"; 3 | import { Handler } from "./models"; 4 | 5 | /* 6 | * FunctionTagHandler module for Android adds an event listener per each Function Call registration. 7 | * To prevent possible messaging collision between native and JS realms, events are prefixed in 8 | * both environments before being registered. 9 | * For example, if a handler is being registered to handle tags with function name of "some_func", the corresponding 10 | * event would be GTM_FUNCTION_CALL_TAG_some_func. This is an implementation detail and does not affect high-level 11 | * APIs of the module. 12 | */ 13 | 14 | const TAG_EVENT_PREFIX = "GTM_FUNCTION_CALL_TAG_"; 15 | 16 | export default (functionName: string, handler: Handler): Promise => { 17 | const event = TAG_EVENT_PREFIX + functionName; 18 | 19 | return TagManagerBridge.registerFunctionCallTagHandler(functionName).then( 20 | () => { 21 | DeviceEventEmitter.addListener(event, payload => { 22 | try { 23 | handler(functionName, payload); 24 | } catch (e) { 25 | console.error( 26 | `Unhandled exception in FunctionCallTag handler: ${e.stack}`, 27 | `\nFunction Name: ${functionName}`, 28 | `\nPayload: ${JSON.stringify(payload)}` 29 | ); 30 | } 31 | }); 32 | return true; 33 | } 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/Helpers/FunctionCallTagHandler/FunctionCallTagHandlerIOS.ts: -------------------------------------------------------------------------------- 1 | import { NativeEventEmitter } from "react-native"; 2 | import { TagManagerBridge } from "../../NativeBridges"; 3 | import { Handler } from "./models"; 4 | 5 | /* 6 | * FunctionTagHandler module for iOS lazily adds the global event listner the first time a function tag 7 | * needs to be registered. Due to some limitations in native enviroment all Function Call tag events 8 | * from native realm are sent over as GTM_FUNCTION_CALL_TAG event. The event objects 9 | * include _fn (function name) and payload attributes which are passed down to registered 10 | * handlers respectively 11 | */ 12 | 13 | const GTM_FUNCTION_CALL_TAG_EVENT = "GTM_FUNCTION_CALL_TAG"; 14 | 15 | interface Listener { 16 | functionName: string; 17 | handler: Handler; 18 | } 19 | 20 | // Downstream events from native realm 21 | const functionCallTagEventEmitter = new NativeEventEmitter(TagManagerBridge); 22 | const listeners: Array = []; 23 | let listenerRegistered = false; 24 | 25 | export default (functionName: string, handler: Handler): Promise => { 26 | if (!listenerRegistered) { 27 | // Register a global listener for Function Tag events 28 | functionCallTagEventEmitter.addListener( 29 | GTM_FUNCTION_CALL_TAG_EVENT, 30 | ({ _fn, payload }) => { 31 | // Pass on the event to listeners 32 | // _fn is basically the same as functionName 33 | listeners.forEach(listener => { 34 | if (listener.functionName === _fn) { 35 | try { 36 | handler(_fn, payload); 37 | } catch (e) { 38 | console.error( 39 | `Unhandled exception in FunctionCallTag handler: ${e.stack}`, 40 | `\nFunction Name: ${_fn}`, 41 | `\nPayload: ${JSON.stringify(payload)}` 42 | ); 43 | } 44 | } 45 | }); 46 | } 47 | ); 48 | 49 | listenerRegistered = true; 50 | } 51 | 52 | return TagManagerBridge.registerFunctionCallTagHandler(functionName).then( 53 | () => { 54 | listeners.push({ 55 | functionName, 56 | handler 57 | }); 58 | 59 | return true; 60 | } 61 | ); 62 | }; 63 | -------------------------------------------------------------------------------- /src/Helpers/FunctionCallTagHandler/index.ts: -------------------------------------------------------------------------------- 1 | import { Platform } from "react-native"; 2 | import { RegisterHandler } from "./models"; 3 | import FunctionCallTagHandlerAndroid from "./FunctionCallTagHandlerAndroid"; 4 | import FunctionCallTagHandlerIOS from "./FunctionCallTagHandlerIOS"; 5 | 6 | const FunctionCallTagHandler = Platform.select({ 7 | ios: FunctionCallTagHandlerIOS, 8 | android: FunctionCallTagHandlerAndroid 9 | }); 10 | 11 | export default FunctionCallTagHandler; 12 | -------------------------------------------------------------------------------- /src/Helpers/FunctionCallTagHandler/models.ts: -------------------------------------------------------------------------------- 1 | export type Handler = (functionName: string, tagArguments: any) => any; 2 | export type RegisterHandler = ( 3 | functionName: string, 4 | handler: Handler 5 | ) => Promise; 6 | -------------------------------------------------------------------------------- /src/NativeBridges.ts: -------------------------------------------------------------------------------- 1 | import { HitPayload } from "./models/Analytics"; 2 | import { NativeModules, EventSubscriptionVendor } from "react-native"; 3 | import DataLayerEvent from "./models/DataLayerEvent"; 4 | const { 5 | GoogleTagManagerBridge, 6 | GoogleAnalyticsBridge, 7 | GoogleAnalyticsSettings 8 | } = NativeModules; 9 | 10 | if ( 11 | !GoogleTagManagerBridge || 12 | !GoogleAnalyticsBridge || 13 | !GoogleAnalyticsSettings 14 | ) { 15 | console.error( 16 | "Something went wrong initializing the native react-native-google-analytics-bridge module.\nPlease check your configuration.\nDid you forget to run 'react-native link' or install your node_modules?" 17 | ); 18 | } 19 | 20 | export interface IGoogleAnalyticsBridge { 21 | trackScreenView( 22 | trackerId: string, 23 | screenName: string, 24 | payload: HitPayload 25 | ): void; 26 | trackEvent( 27 | trackerId: string, 28 | category: string, 29 | action: string, 30 | label: string, 31 | value: string, 32 | payload: HitPayload 33 | ): void; 34 | trackTiming( 35 | trackerId: string, 36 | category: string, 37 | interval: number, 38 | name: string, 39 | label: string, 40 | payload: HitPayload 41 | ): void; 42 | trackException( 43 | trackerId: string, 44 | error: string, 45 | fatal: boolean, 46 | payload: HitPayload 47 | ): void; 48 | trackSocialInteraction( 49 | trackerId: string, 50 | network: string, 51 | action: string, 52 | targetUrl: string, 53 | payload: HitPayload 54 | ): void; 55 | setUser(trackerId: string, userId: string): void; 56 | setClient(trackerId: string, clientId: string): void; 57 | getClientId(trackerId: string): Promise; 58 | allowIDFA(trackerId: string, enabled: boolean): void; 59 | setSamplingRate(trackerId: string, sampleRate: number): void; 60 | setAnonymizeIp(trackerId: string, enabled: boolean): void; 61 | setAppName(trackerId: string, appName: string): void; 62 | setAppVersion(trackerId: string, appVersion: string): void; 63 | setCurrency(trackerId: string, currencyCode: string): void; 64 | setTrackUncaughtExceptions(trackerId: string, enabled: boolean): void; 65 | dispatch(): Promise; 66 | } 67 | 68 | export interface IGoogleTagManagerBridge extends EventSubscriptionVendor { 69 | openContainerWithId(containerId: string): Promise; 70 | refreshContainer(): Promise; 71 | booleanForKey(key: string): Promise; 72 | stringForKey(key: string): Promise; 73 | doubleForKey(key: any): Promise; 74 | pushDataLayerEvent(event: DataLayerEvent): Promise; 75 | registerFunctionCallTagHandler(functionName: string): Promise; 76 | setVerboseLoggingEnabled(enabled: boolean): Promise; 77 | } 78 | 79 | export interface IGoogleAnalyticsSettings { 80 | setOptOut(enabled): void; 81 | setDispatchInterval(intervalInSeconds): void; 82 | setDryRun(enabled): void; 83 | } 84 | 85 | const AnalyticsBridge = GoogleAnalyticsBridge as IGoogleAnalyticsBridge; 86 | const TagManagerBridge = GoogleTagManagerBridge as IGoogleTagManagerBridge; 87 | const AnalyticsSettings = GoogleAnalyticsSettings as IGoogleAnalyticsSettings; 88 | 89 | export { TagManagerBridge }; 90 | export { AnalyticsBridge }; 91 | export { AnalyticsSettings }; 92 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as GoogleAnalyticsTracker } from "./GoogleAnalyticsTracker"; 2 | export { default as GoogleAnalyticsSettings } from "./GoogleAnalyticsSettings"; 3 | export { default as GoogleTagManager } from "./GoogleTagManager"; 4 | export { default as DataLayerEvent } from "./models/DataLayerEvent"; 5 | export { 6 | Product, 7 | ProductActionEnum, 8 | ProductAction, 9 | Transaction 10 | } from "./models/Product"; 11 | export { 12 | CustomDimensionsByField, 13 | CustomDimensionsByIndex, 14 | CustomDimensionsFieldIndexMap, 15 | CustomMetrics 16 | } from "./models/Custom"; 17 | export { HitPayload, EventMetadata, TimingMetadata } from "./models/Analytics"; 18 | -------------------------------------------------------------------------------- /src/models/Analytics.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CustomDimensionsByIndex, 3 | CustomMetrics, 4 | CustomDimensionsByField 5 | } from "./Custom"; 6 | import { Product, ProductAction } from "./Product"; 7 | 8 | export interface EventMetadata { 9 | label?: string; 10 | value?: number; 11 | } 12 | 13 | export interface TimingMetadata { 14 | name: string; 15 | label?: string; 16 | } 17 | 18 | export interface HitPayload { 19 | products?: Product[]; 20 | impressionProducts?: Product[]; 21 | impressionList?: string; 22 | impressionSource?: string; 23 | productAction?: ProductAction; 24 | customDimensions?: CustomDimensionsByIndex | CustomDimensionsByField; 25 | customMetrics?: CustomMetrics; 26 | utmCampaignUrl?: string; 27 | session?: SessionState; 28 | } 29 | 30 | export enum SessionState { 31 | Start = "start", 32 | End = "end" 33 | } 34 | 35 | /** 36 | * 37 | * Used when tracking event 38 | * 39 | * @interface EventMetadata 40 | * @example 41 | * const eventMetadata = { label: "v1.0.3", value: 22 } 42 | * tracker.trackEvent("FinalizeOrderButton", "Click", eventMetadata); 43 | * @param {string} label (Optional) 44 | * @param {number} value (Optional) 45 | */ 46 | 47 | /** 48 | * 49 | * Used when tracking time measurements 50 | * 51 | * @interface TimingMetadata 52 | * @example 53 | * const timingMetadata = { name: "LoadList" } // name is a required value when tracking timing 54 | * tracker.trackTiming("testcategory", 13000, timingMetadata); 55 | * @param {string} name (Required) 56 | * @param {string} label (Optional) 57 | */ 58 | 59 | /** 60 | * The HitPayload object and possible values 61 | * 62 | * Used by the different tracking methods for adding metadata to the hit. 63 | * 64 | * @interface HitPayload 65 | * @example 66 | * // If you want to do send a purchase payload with an event: 67 | * const product = { 68 | * id: "P12345", 69 | * name: "Android Warhol T-Shirt", 70 | * category: "Apparel/T-Shirts", 71 | * brand: "Google", 72 | * variant: "Black", 73 | * price: 29.2, 74 | * quantity: 1, 75 | * couponCode: "APPARELSALE" 76 | * }; 77 | * const transaction = { 78 | * id: "T12345", 79 | * affiliation: "Google Store - Online", 80 | * revenue: 37.39, 81 | * tax: 2.85, 82 | * shipping: 5.34, 83 | * couponCode: "SUMMER2013" 84 | * }; 85 | * const productAction = { 86 | * transaction, 87 | * action: 7 // Purchase action, see ProductActionEnum 88 | * } 89 | * const payload = { products: [ product ], productAction: productAction } 90 | * tracker.trackEvent("FinalizeOrderButton", "Click", null, payload); 91 | * @example 92 | * // If you want to send custom dimensions with a screen view: 93 | * const customDimensions = { 94 | * 1: "Beta", 95 | * 3: "Premium" 96 | * }; 97 | * const payload = { customDimensions }; 98 | * tracker.trackScreenView("SaleScreen", payload); 99 | * @param {Product[]} products (Optional) Used for ecommerce 100 | * @param {Product[]} impressionProducts (Optional) Used for ecommerce 101 | * @param {string} impressionList (Optional) Used for ecommerce 102 | * @param {string} impressionSource (Optional) Used for ecommerce 103 | * @param {ProductAction} productAction (Optional) Used for ecommerce 104 | * @param {CustomDimensionsByIndex | CustomDimensionsByField} customDimensions (Optional) 105 | * @param {CustomMetrics} customMetrics (Optional) 106 | * @param {string} utmCampaignUrl (Optional) Used for campaigns 107 | * @param {string} session (Optional) Only two possible values, "start" or "end". This will either start or end a session. 108 | */ 109 | -------------------------------------------------------------------------------- /src/models/Custom.ts: -------------------------------------------------------------------------------- 1 | export interface CustomDimensionsFieldIndexMap { 2 | [key: string]: number; 3 | } 4 | 5 | export interface CustomDimensionsByIndex { 6 | [key: number]: number | string | boolean; 7 | } 8 | 9 | export interface CustomDimensionsByField { 10 | [key: string]: number | string | boolean; 11 | } 12 | 13 | export interface CustomMetrics { 14 | [key: number]: number; 15 | } 16 | 17 | /** 18 | * A dictionary describing mapping of field names to indices for custom dimensions. 19 | * This is an optional object used by the tracker. 20 | * @example 21 | * // Create something like: 22 | * const fieldIndexMap = { customerType: 1 }; 23 | * // Construct tracker with it: 24 | * const tracker = new GoogleAnalyticsTracker("UA-12345-3", fieldIndexMap); 25 | * // This allows you to send in customDimensions in the`HitPayload by field name instead of index: 26 | * tracker.trackScreenView("Home", { customDimensions: { customerType: "Premium" } }); 27 | * // If you do not provide a map, you instead have to send in by index: 28 | * tracker.trackScreenView("Home", { customDimensions: { 1: "Premium" } }); 29 | * @see CustomDimensionsFieldIndexMap 30 | * @see CustomDimensionsByField 31 | * @interface CustomDimensionsFieldIndexMap 32 | */ 33 | 34 | /** 35 | * A dictionary with custom dimensions values and their index keys. 36 | * @example 37 | * const customDimensions = { 1: "Premium", 3: "Beta", 5: 1200 } 38 | * tracker.trackScreenView("Home", { customDimensions }); 39 | * @see CustomDimensionsFieldIndexMap 40 | * @see CustomDimensionsByField 41 | * @interface CustomDimensionsByIndex 42 | */ 43 | 44 | /** 45 | * A dictionary with custom dimensions values and their (mapped) field name keys. 46 | * In order to use this and send in custom dimensions by field name, you must have 47 | * provided a `CustomDimensionsFieldIndexMap` when constructing the tracker. 48 | * @example 49 | * const customDimensions = { customerType: "Premium", appType: "Beta", credit: 1200 } 50 | * tracker.trackScreenView("Home", { customDimensions }); 51 | * @see CustomDimensionsFieldIndexMap 52 | * @see CustomDimensionsByIndex 53 | * @interface CustomDimensionsByField 54 | */ 55 | 56 | /** 57 | * A dictionary with custom metric values and their index keys. 58 | * @example 59 | * const customMetrics = { 1: 2389, 4: 15000 } 60 | * tracker.trackScreenView("Home", { customMetrics }); 61 | * @interface CustomMetrics 62 | */ 63 | -------------------------------------------------------------------------------- /src/models/DataLayerEvent.ts: -------------------------------------------------------------------------------- 1 | export default interface DataLayerEvent { 2 | event: string; 3 | [key: string]: any; 4 | } 5 | 6 | /** 7 | * The Google Tag Manager DataLayerEvent dictionary. 8 | * 9 | * Populate this event-object with values to push to the DataLayer. The only required property is `event`. 10 | * @example 11 | * const dataLayerEvent = { 12 | * event: "eventName", 13 | * pageId: "/home" 14 | * }; 15 | * GoogleTagManager.pushDataLayerEvent(dataLayerEvent); 16 | * 17 | * @interface DataLayerEvent 18 | * @param {string} event 19 | */ 20 | -------------------------------------------------------------------------------- /src/models/Product.ts: -------------------------------------------------------------------------------- 1 | export interface Product { 2 | id: string; 3 | name: string; 4 | category?: string; 5 | brand?: string; 6 | variant?: string; 7 | price?: number; 8 | couponCode?: string; 9 | quantity?: number; 10 | } 11 | 12 | /** 13 | * Enhanced Ecommerce ProductActionEnum 14 | * 15 | * Used by `ProductAction` when describing the type of product action. The possible values (numbers) are: 16 | * 17 | * * Detail = 1, 18 | * * Click = 2, 19 | * * Add = 3, 20 | * * Remove = 4, 21 | * * Checkout = 5, 22 | * * CheckoutOption = 6, 23 | * * Purchase = 7, 24 | * * Refund = 8 25 | * 26 | * @export 27 | * @enum {number} 28 | */ 29 | export enum ProductActionEnum { 30 | Detail = 1, 31 | Click = 2, 32 | Add = 3, 33 | Remove = 4, 34 | Checkout = 5, 35 | CheckoutOption = 6, 36 | Purchase = 7, 37 | Refund = 8 38 | } 39 | 40 | export interface Transaction { 41 | id: string; 42 | affiliation?: string; 43 | revenue?: number; 44 | tax?: number; 45 | shipping?: number; 46 | couponCode?: string; 47 | } 48 | 49 | export interface ProductAction { 50 | action: ProductActionEnum; 51 | transaction?: Transaction; 52 | checkoutStep?: number; 53 | checkoutOption?: string; 54 | productActionList?: string; 55 | productListSource?: string; 56 | } 57 | 58 | /** 59 | * Enhanced Ecommerce Product 60 | * 61 | * Used by `HitPayload` when populating product actions or impressions 62 | * 63 | * @example 64 | * const product = { 65 | * id: "P12345", 66 | * name: "Android Warhol T-Shirt", 67 | * category: "Apparel/T-Shirts", 68 | * brand: "Google", 69 | * variant: "Black", 70 | * price: 29.2, 71 | * quantity: 1, 72 | * couponCode: "APPARELSALE" 73 | * }; 74 | * @interface Product 75 | * @param {string} id 76 | * @param {string} name 77 | * @param {string} category (Optional) 78 | * @param {string} brand (Optional) 79 | * @param {string} variant (Optional) 80 | * @param {number} price (Optional) 81 | * @param {string} couponCode (Optional) 82 | * @param {number} quantity (Optional) 83 | */ 84 | 85 | /** 86 | * Enhanced Ecommerce Transaction 87 | * 88 | * Used by `ProductAction` when populating describing a purchase/transaction 89 | * @example 90 | * const transaction = { 91 | * id: "T12345", 92 | * affiliation: "Google Store - Online", 93 | * revenue: 37.39, 94 | * tax: 2.85, 95 | * shipping: 5.34, 96 | * couponCode: "SUMMER2013" 97 | * }; 98 | * @interface Transaction 99 | * @param {string} id 100 | * @param {string} affiliation (Optional) 101 | * @param {number} revenue (Optional - but not really) 102 | * @param {number} tax (Optional) 103 | * @param {number} shipping (Optional) 104 | * @param {string} couponCode (Optional) 105 | */ 106 | 107 | /** 108 | * Enhanced Ecommerce Product Action 109 | * 110 | * Used by `HitPayload` when describing a product action 111 | * @example 112 | * const productAction = { 113 | * transaction, 114 | * action: 7 // Purchase action, see ProductActionEnum 115 | * } 116 | * @example 117 | * const productAction = { 118 | * action: 3 // Add action, see ProductActionEnum 119 | * } 120 | * @interface ProductAction 121 | * @param {ProductActionEnum} action 122 | * @param {Transaction} transaction (Optional) 123 | * @param {number} checkoutStep (Optional) 124 | * @param {string} checkoutOption (Optional) 125 | * @param {string} productActionList (Optional) 126 | * @param {string} productListSource (Optional) 127 | */ 128 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "module": "es2015", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "allowSyntheticDefaultImports": true, 8 | "outDir": "./dist", 9 | "noUnusedLocals": true, 10 | "noImplicitAny": false, 11 | "skipLibCheck": true 12 | }, 13 | "include": ["src/**/*"] 14 | } 15 | --------------------------------------------------------------------------------