Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,126 @@ This way config files will be copied on `cordova prepare` step.

## Functions

### initiateOnDeviceConversionMeasurementWithEmail

**initiateOnDeviceConversionMeasurementWithEmail**(`emailAddress`): `Promise`<`void`\>

Initiates on-device conversion measurement with the given email address.

**`Throws`**

This feature is only available on iOS

**`Example`**

```ts
cordova.plugins.firebase.analytics.initiateOnDeviceConversionMeasurementWithEmail("[email protected]");
```

#### Parameters

| Name | Type | Description |
| :------ | :------ | :------ |
| `emailAddress` | `string` | Email address to use for conversion measurement |

#### Returns

`Promise`<`void`\>

Callback when operation is completed

___

### initiateOnDeviceConversionMeasurementWithHashedEmail

**initiateOnDeviceConversionMeasurementWithHashedEmail**(`emailToHash`): `Promise`<`void`\>

Initiates on-device conversion measurement with the given hashed email address.

**`Throws`**

This feature is only available on iOS

**`Example`**

```ts
cordova.plugins.firebase.analytics.initiateOnDeviceConversionMeasurementWithHashedEmail("hashedEmail");
```

#### Parameters

| Name | Type | Description |
| :------ | :------ | :------ |
| `emailToHash` | `string` | SHA256 hashed email address to use for conversion measurement |

#### Returns

`Promise`<`void`\>

Callback when operation is completed

___

### initiateOnDeviceConversionMeasurementWithHashedPhoneNumber

**initiateOnDeviceConversionMeasurementWithHashedPhoneNumber**(`phoneToHash`): `Promise`<`void`\>

Initiates on-device conversion measurement with the given hashed phone number.

**`Throws`**

This feature is only available on iOS

**`Example`**

```ts
cordova.plugins.firebase.analytics.initiateOnDeviceConversionMeasurementWithHashedPhoneNumber("hashedPhoneNumber");
```

#### Parameters

| Name | Type | Description |
| :------ | :------ | :------ |
| `phoneToHash` | `string` | SHA256 hashed phone number to use for conversion measurement |

#### Returns

`Promise`<`void`\>

Callback when operation is completed

___

### initiateOnDeviceConversionMeasurementWithPhoneNumber

**initiateOnDeviceConversionMeasurementWithPhoneNumber**(`phoneNumber`): `Promise`<`void`\>

Initiates on-device conversion measurement with the given phone number.

**`Throws`**

This feature is only available on iOS

**`Example`**

```ts
cordova.plugins.firebase.analytics.initiateOnDeviceConversionMeasurementWithPhoneNumber("+15555555555");
```

#### Parameters

| Name | Type | Description |
| :------ | :------ | :------ |
| `phoneNumber` | `string` | Phone number to use for conversion measurement (must be in E.164 format) |

#### Returns

`Promise`<`void`\>

Callback when operation is completed

___

### logEvent

**logEvent**(`name`, `params`): `Promise`<`void`\>
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"postdocs": "perl -i -0pe 's/(<!-- TypedocGenerated -->).*/$1\n\n/gms' README.md && cat ./docs/README.md >> README.md"
},
"devDependencies": {
"typedoc": "^0.23.9",
"typedoc-plugin-markdown": "^3.13.4"
"typedoc": "^0.23.28",
"typedoc-plugin-markdown": "^3.14.0"
}
}
3 changes: 2 additions & 1 deletion plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"
<preference name="AUTOMATIC_SCREEN_REPORTING_ENABLED" default="true" />

<platform name="ios">
<preference name="IOS_FIREBASE_POD_VERSION" default="10.17.0" />
<preference name="IOS_FIREBASE_POD_VERSION" default="10.22.0" />

<config-file target="config.xml" parent="/*">
<feature name="FirebaseAnalytics">
Expand Down Expand Up @@ -53,6 +53,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"
</config>
<pods use-frameworks="true">
<pod name="Firebase/Analytics" spec="$IOS_FIREBASE_POD_VERSION" />
<pod name="GoogleAppMeasurementOnDeviceConversion" spec="$IOS_FIREBASE_POD_VERSION" />
</pods>
</podspec>
</platform>
Expand Down
20 changes: 20 additions & 0 deletions src/android/FirebaseAnalyticsPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,26 @@ protected void setDefaultEventParameters(CordovaArgs args, CallbackContext callb
callbackContext.success();
}

