@apex-inc/capacitor-plugin 0.3.6 → 0.3.7

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.
@@ -67,6 +67,12 @@ public final class NativeBatchSender {
67
67
  /// Drains the queue, one batch at a time, with exponential backoff
68
68
  /// on retryable failures. Safe to call concurrently — only one
69
69
  /// flush actually runs at a time; concurrent callers no-op.
70
+ ///
71
+ /// Accumulator is passed by value through the recursion (no inout).
72
+ /// The previous inout-based design tripped Swift's exclusivity
73
+ /// runtime check at line 85 because the same `totalFlushed` local
74
+ /// was both captured by the `done` closure and passed inout — a
75
+ /// classic simultaneous-access violation that traps in debug builds.
70
76
  public func flush(queue: NativeOfflineQueue, completion: @escaping (Int, Int) -> Void) {
71
77
  serial.async { [weak self] in
72
78
  guard let self = self else { completion(0, 0); return }
@@ -77,54 +83,56 @@ public final class NativeBatchSender {
77
83
  return
78
84
  }
79
85
  self.inFlight = true
80
-
81
- var totalFlushed = 0
82
- self.drainLoop(queue: queue, flushed: &totalFlushed) { [weak self] in
83
- self?.inFlight = false
84
- let remaining = (try? queue.size()) ?? 0
85
- completion(totalFlushed, remaining)
86
+ self.drainLoop(queue: queue, accumulated: 0) { [weak self] flushed in
87
+ self?.serial.async {
88
+ self?.inFlight = false
89
+ let remaining = (try? queue.size()) ?? 0
90
+ completion(flushed, remaining)
91
+ }
86
92
  }
87
93
  }
88
94
  }
89
95
 
96
+ /// Recursive drain. `accumulated` is the running total of successfully
97
+ /// flushed events; `done` is invoked exactly once with the final count.
90
98
  private func drainLoop(
91
99
  queue: NativeOfflineQueue,
92
- flushed: inout Int,
93
- done: @escaping () -> Void
100
+ accumulated: Int,
101
+ done: @escaping (Int) -> Void
94
102
  ) {
95
103
  let batch: [NativeQueuedEvent]
96
104
  do {
97
105
  batch = try queue.peek(batchSize: batchSize)
98
106
  } catch {
99
107
  if debug { print("[apex-capacitor] peek failed: \(error)") }
100
- done()
108
+ done(accumulated)
101
109
  return
102
110
  }
103
111
  if batch.isEmpty {
104
- done()
112
+ done(accumulated)
105
113
  return
106
114
  }
107
115
 
108
- let localFlushed = flushed
109
116
  sendBatchWithRetry(batch, attempt: 0) { [weak self] outcome in
110
- guard let self = self else { done(); return }
117
+ guard let self = self else { done(accumulated); return }
111
118
  self.serial.async {
112
119
  let ids = batch.map { $0.id }
113
120
  switch outcome {
114
121
  case .success:
115
122
  try? queue.markSent(ids: ids)
116
123
  if self.debug { print("[apex-capacitor] flushed batch of \(batch.count)") }
117
- var nextFlushed = localFlushed + batch.count
118
- self.drainLoop(queue: queue, flushed: &nextFlushed, done: done)
119
- // After the recursive call returns the outer flushed
120
- // is no longer relevant — drain handles its own.
124
+ self.drainLoop(
125
+ queue: queue,
126
+ accumulated: accumulated + batch.count,
127
+ done: done
128
+ )
121
129
  case .nonRetryable(let status):
122
130
  if self.debug { print("[apex-capacitor] non-retryable HTTP \(status); leaving events queued") }
123
- done()
131
+ done(accumulated)
124
132
  case .retryableExhausted(let err):
125
133
  try? queue.markFailed(ids: ids)
126
134
  if self.debug { print("[apex-capacitor] retries exhausted: \(err)") }
127
- done()
135
+ done(accumulated)
128
136
  }
129
137
  }
130
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apex-inc/capacitor-plugin",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
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",