@capgo/capacitor-appsflyer 8.0.0

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.
Files changed (33) hide show
  1. package/CapgoCapacitorAppsflyer.podspec +27 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +30 -0
  4. package/README.md +1257 -0
  5. package/android/build.gradle +178 -0
  6. package/android/src/main/AndroidManifest.xml +7 -0
  7. package/android/src/main/java/capacitor/plugin/appsflyer/sdk/AFHelpers.kt +76 -0
  8. package/android/src/main/java/capacitor/plugin/appsflyer/sdk/AppsFlyerConstants.kt +76 -0
  9. package/android/src/main/java/capacitor/plugin/appsflyer/sdk/AppsFlyerPlugin.kt +812 -0
  10. package/android/src/main/res/.gitkeep +0 -0
  11. package/dist/docs.json +2145 -0
  12. package/dist/esm/Appsflyer_constants.d.ts +29 -0
  13. package/dist/esm/Appsflyer_constants.js +33 -0
  14. package/dist/esm/Appsflyer_constants.js.map +1 -0
  15. package/dist/esm/appsflyer_interfaces.d.ts +200 -0
  16. package/dist/esm/appsflyer_interfaces.js +18 -0
  17. package/dist/esm/appsflyer_interfaces.js.map +1 -0
  18. package/dist/esm/definitions.d.ts +219 -0
  19. package/dist/esm/definitions.js +2 -0
  20. package/dist/esm/definitions.js.map +1 -0
  21. package/dist/esm/index.d.ts +6 -0
  22. package/dist/esm/index.js +7 -0
  23. package/dist/esm/index.js.map +1 -0
  24. package/dist/plugin.cjs.js +60 -0
  25. package/dist/plugin.cjs.js.map +1 -0
  26. package/dist/plugin.js +63 -0
  27. package/dist/plugin.js.map +1 -0
  28. package/ios/Sources/AppsFlyerPlugin/AppsFlyerAttribution.swift +60 -0
  29. package/ios/Sources/AppsFlyerPlugin/AppsFlyerConstants.swift +86 -0
  30. package/ios/Sources/AppsFlyerPlugin/AppsFlyerPlugin.swift +1007 -0
  31. package/ios/Sources/AppsFlyerPlugin/Extensions.swift +78 -0
  32. package/ios/Tests/AppsFlyerPluginTests/AppsFlyerPluginTests.swift +8 -0
  33. package/package.json +93 -0