@CordovaMethod
protected void initiateOnDeviceConversionMeasurementWithEmail(CordovaArgs args, CallbackContext callbackContext) throws JSONException {
callbackContext.error("This feature is only available on iOS");
}

@CordovaMethod
protected void initiateOnDeviceConversionMeasurementWithPhoneNumber(CordovaArgs args, CallbackContext callbackContext) throws JSONException {
callbackContext.error("This feature is only available on iOS");
}

@CordovaMethod
protected void initiateOnDeviceConversionMeasurementWithHashedEmail(CordovaArgs args, CallbackContext callbackContext) throws JSONException {
callbackContext.error("This feature is only available on iOS");
}

@CordovaMethod
protected void initiateOnDeviceConversionMeasurementWithHashedPhoneNumber(CordovaArgs args, CallbackContext callbackContext) throws JSONException {
callbackContext.error("This feature is only available on iOS");
}

private static Bundle parse(JSONObject params) throws JSONException {
Bundle bundle = new Bundle();
Iterator<String> it = params.keys();
Expand Down
5 changes: 4 additions & 1 deletion src/ios/FirebaseAnalyticsPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
- (void)setCurrentScreen:(CDVInvokedUrlCommand*)command;
- (void)resetAnalyticsData:(CDVInvokedUrlCommand*)command;
- (void)setDefaultEventParameters:(CDVInvokedUrlCommand*)command;

- (void)initiateOnDeviceConversionMeasurementWithEmail:(CDVInvokedUrlCommand*)command;
- (void)initiateOnDeviceConversionMeasurementWithPhoneNumber:(CDVInvokedUrlCommand*)command;
- (void)initiateOnDeviceConversionMeasurementWithHashedEmail:(CDVInvokedUrlCommand*)command;
- (void)initiateOnDeviceConversionMeasurementWithHashedPhoneNumber:(CDVInvokedUrlCommand*)command;
@end
137 changes: 137 additions & 0 deletions src/ios/FirebaseAnalyticsPlugin.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "FirebaseAnalyticsPlugin.h"
#import <CommonCrypto/CommonDigest.h>

@import FirebaseCore;
@import FirebaseAnalytics;
Expand Down Expand Up @@ -78,4 +79,140 @@ - (void)setDefaultEventParameters:(CDVInvokedUrlCommand *)command {
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

- (void)initiateOnDeviceConversionMeasurementWithEmail:(CDVInvokedUrlCommand *)command {
if ([command.arguments count] == 0) {
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Email address is required"];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

NSString* emailAddress = [command.arguments objectAtIndex:0];

if (![self isValidEmail:emailAddress]) {
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid email address format"];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

[FIRAnalytics initiateOnDeviceConversionMeasurementWithEmailAddress:emailAddress];

CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

- (void)initiateOnDeviceConversionMeasurementWithPhoneNumber:(CDVInvokedUrlCommand *)command {
if ([command.arguments count] == 0) {
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Phone number is required"];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

NSString* phoneNumber = [command.arguments objectAtIndex:0];

if (![self isValidE164PhoneNumber:phoneNumber]) {
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid phone number format. Must be in E.164 format (e.g., +15555555555)"];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

[FIRAnalytics initiateOnDeviceConversionMeasurementWithPhoneNumber:phoneNumber];

CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

- (void)initiateOnDeviceConversionMeasurementWithHashedEmail:(CDVInvokedUrlCommand *)command {
if ([command.arguments count] == 0) {
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Email address is required"];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

NSString* emailAddress = [command.arguments objectAtIndex:0];

if (![self isValidEmail:emailAddress]) {
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid email address format"];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

NSString* normalizedEmail = [self normalizeEmail:emailAddress];
NSData* hashedEmail = [self sha256:normalizedEmail];

[FIRAnalytics initiateOnDeviceConversionMeasurementWithHashedEmailAddress:hashedEmail];

CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

- (void)initiateOnDeviceConversionMeasurementWithHashedPhoneNumber:(CDVInvokedUrlCommand *)command {
if ([command.arguments count] == 0) {
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Phone number is required"];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

NSString* phoneNumber = [command.arguments objectAtIndex:0];

if (![self isValidE164PhoneNumber:phoneNumber]) {
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid phone number format. Must be in E.164 format (e.g., +15555555555)"];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

NSData* hashedPhoneNumber = [self sha256:phoneNumber];

[FIRAnalytics initiateOnDeviceConversionMeasurementWithHashedPhoneNumber:hashedPhoneNumber];

CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

- (NSString*)normalizeEmail:(NSString*)email {
NSString* normalized = [email lowercaseString];

if ([normalized hasSuffix:@"@googlemail.com"]) {
normalized = [normalized stringByReplacingOccurrencesOfString:@"@googlemail.com" withString:@"@gmail.com"];
}

if ([normalized hasSuffix:@"@gmail.com"]) {
NSArray* components = [normalized componentsSeparatedByString:@"@"];
if ([components count] == 2) {
NSString* username = components[0];

username = [username stringByReplacingOccurrencesOfString:@"." withString:@""];

username = [username stringByReplacingOccurrencesOfString:@"i" withString:@"l"];
username = [username stringByReplacingOccurrencesOfString:@"I" withString:@"l"];
username = [username stringByReplacingOccurrencesOfString:@"1" withString:@"l"];
username = [username stringByReplacingOccurrencesOfString:@"0" withString:@"o"];
username = [username stringByReplacingOccurrencesOfString:@"2" withString:@"z"];
username = [username stringByReplacingOccurrencesOfString:@"5" withString:@"s"];

normalized = [NSString stringWithFormat:@"%@@gmail.com", username];
}
}

return normalized;
}

- (BOOL)isValidEmail:(NSString*)email {
NSString* pattern = @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
return [predicate evaluateWithObject:email];
}

- (BOOL)isValidE164PhoneNumber:(NSString*)phoneNumber {
NSString* pattern = @"^\\+[0-9]{1,3}[0-9]{1,12}$";
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
return [predicate evaluateWithObject:phoneNumber];
}

- (NSData*)sha256:(NSString*)string {
const char* str = [string UTF8String];
unsigned char result[32];
CC_SHA256(str, (CC_LONG)strlen(str), result);
return [NSData dataWithBytes:result length:32];
}

@end
40 changes: 40 additions & 0 deletions types/FirebaseAnalytics.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,43 @@ export function setCurrentScreen(screenName: string): Promise<void>;
* cordova.plugins.firebase.analytics.setDefaultEventParameters({foo: "bar"});
*/
export function setDefaultEventParameters(defaults: Record<string, number | string | Array<object>>): Promise<void>;
/**
* Initiates on-device conversion measurement with the given email address.
* @param {string} emailAddress Email address to use for conversion measurement
* @returns {Promise<void>} Callback when operation is completed
* @throws {Error} This feature is only available on iOS
*
* @example
* cordova.plugins.firebase.analytics.initiateOnDeviceConversionMeasurementWithEmail("[email protected]");
*/
export function initiateOnDeviceConversionMeasurementWithEmail(emailAddress: string): Promise<void>;
/**
* Initiates on-device conversion measurement with the given phone number.
* @param {string} phoneNumber Phone number to use for conversion measurement (must be in E.164 format)
* @returns {Promise<void>} Callback when operation is completed
* @throws {Error} This feature is only available on iOS
*
* @example
* cordova.plugins.firebase.analytics.initiateOnDeviceConversionMeasurementWithPhoneNumber("+15555555555");
*/
export function initiateOnDeviceConversionMeasurementWithPhoneNumber(phoneNumber: string): Promise<void>;
/**
* Initiates on-device conversion measurement with the given hashed email address.
* @param {string} emailToHash SHA256 hashed email address to use for conversion measurement
* @returns {Promise<void>} Callback when operation is completed
* @throws {Error} This feature is only available on iOS
*
* @example
* cordova.plugins.firebase.analytics.initiateOnDeviceConversionMeasurementWithHashedEmail("hashedEmail");
*/
export function initiateOnDeviceConversionMeasurementWithHashedEmail(emailToHash: string): Promise<void>;
/**
* Initiates on-device conversion measurement with the given hashed phone number.
* @param {string} phoneToHash SHA256 hashed phone number to use for conversion measurement
* @returns {Promise<void>} Callback when operation is completed
* @throws {Error} This feature is only available on iOS
*
* @example
* cordova.plugins.firebase.analytics.initiateOnDeviceConversionMeasurementWithHashedPhoneNumber("hashedPhoneNumber");
*/
export function initiateOnDeviceConversionMeasurementWithHashedPhoneNumber(phoneToHash: string): Promise<void>;
Loading