diff --git a/packages/core/RNAnalytics.podspec b/packages/core/RNAnalytics.podspec index 25bce4663..a9d3807cd 100644 --- a/packages/core/RNAnalytics.podspec +++ b/packages/core/RNAnalytics.podspec @@ -20,7 +20,7 @@ Pod::Spec.new do |s| s.source_files = 'ios/**/*.{m,h}' s.static_framework = true - s.dependency 'Analytics', '~> 4.0.4' + s.dependency 'Analytics', '~> 4.0.5' s.dependency 'React' end diff --git a/packages/core/android/src/main/java/com/segment/analytics/reactnative/core/RNAnalytics.kt b/packages/core/android/src/main/java/com/segment/analytics/reactnative/core/RNAnalytics.kt index e73494c39..f78c79354 100644 --- a/packages/core/android/src/main/java/com/segment/analytics/reactnative/core/RNAnalytics.kt +++ b/packages/core/android/src/main/java/com/segment/analytics/reactnative/core/RNAnalytics.kt @@ -31,6 +31,10 @@ object RNAnalytics { private val integrations = mutableSetOf() private val onReadyCallbacks = mutableMapOf>() + fun setIDFA(idfa: String) { + // do nothing; iOS only. + } + fun addIntegration(integration: Integration.Factory) { integrations.add(integration) } diff --git a/packages/core/docs/README.md b/packages/core/docs/README.md index 0551e089c..b3c56e67c 100644 --- a/packages/core/docs/README.md +++ b/packages/core/docs/README.md @@ -24,7 +24,7 @@ **Ƭ Integration**: *`function` \| `object`* -*Defined in [analytics.ts:8](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L8)* +*Defined in analytics.ts:8* ___ diff --git a/packages/core/docs/classes/analytics.client.md b/packages/core/docs/classes/analytics.client.md index ae51bb114..b89051250 100644 --- a/packages/core/docs/classes/analytics.client.md +++ b/packages/core/docs/classes/analytics.client.md @@ -25,6 +25,7 @@ * [middleware](analytics.client.md#middleware) * [reset](analytics.client.md#reset) * [screen](analytics.client.md#screen) +* [setIDFA](analytics.client.md#setidfa) * [setup](analytics.client.md#setup) * [track](analytics.client.md#track) * [useNativeConfiguration](analytics.client.md#usenativeconfiguration) @@ -39,7 +40,7 @@ **● ready**: *`false`* = false -*Defined in [analytics.ts:147](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L147)* +*Defined in analytics.ts:147* Whether the client is ready to send events to Segment. @@ -55,7 +56,7 @@ ___ ▸ **alias**(newId: *`string`*, options?: *[Options]()*): `Promise`<`void`> -*Defined in [analytics.ts:317](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L317)* +*Defined in analytics.ts:325* Merge two user identities, effectively connecting two sets of user data as one. This may not be supported by all integrations. @@ -77,7 +78,7 @@ ___ ▸ **catch**(handler: *[ErrorHandler]()*): `this` -*Defined in [analytics.ts:162](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L162)* +*Defined in analytics.ts:162* Catch React-Native bridge errors @@ -98,7 +99,7 @@ ___ ▸ **disable**(): `Promise`<`void`> -*Defined in [analytics.ts:356](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L356)* +*Defined in analytics.ts:364* Completely disable the sending of any analytics data. @@ -113,7 +114,7 @@ ___ ▸ **enable**(): `Promise`<`void`> -*Defined in [analytics.ts:346](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L346)* +*Defined in analytics.ts:354* Enable the sending of analytics data. Enabled by default. @@ -128,7 +129,7 @@ ___ ▸ **flush**(): `Promise`<`void`> -*Defined in [analytics.ts:337](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L337)* +*Defined in analytics.ts:345* Trigger an upload of all queued events. @@ -143,7 +144,7 @@ ___ ▸ **getAnonymousId**(): `Promise`<`string`> -*Defined in [analytics.ts:361](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L361)* +*Defined in analytics.ts:369* Retrieve the anonymousId. @@ -156,7 +157,7 @@ ___ ▸ **group**(groupId: *`string`*, traits?: *[JsonMap]()*, options?: *[Options]()*): `Promise`<`void`> -*Defined in [analytics.ts:304](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L304)* +*Defined in analytics.ts:312* Associate a user with a group, organization, company, project, or w/e _you_ call them. @@ -179,7 +180,7 @@ ___ ▸ **identify**(user: *`string`*, traits?: *[JsonMap]()*, options?: *[Options]()*): `Promise`<`void`> -*Defined in [analytics.ts:291](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L291)* +*Defined in analytics.ts:299* Associate a user with their unique ID and record traits about them. @@ -202,7 +203,7 @@ ___ ▸ **middleware**(middleware: *[Middleware]()*): `this` -*Defined in [analytics.ts:200](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L200)* +*Defined in analytics.ts:208* Append a new middleware to the middleware chain. @@ -240,7 +241,7 @@ ___ ▸ **reset**(): `Promise`<`void`> -*Defined in [analytics.ts:327](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L327)* +*Defined in analytics.ts:335* Reset any user state that is cached on the device. @@ -255,7 +256,7 @@ ___ ▸ **screen**(name: *`string`*, properties?: *[JsonMap]()*, options?: *[Options]()*): `Promise`<`void`> -*Defined in [analytics.ts:276](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L276)* +*Defined in analytics.ts:284* Record the screens or views your users see. @@ -271,6 +272,25 @@ When a user views a screen in your app, you'll want to record that here. For som **Returns:** `Promise`<`void`> +___ + + +### setIDFA + +▸ **setIDFA**(idfa: *`string`*): `void` + +*Defined in analytics.ts:172* + +Sets the IDFA value on iOS. Customers are now responsible for collecting IDFA on their own. + +**Parameters:** + +| Name | Type | +| ------ | ------ | +| idfa | `string` | + +**Returns:** `void` + ___ @@ -278,7 +298,7 @@ ___ ▸ **setup**(writeKey: *`string`*, configuration?: *[Configuration](../interfaces/analytics.configuration.md)*): `Promise`<`void`> -*Defined in [analytics.ts:239](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L239)* +*Defined in analytics.ts:247* Setup the Analytics module. All calls made before are queued and only executed if the configuration was successful. @@ -308,7 +328,7 @@ ___ ▸ **track**(event: *`string`*, properties?: *[JsonMap]()*, options?: *[Options]()*): `Promise`<`void`> -*Defined in [analytics.ts:258](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L258)* +*Defined in analytics.ts:266* Record the actions your users perform. @@ -331,7 +351,7 @@ ___ ▸ **useNativeConfiguration**(): `this` -*Defined in [analytics.ts:212](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L212)* +*Defined in analytics.ts:220* Use the native configuration. diff --git a/packages/core/docs/interfaces/analytics.configuration.md b/packages/core/docs/interfaces/analytics.configuration.md index a6f3b3fdf..391e8bf1e 100644 --- a/packages/core/docs/interfaces/analytics.configuration.md +++ b/packages/core/docs/interfaces/analytics.configuration.md @@ -31,7 +31,7 @@ **● android**: *`undefined` \| `object`* -*Defined in [analytics.ts:120](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L120)* +*Defined in analytics.ts:120* Android specific settings. @@ -42,7 +42,7 @@ ___ **● debug**: *`undefined` \| `false` \| `true`* -*Defined in [analytics.ts:38](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L38)* +*Defined in analytics.ts:38* ___ @@ -51,7 +51,7 @@ ___ **● defaultProjectSettings**: *`undefined` \| `object`* -*Defined in [analytics.ts:46](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L46)* +*Defined in analytics.ts:46* Default project settings to use, if Segment.com cannot be reached. An example configuration can be found here, using your write key: [](https://cdn-settings.segment.com/v1/projects/YOUR_WRITE_KEY/settings)[https://cdn-settings.segment.com/v1/projects/YOUR\_WRITE\_KEY/settings](https://cdn-settings.segment.com/v1/projects/YOUR_WRITE_KEY/settings) @@ -62,7 +62,7 @@ ___ **● flushAt**: *`undefined` \| `number`* -*Defined in [analytics.ts:54](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L54)* +*Defined in analytics.ts:54* The number of queued events that the analytics client should flush at. Setting this to `1` will not queue any events and will use more battery. @@ -75,7 +75,7 @@ ___ **● ios**: *`undefined` \| `object`* -*Defined in [analytics.ts:102](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L102)* +*Defined in analytics.ts:102* iOS specific settings. @@ -86,7 +86,7 @@ ___ **● proxy**: *`undefined` \| `object`* -*Defined in [analytics.ts:72](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L72)* +*Defined in analytics.ts:72* Whether the analytics client should send all requests through your own hosted proxy rather than directly to Segment. See: iOS: [https://segment.com/docs/connections/sources/catalog/libraries/mobile/ios/#proxy-http-calls](https://segment.com/docs/connections/sources/catalog/libraries/mobile/ios/#proxy-http-calls) android: [https://segment.com/docs/connections/sources/catalog/libraries/mobile/android/#proxy-http-calls](https://segment.com/docs/connections/sources/catalog/libraries/mobile/android/#proxy-http-calls) @@ -99,7 +99,7 @@ ___ **● recordScreenViews**: *`undefined` \| `false` \| `true`* -*Defined in [analytics.ts:19](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L19)* +*Defined in analytics.ts:19* Whether the analytics client should automatically make a screen call when a view controller is added to a view hierarchy. Because the iOS underlying implementation uses method swizzling, we recommend initializing the analytics client as early as possible. @@ -112,7 +112,7 @@ ___ **● trackAppLifecycleEvents**: *`undefined` \| `false` \| `true`* -*Defined in [analytics.ts:26](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L26)* +*Defined in analytics.ts:26* Whether the analytics client should automatically track application lifecycle events, such as "Application Installed", "Application Updated" and "Application Opened". @@ -125,7 +125,7 @@ ___ **● trackAttributionData**: *`undefined` \| `false` \| `true`* -*Defined in [analytics.ts:32](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L32)* +*Defined in analytics.ts:32* Whether the analytics client should automatically track attribution data from enabled providers using the mobile service. @@ -138,7 +138,7 @@ ___ **● using**: *[Integration](../#integration)[]* -*Defined in [analytics.ts:37](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L37)* +*Defined in analytics.ts:37* Register a set of integrations to be used with this Analytics instance. diff --git a/packages/core/ios/RNAnalytics/RNAnalytics.m b/packages/core/ios/RNAnalytics/RNAnalytics.m index d4cd2079b..8b71d6f59 100644 --- a/packages/core/ios/RNAnalytics/RNAnalytics.m +++ b/packages/core/ios/RNAnalytics/RNAnalytics.m @@ -12,6 +12,8 @@ static NSMutableSet* RNAnalyticsIntegrations = nil; static NSLock* RNAnalyticsIntegrationsLock = nil; +static NSString* RNAnalyticsAdvertisingId = nil; +static BOOL RNAnalyaticsUseAdvertisingId = NO; @implementation RNAnalytics @@ -60,9 +62,14 @@ +(void)initialize { config.trackApplicationLifecycleEvents = [options[@"trackAppLifecycleEvents"] boolValue]; config.trackAttributionData = [options[@"trackAttributionData"] boolValue]; config.flushAt = [options[@"flushAt"] integerValue]; - config.enableAdvertisingTracking = [options[@"ios"][@"trackAdvertising"] boolValue]; + config.enableAdvertisingTracking = RNAnalyaticsUseAdvertisingId = [options[@"ios"][@"trackAdvertising"] boolValue]; config.defaultSettings = options[@"defaultProjectSettings"]; + // set this block regardless. the data will come in after the fact most likely. + config.adSupportBlock = ^NSString * _Nonnull{ + return RNAnalyticsAdvertisingId; + }; + if ([options valueForKey:@"proxy"]) { NSDictionary *proxyOptions = (NSDictionary *)[options valueForKey:@"proxy"]; @@ -124,6 +131,10 @@ - (NSDictionary*)withContextAndIntegrations :(NSDictionary*)context :(NSDictiona return @{ @"context": context, @"integrations": integrations ?: @{}}; } +RCT_EXPORT_METHOD(setIDFA:(NSString *)idfa) { + RNAnalyticsAdvertisingId = idfa; +} + RCT_EXPORT_METHOD(track:(NSString*)name :(NSDictionary*)properties :(NSDictionary*)integrations :(NSDictionary*)context) { [SEGAnalytics.sharedAnalytics track:name diff --git a/packages/core/src/__tests__/configuration.spec.ts b/packages/core/src/__tests__/configuration.spec.ts index aa5ca44e0..4590e893b 100644 --- a/packages/core/src/__tests__/configuration.spec.ts +++ b/packages/core/src/__tests__/configuration.spec.ts @@ -27,7 +27,7 @@ it('uses the default configuration', async () => { flushInterval: undefined }, ios: { - trackAdvertising: true, + trackAdvertising: false, trackDeepLinks: false } }) @@ -54,7 +54,7 @@ it('produces a valid configuration', async () => { flushInterval: 72 }, ios: { - trackAdvertising: true, + trackAdvertising: false, trackDeepLinks: true } }) @@ -80,7 +80,7 @@ it('produces a valid configuration', async () => { flushInterval: 72 }, ios: { - trackAdvertising: true, + trackAdvertising: false, trackDeepLinks: true } }) diff --git a/packages/core/src/analytics.ts b/packages/core/src/analytics.ts index b8a1ec405..c51a7a4b9 100644 --- a/packages/core/src/analytics.ts +++ b/packages/core/src/analytics.ts @@ -164,6 +164,14 @@ export module Analytics { return this } + + /** + * Sets the IDFA value on iOS. Customers are now responsible for collecting + * IDFA on their own. + */ + public setIDFA(idfa: string) { + Bridge.setIDFA(idfa) + } /** * Append a new middleware to the middleware chain. diff --git a/packages/core/src/bridge.ts b/packages/core/src/bridge.ts index 64244983b..ae39401eb 100644 --- a/packages/core/src/bridge.ts +++ b/packages/core/src/bridge.ts @@ -81,6 +81,7 @@ export interface Bridge { enable(): Promise disable(): Promise getAnonymousId(): Promise + setIDFA(idfa: string): Promise } const bridge: Bridge = NativeModules.RNAnalytics diff --git a/packages/core/src/configuration.ts b/packages/core/src/configuration.ts index af3485666..5a8746dae 100644 --- a/packages/core/src/configuration.ts +++ b/packages/core/src/configuration.ts @@ -10,7 +10,7 @@ const defaults = { flushInterval }), ios: ({ - trackAdvertising = true, + trackAdvertising = false, trackDeepLinks = false }: Partial) => ({ trackAdvertising, @@ -28,7 +28,7 @@ export const configure = async ( trackAttributionData = false, using = [], defaultProjectSettings = {}, - proxy = undefined, + proxy, ios = {}, android = {} @@ -44,11 +44,11 @@ export const configure = async ( debug, defaultProjectSettings, flushAt, + proxy, recordScreenViews, trackAppLifecycleEvents, trackAttributionData, writeKey, - proxy, android: defaults.android(android), ios: defaults.ios(ios) diff --git a/packages/test-app/patches/TestApp.xcodeproj/Podfile b/packages/test-app/patches/TestApp.xcodeproj/Podfile new file mode 100644 index 000000000..4f13affb7 --- /dev/null +++ b/packages/test-app/patches/TestApp.xcodeproj/Podfile @@ -0,0 +1,68 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '11.0' + +target 'TestApp' do + # Uncomment the next line if you're using Swift or would like to use dynamic frameworks + # use_frameworks! + + # Pods for TestApp + pod 'Analytics', '~> 4.0.4' + pod 'RNAnalytics', :path => "../node_modules/@segment/analytics-react-native" + + # Device Mode Integrations (iOS only of course) + pod 'RNAnalyticsIntegration-Adjust', :path => "../node_modules/@segment/analytics-react-native-adjust" + pod 'RNAnalyticsIntegration-Amplitude', :path => "../node_modules/@segment/analytics-react-native-amplitude" + pod 'RNAnalyticsIntegration-Appboy', :path => "../node_modules/@segment/analytics-react-native-appboy" + pod 'RNAnalyticsIntegration-AppsFlyer', :path => "../node_modules/@segment/analytics-react-native-appsflyer" + pod 'RNAnalyticsIntegration-Branch', :path => "../node_modules/@segment/analytics-react-native-branch" + pod 'RNAnalyticsIntegration-Bugsnag', :path => "../node_modules/@segment/analytics-react-native-bugsnag" + pod 'RNAnalyticsIntegration-CleverTap', :path => "../node_modules/@segment/analytics-react-native-clevertap" + pod 'RNAnalyticsIntegration-ComScore', :path => "../node_modules/@segment/analytics-react-native-comscore-ios" + pod 'RNAnalyticsIntegration-Countly', :path => "../node_modules/@segment/analytics-react-native-countly" + pod 'RNAnalyticsIntegration-Crittercism', :path => "../node_modules/@segment/analytics-react-native-crittercism" + pod 'RNAnalyticsIntegration-Facebook-App-Events', :path => "../node_modules/@segment/analytics-react-native-facebook-app-events-ios" + pod 'RNAnalyticsIntegration-Firebase', :path => "../node_modules/@segment/analytics-react-native-firebase" + pod 'RNAnalyticsIntegration-Flurry', :path => "../node_modules/@segment/analytics-react-native-flurry" + pod 'RNAnalyticsIntegration-Google-Analytics', :path => "../node_modules/@segment/analytics-react-native-google-analytics" + pod 'RNAnalyticsIntegration-Intercom', :path => "../node_modules/@segment/analytics-react-native-intercom" + pod 'RNAnalyticsIntegration-Localytics', :path => "../node_modules/@segment/analytics-react-native-localytics" + pod 'RNAnalyticsIntegration-Mixpanel', :path => "../node_modules/@segment/analytics-react-native-mixpanel" + pod 'RNAnalyticsIntegration-Taplytics', :path => "../node_modules/@segment/analytics-react-native-taplytics-ios" + + # Your 'node_modules' directory is probably in the root of your project, + # but if not, adjust the `:path` accordingly + + pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector" + pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec" + pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired" + pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety" + pod 'React', :path => '../node_modules/react-native/' + pod 'React-Core', :path => '../node_modules/react-native/' + pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules' + pod 'React-Core/DevSupport', :path => '../node_modules/react-native/' + pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS' + pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation' + pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob' + pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image' + pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS' + pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network' + pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings' + pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text' + pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration' + pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/' + + pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact' + pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi' + pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor' + pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector' + pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon" + pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon" + + # Explicitly include Yoga if you are using RN >= 0.42.0 + pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga' + + # Third party deps podspec link + pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' + pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' + pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' +end