@@ -0,0 +1,1007 @@
1
+ // swiftlint:disable all
2
+ import Foundation
3
+ import Capacitor
4
+ import AppsFlyerLib
5
+
6
+
7
+ @objc(AppsFlyerPlugin)
8
+ public class AppsFlyerPlugin: CAPPlugin, CAPBridgedPlugin {
9
+ public let identifier = "AppsFlyerPlugin"
10
+ public let jsName = "AppsFlyerPlugin"
11
+ public let pluginMethods: [CAPPluginMethod] = [
12
+ CAPPluginMethod(name: "initSDK", returnType: CAPPluginReturnPromise),
13
+ CAPPluginMethod(name: "startSDK", returnType: CAPPluginReturnPromise),
14
+ CAPPluginMethod(name: "logEvent", returnType: CAPPluginReturnPromise),
15
+ CAPPluginMethod(name: "setCustomerUserId", returnType: CAPPluginReturnNone),
16
+ CAPPluginMethod(name: "setCurrencyCode", returnType: CAPPluginReturnNone),
17
+ CAPPluginMethod(name: "updateServerUninstallToken", returnType: CAPPluginReturnNone),
18
+ CAPPluginMethod(name: "setAppInviteOneLink", returnType: CAPPluginReturnNone),
19
+ CAPPluginMethod(name: "setOneLinkCustomDomain", returnType: CAPPluginReturnNone),
20
+ CAPPluginMethod(name: "appendParametersToDeepLinkingURL", returnType: CAPPluginReturnNone),
21
+ CAPPluginMethod(name: "setResolveDeepLinkURLs", returnType: CAPPluginReturnNone),
22
+ CAPPluginMethod(name: "addPushNotificationDeepLinkPath", returnType: CAPPluginReturnNone),
23
+ CAPPluginMethod(name: "setSharingFilter", returnType: CAPPluginReturnNone),
24
+ CAPPluginMethod(name: "setSharingFilterForAllPartners", returnType: CAPPluginReturnNone),
25
+ CAPPluginMethod(name: "setSharingFilterForPartners", returnType: CAPPluginReturnNone),
26
+ CAPPluginMethod(name: "setAdditionalData", returnType: CAPPluginReturnNone),
27
+ CAPPluginMethod(name: "getAppsFlyerUID", returnType: CAPPluginReturnPromise),
28
+ CAPPluginMethod(name: "anonymizeUser", returnType: CAPPluginReturnNone),
29
+ CAPPluginMethod(name: "stop", returnType: CAPPluginReturnPromise),
30
+ CAPPluginMethod(name: "disableSKAdNetwork", returnType: CAPPluginReturnNone),
31
+ CAPPluginMethod(name: "disableAdvertisingIdentifier", returnType: CAPPluginReturnNone),
32
+ CAPPluginMethod(name: "disableCollectASA", returnType: CAPPluginReturnNone),
33
+ CAPPluginMethod(name: "setHost", returnType: CAPPluginReturnNone),
34
+ CAPPluginMethod(name: "generateInviteLink", returnType: CAPPluginReturnPromise),
35
+ CAPPluginMethod(name: "validateAndLogInAppPurchaseAndroid", returnType: CAPPluginReturnPromise),
36
+ CAPPluginMethod(name: "validateAndLogInAppPurchaseIos", returnType: CAPPluginReturnPromise),
37
+ CAPPluginMethod(name: "getSdkVersion", returnType: CAPPluginReturnPromise),
38
+ CAPPluginMethod(name: "enableFacebookDeferredApplinks", returnType: CAPPluginReturnPromise),
39
+ CAPPluginMethod(name: "sendPushNotificationData", returnType: CAPPluginReturnNone),
40
+ CAPPluginMethod(name: "setCurrentDeviceLanguage", returnType: CAPPluginReturnPromise),
41
+ CAPPluginMethod(name: "logCrossPromoteImpression", returnType: CAPPluginReturnPromise),
42
+ CAPPluginMethod(name: "setUserEmails", returnType: CAPPluginReturnPromise),
43
+ CAPPluginMethod(name: "logLocation", returnType: CAPPluginReturnPromise),
44
+ CAPPluginMethod(name: "setPhoneNumber", returnType: CAPPluginReturnPromise),
45
+ CAPPluginMethod(name: "setPartnerData", returnType: CAPPluginReturnPromise),
46
+ CAPPluginMethod(name: "logInvite", returnType: CAPPluginReturnPromise),
47
+ CAPPluginMethod(name: "setDisableNetworkData", returnType: CAPPluginReturnNone),
48
+ CAPPluginMethod(name: "enableTCFDataCollection", returnType: CAPPluginReturnNone),
49
+ CAPPluginMethod(name: "setConsentData", returnType: CAPPluginReturnNone),
50
+ CAPPluginMethod(name: "logAdRevenue", returnType: CAPPluginReturnNone),
51
+ CAPPluginMethod(name: "setConsentDataV2", returnType: CAPPluginReturnNone),
52
+ CAPPluginMethod(name: "isSDKStarted", returnType: CAPPluginReturnPromise),
53
+ CAPPluginMethod(name: "isSDKStopped", returnType: CAPPluginReturnPromise),
54
+ CAPPluginMethod(name: "disableAppSetId", returnType: CAPPluginReturnNone),
55
+ CAPPluginMethod(name: "validateAndLogInAppPurchaseV2", returnType: CAPPluginReturnPromise)
56
+ ]
57
+
58
+ private let APPSFLYER_PLUGIN_VERSION = "8.0.0"
59
+ private var conversion = true
60
+ private var oaoa = true
61
+ private var udl = false
62
+ private var hasSDKStarted: Bool = false
63
+
64
+ override public func load() {
65
+
66
+ NotificationCenter.default
67
+ .addObserver(
68
+ self,
69
+ selector: #selector(self.handleUrlOpened(notification:)),
70
+ name: Notification.Name.capacitorOpenURL,
71
+ object: nil
72
+ )
73
+ NotificationCenter.default
74
+ .addObserver(
75
+ self,
76
+ selector: #selector(self.handleUniversalLink(notification:)),
77
+ name: Notification.Name.capacitorOpenUniversalLink,
78
+ object: nil
79
+ )
80
+
81
+ }
82
+
83
+
84
+ @objc func initSDK(_ call: CAPPluginCall){
85
+ let appsflyer = AppsFlyerLib.shared()
86
+
87
+ appsflyer
88
+ .setPluginInfo(
89
+ plugin: .capacitor,
90
+ version: APPSFLYER_PLUGIN_VERSION,
91
+ additionalParams: nil
92
+ )
93
+ guard let devKey = call.getString(AppsFlyerConstants.AF_DEV_KEY) else{
94
+ call.reject("devkey is missing")
95
+ return
96
+ }
97
+ guard let appID = call.getString(AppsFlyerConstants.AF_APP_ID) else{
98
+ call.reject("appID is missing")
99
+ return
100
+ }
101
+ let attInterval = call.getInt(AppsFlyerConstants.AF_ATT)
102
+
103
+ let debug = call.getBool(AppsFlyerConstants.AF_DEBUG, false)
104
+ let sandbox = call.getBool(AppsFlyerConstants.AF_SANDBOX, false)
105
+ let receiptSandbox = call.getBool(
106
+ AppsFlyerConstants.AF_RECEIPT_SANDBOX,
107
+ false
108
+ )
109
+ let manualStart = call.getBool(
110
+ AppsFlyerConstants.AF_MANUAL_START,
111
+ false
112
+ )
113
+
114
+ conversion = call
115
+ .getBool(AppsFlyerConstants.AF_CONVERSION_LISTENER, true)
116
+ oaoa = call.getBool(AppsFlyerConstants.AF_OAOA, true)
117
+ udl = call.getBool(AppsFlyerConstants.AF_UDL, false)
118
+
119
+ appsflyer.isDebug = debug
120
+ appsflyer.appsFlyerDevKey = devKey
121
+ appsflyer.appleAppID = appID
122
+ appsflyer.useUninstallSandbox = sandbox
123
+ appsflyer.useReceiptValidationSandbox = receiptSandbox
124
+
125
+ if let minTime = call.getInt(AppsFlyerConstants.AF_MIN_TIME){
126
+ appsflyer.minTimeBetweenSessions = UInt(minTime)
127
+ }
128
+
129
+ if let timeout = call.getInt(AppsFlyerConstants.AF_DEEP_LINK_TIME_OUT){
130
+ appsflyer.deepLinkTimeout = UInt(timeout)
131
+ }
132
+
133
+ if conversion || oaoa {
134
+ appsflyer.delegate = self
135
+ }
136
+
137
+ if udl {
138
+ appsflyer.deepLinkDelegate = self
139
+ }
140
+
141
+ reportBridgeReady()
142
+
143
+ #if !AFSDK_NO_IDFA
144
+ if attInterval != nil {
145
+ appsflyer
146
+ .waitForATTUserAuthorization(
147
+ timeoutInterval: Double(attInterval!)
148
+ )
149
+ }
150
+ #endif
151
+
152
+ if !manualStart {
153
+ startSDK(call)
154
+ } else {
155
+ call
156
+ .resolve(
157
+ ["res": "SDK initiated successfully. SDK has NOT started yet"]
158
+ )
159
+ }
160
+ }
161
+
162
+ @objc func startSDK(_ call: CAPPluginCall) {
163
+ NotificationCenter.default
164
+ .addObserver(
165
+ self,
166
+ selector: #selector(sendLaunch),
167
+ name: UIApplication.didBecomeActiveNotification,
168
+ object: nil
169
+ )
170
+
171
+ AppsFlyerLib.shared().start { [weak self] dictionary, error in
172
+ if let error = error {
173
+ call.reject(error.localizedDescription)
174
+ } else {
175
+ self?.hasSDKStarted = true
176
+ call.resolve(["res": "success"])
177
+ }
178
+ }
179
+ }
180
+
181
+ @objc func logEvent(_ call: CAPPluginCall){
182
+ guard let eventName = call.getString(AppsFlyerConstants.AF_EVENT_NAME) else{
183
+ call.reject("missing event name")
184
+ return
185
+ }
186
+ let eventValue = call.getObject(AppsFlyerConstants.AF_EVENT_VALUE)
187
+
188
+ AppsFlyerLib
189
+ .shared()
190
+ .logEvent(name: eventName, values: eventValue) { (
191
+ response: [String : Any]?,
192
+ error: Error?
193
+ ) in
194
+ if let response = response {
195
+ call.resolve(["res":response])
196
+ }
197
+ if let error = error {
198
+ call.reject(error.localizedDescription, nil, error)
199
+ }
200
+ }
201
+ }
202
+
203
+ @objc func setCustomerUserId(_ call: CAPPluginCall) {
204
+ guard let cuid = call.getString(AppsFlyerConstants.AF_CUID) else {
205
+ call.reject("Invalid Customer User ID")
206
+ return
207
+ }
208
+ AppsFlyerLib.shared().customerUserID = cuid
209
+ }
210
+
211
+
212
+ @objc func logAdRevenue(_ call: CAPPluginCall) {
213
+ let adRevenueData = call.jsObjectRepresentation
214
+ if adRevenueData.isEmpty {
215
+ call
216
+ .reject(
217
+ "adRevenueData is missing, the data mandatory to use this API."
218
+ )
219
+ return
220
+ }
221
+
222
+ // Parse the fields from the adRevenueData object
223
+ guard let myMonetizationNetwork = adRevenueData[AppsFlyerConstants.AF_MONETIZATION_NETWORK] as? String else {
224
+ call.reject("monetizationNetwork is missing")
225
+ return
226
+ }
227
+ guard let myCurrencyIso4217Code = adRevenueData[AppsFlyerConstants.AF_CURRENCY_ISO4217_CODE] as? String else {
228
+ call.reject("currencyIso4217Code is missing")
229
+ return
230
+ }
231
+ guard let revenue = adRevenueData[AppsFlyerConstants.AF_REVENUE] as? NSNumber else {
232
+ call.reject("revenue is missing or not a number")
233
+ return
234
+ }
235
+ guard let mediationNetworkString = adRevenueData[AppsFlyerConstants.AF_MEDIATION_NETWORK] as? String else {
236
+ call.reject("mediationNetwork is missing")
237
+ return
238
+ }
239
+
240
+ let additionalParams = adRevenueData[AppsFlyerConstants.AF_ADDITIONAL_PARAMETERS] as? [String: Any]
241
+
242
+ // Handle the mediation network mapping to AppsFlyer SDK enum
243
+ guard let myMediationNetwork = MediationNetworkType(rawValue: mediationNetworkString) else {
244
+ call.reject("Invalid mediation network")
245
+ return
246
+ }
247
+
248
+ // Create the AFAdRevenueData instance
249
+ let myAdRevenueData = AFAdRevenueData(
250
+ monetizationNetwork: myMonetizationNetwork,
251
+ mediationNetwork: myMediationNetwork,
252
+ currencyIso4217Code: myCurrencyIso4217Code,
253
+ eventRevenue: revenue
254
+ )
255
+
256
+ // Log the ad revenue to the AppsFlyer SDK
257
+ AppsFlyerLib
258
+ .shared()
259
+ .logAdRevenue(
260
+ myAdRevenueData,
261
+ additionalParameters: additionalParams
262
+ )
263
+
264
+ call.resolve()
265
+ }
266
+
267
+ // Helper function to map mediation network string to the AppsFlyer enum
268
+ private func MediationNetworkType(rawValue: String) -> MediationNetworkType? {
269
+ switch rawValue {
270
+ case "google_admob":
271
+ return .googleAdMob
272
+ case "fyber":
273
+ return .fyber
274
+ case "ironsource":
275
+ return .ironSource
276
+ case "applovin_max":
277
+ return .applovinMax
278
+ case "appodeal":
279
+ return .appodeal
280
+ case "admost":
281
+ return .admost
282
+ case "topon":
283
+ return .topon
284
+ case "tradplus":
285
+ return .tradplus
286
+ case "yandex":
287
+ return .yandex
288
+ case "chartboost":
289
+ return .chartBoost
290
+ case "unity":
291
+ return .unity
292
+ case "topon_pte":
293
+ return .toponPte
294
+ case "custom_mediation":
295
+ return .custom
296
+ case "direct_monetization_network":
297
+ return .directMonetization
298
+ default:
299
+ return nil
300
+ }
301
+ }
302
+
303
+ @objc func setCurrencyCode(_ call: CAPPluginCall){
304
+ guard let code = call.getString(AppsFlyerConstants.AF_CURRENCY_CODE) else {
305
+ call.reject("Invalid Currency Code")
306
+ return
307
+ }
308
+ AppsFlyerLib.shared().currencyCode = code
309
+
310
+
311
+ }
312
+
313
+ @objc func updateServerUninstallToken(_ call: CAPPluginCall){
314
+ guard let token = call.getString(AppsFlyerConstants.AF_TOKEN) else {
315
+ call.reject("Invalid device token")
316
+
317
+ return
318
+ }
319
+
320
+ guard let deviceTokenData = token.hexadecimalToData else{
321
+ call.reject("Invalid device token")
322
+ return
323
+ }
324
+ AppsFlyerLib.shared().registerUninstall(deviceTokenData)
325
+ }
326
+
327
+ @objc func setAppInviteOneLink(_ call: CAPPluginCall){
328
+ guard let id = call.getString(AppsFlyerConstants.AF_ONELINK_ID) else {
329
+ call.reject("Onelink id is missing")
330
+ return
331
+ }
332
+ AppsFlyerLib.shared().appInviteOneLinkID = id
333
+
334
+ }
335
+
336
+ @objc func setOneLinkCustomDomain(_ call: CAPPluginCall){
337
+ guard let arr = call.getArray(AppsFlyerConstants.AF_ONELINK_DOMAIN) else {
338
+ call.reject("Domains are missing")
339
+ return
340
+ }
341
+ var domains :[String] = []
342
+ for dom in arr {
343
+ domains.append(dom as! String)
344
+ }
345
+ AppsFlyerLib.shared().oneLinkCustomDomains = domains
346
+
347
+ }
348
+
349
+ @objc func appendParametersToDeepLinkingURL(_ call: CAPPluginCall){
350
+ guard let contains = call.getString(AppsFlyerConstants.AF_CONTAINS) else {
351
+ return
352
+ }
353
+ guard let parameters = call.getObject(AppsFlyerConstants.AF_PARAMETERS) else {
354
+ return
355
+ }
356
+ var params: [String:String] = [:]
357
+ for (k,v) in parameters{
358
+ params[k] = (v as! String)
359
+ }
360
+ AppsFlyerLib
361
+ .shared()
362
+ .appendParametersToDeeplinkURL(
363
+ contains: contains,
364
+ parameters:params
365
+ )
366
+
367
+ }
368
+
369
+
370
+ @objc func setResolveDeepLinkURLs(_ call: CAPPluginCall){
371
+ guard let arr = call.getArray(AppsFlyerConstants.AF_DEEPLINK_URLS) else {
372
+ call.reject("URLs are missing")
373
+ return
374
+ }
375
+ var urls :[String] = []
376
+ for url in arr {
377
+ urls.append(url as! String)
378
+ }
379
+ AppsFlyerLib.shared().resolveDeepLinkURLs = urls
380
+
381
+ }
382
+
383
+ @objc func addPushNotificationDeepLinkPath(_ call: CAPPluginCall){
384
+ guard let arr = call.getArray(AppsFlyerConstants.AF_PATH) else {
385
+ call.reject("Path is missing")
386
+ return
387
+ }
388
+ var path :[String] = []
389
+ for p in arr {
390
+ path.append(p as! String)
391
+ }
392
+ AppsFlyerLib.shared().addPushNotificationDeepLinkPath(path)
393
+
394
+ }
395
+
396
+ @available(*, deprecated, message: "Use setSharingFilterForPartners")
397
+ @objc func setSharingFilter(_ call: CAPPluginCall){
398
+ let filters = call.getArray(AppsFlyerConstants.AF_FILTERS , String.self)
399
+
400
+ AppsFlyerLib.shared().sharingFilter = filters
401
+
402
+ }
403
+
404
+ @available(*, deprecated, message: "Use setSharingFilterForPartners")
405
+ @objc func setSharingFilterForAllPartners(_ call: CAPPluginCall){
406
+
407
+ AppsFlyerLib.shared().setSharingFilterForAllPartners()
408
+
409
+ }
410
+
411
+ @objc func setSharingFilterForPartners(_ call: CAPPluginCall){
412
+ guard let filters = call.getArray(AppsFlyerConstants.AF_FILTERS , String.self) else{
413
+ return call.reject("cannot extract the filters value")
414
+ }
415
+
416
+ AppsFlyerLib.shared().setSharingFilterForPartners(filters)
417
+
418
+ }
419
+
420
+ @objc func setAdditionalData(_ call: CAPPluginCall){
421
+ guard let data = call.getObject(AppsFlyerConstants.AF_ADDITIONAL_DATA) else {
422
+ call.reject("Data is missing")
423
+ return
424
+ }
425
+ AppsFlyerLib.shared().customData = data
426
+
427
+ }
428
+
429
+
430
+ @objc func getAppsFlyerUID(_ call: CAPPluginCall){
431
+ let uid = AppsFlyerLib.shared().getAppsFlyerUID()
432
+ call.resolve(["uid":uid])
433
+
434
+ }
435
+
436
+ @objc func setDisableNetworkData(_ call: CAPPluginCall){
437
+ call.unavailable("Android only method - has no effact on iOS apps")
438
+ }
439
+
440
+ @objc func enableTCFDataCollection(_ call: CAPPluginCall){
441
+ guard let shouldEnableTCFDataCollection = call.getBool(AppsFlyerConstants.AF_ENABLE_TCF_DATA_COLLECTION) else {
442
+ call.reject("Missing boolean value shouldEnableTCFDataCollection")
443
+ return
444
+ }
445
+ AppsFlyerLib
446
+ .shared()
447
+ .enableTCFDataCollection(shouldEnableTCFDataCollection)
448
+ }
449
+
450
+ @objc func setConsentData(_ call: CAPPluginCall) {
451
+ guard let consentData = call.getObject("data") else {
452
+ call.reject("Consent data is missing")
453
+ return
454
+ }
455
+
456
+ let isUserSubjectToGDPR = consentData[AppsFlyerConstants.AF_IS_SUBJECTED_TO_DGPR] as? Bool ?? false
457
+ let hasConsentForDataUsage = consentData[AppsFlyerConstants.AF_CONSENT_FOR_DATA_USAGE] as? Bool ?? false
458
+ let hasConsentForAdsPersonalization = consentData[AppsFlyerConstants.AF_CONSENT_FOR_ADS_PERSONALIZATION] as? Bool ?? false
459
+
460
+ let consentObject: AppsFlyerConsent
461
+ if isUserSubjectToGDPR {
462
+ consentObject = AppsFlyerConsent(
463
+ forGDPRUserWithHasConsentForDataUsage: hasConsentForDataUsage,
464
+ hasConsentForAdsPersonalization: hasConsentForAdsPersonalization
465
+ )
466
+ } else {
467
+ consentObject = AppsFlyerConsent(nonGDPRUser: ())
468
+ }
469
+
470
+ AppsFlyerLib.shared().setConsentData(consentObject)
471
+
472
+ call.resolve()
473
+ }
474
+
475
+ @objc func setConsentDataV2(_ call: CAPPluginCall) {
476
+ // inner helper: convert Bool? to NSNumber?
477
+ func toNSNumber(_ value: Bool?) -> NSNumber? {
478
+ guard let value = value else { return nil }
479
+ return NSNumber(value: value)
480
+ }
481
+
482
+ let isUserSubjectToGDPR = toNSNumber(call.getBool(AppsFlyerConstants.AF_IS_SUBJECTED_TO_DGPR))
483
+ let hasConsentForDataUsage = toNSNumber(call.getBool(AppsFlyerConstants.AF_CONSENT_FOR_DATA_USAGE))
484
+ let hasConsentForAdsPersonalization = toNSNumber(call.getBool(AppsFlyerConstants.AF_CONSENT_FOR_ADS_PERSONALIZATION))
485
+ let hasConsentForAdStorage = toNSNumber(call.getBool(AppsFlyerConstants.AF_CONSENT_FOR_ADS_STORAGE))
486
+
487
+ // Build consent options object and pass to AppsFlyer SDK
488
+ let consentOptions = AppsFlyerConsent(
489
+ isUserSubjectToGDPR: isUserSubjectToGDPR,
490
+ hasConsentForDataUsage: hasConsentForDataUsage,
491
+ hasConsentForAdsPersonalization: hasConsentForAdsPersonalization,
492
+ hasConsentForAdStorage: hasConsentForAdStorage
493
+ )
494
+
495
+ AppsFlyerLib.shared().setConsentData(consentOptions)
496
+ call.resolve()
497
+ }
498
+
499
+ @objc func anonymizeUser(_ call: CAPPluginCall){
500
+ guard let anonymize = call.getBool(AppsFlyerConstants.AF_ANONYMIZE_USER) else{
501
+ call.reject("Missing boolean value anonymizeUser")
502
+ return
503
+ }
504
+ AppsFlyerLib.shared().anonymizeUser = anonymize
505
+
506
+ }
507
+
508
+ @objc func stop(_ call: CAPPluginCall){
509
+ let stop = call.getBool(AppsFlyerConstants.AF_STOP)
510
+ if stop != nil {
511
+ AppsFlyerLib.shared().isStopped = stop!
512
+ }
513
+ call
514
+ .resolve(
515
+ [AppsFlyerConstants.AF_IS_STOP : AppsFlyerLib.shared().isStopped ]
516
+ )
517
+ }
518
+
519
+ @objc func disableSKAdNetwork(_ call: CAPPluginCall){
520
+ guard let disable = call.getBool(AppsFlyerConstants.AF_DISABLE_SKAD) else {
521
+ call.reject("Missing boolean value shouldDisable")
522
+ return
523
+ }
524
+ AppsFlyerLib.shared().disableSKAdNetwork = disable
525
+
526
+ }
527
+
528
+ #if !AFSDK_NO_IDFA
529
+ @objc func disableAdvertisingIdentifier(_ call: CAPPluginCall){
530
+ guard let disable = call.getBool(AppsFlyerConstants.AF_DISABLE_SKAD) else {
531
+ call.reject("Missing boolean value shouldDisable")
532
+ return
533
+ }
534
+ AppsFlyerLib.shared().disableAdvertisingIdentifier = disable
535
+
536
+ }
537
+ #endif
538
+
539
+ @objc func disableCollectASA(_ call: CAPPluginCall){
540
+ guard let disable = call.getBool(AppsFlyerConstants.AF_DISABLE_SKAD) else {
541
+ call.reject("Missing boolean value shouldDisable")
542
+ return
543
+ }
544
+ AppsFlyerLib.shared().disableCollectASA = disable
545
+
546
+ }
547
+ @objc func setHost(_ call: CAPPluginCall){
548
+ let pre = call.getString(AppsFlyerConstants.AF_HOST_PREFIX)
549
+ let post = call.getString(AppsFlyerConstants.AF_HOST_POSTFIX)
550
+ if (pre != nil && post != nil) {
551
+ AppsFlyerLib.shared().setHost(post!, withHostPrefix: pre!)
552
+ } else {
553
+ call.reject("Missing host prefix and/or host name")
554
+ }
555
+ }
556
+
557
+ @objc func generateInviteLink(_ call: CAPPluginCall){
558
+ AppsFlyerShareInviteHelper.generateInviteUrl(
559
+ linkGenerator:
560
+ {(
561
+ _ generator: AppsFlyerLinkGenerator
562
+ ) -> AppsFlyerLinkGenerator in
563
+ if let channel = call.getString(AppsFlyerConstants.AF_CHANNEL){
564
+ generator.setChannel(channel)
565
+ }
566
+ if let brandDomain = call.getString(AppsFlyerConstants.AF_BRAND_DOMAIN){
567
+ generator.brandDomain = brandDomain
568
+ }
569
+ if let campaign = call.getString(AppsFlyerConstants.AF_CAMPAIGN){
570
+ generator.setCampaign(campaign)
571
+ }
572
+ if let referrerName = call.getString(
573
+ AppsFlyerConstants.AF_REFERRER_NAME
574
+ ){
575
+ generator.setReferrerName(referrerName)
576
+ }
577
+ if let referrerImageURL = call.getString(
578
+ AppsFlyerConstants.AF_REFERRER_IMAGE_URL
579
+ ){
580
+ generator.setReferrerImageURL(referrerImageURL)
581
+ }
582
+ if let referrerCustomerId = call.getString(
583
+ AppsFlyerConstants.AF_REFERRER_CUSTOMER_ID
584
+ ){
585
+ generator.setReferrerCustomerId(referrerCustomerId)
586
+ }
587
+ if let baseDeeplink = call.getString(
588
+ AppsFlyerConstants.AF_BASE_DEEPLINK
589
+ ){
590
+ generator.setBaseDeeplink(baseDeeplink)
591
+ }
592
+ if let addParameters = call.getObject(
593
+ AppsFlyerConstants.AF_ADD_PARAMETERS
594
+ ){
595
+ generator.addParameters(addParameters)
596
+ }
597
+
598
+ return generator
599
+ },
600
+ completionHandler: {url in
601
+ if url != nil{
602
+ call.resolve([AppsFlyerConstants.AF_LINK_READY: url!.absoluteString])
603
+ }else{
604
+ call.reject("Failed to generate a link")
605
+ }
606
+ }
607
+ )
608
+
609
+ }
610
+ @available(*, deprecated, message: "Use validateAndLogInAppPurchaseV2 instead")
611
+ @objc func validateAndLogInAppPurchaseAndroid(_ call: CAPPluginCall){
612
+ call.unavailable()
613
+ }
614
+
615
+ @available(*, deprecated, message: "Use validateAndLogInAppPurchaseV2 instead")
616
+ @objc func validateAndLogInAppPurchaseIos(_ call: CAPPluginCall){
617
+ let currency = call.getString(AppsFlyerConstants.AF_CURRENCY)
618
+ let price = call.getString(AppsFlyerConstants.AF_PRICE)
619
+ let _inAppPurchase = call.getString(
620
+ AppsFlyerConstants.AF_IN_APP_PURCHASE
621
+ )
622
+ let transactionId = call.getString(AppsFlyerConstants.AF_TRANSACTION_ID)
623
+ let additionalParameters = call.getObject(AppsFlyerConstants.AF_ADDITIONAL_PARAMETERS) ?? [:]
624
+
625
+ if currency != nil && price != nil && _inAppPurchase != nil && transactionId != nil && currency != nil {
626
+ AppsFlyerLib.shared().validateAndLog(
627
+ inAppPurchase: _inAppPurchase,
628
+ price: price,
629
+ currency: currency,
630
+ transactionId: transactionId,
631
+ additionalParameters: additionalParameters,
632
+ success: {result in
633
+ call.resolve(["res":result])
634
+ },
635
+ failure: { error, result in
636
+ guard let emptyInApp = result as? [String:Any]
637
+ else
638
+ {
639
+ call.reject((error)?.localizedDescription ?? "error" )
640
+ return
641
+ }
642
+ call.reject((error)?.localizedDescription ?? "error" , emptyInApp.jsonStringRepresentation)
643
+ })
644
+ }else{
645
+ call.reject("Missing some fields")
646
+ }
647
+
648
+
649
+ }
650
+
651
+ @objc func getSdkVersion(_ call: CAPPluginCall){
652
+
653
+ call.resolve(["res": AppsFlyerLib.shared().getSDKVersion()])
654
+ }
655
+
656
+ @objc func enableFacebookDeferredApplinks(_ call: CAPPluginCall){
657
+ guard let enable = call.getBool(AppsFlyerConstants.AF_FB) else {
658
+ call.reject("missing boolean value: \(AppsFlyerConstants.AF_FB)")
659
+ return
660
+ }
661
+ if enable{
662
+ #if canImport(FacebookCore)
663
+ AppsFlyerLib.shared().enableFacebookDeferredApplinks(with: FBSDKAppLinkUtility.self)
664
+ call.resolve(["res": "enabled"])
665
+ #else
666
+ call.reject("Please install FBSDK First!")
667
+ #endif
668
+ }else{
669
+ AppsFlyerLib.shared().enableFacebookDeferredApplinks(with: nil)
670
+ call.resolve(["res": "disabled"])
671
+
672
+ }
673
+
674
+ }
675
+
676
+ @objc func sendPushNotificationData(_ call: CAPPluginCall){
677
+ let json = call.getObject(AppsFlyerConstants.AF_PUSH_PAYLOAD)
678
+ AppsFlyerLib.shared().handlePushNotification(json)
679
+
680
+ }
681
+
682
+
683
+ @objc func setCurrentDeviceLanguage(_ call: CAPPluginCall){
684
+ guard let language = call.getString(AppsFlyerConstants.AF_LANGUAGE) else {
685
+ call.reject("cannot extract the language value")
686
+ return
687
+ }
688
+ AppsFlyerLib.shared().currentDeviceLanguage = language
689
+ call.resolve(["res": "ok"])
690
+
691
+ }
692
+
693
+ @objc func logCrossPromoteImpression(_ call: CAPPluginCall){
694
+ guard let appID = call.getString(AppsFlyerConstants.AF_APP_ID) else {
695
+ call.reject("cannot extract the appID value")
696
+ return
697
+ }
698
+ guard let campaign = call.getString(AppsFlyerConstants.AF_CAMPAIGN) else {
699
+ call.reject("cannot extract the campaign value")
700
+ return
701
+ }
702
+ guard let parameters = call.getObject(AppsFlyerConstants.AF_PARAMETERS) else {
703
+ call.reject("cannot extract the parameters value")
704
+ return
705
+ }
706
+ AppsFlyerCrossPromotionHelper
707
+ .logCrossPromoteImpression(
708
+ appID,
709
+ campaign: campaign,
710
+ parameters: parameters
711
+ )
712
+ call.resolve(["res": "ok"])
713
+
714
+ }
715
+
716
+ @objc func setUserEmails(_ call: CAPPluginCall){
717
+ guard let emails = call.getArray(AppsFlyerConstants.AF_EMAILS, String.self) else {
718
+ call.reject("cannot extract the emails value")
719
+ return
720
+ }
721
+ if let enc = call.getBool(AppsFlyerConstants.AF_ENCODE) , enc == true{
722
+ AppsFlyerLib
723
+ .shared()
724
+ .setUserEmails(emails, with: EmailCryptTypeSHA256)
725
+
726
+ }else{
727
+ AppsFlyerLib
728
+ .shared()
729
+ .setUserEmails(emails, with: EmailCryptTypeNone)
730
+
731
+ }
732
+ call.resolve(["res": "ok"])
733
+
734
+ }
735
+
736
+ @objc func logLocation(_ call: CAPPluginCall){
737
+ guard let longitude = call.getDouble(AppsFlyerConstants.AF_LONGITUDE) else {
738
+ call.reject("cannot extract the longitude value")
739
+ return
740
+ }
741
+ guard let latitude = call.getDouble(AppsFlyerConstants.AF_LATITUDE) else {
742
+ call.reject("cannot extract the longitude value")
743
+ return
744
+ }
745
+
746
+ AppsFlyerLib
747
+ .shared()
748
+ .logLocation(longitude: longitude, latitude: latitude)
749
+ call.resolve(["res": "ok"])
750
+
751
+ }
752
+
753
+ @objc func setPhoneNumber(_ call: CAPPluginCall){
754
+ guard let phone = call.getString(AppsFlyerConstants.AF_PHONE) else {
755
+ call.reject("cannot extract the phone value")
756
+ return
757
+ }
758
+
759
+ AppsFlyerLib.shared().phoneNumber = phone
760
+ call.resolve(["res": "ok"])
761
+
762
+ }
763
+
764
+ @objc func setPartnerData(_ call: CAPPluginCall){
765
+ guard let data = call.getObject(AppsFlyerConstants.AF_DATA) else {
766
+ call.reject("cannot extract the data value")
767
+ return
768
+ }
769
+ guard let pid = call.getString(AppsFlyerConstants.AF_PARTNER_ID) else {
770
+ call.reject("cannot extract the partnerId value")
771
+ return
772
+ }
773
+
774
+ AppsFlyerLib.shared().setPartnerData(partnerId: pid, partnerInfo: data)
775
+ call.resolve(["res": "ok"])
776
+
777
+ }
778
+
779
+ @objc func logInvite(_ call: CAPPluginCall){
780
+ guard let data = call.getObject(AppsFlyerConstants.AF_EVENT_PARAMETERS) else {
781
+ call.reject("cannot extract the eventParameters value")
782
+ return
783
+ }
784
+ guard let channel = call.getString(AppsFlyerConstants.AF_CHANNEL) else {
785
+ call.reject("cannot extract the channel value")
786
+ return
787
+ }
788
+
789
+ AppsFlyerShareInviteHelper.logInvite(channel, parameters: data)
790
+ call.resolve(["res": "ok"])
791
+
792
+ }
793
+
794
+ /**
795
+ * Returns whether the AppsFlyer SDK has been started in the current session.
796
+ */
797
+ @objc func isSDKStarted(_ call: CAPPluginCall) {
798
+ call.resolve(["isStarted": hasSDKStarted])
799
+ }
800
+
801
+ /**
802
+ * Returns whether the AppsFlyer SDK is currently stopped.
803
+ */
804
+ @objc func isSDKStopped(_ call: CAPPluginCall) {
805
+ call.resolve(["isStopped": AppsFlyerLib.shared().isStopped])
806
+ }
807
+
808
+ @objc func disableAppSetId(_ call: CAPPluginCall){
809
+ call.unavailable()
810
+ }
811
+
812
+ @objc func validateAndLogInAppPurchaseV2(_ call: CAPPluginCall) {
813
+ guard let purchaseDetailsMap = call.getObject(AppsFlyerConstants.AF_PURCHASE_DETAILS) else {
814
+ call.reject("Purchase details are required")
815
+ return
816
+ }
817
+
818
+ let additionalParameters = call.getObject(AppsFlyerConstants.AF_ADDITIONAL_PARAMETERS)
819
+
820
+ // Validate required fields
821
+ guard let purchaseTypeString = purchaseDetailsMap[AppsFlyerConstants.AF_PURCHASE_TYPE] as? String else {
822
+ call.reject("Purchase type is required")
823
+ return
824
+ }
825
+ guard let purchaseType = mapPurchaseType(purchaseTypeString) else {
826
+ call.reject("Unkown purchase type")
827
+ return
828
+ }
829
+
830
+ guard let purchaseToken = purchaseDetailsMap[AppsFlyerConstants.AF_PURCHASE_TOKEN] as? String else {
831
+ call.reject("Purchase token is required")
832
+ return
833
+ }
834
+
835
+ guard let productId = purchaseDetailsMap[AppsFlyerConstants.AF_PRODUCT_ID] as? String else {
836
+ call.reject("Product ID is required")
837
+ return
838
+ }
839
+
840
+ // Create a new instance of AFSDKPurchaseDetails with the required information
841
+ let purchaseDetails: AFSDKPurchaseDetails = AFSDKPurchaseDetails(
842
+ productId: productId,
843
+ transactionId: purchaseToken,
844
+ purchaseType: purchaseType
845
+ )
846
+
847
+
848
+ // For iOS, we use the existing validateAndLog method with the new V2 parameters
849
+ // The purchaseToken serves as the transactionId for iOS
850
+ AppsFlyerLib.shared().validateAndLogInAppPurchase(purchaseDetails: purchaseDetails, purchaseAdditionalDetails: additionalParameters){result, error in
851
+ if result != nil {
852
+ var response: [String: Any] = [:]
853
+ if let resultDict = result as? [String: Any] {
854
+ response = resultDict
855
+ } else {
856
+ response["result"] = result
857
+ }
858
+ call.resolve(response)
859
+ } else if error != nil {
860
+ var errorDict: [String: Any] = [:]
861
+ if let nsError = error as NSError? {
862
+ errorDict["error"] = nsError.localizedDescription
863
+ errorDict["code"] = nsError.code
864
+ errorDict["domain"] = nsError.domain
865
+ // Convert userInfo to JSON-safe values (userInfo may contain non-serializable objects like NSError)
866
+ errorDict["userInfo"] = nsError.userInfo.jsonSafeRepresentation()
867
+ }
868
+ call.reject("Validation failed", errorDict.jsonStringRepresentation ?? "")
869
+ }
870
+ }
871
+ }
872
+
873
+ func mapPurchaseType(_ purchaseTypeString: String) -> AFSDKPurchaseType? {
874
+ switch purchaseTypeString {
875
+ case "subscription":
876
+ return .subscription
877
+
878
+ case "one_time_purchase":
879
+ return .oneTimePurchase
880
+
881
+ default:
882
+ return nil
883
+ }
884
+ }
885
+ }
886
+
887
+ extension AppsFlyerPlugin{
888
+ private func reportBridgeReady(){
889
+ AppsFlyerAttribution.shared.bridgReady = true
890
+ NotificationCenter.default
891
+ .post(name: Notification.Name.appsflyerBridge, object: nil)
892
+ }
893
+
894
+ @objc private func sendLaunch(){
895
+ AppsFlyerLib.shared().start()
896
+ }
897
+
898
+ @objc func handleUrlOpened(notification: NSNotification) {
899
+ guard let object = notification.object as? [String: Any?] else {
900
+ return
901
+ }
902
+ guard let url = object["url"] else {
903
+ afLogger(msg: "handleUrlOpened url is nil")
904
+ return
905
+ }
906
+ guard let options = object["options"] else {
907
+ afLogger(msg: "handleUrlOpened options is nil")
908
+
909
+ return
910
+ }
911
+ afLogger(msg: "handleUrlOpened with \((url as! URL).absoluteString)")
912
+ AppsFlyerAttribution.shared
913
+ .handleOpenUrl(
914
+ open: url as! URL,
915
+ options: options as! [UIApplication.OpenURLOptionsKey: Any]
916
+ )
917
+
918
+ }
919
+
920
+ @objc func handleUniversalLink(notification: NSNotification) {
921
+ guard let object = notification.object as? [String: Any?] else {
922
+ return
923
+ }
924
+ let user = NSUserActivity(activityType: NSUserActivityTypeBrowsingWeb)
925
+ guard let url = object["url"] else {
926
+ afLogger(msg: "handleUrlOpened options is url")
927
+ return
928
+ }
929
+ user.webpageURL = (url as! URL)
930
+ afLogger(
931
+ msg: "handleUniversalLink with \(user.webpageURL?.absoluteString ?? "null")"
932
+ )
933
+ AppsFlyerAttribution.shared.continueUserActivity(userActivity: user)
934
+
935
+ }
936
+
937
+ }
938
+
939
+ extension AppsFlyerPlugin : AppsFlyerLibDelegate {
940
+ public func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]) {
941
+ let json : [String: Any] = [
942
+ "callbackName":"onConversionDataSuccess",
943
+ "data":conversionInfo
944
+ ]
945
+ self.notifyListeners(AppsFlyerConstants.CONVERSION_CALLBACK, data: json)
946
+
947
+ }
948
+
949
+ public func onConversionDataFail(_ error: Error) {
950
+ let json : [String: Any] = [
951
+ "callbackName":"onConversionDataFail",
952
+ "status":error.localizedDescription
953
+ ]
954
+ self.notifyListeners(AppsFlyerConstants.CONVERSION_CALLBACK, data: json)
955
+ }
956
+ public func onAppOpenAttribution(_ attributionData: [AnyHashable : Any]) {
957
+ let json : [String: Any] = [
958
+ "callbackName":"onAppOpenAttribution",
959
+ "data":attributionData
960
+ ]
961
+ self.notifyListeners(AppsFlyerConstants.OAOA_CALLBACK, data: json)
962
+ }
963
+
964
+ public func onAppOpenAttributionFailure(_ error: Error) {
965
+ let json : [String: Any] = [
966
+ "callbackName":"onAppOpenAttributionFailure",
967
+ "status":error.localizedDescription
968
+ ]
969
+ self.notifyListeners(AppsFlyerConstants.OAOA_CALLBACK, data: json)
970
+
971
+ }
972
+
973
+ }
974
+
975
+
976
+ // Mark -
977
+ extension AppsFlyerPlugin : DeepLinkDelegate{
978
+ public func didResolveDeepLink(_ result: DeepLinkResult) {
979
+ var json : [String: Any] = [:]
980
+
981
+ switch result.status {
982
+ case .notFound :
983
+ json["status"] = "NOT_FOUND"
984
+ case .failure :
985
+ json["status"] = "FAILURE"
986
+ case .found :
987
+ json["status"] = "FOUND"
988
+ }
989
+
990
+ if result.error != nil {
991
+ json["error"] = result.error!.localizedDescription
992
+ }
993
+ if result.deepLink != nil {
994
+ var deepLinkDic = result.deepLink!.clickEvent
995
+ deepLinkDic["is_deferred"] = result.deepLink!.isDeferred
996
+ json["deepLink"] = deepLinkDic
997
+ }
998
+ self.notifyListeners(AppsFlyerConstants.UDL_CALLBACK, data: json)
999
+ }
1000
+
1001
+ }
1002
+
1003
+ extension AppsFlyerPlugin{
1004
+ private func afLogger(msg : String){
1005
+ NSLog ("AppsFlyer [Debug][Capacitor]: \(msg)");
1006
+ }
1007
+ }