From 554aac69c4a031f1347950475e3b23a7798952e8 Mon Sep 17 00:00:00 2001 From: Alan Charles Date: Wed, 8 Mar 2023 13:31:36 -0700 Subject: [PATCH 01/10] feat: add device token plugin --- .../project.pbxproj | 5 +- .../plugins/plugin-device-token/CHANGELOG.md | 39 +++++++++ packages/plugins/plugin-device-token/LICENSE | 21 +++++ .../plugins/plugin-device-token/README.md | 34 ++++++++ .../plugin-device-token/babel.config.js | 3 + .../plugin-device-token/jest.config.js | 16 ++++ .../plugins/plugin-device-token/package.json | 81 ++++++++++++++++++ .../plugin-device-token/release.config.js | 3 + .../src/DeviceTokenPlugin.tsx | 70 ++++++++++++++++ .../plugins/plugin-device-token/src/index.ts | 1 + .../___tests__/DeviceTokenPlugin.test.ts | 82 +++++++++++++++++++ .../plugins/plugin-device-token/tsconfig.json | 11 +++ yarn.lock | 5 ++ 13 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 packages/plugins/plugin-device-token/CHANGELOG.md create mode 100644 packages/plugins/plugin-device-token/LICENSE create mode 100644 packages/plugins/plugin-device-token/README.md create mode 100644 packages/plugins/plugin-device-token/babel.config.js create mode 100644 packages/plugins/plugin-device-token/jest.config.js create mode 100644 packages/plugins/plugin-device-token/package.json create mode 100644 packages/plugins/plugin-device-token/release.config.js create mode 100644 packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx create mode 100644 packages/plugins/plugin-device-token/src/index.ts create mode 100644 packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts create mode 100644 packages/plugins/plugin-device-token/tsconfig.json diff --git a/example/ios/AnalyticsReactNativeExample.xcodeproj/project.pbxproj b/example/ios/AnalyticsReactNativeExample.xcodeproj/project.pbxproj index f8316d7e..dfe99ad7 100644 --- a/example/ios/AnalyticsReactNativeExample.xcodeproj/project.pbxproj +++ b/example/ios/AnalyticsReactNativeExample.xcodeproj/project.pbxproj @@ -148,6 +148,7 @@ ORGANIZATIONNAME = Facebook; TargetAttributes = { 13B07F861A680F5B00A75B9A = { + DevelopmentTeam = V8R4668S2H; LastSwiftMigration = 1110; }; }; @@ -283,7 +284,7 @@ CODE_SIGN_ENTITLEMENTS = AnalyticsReactNativeExample/AnalyticsReactNativeExample.entitlements; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = V8R4668S2H; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; INFOPLIST_FILE = AnalyticsReactNativeExample/Info.plist; @@ -319,7 +320,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = AnalyticsReactNativeExample/AnalyticsReactNativeExample.entitlements; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = V8R4668S2H; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=*]" = arm64; INFOPLIST_FILE = AnalyticsReactNativeExample/Info.plist; diff --git a/packages/plugins/plugin-device-token/CHANGELOG.md b/packages/plugins/plugin-device-token/CHANGELOG.md new file mode 100644 index 00000000..bc0874e2 --- /dev/null +++ b/packages/plugins/plugin-device-token/CHANGELOG.md @@ -0,0 +1,39 @@ +## [@segment/analytics-react-native-plugin-firebase-v0.3.4](https://github.com/segmentio/analytics-react-native/compare/@segment/analytics-react-native-plugin-firebase-v0.3.3...@segment/analytics-react-native-plugin-firebase-v0.3.4) (2023-02-28) + + +### Bug Fixes + +* add logic to convert traits to strings ([#763](https://github.com/segmentio/analytics-react-native/issues/763)) ([559a3bb](https://github.com/segmentio/analytics-react-native/commit/559a3bb70654faee04546b4f18ed6f340d5712db)) + +## [@segment/analytics-react-native-plugin-firebase-v0.3.3](https://github.com/segmentio/analytics-react-native/compare/@segment/analytics-react-native-plugin-firebase-v0.3.2...@segment/analytics-react-native-plugin-firebase-v0.3.3) (2022-11-30) + + +### Bug Fixes + +* Firebase custom screen properties ([#707](https://github.com/segmentio/analytics-react-native/issues/707)) ([18b75af](https://github.com/segmentio/analytics-react-native/commit/18b75af1bb38246d75ccbfba06d6d972c6db0339)) + +## [@segment/analytics-react-native-plugin-firebase-v0.3.2](https://github.com/segmentio/analytics-react-native/compare/@segment/analytics-react-native-plugin-firebase-v0.3.1...@segment/analytics-react-native-plugin-firebase-v0.3.2) (2022-10-07) + + +### Bug Fixes + +* avoids calling Firebase setUserId with undefined value ([#676](https://github.com/segmentio/analytics-react-native/issues/676)) ([076848f](https://github.com/segmentio/analytics-react-native/commit/076848f9fffbd9bcf126805b177f4d62029017b2)) + +## [@segment/analytics-react-native-plugin-firebase-v0.3.1](https://github.com/segmentio/analytics-react-native/compare/@segment/analytics-react-native-plugin-firebase-v0.3.0...@segment/analytics-react-native-plugin-firebase-v0.3.1) (2022-07-28) + + +### Bug Fixes + +* replace allSettled shim, fix imports from plugins ([#620](https://github.com/segmentio/analytics-react-native/issues/620)) ([18f8ecd](https://github.com/segmentio/analytics-react-native/commit/18f8ecdb291d8c5ecb02e087aa0043df4fc72e97)) + +## [@segment/analytics-react-native-plugin-firebase-v0.3.0](https://github.com/segmentio/analytics-react-native/compare/@segment/analytics-react-native-plugin-firebase-v0.2.1...@segment/analytics-react-native-plugin-firebase-v0.3.0) (2022-07-15) + + +### Features + +* spin off the queue handling into a reusable plugin ([#502](https://github.com/segmentio/analytics-react-native/issues/502)) ([55d7988](https://github.com/segmentio/analytics-react-native/commit/55d798821163d5a41902a6bc099b1bfcbd853a17)) + + +### Bug Fixes + +* map to correct firebase event name/attributes ([#596](https://github.com/segmentio/analytics-react-native/issues/596)) ([e21f541](https://github.com/segmentio/analytics-react-native/commit/e21f541725622135cbe5a3d417689325b8a8d2e3)) diff --git a/packages/plugins/plugin-device-token/LICENSE b/packages/plugins/plugin-device-token/LICENSE new file mode 100644 index 00000000..bea1c8db --- /dev/null +++ b/packages/plugins/plugin-device-token/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Segment + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/plugins/plugin-device-token/README.md b/packages/plugins/plugin-device-token/README.md new file mode 100644 index 00000000..172d87ca --- /dev/null +++ b/packages/plugins/plugin-device-token/README.md @@ -0,0 +1,34 @@ +# @segment/analytics-react-native-plugin-device-token + +## Support + +Please use Github issues, Pull Requests, or feel free to reach out to our [support team](https://segment.com/help/). + +## Integrating with Segment + +Interested in integrating your service with us? Check out our [Partners page](https://segment.com/partners/) for more details. + +## License +``` +MIT License + +Copyright (c) 2021 Segment + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` \ No newline at end of file diff --git a/packages/plugins/plugin-device-token/babel.config.js b/packages/plugins/plugin-device-token/babel.config.js new file mode 100644 index 00000000..f842b77f --- /dev/null +++ b/packages/plugins/plugin-device-token/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['module:metro-react-native-babel-preset'], +}; diff --git a/packages/plugins/plugin-device-token/jest.config.js b/packages/plugins/plugin-device-token/jest.config.js new file mode 100644 index 00000000..9ff095a8 --- /dev/null +++ b/packages/plugins/plugin-device-token/jest.config.js @@ -0,0 +1,16 @@ +const { pathsToModuleNameMapper } = require('ts-jest'); +const { compilerOptions } = require('./tsconfig'); + +module.exports = { + preset: 'react-native', + roots: [''], + setupFiles: ['../../core/src/__tests__/__helpers__/setup.js'], + testPathIgnorePatterns: ['.../../core/src/__tests__/__helpers__/'], + modulePathIgnorePatterns: ['/lib/'], + transform: { + '^.+\\.tsx?$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + modulePaths: [compilerOptions.baseUrl], + moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths), +}; diff --git a/packages/plugins/plugin-device-token/package.json b/packages/plugins/plugin-device-token/package.json new file mode 100644 index 00000000..0e99a716 --- /dev/null +++ b/packages/plugins/plugin-device-token/package.json @@ -0,0 +1,81 @@ +{ + "name": "@segment/analytics-react-native-plugin-device-token", + "version": "0.1.0", + "description": "The hassle-free way to add Segment analytics to your React-Native app.", + "main": "lib/commonjs/index", + "scripts": { + "build": "bob build", + "test": "jest", + "typescript": "tsc --noEmit", + "clean": "rimraf lib node_modules", + "release": "semantic-release" + }, + "keywords": [ + "segment", + "react-native", + "ios", + "android" + ], + "module": "lib/module/index", + "types": "lib/typescript/src/index.d.ts", + "react-native": "src/index", + "source": "src/index", + "files": [ + "src", + "lib", + "android", + "ios", + "cpp", + "segment-analytics-react-native.podspec", + "package.json", + "!src/**/*.e2e.mock.js", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__" + ], + "repository": { + "type": "git", + "url": "https://github.com/segmentio/analytics-react-native.git", + "directory": "packages/core" + }, + "author": "Segment (https://segment.com/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/segmentio/analytics-react-native/issues" + }, + "homepage": "https://github.com/segmentio/analytics-react-native/tree/master/packages/plugins/plugin-firebase#readme", + "peerDependencies": { + "@react-native-firebase/app": "*", + "@segment/analytics-react-native": "*" + }, + "dependencies": { + "@react-native-firebase/messaging": "^17.3.2" + }, + "devDependencies": { + "@semantic-release/changelog": "^6.0.1", + "@semantic-release/commit-analyzer": "^9.0.2", + "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^8.0.4", + "@semantic-release/npm": "^9.0.1", + "@semantic-release/release-notes-generator": "^10.0.3", + "@types/jest": "^27.0.3", + "conventional-changelog-conventionalcommits": "^5.0.0", + "rimraf": "^3.0.2", + "semantic-release": "^19.0.3", + "semantic-release-monorepo": "^7.0.5", + "ts-jest": "^27.0.7", + "typescript": "^4.4.4" + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "commonjs", + "module", + "typescript" + ] + }, + "engines": { + "node": ">=12" + } +} diff --git a/packages/plugins/plugin-device-token/release.config.js b/packages/plugins/plugin-device-token/release.config.js new file mode 100644 index 00000000..57ec0e5b --- /dev/null +++ b/packages/plugins/plugin-device-token/release.config.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ['../../../release.config.js', 'semantic-release-monorepo'], +}; diff --git a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx new file mode 100644 index 00000000..05f8612b --- /dev/null +++ b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx @@ -0,0 +1,70 @@ +import { + PlatformPlugin, + SegmentClient, + PluginType, +} from '@segment/analytics-react-native'; +import { Platform } from 'react-native'; + +import messaging from '@react-native-firebase/messaging'; +import type { FirebaseMessagingTypes } from '@react-native-firebase/messaging'; + +export class DeviceTokenPlugin extends PlatformPlugin { + type = PluginType.enrichment; + + authStatus: Promise = + requestUserPermission(); + APNSToken: Promise | null | string = null; + + async configure(analytics: SegmentClient) { + const isAuthorized = await this.authStatus; + this.analytics = analytics; + + if (isAuthorized) { + this.requestDeviceToken(); + } else { + this.analytics?.logger.warn('Not authorized to retrieve device token'); + } + } + + async requestDeviceToken() { + if (Platform.OS === 'ios') { + this.APNSToken = await retrieveAPNSToken(); + if (this.APNSToken !== null) { + this.analytics?.context.set({ device: { token: this.APNSToken } }); + this.analytics?.track('Push Notifications Enabled'); + } + } else { + let deviceToken = await getDeviceToken(); + if (deviceToken !== undefined && deviceToken.length) { + await this.analytics?.context.set({ device: { token: deviceToken } }); + this.analytics?.track('Push Notifications Enabled'); + } else { + this.analytics?.logger.warn('Unable to retrieve device token'); + } + } + } + + async updatePermissionStatus() { + const isAuthorized = await this.authStatus; + + if (isAuthorized) { + this.requestDeviceToken(); + } + } +} + +async function retrieveAPNSToken() { + if (Platform.OS === 'ios') { + return await messaging().getAPNSToken(); + } else { + return null; + } +} + +async function requestUserPermission() { + return await messaging().hasPermission(); +} + +async function getDeviceToken() { + return await messaging().getToken(); +} diff --git a/packages/plugins/plugin-device-token/src/index.ts b/packages/plugins/plugin-device-token/src/index.ts new file mode 100644 index 00000000..1cf585d3 --- /dev/null +++ b/packages/plugins/plugin-device-token/src/index.ts @@ -0,0 +1 @@ +export * from './DeviceTokenPlugin'; diff --git a/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts b/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts new file mode 100644 index 00000000..f8b17a3d --- /dev/null +++ b/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts @@ -0,0 +1,82 @@ +import { DeviceTokenPlugin } from '../../DeviceTokenPlugin'; +import { MockSegmentStore } from '../../../../../core/src/__tests__/__helpers__/mockSegmentStore'; +import { getMockLogger } from '../../../../../core/src/__tests__/__helpers__/mockLogger'; +import { SegmentClient } from '../../../../../core/src/analytics'; +import { Platform } from 'react-native'; + +const mockRequestPermission = jest.fn().mockReturnValue(1); +const mockGetAPNSToken = jest.fn(); +const mockGetDeviceToken = jest.fn(); + +jest.mock('@react-native-firebase/messaging', () => () => ({ + getAPNSToken: mockGetAPNSToken, + getToken: mockGetDeviceToken, + hasPermission: mockRequestPermission, +})); + +describe('DeviceTokenPlugin', () => { + const store = new MockSegmentStore(); + const clientArgs = { + logger: getMockLogger(), + config: { + writeKey: '123-456', + trackApplicationLifecycleEvents: true, + }, + store, + }; + let plugin: DeviceTokenPlugin = new DeviceTokenPlugin(); + + beforeEach(() => { + store.reset(); + jest.clearAllMocks(); + plugin = new DeviceTokenPlugin(); + }); + + it('requests authorization when configure is called', async () => { + let configureSpy = jest.spyOn(plugin, 'configure'); + let analytics = new SegmentClient(clientArgs); + + jest.mock('react-native/Libraries/Utilities/Platform', () => ({ + OS: 'ios', + select: () => null, + })); + + await plugin.configure(analytics); + + expect(configureSpy).toHaveBeenCalledWith(analytics); + expect(mockRequestPermission).toHaveBeenCalled(); + }); + + it('retrieves the APNS value if authorized and OS is iOS', async () => { + let configureSpy = jest.spyOn(plugin, 'configure'); + let analytics = new SegmentClient(clientArgs); + Platform.OS = 'ios'; + await plugin.configure(analytics); + + expect(Platform.OS).toEqual('ios'); + expect(mockRequestPermission).toHaveReturnedWith(1); + + expect(configureSpy).toHaveBeenCalledWith(analytics); + expect(mockGetAPNSToken).toHaveBeenCalled(); + }); + + it('retrieves the device token for Android builds', async () => { + let configureSpy = jest.spyOn(plugin, 'configure'); + let analytics = new SegmentClient(clientArgs); + Platform.OS = 'android'; + await plugin.configure(analytics); + + expect(Platform.OS).toEqual('android'); + + expect(configureSpy).toHaveBeenCalledWith(analytics); + expect(mockGetDeviceToken).toHaveBeenCalled(); + }); + + it('retrieves the device token when updatePermissions is called', async () => { + Platform.OS = 'ios'; + + await plugin.updatePermissionStatus(); + + expect(mockGetAPNSToken).toHaveBeenCalled(); + }); +}); diff --git a/packages/plugins/plugin-device-token/tsconfig.json b/packages/plugins/plugin-device-token/tsconfig.json new file mode 100644 index 00000000..5695a71a --- /dev/null +++ b/packages/plugins/plugin-device-token/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "lib/typescript", + "baseUrl": ".", + "paths": { + "@segment/analytics-react-native": ["/../../core/src/index"] + } + }, + "references": [{ "path": "../../core" }] +} diff --git a/yarn.lock b/yarn.lock index 40fcdd96..357923b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2443,6 +2443,11 @@ opencollective-postinstall "^2.0.1" superstruct "^0.6.2" +"@react-native-firebase/messaging@^17.3.2": + version "17.3.2" + resolved "https://registry.yarnpkg.com/@react-native-firebase/messaging/-/messaging-17.3.2.tgz#1d37fdf8cc3027542fdbab871f5df3ea2fb687c8" + integrity sha512-7IO12OjNdjtvtxHXY/8qJ9a0RLTg4cUddnE1sNxj3RciDz+Sx7SRJX0fsyX19i1m7dcPh73p9NzrrtnvNnILfw== + "@react-native/assets@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" From eed6c168c7c3678ddd4c3dfa4f1ec5caa8c5b0d0 Mon Sep 17 00:00:00 2001 From: Alan Charles Date: Wed, 8 Mar 2023 13:33:31 -0700 Subject: [PATCH 02/10] chore: remove dev team from xcode proj --- .../AnalyticsReactNativeExample.xcodeproj/project.pbxproj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/example/ios/AnalyticsReactNativeExample.xcodeproj/project.pbxproj b/example/ios/AnalyticsReactNativeExample.xcodeproj/project.pbxproj index dfe99ad7..f8316d7e 100644 --- a/example/ios/AnalyticsReactNativeExample.xcodeproj/project.pbxproj +++ b/example/ios/AnalyticsReactNativeExample.xcodeproj/project.pbxproj @@ -148,7 +148,6 @@ ORGANIZATIONNAME = Facebook; TargetAttributes = { 13B07F861A680F5B00A75B9A = { - DevelopmentTeam = V8R4668S2H; LastSwiftMigration = 1110; }; }; @@ -284,7 +283,7 @@ CODE_SIGN_ENTITLEMENTS = AnalyticsReactNativeExample/AnalyticsReactNativeExample.entitlements; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; - DEVELOPMENT_TEAM = V8R4668S2H; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; INFOPLIST_FILE = AnalyticsReactNativeExample/Info.plist; @@ -320,7 +319,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = AnalyticsReactNativeExample/AnalyticsReactNativeExample.entitlements; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = V8R4668S2H; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=*]" = arm64; INFOPLIST_FILE = AnalyticsReactNativeExample/Info.plist; From 2933344ff492adad7c16941f0c08af071007d7fe Mon Sep 17 00:00:00 2001 From: Alan Charles Date: Thu, 9 Mar 2023 10:46:18 -0700 Subject: [PATCH 03/10] chore: refactor for oscar's review --- .../plugins/plugin-device-token/package.json | 6 +-- .../src/DeviceTokenPlugin.tsx | 42 ++++++---------- .../___tests__/DeviceTokenPlugin.test.ts | 50 ++++++++++++------- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/packages/plugins/plugin-device-token/package.json b/packages/plugins/plugin-device-token/package.json index 0e99a716..9191559b 100644 --- a/packages/plugins/plugin-device-token/package.json +++ b/packages/plugins/plugin-device-token/package.json @@ -45,12 +45,10 @@ }, "homepage": "https://github.com/segmentio/analytics-react-native/tree/master/packages/plugins/plugin-firebase#readme", "peerDependencies": { - "@react-native-firebase/app": "*", + "@react-native-firebase/app": "17.3.2", + "@react-native-firebase/messaging": "^17.3.2", "@segment/analytics-react-native": "*" }, - "dependencies": { - "@react-native-firebase/messaging": "^17.3.2" - }, "devDependencies": { "@semantic-release/changelog": "^6.0.1", "@semantic-release/commit-analyzer": "^9.0.2", diff --git a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx index 05f8612b..542ffd50 100644 --- a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx +++ b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx @@ -4,7 +4,6 @@ import { PluginType, } from '@segment/analytics-react-native'; import { Platform } from 'react-native'; - import messaging from '@react-native-firebase/messaging'; import type { FirebaseMessagingTypes } from '@react-native-firebase/messaging'; @@ -12,34 +11,35 @@ export class DeviceTokenPlugin extends PlatformPlugin { type = PluginType.enrichment; authStatus: Promise = - requestUserPermission(); - APNSToken: Promise | null | string = null; + checkUserPermission(); async configure(analytics: SegmentClient) { const isAuthorized = await this.authStatus; this.analytics = analytics; if (isAuthorized) { - this.requestDeviceToken(); + this.retrieveDeviceToken(); } else { this.analytics?.logger.warn('Not authorized to retrieve device token'); } } - async requestDeviceToken() { + async retrieveDeviceToken() { if (Platform.OS === 'ios') { - this.APNSToken = await retrieveAPNSToken(); - if (this.APNSToken !== null) { - this.analytics?.context.set({ device: { token: this.APNSToken } }); - this.analytics?.track('Push Notifications Enabled'); + let APNSToken = await messaging().getAPNSToken(); + if (APNSToken !== null) { + await this.analytics?.context.set({ device: { token: APNSToken } }); + this.analytics?.track('Device Token Retrieved'); } - } else { - let deviceToken = await getDeviceToken(); + } else if (Platform.OS === 'android') { + let deviceToken = await messaging().getToken(); if (deviceToken !== undefined && deviceToken.length) { await this.analytics?.context.set({ device: { token: deviceToken } }); - this.analytics?.track('Push Notifications Enabled'); + this.analytics?.track('Device Token Retrieved'); } else { - this.analytics?.logger.warn('Unable to retrieve device token'); + this.analytics?.logger.warn( + 'Device token only available on iOS and Android platforms' + ); } } } @@ -48,23 +48,11 @@ export class DeviceTokenPlugin extends PlatformPlugin { const isAuthorized = await this.authStatus; if (isAuthorized) { - this.requestDeviceToken(); + this.retrieveDeviceToken(); } } } -async function retrieveAPNSToken() { - if (Platform.OS === 'ios') { - return await messaging().getAPNSToken(); - } else { - return null; - } -} - -async function requestUserPermission() { +async function checkUserPermission() { return await messaging().hasPermission(); } - -async function getDeviceToken() { - return await messaging().getToken(); -} diff --git a/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts b/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts index f8b17a3d..3ad8e003 100644 --- a/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts +++ b/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts @@ -4,9 +4,9 @@ import { getMockLogger } from '../../../../../core/src/__tests__/__helpers__/moc import { SegmentClient } from '../../../../../core/src/analytics'; import { Platform } from 'react-native'; -const mockRequestPermission = jest.fn().mockReturnValue(1); -const mockGetAPNSToken = jest.fn(); -const mockGetDeviceToken = jest.fn(); +let mockRequestPermission = jest.fn().mockReturnValue(1); +const mockGetAPNSToken = jest.fn().mockReturnValue('device-token'); +const mockGetDeviceToken = jest.fn().mockReturnValue('device-token'); jest.mock('@react-native-firebase/messaging', () => () => ({ getAPNSToken: mockGetAPNSToken, @@ -14,6 +14,11 @@ jest.mock('@react-native-firebase/messaging', () => () => ({ hasPermission: mockRequestPermission, })); +jest.mock('react-native/Libraries/Utilities/Platform', () => ({ + OS: 'ios', + select: () => null, +})); + describe('DeviceTokenPlugin', () => { const store = new MockSegmentStore(); const clientArgs = { @@ -33,42 +38,28 @@ describe('DeviceTokenPlugin', () => { }); it('requests authorization when configure is called', async () => { - let configureSpy = jest.spyOn(plugin, 'configure'); let analytics = new SegmentClient(clientArgs); - jest.mock('react-native/Libraries/Utilities/Platform', () => ({ - OS: 'ios', - select: () => null, - })); - await plugin.configure(analytics); - expect(configureSpy).toHaveBeenCalledWith(analytics); expect(mockRequestPermission).toHaveBeenCalled(); }); it('retrieves the APNS value if authorized and OS is iOS', async () => { - let configureSpy = jest.spyOn(plugin, 'configure'); let analytics = new SegmentClient(clientArgs); Platform.OS = 'ios'; await plugin.configure(analytics); - expect(Platform.OS).toEqual('ios'); expect(mockRequestPermission).toHaveReturnedWith(1); - - expect(configureSpy).toHaveBeenCalledWith(analytics); expect(mockGetAPNSToken).toHaveBeenCalled(); }); it('retrieves the device token for Android builds', async () => { - let configureSpy = jest.spyOn(plugin, 'configure'); let analytics = new SegmentClient(clientArgs); Platform.OS = 'android'; - await plugin.configure(analytics); - expect(Platform.OS).toEqual('android'); + await plugin.configure(analytics); - expect(configureSpy).toHaveBeenCalledWith(analytics); expect(mockGetDeviceToken).toHaveBeenCalled(); }); @@ -79,4 +70,27 @@ describe('DeviceTokenPlugin', () => { expect(mockGetAPNSToken).toHaveBeenCalled(); }); + + it('sets the device token in context for iOS', async () => { + let analytics = new SegmentClient(clientArgs); + await plugin.configure(analytics); + + expect(store.context.get()).toEqual({ + device: { + token: 'device-token', + }, + }); + }); + + it('sets the device token in context for Android', async () => { + Platform.OS = 'android'; + let analytics = new SegmentClient(clientArgs); + await plugin.configure(analytics); + + expect(store.context.get()).toEqual({ + device: { + token: 'device-token', + }, + }); + }); }); From de9272ea126c9d357f25268881a9720765a133b2 Mon Sep 17 00:00:00 2001 From: Alan Charles Date: Thu, 9 Mar 2023 11:05:19 -0700 Subject: [PATCH 04/10] chore: move messaging package back to deps --- packages/plugins/plugin-device-token/package.json | 4 +++- yarn.lock | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/plugins/plugin-device-token/package.json b/packages/plugins/plugin-device-token/package.json index 9191559b..294bc3c8 100644 --- a/packages/plugins/plugin-device-token/package.json +++ b/packages/plugins/plugin-device-token/package.json @@ -46,9 +46,11 @@ "homepage": "https://github.com/segmentio/analytics-react-native/tree/master/packages/plugins/plugin-firebase#readme", "peerDependencies": { "@react-native-firebase/app": "17.3.2", - "@react-native-firebase/messaging": "^17.3.2", "@segment/analytics-react-native": "*" }, + "dependencies": { + "@react-native-firebase/messaging": "^17.3.2" + }, "devDependencies": { "@semantic-release/changelog": "^6.0.1", "@semantic-release/commit-analyzer": "^9.0.2", diff --git a/yarn.lock b/yarn.lock index 357923b7..40fcdd96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2443,11 +2443,6 @@ opencollective-postinstall "^2.0.1" superstruct "^0.6.2" -"@react-native-firebase/messaging@^17.3.2": - version "17.3.2" - resolved "https://registry.yarnpkg.com/@react-native-firebase/messaging/-/messaging-17.3.2.tgz#1d37fdf8cc3027542fdbab871f5df3ea2fb687c8" - integrity sha512-7IO12OjNdjtvtxHXY/8qJ9a0RLTg4cUddnE1sNxj3RciDz+Sx7SRJX0fsyX19i1m7dcPh73p9NzrrtnvNnILfw== - "@react-native/assets@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" From af53eb8ed28895ab51461942c2f0b25f568fc5d8 Mon Sep 17 00:00:00 2001 From: Alan Charles Date: Thu, 9 Mar 2023 11:47:53 -0700 Subject: [PATCH 05/10] chore: add additional refactors --- .../plugins/plugin-device-token/package.json | 2 +- .../src/DeviceTokenPlugin.tsx | 30 +++++++++++-------- .../___tests__/DeviceTokenPlugin.test.ts | 14 +++------ 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/packages/plugins/plugin-device-token/package.json b/packages/plugins/plugin-device-token/package.json index 294bc3c8..6454c3d0 100644 --- a/packages/plugins/plugin-device-token/package.json +++ b/packages/plugins/plugin-device-token/package.json @@ -45,7 +45,7 @@ }, "homepage": "https://github.com/segmentio/analytics-react-native/tree/master/packages/plugins/plugin-firebase#readme", "peerDependencies": { - "@react-native-firebase/app": "17.3.2", + "@react-native-firebase/app": "17.x", "@segment/analytics-react-native": "*" }, "dependencies": { diff --git a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx index 542ffd50..c021fc41 100644 --- a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx +++ b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx @@ -5,16 +5,12 @@ import { } from '@segment/analytics-react-native'; import { Platform } from 'react-native'; import messaging from '@react-native-firebase/messaging'; -import type { FirebaseMessagingTypes } from '@react-native-firebase/messaging'; export class DeviceTokenPlugin extends PlatformPlugin { type = PluginType.enrichment; - authStatus: Promise = - checkUserPermission(); - async configure(analytics: SegmentClient) { - const isAuthorized = await this.authStatus; + const isAuthorized = await this.checkUserPermission(); this.analytics = analytics; if (isAuthorized) { @@ -28,14 +24,12 @@ export class DeviceTokenPlugin extends PlatformPlugin { if (Platform.OS === 'ios') { let APNSToken = await messaging().getAPNSToken(); if (APNSToken !== null) { - await this.analytics?.context.set({ device: { token: APNSToken } }); - this.analytics?.track('Device Token Retrieved'); + this.setDeviceToken(APNSToken); } } else if (Platform.OS === 'android') { let deviceToken = await messaging().getToken(); if (deviceToken !== undefined && deviceToken.length) { - await this.analytics?.context.set({ device: { token: deviceToken } }); - this.analytics?.track('Device Token Retrieved'); + this.setDeviceToken(deviceToken); } else { this.analytics?.logger.warn( 'Device token only available on iOS and Android platforms' @@ -44,15 +38,25 @@ export class DeviceTokenPlugin extends PlatformPlugin { } } + async setDeviceToken(token: string) { + await this.analytics?.context.set({ device: { token: token } }); + this.analytics?.track('Device Token Retrieved'); + } + async updatePermissionStatus() { - const isAuthorized = await this.authStatus; + const isAuthorized = await this.checkUserPermission(); if (isAuthorized) { this.retrieveDeviceToken(); } } -} -async function checkUserPermission() { - return await messaging().hasPermission(); + async checkUserPermission() { + try { + return await messaging().hasPermission(); + } catch (e) { + this.analytics?.logger.warn(e); + return; + } + } } diff --git a/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts b/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts index 3ad8e003..89663f41 100644 --- a/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts +++ b/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts @@ -75,11 +75,8 @@ describe('DeviceTokenPlugin', () => { let analytics = new SegmentClient(clientArgs); await plugin.configure(analytics); - expect(store.context.get()).toEqual({ - device: { - token: 'device-token', - }, - }); + const token = await store.context.get(true); + expect(token).toEqual({ device: { token: 'device-token' } }); }); it('sets the device token in context for Android', async () => { @@ -87,10 +84,7 @@ describe('DeviceTokenPlugin', () => { let analytics = new SegmentClient(clientArgs); await plugin.configure(analytics); - expect(store.context.get()).toEqual({ - device: { - token: 'device-token', - }, - }); + const token = await store.context.get(true); + expect(token).toEqual({ device: { token: 'device-token' } }); }); }); From 583d862864d3e76ae2a399499ecef3443b37d396 Mon Sep 17 00:00:00 2001 From: Alan Charles Date: Thu, 9 Mar 2023 13:42:47 -0700 Subject: [PATCH 06/10] chore: refactor getDeviceToken() --- .../src/DeviceTokenPlugin.tsx | 45 ++++++++++++------- yarn.lock | 5 +++ 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx index c021fc41..d46343ef 100644 --- a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx +++ b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx @@ -5,37 +5,44 @@ import { } from '@segment/analytics-react-native'; import { Platform } from 'react-native'; import messaging from '@react-native-firebase/messaging'; +import type { FirebaseMessagingTypes } from '@react-native-firebase/messaging'; export class DeviceTokenPlugin extends PlatformPlugin { type = PluginType.enrichment; + authStatus: Promise = + this.checkUserPermission(); async configure(analytics: SegmentClient) { - const isAuthorized = await this.checkUserPermission(); + const isAuthorized = await this.authStatus; this.analytics = analytics; if (isAuthorized) { - this.retrieveDeviceToken(); + let token = await this.getDeviceToken(); + + if (token !== undefined) { + this.setDeviceToken(token); + } } else { this.analytics?.logger.warn('Not authorized to retrieve device token'); } } - async retrieveDeviceToken() { + private async getDeviceToken(): Promise { if (Platform.OS === 'ios') { - let APNSToken = await messaging().getAPNSToken(); - if (APNSToken !== null) { - this.setDeviceToken(APNSToken); - } - } else if (Platform.OS === 'android') { - let deviceToken = await messaging().getToken(); - if (deviceToken !== undefined && deviceToken.length) { - this.setDeviceToken(deviceToken); + return (await messaging().getAPNSToken()) ?? undefined; + } + if (Platform.OS === 'android') { + let deviceToken = (await messaging().getToken()) ?? undefined; + if (deviceToken !== undefined && deviceToken.length > 0) { + return deviceToken; } else { - this.analytics?.logger.warn( - 'Device token only available on iOS and Android platforms' - ); + return undefined; } } + this.analytics?.logger.warn( + 'Device token only available on iOS and Android platforms' + ); + return undefined; } async setDeviceToken(token: string) { @@ -47,16 +54,20 @@ export class DeviceTokenPlugin extends PlatformPlugin { const isAuthorized = await this.checkUserPermission(); if (isAuthorized) { - this.retrieveDeviceToken(); + let token = await this.getDeviceToken(); + + if (token !== undefined) { + this.setDeviceToken(token); + } } } - async checkUserPermission() { + private async checkUserPermission() { try { return await messaging().hasPermission(); } catch (e) { this.analytics?.logger.warn(e); - return; + return undefined; } } } diff --git a/yarn.lock b/yarn.lock index 40fcdd96..357923b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2443,6 +2443,11 @@ opencollective-postinstall "^2.0.1" superstruct "^0.6.2" +"@react-native-firebase/messaging@^17.3.2": + version "17.3.2" + resolved "https://registry.yarnpkg.com/@react-native-firebase/messaging/-/messaging-17.3.2.tgz#1d37fdf8cc3027542fdbab871f5df3ea2fb687c8" + integrity sha512-7IO12OjNdjtvtxHXY/8qJ9a0RLTg4cUddnE1sNxj3RciDz+Sx7SRJX0fsyX19i1m7dcPh73p9NzrrtnvNnILfw== + "@react-native/assets@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" From 966480243a2754228941b9dbdd4d4e93817fce0b Mon Sep 17 00:00:00 2001 From: Alan Charles Date: Thu, 9 Mar 2023 13:46:17 -0700 Subject: [PATCH 07/10] chore: add return type to checkUserPermission() --- .../plugins/plugin-device-token/src/DeviceTokenPlugin.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx index d46343ef..100e42c5 100644 --- a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx +++ b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx @@ -62,7 +62,9 @@ export class DeviceTokenPlugin extends PlatformPlugin { } } - private async checkUserPermission() { + private async checkUserPermission(): Promise< + FirebaseMessagingTypes.AuthorizationStatus | undefined + > { try { return await messaging().hasPermission(); } catch (e) { From bd9a97e735c0913cf64f25e68de34fddf026e707 Mon Sep 17 00:00:00 2001 From: Alan Charles Date: Fri, 10 Mar 2023 10:09:58 -0700 Subject: [PATCH 08/10] refactor: add try/catch and error handler --- .../src/DeviceTokenPlugin.tsx | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx index 100e42c5..aa58b85c 100644 --- a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx +++ b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx @@ -2,6 +2,8 @@ import { PlatformPlugin, SegmentClient, PluginType, + ErrorType, + SegmentError, } from '@segment/analytics-react-native'; import { Platform } from 'react-native'; import messaging from '@react-native-firebase/messaging'; @@ -13,17 +15,28 @@ export class DeviceTokenPlugin extends PlatformPlugin { this.checkUserPermission(); async configure(analytics: SegmentClient) { - const isAuthorized = await this.authStatus; this.analytics = analytics; + try { + const isAuthorized = await this.authStatus; - if (isAuthorized) { - let token = await this.getDeviceToken(); + if (isAuthorized) { + let token = await this.getDeviceToken(); - if (token !== undefined) { - this.setDeviceToken(token); + if (token !== undefined) { + this.setDeviceToken(token); + } + } else { + this.analytics?.logger.warn('Not authorized to retrieve device token'); } - } else { - this.analytics?.logger.warn('Not authorized to retrieve device token'); + } catch (error) { + this.analytics?.logger.warn(error); + this.analytics?.reportInternalError( + new SegmentError( + ErrorType.PluginError, + 'Unable to confirm authorization status', + error + ) + ); } } @@ -67,8 +80,15 @@ export class DeviceTokenPlugin extends PlatformPlugin { > { try { return await messaging().hasPermission(); - } catch (e) { - this.analytics?.logger.warn(e); + } catch (error) { + this.analytics?.logger.warn(error); + this.analytics?.reportInternalError( + new SegmentError( + ErrorType.PluginError, + 'Unable to confirm authorization status', + error + ) + ); return undefined; } } From 138df5a4ee2bd5e699506cf6f2e456c1d70f0e66 Mon Sep 17 00:00:00 2001 From: Alan Charles Date: Fri, 10 Mar 2023 10:43:46 -0700 Subject: [PATCH 09/10] chore: update readme --- .../plugins/plugin-device-token/README.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/packages/plugins/plugin-device-token/README.md b/packages/plugins/plugin-device-token/README.md index 172d87ca..e6b5a23d 100644 --- a/packages/plugins/plugin-device-token/README.md +++ b/packages/plugins/plugin-device-token/README.md @@ -1,5 +1,70 @@ # @segment/analytics-react-native-plugin-device-token +`EnrichmentPlugin` to collect device token values with (Firebase Cloud Messaging)[]. This plugin makes it possible to collect Android's FCM and Apple's APNS device tokens. +## Installation + +Install the dependencies. + +Using NPM: +```bash +npm install --save @segment/analytics-react-native-plugin-device-token +@react-native-firebase/app @react-native-firebase/messaging +``` + +Using Yarn: +```bash +yarn add @segment/analytics-react-native-plugin-device-token +@react-native-firebase/app @react-native-firebase/messaging +``` + +Run `pod install` after the installation to autolink the Firebase SDK. + +> warning "" +> Refer to Apple's [Push Notification Services](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns) and [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) for additional setup requirements. + +## Usage + + +Follow the [instructions for adding plugins](https://github.com/segmentio/analytics-react-native#adding-plugins) on the main Analytics client: + +In your code where you initialize the analytics client call the `.add(plugin)` method with an `DeviceTokenPlugin` instance. + + +```ts +import { createClient } from '@segment/analytics-react-native'; + +import { FirebasePlugin } from '@segment/analytics-react-native-plugin-device-token'; + +const segmentClient = createClient({ + writeKey: 'SEGMENT_KEY' +}); + +segmentClient.add({ plugin: new DeviceTokenPlugin() }); +``` + +### updatePermission() + +iOS builds require a user to give explicit permission to retrieve the APNS value. This plugin only checks to see if permission has been authorized. You will need to handle permission requests yourself. Once permission has been granted you can call the `updatePermission()` method to begin collecting the device token. + + +```ts +import messaging from '@react-native-firebase/messaging'; +import { DeviceTokenPlugin } from '@segment/analytics-react-native-plugin-device-token' + +const deviceTokenPlugin = new DeviceTokenPlugin() + +segmentClient.add({plugin: deviceTokenPlugin }) + +// handle firebase permissions +async handlePermission() { + let permissionStatus = await messaging.requestPermission() + + if (permissionStatus) { + deviceTokenPlugin.updatePermission() + } +} +``` + ## Support Please use Github issues, Pull Requests, or feel free to reach out to our [support team](https://segment.com/help/). From 09750991383c0b04711f70e94b762b83a7e29ac5 Mon Sep 17 00:00:00 2001 From: Alan Charles Date: Fri, 10 Mar 2023 11:43:07 -0700 Subject: [PATCH 10/10] chore: fix readme add awaits --- packages/plugins/plugin-device-token/README.md | 8 +++++--- packages/plugins/plugin-device-token/package.json | 4 +--- .../plugins/plugin-device-token/src/DeviceTokenPlugin.tsx | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/plugins/plugin-device-token/README.md b/packages/plugins/plugin-device-token/README.md index e6b5a23d..634fc56e 100644 --- a/packages/plugins/plugin-device-token/README.md +++ b/packages/plugins/plugin-device-token/README.md @@ -1,8 +1,11 @@ # @segment/analytics-react-native-plugin-device-token -`EnrichmentPlugin` to collect device token values with (Firebase Cloud Messaging)[]. This plugin makes it possible to collect Android's FCM and Apple's APNS device tokens. +`EnrichmentPlugin` to collect device token values with [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging). This plugin makes it possible to collect Android's FCM and Apple's APNS device tokens. ## Installation +> warning "" +> This plugin assumes you are using Firebase Cloud Messaging for Android push notifications. If you are strictly using Apple's Push Notification Services, we recommend creating your own enrichment plugin. + Install the dependencies. Using NPM: @@ -44,8 +47,7 @@ segmentClient.add({ plugin: new DeviceTokenPlugin() }); ### updatePermission() -iOS builds require a user to give explicit permission to retrieve the APNS value. This plugin only checks to see if permission has been authorized. You will need to handle permission requests yourself. Once permission has been granted you can call the `updatePermission()` method to begin collecting the device token. - + This plugin only checks to see if permission has been authorized, it does not ask for permissions. You will need to handle permission requests yourself. Once permission has been granted you can call the `updatePermission()` method to begin collecting the device token. ```ts import messaging from '@react-native-firebase/messaging'; diff --git a/packages/plugins/plugin-device-token/package.json b/packages/plugins/plugin-device-token/package.json index 6454c3d0..b9bb8791 100644 --- a/packages/plugins/plugin-device-token/package.json +++ b/packages/plugins/plugin-device-token/package.json @@ -48,10 +48,8 @@ "@react-native-firebase/app": "17.x", "@segment/analytics-react-native": "*" }, - "dependencies": { - "@react-native-firebase/messaging": "^17.3.2" - }, "devDependencies": { + "@react-native-firebase/messaging": "^17.3.2", "@semantic-release/changelog": "^6.0.1", "@semantic-release/commit-analyzer": "^9.0.2", "@semantic-release/git": "^10.0.1", diff --git a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx index aa58b85c..928c45e2 100644 --- a/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx +++ b/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx @@ -60,7 +60,7 @@ export class DeviceTokenPlugin extends PlatformPlugin { async setDeviceToken(token: string) { await this.analytics?.context.set({ device: { token: token } }); - this.analytics?.track('Device Token Retrieved'); + await this.analytics?.track('Device Token Retrieved'); } async updatePermissionStatus() { @@ -70,7 +70,7 @@ export class DeviceTokenPlugin extends PlatformPlugin { let token = await this.getDeviceToken(); if (token !== undefined) { - this.setDeviceToken(token); + await this.setDeviceToken(token); } } }