@apex-inc/capacitor-plugin 0.3.7 → 0.3.8

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.
@@ -76,6 +76,11 @@ public class ApexCapacitorPlugin: CAPPlugin, CAPBridgedPlugin, UNUserNotificatio
76
76
  // reached the server. See friction-log F6.
77
77
  private var batchSender: NativeBatchSender?
78
78
  private var flushTimer: Timer?
79
+ // MMP-207 — debounced flush. Without this, every `track()` call
80
+ // kicked a POST, so 8 quick taps = 8 separate single-event HTTP
81
+ // requests. With debounce, rapid-fire events coalesce into one
82
+ // batch on the next tick.
83
+ private var flushDebounceWork: DispatchWorkItem?
79
84
 
80
85
  public override func load() {
81
86
  let defaults = UserDefaults.standard
@@ -220,16 +225,29 @@ public class ApexCapacitorPlugin: CAPPlugin, CAPBridgedPlugin, UNUserNotificatio
220
225
  }
221
226
  }
222
227
 
223
- /// Fire-and-forget flush. Safe to call freely; the sender's serial
224
- /// queue ensures only one drain runs at a time.
228
+ /// Fire-and-forget flush. Track-triggered calls are debounced
229
+ /// 250ms so rapid tap sessions coalesce into a single batch POST.
230
+ /// Timer + foreground + initialize callers bypass the debounce
231
+ /// since they're already paced.
225
232
  private func kickFlush(reason: String) {
226
233
  guard let queue = offlineQueue, let sender = batchSender else { return }
227
- if debug { print("[apex-capacitor] flush kicked (\(reason))") }
228
- sender.flush(queue: queue) { [weak self] flushed, remaining in
229
- if self?.debug == true && (flushed > 0 || remaining > 0) {
230
- print("[apex-capacitor] flush done sent \(flushed), remaining \(remaining)")
234
+ let runFlush: () -> Void = { [weak self] in
235
+ guard let self = self else { return }
236
+ if self.debug { print("[apex-capacitor] flush kicked (\(reason))") }
237
+ sender.flush(queue: queue) { [weak self] flushed, remaining in
238
+ if self?.debug == true && (flushed > 0 || remaining > 0) {
239
+ print("[apex-capacitor] flush done — sent \(flushed), remaining \(remaining)")
240
+ }
231
241
  }
232
242
  }
243
+ if reason == "track" {
244
+ flushDebounceWork?.cancel()
245
+ let work = DispatchWorkItem(block: runFlush)
246
+ flushDebounceWork = work
247
+ DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(250), execute: work)
248
+ } else {
249
+ runFlush()
250
+ }
233
251
  }
234
252
 
235
253
  // MARK: - ATT
@@ -424,6 +442,14 @@ public class ApexCapacitorPlugin: CAPPlugin, CAPBridgedPlugin, UNUserNotificatio
424
442
  }
425
443
  codablePayload["platform"] = AnyCodable("ios")
426
444
  codablePayload["timestamp"] = AnyCodable(ISO8601DateFormatter().string(from: Date()))
445
+ // MMP-207 — stamp visitorId. Without this, /api/events accepts
446
+ // the event into the SDK firehose store but its second pass
447
+ // bails at `if (!visitorId) continue;` so the event never
448
+ // reaches the TESTEVT# / EVT# stores the debug dashboard reads.
449
+ // Net result before this fix: events arrived but the dashboard
450
+ // showed nothing. The visitor id is the stable per-install id
451
+ // we mint in load() and persist to UserDefaults.
452
+ codablePayload["visitorId"] = AnyCodable(visitorId)
427
453
  let event = NativeQueuedEvent(
428
454
  id: id,
429
455
  payload: codablePayload,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apex-inc/capacitor-plugin",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Apex Capacitor plugin — iOS/Android attribution, events, deep linking, SKAN, and offline-tolerant tracking for Capacitor apps.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",