react-native-swipeable-actions 0.1.10 → 0.1.11

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.
@@ -66,8 +66,25 @@ public class SwipeableView: ExpoView {
66
66
  registryQueue.async(flags: .barrier) { _viewRegistry[key] = WeakRef(view) }
67
67
  }
68
68
 
69
- private static func unregisterView(for key: String) {
70
- registryQueue.async(flags: .barrier) { _viewRegistry.removeValue(forKey: key) }
69
+ private static func unregisterView(_ view: SwipeableView, for key: String) {
70
+ // Identity-check: a newer SwipeableView may have already taken over this
71
+ // key (FlashList remounts a new instance before the old one's deinit
72
+ // fires). Without this guard the old view's deinit blindly removes the
73
+ // key, leaving the new view orphaned in the registry — closeByKey for
74
+ // that key then returns nil and the visible row can't be closed.
75
+ //
76
+ // Capture identity as a *value* (ObjectIdentifier), never reference
77
+ // `view` inside the async closure. Callers include `deinit`, where
78
+ // `self` is mid-deallocation; strongly capturing it in an escaping
79
+ // closure produces a zombie reference and crashes (EXC_BAD_ACCESS) when
80
+ // the barrier finally runs after the object has been freed.
81
+ let identity = ObjectIdentifier(view)
82
+ registryQueue.async(flags: .barrier) {
83
+ if let registered = _viewRegistry[key]?.value,
84
+ ObjectIdentifier(registered) == identity {
85
+ _viewRegistry.removeValue(forKey: key)
86
+ }
87
+ }
71
88
  }
72
89
 
73
90
  private static func clearOpenState(for key: String) {
@@ -88,7 +105,15 @@ public class SwipeableView: ExpoView {
88
105
 
89
106
  static func closeByKey(_ key: String, animated: Bool = true) {
90
107
  DispatchQueue.main.async {
91
- getView(for: key)?.close(animated: animated)
108
+ if let view = getView(for: key) {
109
+ view.close(animated: animated)
110
+ } else {
111
+ // View was recycled away (or unregistered) before this close
112
+ // could run. Update the cache directly so a future cell binding
113
+ // to this key starts closed instead of inheriting a stale open
114
+ // state — see recyclingKey setter for the matching invariant.
115
+ setOpenState(for: key, isOpen: false)
116
+ }
92
117
  }
93
118
  }
94
119
 
@@ -229,10 +254,15 @@ public class SwipeableView: ExpoView {
229
254
  let shouldOpen = recyclingKey.map { Self.getOpenState(for: $0) } ?? false
230
255
  pendingShouldOpen = shouldOpen
231
256
 
232
- // Thread-safe write of old key's state and unregister
257
+ // Unregister from the old key. We intentionally do NOT write back
258
+ // `isOpen` here: open() and close() already maintain the cache when
259
+ // the user actually triggers the state change. Writing the view's
260
+ // current `isOpen` at reassignment time captures stale state when a
261
+ // pending async close hasn't run yet, leaving the old key cached as
262
+ // open forever — the next cell binding to it would snap open
263
+ // without any user intent.
233
264
  if let old = oldValue {
234
- Self.setOpenState(for: old, isOpen: isOpen)
235
- Self.unregisterView(for: old)
265
+ Self.unregisterView(self, for: old)
236
266
  }
237
267
 
238
268
  // Thread-safe register with new key
@@ -307,7 +337,7 @@ public class SwipeableView: ExpoView {
307
337
  removeGestureRecognizer(panGesture)
308
338
  // Thread-safe deregister from view registry
309
339
  if let key = recyclingKey {
310
- Self.unregisterView(for: key)
340
+ Self.unregisterView(self, for: key)
311
341
  }
312
342
  }
313
343
 
@@ -340,7 +370,7 @@ public class SwipeableView: ExpoView {
340
370
 
341
371
  // Unregister from static registry and clear cached state
342
372
  if let key = recyclingKey {
343
- Self.unregisterView(for: key)
373
+ Self.unregisterView(self, for: key)
344
374
  Self.clearOpenState(for: key)
345
375
  }
346
376
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-swipeable-actions",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "High-performance native swipeable row component for React Native using Expo Modules",
5
5
  "main": "src/index.ts",
6
6
  "types": "build/index.d.ts",