@suro4ek/appmetrica-push-sdk 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AppMetricaPushSDK.podspec +28 -0
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/android/.gradle/9.0.0/checksums/checksums.lock +0 -0
- package/android/.gradle/9.0.0/fileChanges/last-build.bin +0 -0
- package/android/.gradle/9.0.0/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/9.0.0/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/9.0.0/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build.gradle +44 -0
- package/android/settings.gradle +1 -0
- package/android/src/main/java/com/appmetricapush/AppMetricaPushModule.kt +176 -0
- package/android/src/main/java/com/appmetricapush/AppMetricaPushPackage.kt +21 -0
- package/android/src/main/java/com/appmetricapush/FirebaseMessagingMainService.kt +79 -0
- package/android/src/main/java/com/appmetricapush/SilentPushReceiver.kt +53 -0
- package/docs/ANALYTICS_GUIDE.md +68 -0
- package/docs/INTEGRATION_GUIDE.md +652 -0
- package/docs/IOS_APNS_SETUP.md +95 -0
- package/ios/AppMetricaPushSDK/AppMetricaPushModule.m +25 -0
- package/ios/AppMetricaPushSDK/AppMetricaPushModule.swift +200 -0
- package/package.json +75 -0
- package/src/AppMetricaPushModule.ts +188 -0
- package/src/hooks/useAppMetricaPush.ts +81 -0
- package/src/index.ts +24 -0
- package/src/types/index.ts +35 -0
- package/src/utils/index.ts +121 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Настройка APNS токена для iOS
|
|
2
|
+
|
|
3
|
+
## Обязательные требования
|
|
4
|
+
|
|
5
|
+
Для корректной работы AppMetrica Push SDK на iOS **обязательно** необходимо передавать APNS токен при инициализации.
|
|
6
|
+
|
|
7
|
+
## Установка зависимостей
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @react-native-firebase/messaging
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Получение APNS токена
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { Platform } from "react-native";
|
|
17
|
+
import { getAPNSToken, getMessaging } from "@react-native-firebase/messaging";
|
|
18
|
+
|
|
19
|
+
// Получение APNS токена для iOS
|
|
20
|
+
let apnsToken = "";
|
|
21
|
+
if (Platform.OS === "ios") {
|
|
22
|
+
const messaging = getMessaging();
|
|
23
|
+
apnsToken = (await getAPNSToken(messaging)) ?? "";
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Инициализация с APNS токеном
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { AppMetricaPush } from "@suro4ek/appmetrica-push-sdk";
|
|
31
|
+
|
|
32
|
+
await AppMetricaPush.initialize({
|
|
33
|
+
debugMode: __DEV__,
|
|
34
|
+
apnsToken: Platform.OS === "ios" ? apnsToken : undefined,
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Полный пример
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { Platform } from "react-native";
|
|
42
|
+
import { getAPNSToken, getMessaging } from "@react-native-firebase/messaging";
|
|
43
|
+
import { AppMetricaPush } from "@suro4ek/appmetrica-push-sdk";
|
|
44
|
+
|
|
45
|
+
const initializeAppMetricaPush = async () => {
|
|
46
|
+
try {
|
|
47
|
+
// Получение APNS токена для iOS
|
|
48
|
+
let apnsToken = "";
|
|
49
|
+
if (Platform.OS === "ios") {
|
|
50
|
+
const messaging = getMessaging();
|
|
51
|
+
apnsToken = (await getAPNSToken(messaging)) ?? "";
|
|
52
|
+
|
|
53
|
+
if (!apnsToken) {
|
|
54
|
+
console.error("Failed to get APNS token for iOS");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Инициализация AppMetrica Push SDK
|
|
60
|
+
const result = await AppMetricaPush.initialize({
|
|
61
|
+
debugMode: __DEV__,
|
|
62
|
+
apnsToken: Platform.OS === "ios" ? apnsToken : undefined,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (result.success) {
|
|
66
|
+
console.log("AppMetrica Push SDK initialized successfully");
|
|
67
|
+
} else {
|
|
68
|
+
console.error("Failed to initialize AppMetrica Push SDK:", result.error);
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error("Error initializing AppMetrica Push SDK:", error);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Важные моменты
|
|
77
|
+
|
|
78
|
+
1. **APNS токен обязателен для iOS** - без него push уведомления не будут работать
|
|
79
|
+
2. **Android не требует APNS токен** - для Android используется Firebase Cloud Messaging
|
|
80
|
+
3. **Токен получается асинхронно** - используйте `await` при получении токена
|
|
81
|
+
4. **Проверяйте наличие токена** - убедитесь, что токен получен перед инициализацией
|
|
82
|
+
|
|
83
|
+
## Troubleshooting
|
|
84
|
+
|
|
85
|
+
### APNS токен не получается
|
|
86
|
+
|
|
87
|
+
1. Убедитесь, что Firebase настроен правильно
|
|
88
|
+
2. Проверьте, что приложение имеет разрешения на push уведомления
|
|
89
|
+
3. Убедитесь, что используется правильный bundle ID в Firebase консоли
|
|
90
|
+
|
|
91
|
+
### Push уведомления не приходят на iOS
|
|
92
|
+
|
|
93
|
+
1. Проверьте, что APNS токен передается при инициализации
|
|
94
|
+
2. Убедитесь, что сертификаты APNS настроены правильно
|
|
95
|
+
3. Проверьте, что AppMetrica Push SDK инициализирован успешно
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#import <React/RCTBridgeModule.h>
|
|
2
|
+
|
|
3
|
+
@interface RCT_EXTERN_MODULE(AppMetricaPushModule, NSObject)
|
|
4
|
+
|
|
5
|
+
RCT_EXTERN_METHOD(initialize:(NSDictionary *)config
|
|
6
|
+
resolver:(RCTPromiseResolveBlock)resolver
|
|
7
|
+
rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
8
|
+
|
|
9
|
+
RCT_EXTERN_METHOD(isNotificationFromAppMetrica:(NSDictionary *)notification
|
|
10
|
+
resolver:(RCTPromiseResolveBlock)resolver
|
|
11
|
+
rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
12
|
+
|
|
13
|
+
RCT_EXTERN_METHOD(getSDKInfo:(RCTPromiseResolveBlock)resolver
|
|
14
|
+
rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
15
|
+
|
|
16
|
+
RCT_EXTERN_METHOD(getUserData:(NSDictionary *)notification
|
|
17
|
+
resolver:(RCTPromiseResolveBlock)resolver
|
|
18
|
+
rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
19
|
+
|
|
20
|
+
RCT_EXTERN_METHOD(registerDeviceToken:(NSString *)deviceToken
|
|
21
|
+
resolver:(RCTPromiseResolveBlock)resolver
|
|
22
|
+
rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@end
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import React
|
|
3
|
+
import AppMetricaPush
|
|
4
|
+
import UserNotifications
|
|
5
|
+
|
|
6
|
+
// MARK: - Data Extension for Hex String
|
|
7
|
+
extension Data {
|
|
8
|
+
init?(hexString: String) {
|
|
9
|
+
let len = hexString.count / 2
|
|
10
|
+
var data = Data(capacity: len)
|
|
11
|
+
var i = hexString.startIndex
|
|
12
|
+
for _ in 0..<len {
|
|
13
|
+
let j = hexString.index(i, offsetBy: 2)
|
|
14
|
+
let bytes = hexString[i..<j]
|
|
15
|
+
if var num = UInt8(bytes, radix: 16) {
|
|
16
|
+
data.append(&num, count: 1)
|
|
17
|
+
} else {
|
|
18
|
+
return nil
|
|
19
|
+
}
|
|
20
|
+
i = j
|
|
21
|
+
}
|
|
22
|
+
self = data
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@objc(AppMetricaPushModule)
|
|
28
|
+
class AppMetricaPushModule: NSObject, RCTBridgeModule {
|
|
29
|
+
|
|
30
|
+
// MARK: - RCTBridgeModule Protocol
|
|
31
|
+
|
|
32
|
+
static func requiresMainQueueSetup() -> Bool {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static func moduleName() -> String! {
|
|
37
|
+
return "AppMetricaPushModule"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// MARK: - Initialization (Deprecated - use AppMetricaPushInitializer)
|
|
41
|
+
|
|
42
|
+
@objc
|
|
43
|
+
func initialize(_ config: NSDictionary, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
44
|
+
let debugMode = config["debugMode"] as? Bool ?? false
|
|
45
|
+
|
|
46
|
+
DispatchQueue.main.async {
|
|
47
|
+
do {
|
|
48
|
+
// Выполняем инициализацию
|
|
49
|
+
self.performInitialization(config: config, debugMode: debugMode, resolver: resolver, rejecter: rejecter)
|
|
50
|
+
|
|
51
|
+
} catch {
|
|
52
|
+
rejecter("INIT_ERROR", error.localizedDescription, error)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private func performInitialization(config: NSDictionary, debugMode: Bool, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
58
|
+
do {
|
|
59
|
+
// 1. Настройка App Group если указан
|
|
60
|
+
if let appGroup = config["appGroup"] as? String {
|
|
61
|
+
AppMetricaPush.setExtensionAppGroup(appGroup)
|
|
62
|
+
if debugMode {
|
|
63
|
+
print("App Group set: \(appGroup)")
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 2. Инициализация AppMetrica Push SDK
|
|
68
|
+
AppMetricaPush.handleApplicationDidFinishLaunching(options: nil)
|
|
69
|
+
|
|
70
|
+
// 3. Настройка push-уведомлений
|
|
71
|
+
self.setupPushNotifications(debugMode: debugMode)
|
|
72
|
+
|
|
73
|
+
// 4. Настройка автоматической обработки push-уведомлений
|
|
74
|
+
self.setupNotificationDelegate()
|
|
75
|
+
|
|
76
|
+
if debugMode {
|
|
77
|
+
print("AppMetrica Push SDK initialized successfully from TypeScript")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
resolver(true)
|
|
81
|
+
} catch {
|
|
82
|
+
rejecter("INIT_ERROR", error.localizedDescription, error)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Настройка push-уведомлений
|
|
88
|
+
*/
|
|
89
|
+
private func setupPushNotifications(debugMode: Bool) {
|
|
90
|
+
let center = UNUserNotificationCenter.current()
|
|
91
|
+
center.requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in
|
|
92
|
+
if granted {
|
|
93
|
+
DispatchQueue.main.async {
|
|
94
|
+
UIApplication.shared.registerForRemoteNotifications()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if debugMode {
|
|
98
|
+
print("Push notification permissions granted")
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
if debugMode {
|
|
102
|
+
print("Push notification permissions denied")
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Настройка автоматической обработки push-уведомлений
|
|
110
|
+
*/
|
|
111
|
+
private func setupNotificationDelegate() {
|
|
112
|
+
let delegate = AppMetricaPush.userNotificationCenterDelegate
|
|
113
|
+
UNUserNotificationCenter.current().delegate = delegate
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// MARK: - Notification Analysis
|
|
118
|
+
|
|
119
|
+
@objc
|
|
120
|
+
func isNotificationFromAppMetrica(_ notification: NSDictionary, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
121
|
+
DispatchQueue.main.async {
|
|
122
|
+
// Преобразуем NSDictionary в [AnyHashable : Any] для AppMetrica
|
|
123
|
+
let notificationDict = notification as? [AnyHashable : Any] ?? [:]
|
|
124
|
+
let isRelatedToAppMetricaSDK = AppMetricaPush.isNotificationRelated(toSDK: notificationDict)
|
|
125
|
+
resolver(isRelatedToAppMetricaSDK)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// MARK: - SDK Information
|
|
130
|
+
|
|
131
|
+
@objc
|
|
132
|
+
func getSDKInfo(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
133
|
+
DispatchQueue.main.async {
|
|
134
|
+
let info: [String: Any] = [
|
|
135
|
+
"version": "3.2.0",
|
|
136
|
+
"platform": "ios",
|
|
137
|
+
"sdkName": "AppMetrica Push SDK",
|
|
138
|
+
"libraryVersion": "1.0.0"
|
|
139
|
+
]
|
|
140
|
+
resolver(info)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// MARK: - User Data Extraction
|
|
145
|
+
|
|
146
|
+
@objc
|
|
147
|
+
func getUserData(_ notification: NSDictionary, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
148
|
+
DispatchQueue.main.async {
|
|
149
|
+
// Преобразуем NSDictionary в [AnyHashable : Any] для AppMetrica
|
|
150
|
+
let notificationDict = notification as? [AnyHashable : Any] ?? [:]
|
|
151
|
+
// Получаем дополнительную информацию из push-уведомления согласно документации
|
|
152
|
+
let userData = AppMetricaPush.userData(forNotification: notificationDict)
|
|
153
|
+
resolver(userData)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// MARK: - Device Token Registration
|
|
158
|
+
|
|
159
|
+
@objc
|
|
160
|
+
func registerDeviceToken(_ deviceToken: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
161
|
+
DispatchQueue.main.async {
|
|
162
|
+
do {
|
|
163
|
+
// Преобразуем строку в Data
|
|
164
|
+
// Пробуем сначала как hex строку, потом как обычную строку
|
|
165
|
+
var tokenData: Data?
|
|
166
|
+
|
|
167
|
+
// Пробуем как hex строку (APNs токен)
|
|
168
|
+
if deviceToken.count % 2 == 0 && deviceToken.allSatisfy({ $0.isHexDigit }) {
|
|
169
|
+
tokenData = Data(hexString: deviceToken)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Если не получилось как hex, пробуем как обычную строку
|
|
173
|
+
if tokenData == nil {
|
|
174
|
+
tokenData = deviceToken.data(using: .utf8)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
guard let finalTokenData = tokenData else {
|
|
178
|
+
rejecter("INVALID_TOKEN", "Failed to convert device token to Data", nil)
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Определяем окружение для APNs
|
|
183
|
+
#if DEBUG
|
|
184
|
+
let pushEnvironment = AppMetricaPushEnvironment.development
|
|
185
|
+
#else
|
|
186
|
+
let pushEnvironment = AppMetricaPushEnvironment.production
|
|
187
|
+
#endif
|
|
188
|
+
|
|
189
|
+
AppMetricaPush.setDeviceTokenFrom(finalTokenData, pushEnvironment: pushEnvironment)
|
|
190
|
+
|
|
191
|
+
resolver(true)
|
|
192
|
+
} catch {
|
|
193
|
+
rejecter("REGISTRATION_ERROR", error.localizedDescription, error)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
}
|
|
200
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@suro4ek/appmetrica-push-sdk",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "React Native library for Yandex AppMetrica Push SDK",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
11
|
+
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
12
|
+
"prepublishOnly": "npm run lint",
|
|
13
|
+
"publish:patch": "./scripts/publish.sh patch",
|
|
14
|
+
"publish:minor": "./scripts/publish.sh minor",
|
|
15
|
+
"publish:major": "./scripts/publish.sh major",
|
|
16
|
+
"publish:check": "npm pack --dry-run"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"react-native",
|
|
20
|
+
"appmetrica",
|
|
21
|
+
"push",
|
|
22
|
+
"notifications",
|
|
23
|
+
"yandex",
|
|
24
|
+
"analytics"
|
|
25
|
+
],
|
|
26
|
+
"author": "Artem Egorov",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/suro4ek/appmetrica-push-sdk.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/suro4ek/appmetrica-push-sdk/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/suro4ek/appmetrica-push-sdk#readme",
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/react": "^18.0.0",
|
|
38
|
+
"@types/react-native": "^0.72.0",
|
|
39
|
+
"typescript": "^5.0.0",
|
|
40
|
+
"eslint": "^8.0.0",
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
42
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
43
|
+
"jest": "^29.0.0",
|
|
44
|
+
"@types/jest": "^29.0.0"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"react-native-permissions": "^3.0.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"react": ">=16.8.0",
|
|
51
|
+
"react-native": ">=0.60.0",
|
|
52
|
+
"@react-native-firebase/messaging": ">=18.0.0",
|
|
53
|
+
"@appmetrica/react-native-analytics": ">=3.5.0"
|
|
54
|
+
},
|
|
55
|
+
"react-native": {
|
|
56
|
+
"android": {
|
|
57
|
+
"sourceDir": "android",
|
|
58
|
+
"packageImportPath": "import com.appmetricapush.AppMetricaPushPackage;"
|
|
59
|
+
},
|
|
60
|
+
"ios": {
|
|
61
|
+
"sharedLibraries": [
|
|
62
|
+
"libc++"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"files": [
|
|
67
|
+
"src",
|
|
68
|
+
"android",
|
|
69
|
+
"ios",
|
|
70
|
+
"AppMetricaPushSDK.podspec",
|
|
71
|
+
"README.md",
|
|
72
|
+
"LICENSE",
|
|
73
|
+
"docs"
|
|
74
|
+
]
|
|
75
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { NativeModules, Platform } from "react-native";
|
|
2
|
+
import { PushConfig, SDKInfo, InitializationResult } from "./types";
|
|
3
|
+
|
|
4
|
+
const { AppMetricaPushModule } = NativeModules;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Основной класс для работы с AppMetrica Push SDK
|
|
8
|
+
*/
|
|
9
|
+
class AppMetricaPush {
|
|
10
|
+
private static instance: AppMetricaPush | null = null;
|
|
11
|
+
private isInitialized = false;
|
|
12
|
+
private config: PushConfig | null = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Получить экземпляр класса (Singleton)
|
|
16
|
+
*/
|
|
17
|
+
static getInstance(): AppMetricaPush {
|
|
18
|
+
if (!AppMetricaPush.instance) {
|
|
19
|
+
AppMetricaPush.instance = new AppMetricaPush();
|
|
20
|
+
}
|
|
21
|
+
return AppMetricaPush.instance;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Инициализация AppMetrica Push SDK
|
|
26
|
+
*/
|
|
27
|
+
async initialize(config: PushConfig): Promise<InitializationResult> {
|
|
28
|
+
try {
|
|
29
|
+
if (!AppMetricaPushModule) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"AppMetricaPushModule is not available. Make sure the native module is properly linked."
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (this.isInitialized) {
|
|
36
|
+
console.warn("AppMetrica Push SDK is already initialized");
|
|
37
|
+
return { success: true };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const result = await AppMetricaPushModule.initialize(config);
|
|
41
|
+
|
|
42
|
+
if (result) {
|
|
43
|
+
this.isInitialized = true;
|
|
44
|
+
this.config = config;
|
|
45
|
+
|
|
46
|
+
if (config.debugMode) {
|
|
47
|
+
console.log("AppMetrica Push SDK initialized successfully");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Автоматически регистрируем APNs device token для iOS если предоставлен
|
|
51
|
+
// APNs токен должен быть получен через messaging.getAPNSToken()
|
|
52
|
+
if (config.apnsToken && Platform.OS === "ios") {
|
|
53
|
+
await this.registerDeviceToken(config.apnsToken);
|
|
54
|
+
|
|
55
|
+
if (config.debugMode) {
|
|
56
|
+
console.log("APNs device token registered for iOS");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { success: true };
|
|
61
|
+
} else {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: "Failed to initialize AppMetrica Push SDK",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const errorMessage =
|
|
69
|
+
error instanceof Error ? error.message : "Unknown error";
|
|
70
|
+
console.error("AppMetrica Push initialization failed:", errorMessage);
|
|
71
|
+
return { success: false, error: errorMessage };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Проверка, что уведомление от AppMetrica
|
|
77
|
+
* Используется в собственных сервисах обработки push уведомлений
|
|
78
|
+
*/
|
|
79
|
+
async isNotificationFromAppMetrica(notification: any): Promise<boolean> {
|
|
80
|
+
try {
|
|
81
|
+
if (!AppMetricaPushModule) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Проверяем наличие метода в нативном модуле
|
|
86
|
+
if (
|
|
87
|
+
typeof AppMetricaPushModule.isNotificationFromAppMetrica === "function"
|
|
88
|
+
) {
|
|
89
|
+
return await AppMetricaPushModule.isNotificationFromAppMetrica(
|
|
90
|
+
notification
|
|
91
|
+
);
|
|
92
|
+
} else {
|
|
93
|
+
// Fallback для iOS - проверяем поля вручную
|
|
94
|
+
if (Platform.OS === "ios") {
|
|
95
|
+
const data = notification?.data || notification?.aps?.data;
|
|
96
|
+
return !!(
|
|
97
|
+
data?.ym_push_id ||
|
|
98
|
+
data?.ym_campaign_id ||
|
|
99
|
+
data?.ym_message_id
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error(
|
|
106
|
+
"Failed to check if notification is from AppMetrica:",
|
|
107
|
+
error
|
|
108
|
+
);
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Получение информации о SDK
|
|
115
|
+
*/
|
|
116
|
+
async getSDKInfo(): Promise<SDKInfo | null> {
|
|
117
|
+
try {
|
|
118
|
+
if (!AppMetricaPushModule) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return await AppMetricaPushModule.getSDKInfo();
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error("Failed to get SDK info:", error);
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Получение дополнительной информации из push-уведомления
|
|
131
|
+
* Согласно документации AppMetrica Push SDK
|
|
132
|
+
*/
|
|
133
|
+
async getUserData(notification: any): Promise<any> {
|
|
134
|
+
try {
|
|
135
|
+
if (!AppMetricaPushModule) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return await AppMetricaPushModule.getUserData(notification);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error("Failed to get user data:", error);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Регистрация device token для push-уведомлений
|
|
148
|
+
*/
|
|
149
|
+
async registerDeviceToken(deviceToken: string): Promise<boolean> {
|
|
150
|
+
try {
|
|
151
|
+
if (!AppMetricaPushModule) {
|
|
152
|
+
throw new Error("AppMetricaPushModule is not available");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const result = await AppMetricaPushModule.registerDeviceToken(
|
|
156
|
+
deviceToken
|
|
157
|
+
);
|
|
158
|
+
return result;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error("Failed to register device token:", error);
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Проверка инициализации
|
|
167
|
+
*/
|
|
168
|
+
isSDKInitialized(): boolean {
|
|
169
|
+
return this.isInitialized;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Получение текущей конфигурации
|
|
174
|
+
*/
|
|
175
|
+
getConfig(): PushConfig | null {
|
|
176
|
+
return this.config;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Сброс состояния (для тестирования)
|
|
181
|
+
*/
|
|
182
|
+
reset(): void {
|
|
183
|
+
this.isInitialized = false;
|
|
184
|
+
this.config = null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export default AppMetricaPush;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useEffect, useState, useCallback } from 'react'
|
|
2
|
+
import { getPushSDKInfo, isSDKInitialized } from '../utils'
|
|
3
|
+
import { SDKInfo } from '../types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Интерфейс возвращаемых значений хука
|
|
7
|
+
*/
|
|
8
|
+
export interface UseAppMetricaPushReturn {
|
|
9
|
+
/** Информация о SDK */
|
|
10
|
+
sdkInfo: SDKInfo | null
|
|
11
|
+
/** Статус инициализации SDK */
|
|
12
|
+
isInitialized: boolean
|
|
13
|
+
/** Загрузка данных */
|
|
14
|
+
isLoading: boolean
|
|
15
|
+
/** Обновление информации о SDK */
|
|
16
|
+
refreshSDKInfo: () => Promise<void>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Хук для работы с AppMetrica Push уведомлениями
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* import { useAppMetricaPush } from '@moyka/appmetrica-push-sdk'
|
|
25
|
+
*
|
|
26
|
+
* const MyComponent = () => {
|
|
27
|
+
* const {
|
|
28
|
+
* sdkInfo,
|
|
29
|
+
* isInitialized
|
|
30
|
+
* } = useAppMetricaPush()
|
|
31
|
+
*
|
|
32
|
+
* return (
|
|
33
|
+
* <View>
|
|
34
|
+
* <Text>SDK initialized: {isInitialized ? 'Yes' : 'No'}</Text>
|
|
35
|
+
* </View>
|
|
36
|
+
* )
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export const useAppMetricaPush = (): UseAppMetricaPushReturn => {
|
|
41
|
+
const [sdkInfo, setSdkInfo] = useState<SDKInfo | null>(null)
|
|
42
|
+
const [isInitialized, setIsInitialized] = useState<boolean>(false)
|
|
43
|
+
const [isLoading, setIsLoading] = useState<boolean>(true)
|
|
44
|
+
|
|
45
|
+
const loadSDKInfo = useCallback(async () => {
|
|
46
|
+
try {
|
|
47
|
+
const info = await getPushSDKInfo()
|
|
48
|
+
setSdkInfo(info)
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Failed to load SDK info:', error)
|
|
51
|
+
}
|
|
52
|
+
}, [])
|
|
53
|
+
|
|
54
|
+
const checkInitialization = useCallback(() => {
|
|
55
|
+
const initialized = isSDKInitialized()
|
|
56
|
+
setIsInitialized(initialized)
|
|
57
|
+
}, [])
|
|
58
|
+
|
|
59
|
+
const refreshSDKInfo = useCallback(async () => {
|
|
60
|
+
await loadSDKInfo()
|
|
61
|
+
}, [loadSDKInfo])
|
|
62
|
+
|
|
63
|
+
// Инициализация при монтировании компонента
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const initialize = async () => {
|
|
66
|
+
setIsLoading(true)
|
|
67
|
+
checkInitialization()
|
|
68
|
+
await loadSDKInfo()
|
|
69
|
+
setIsLoading(false)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
initialize()
|
|
73
|
+
}, [loadSDKInfo, checkInitialization])
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
sdkInfo,
|
|
77
|
+
isInitialized,
|
|
78
|
+
isLoading,
|
|
79
|
+
refreshSDKInfo,
|
|
80
|
+
}
|
|
81
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Основной класс
|
|
2
|
+
export { default as AppMetricaPush } from "./AppMetricaPushModule";
|
|
3
|
+
|
|
4
|
+
// Хуки
|
|
5
|
+
export { useAppMetricaPush } from "./hooks/useAppMetricaPush";
|
|
6
|
+
export type { UseAppMetricaPushReturn } from "./hooks/useAppMetricaPush";
|
|
7
|
+
|
|
8
|
+
// Утилиты
|
|
9
|
+
export {
|
|
10
|
+
initializeAppMetricaPush,
|
|
11
|
+
isNotificationFromAppMetrica,
|
|
12
|
+
getPushSDKInfo,
|
|
13
|
+
getUserDataFromNotification,
|
|
14
|
+
registerDeviceToken,
|
|
15
|
+
isSDKInitialized,
|
|
16
|
+
getCurrentConfig,
|
|
17
|
+
resetSDK,
|
|
18
|
+
} from "./utils";
|
|
19
|
+
|
|
20
|
+
// Типы
|
|
21
|
+
export type { PushConfig, SDKInfo, InitializationResult } from "./types";
|
|
22
|
+
|
|
23
|
+
// Версия библиотеки
|
|
24
|
+
export const VERSION = "1.0.0";
|