pythonnative 0.24.0__py3-none-any.whl → 0.26.0__py3-none-any.whl

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 (52) hide show
  1. pythonnative/__init__.py +16 -4
  2. pythonnative/animated.py +2 -2
  3. pythonnative/cli/pn.py +172 -12
  4. pythonnative/components.py +59 -0
  5. pythonnative/diagnostics.py +27 -0
  6. pythonnative/hooks.py +364 -81
  7. pythonnative/native_modules/app_state.py +2 -1
  8. pythonnative/native_modules/battery.py +3 -2
  9. pythonnative/native_modules/biometrics.py +2 -1
  10. pythonnative/native_modules/camera.py +16 -7
  11. pythonnative/native_modules/clipboard.py +3 -2
  12. pythonnative/native_modules/file_system.py +3 -2
  13. pythonnative/native_modules/haptics.py +7 -6
  14. pythonnative/native_modules/linking.py +62 -6
  15. pythonnative/native_modules/location.py +11 -8
  16. pythonnative/native_modules/net_info.py +2 -1
  17. pythonnative/native_modules/notifications.py +132 -9
  18. pythonnative/native_modules/permissions.py +27 -3
  19. pythonnative/native_modules/share.py +2 -1
  20. pythonnative/native_views/android.py +149 -124
  21. pythonnative/native_views/desktop.py +50 -49
  22. pythonnative/native_views/ios.py +240 -161
  23. pythonnative/project/android.py +15 -3
  24. pythonnative/project/builder.py +172 -70
  25. pythonnative/project/config.py +28 -6
  26. pythonnative/project/devices.py +291 -0
  27. pythonnative/project/doctor.py +5 -4
  28. pythonnative/project/ios.py +51 -77
  29. pythonnative/project/permissions.py +19 -1
  30. pythonnative/project/runtime_assets.py +78 -166
  31. pythonnative/reconciler.py +518 -37
  32. pythonnative/runtime.py +407 -215
  33. pythonnative/screen.py +53 -7
  34. pythonnative/storage.py +2 -2
  35. pythonnative/suspense.py +417 -0
  36. pythonnative/templates/android_template/app/build.gradle +5 -0
  37. pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +1 -0
  38. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +88 -12
  39. pythonnative/templates/ios_template/ios_template/AppDelegate.swift +41 -17
  40. pythonnative/templates/ios_template/ios_template/BridgingHeader.h +14 -0
  41. pythonnative/templates/ios_template/ios_template/Info.plist +0 -2
  42. pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +397 -0
  43. pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +31 -20
  44. pythonnative/templates/ios_template/ios_template/ViewController.swift +77 -198
  45. pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +72 -47
  46. {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/METADATA +2 -1
  47. {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/RECORD +51 -48
  48. {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/WHEEL +1 -1
  49. pythonnative/templates/ios_template/ios_template/Base.lproj/Main.storyboard +0 -24
  50. {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/entry_points.txt +0 -0
  51. {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/licenses/LICENSE +0 -0
  52. {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/top_level.txt +0 -0
@@ -42,6 +42,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple
42
42
 
43
43
  from rubicon.objc import SEL, ObjCClass, ObjCInstance, objc_method
44
44
 
45
+ from .. import diagnostics
45
46
  from ..events import dispatch_event, event_names
46
47
  from . import _tripwire_log
47
48
  from .base import ViewHandler, _safe_max, parse_color_int
@@ -80,7 +81,7 @@ try:
80
81
  _UIView = ObjCClass("UIView")
81
82
  _UIView.declare_property("superview")
82
83
  except Exception:
83
- pass
84
+ diagnostics.swallowed("ios.<module>")
84
85
 
85
86
 
86
87
  def _objc_ptr(obj: Any) -> Optional[int]:
@@ -267,17 +268,17 @@ def _apply_border(layer: Any, props: Dict[str, Any]) -> None:
267
268
  # asks for corners (matches iOS UIKit common practice).
268
269
  layer.setMasksToBounds_(True)
269
270
  except Exception:
270
- pass
271
+ diagnostics.swallowed("ios._apply_border")
271
272
  if "border_width" in props and props["border_width"] is not None:
272
273
  try:
273
274
  layer.setBorderWidth_(float(props["border_width"]))
274
275
  except Exception:
275
- pass
276
+ diagnostics.swallowed("ios._apply_border")
276
277
  if "border_color" in props and props["border_color"] is not None:
277
278
  try:
278
279
  layer.setBorderColor_(_cgcolor(props["border_color"]))
279
280
  except Exception:
280
- pass
281
+ diagnostics.swallowed("ios._apply_border")
281
282
 
282
283
 
283
284
  def _apply_view_border(view: Any, props: Dict[str, Any]) -> None:
@@ -294,13 +295,13 @@ def _apply_view_border(view: Any, props: Dict[str, Any]) -> None:
294
295
  if width > 0.0 and height > 0.0:
295
296
  radius = min(requested, min(width, height) / 2.0)
296
297
  except Exception:
297
- pass
298
+ diagnostics.swallowed("ios._apply_view_border")
298
299
  border_props = dict(props)
299
300
  border_props["border_radius"] = radius
300
301
  _apply_border(view.layer, border_props)
301
302
  return
302
303
  except Exception:
303
- pass
304
+ diagnostics.swallowed("ios._apply_view_border")
304
305
  _apply_border(view.layer, props)
305
306
 
306
307
 
@@ -344,7 +345,7 @@ def _apply_side_borders(view: Any, props: Dict[str, Any]) -> None:
344
345
  try:
345
346
  view.layer.setBorderWidth_(0.0)
346
347
  except Exception:
347
- pass
348
+ diagnostics.swallowed("ios._apply_side_borders")
348
349
  try:
349
350
  bounds = view.bounds
350
351
  w = float(bounds.size.width)
@@ -352,7 +353,7 @@ def _apply_side_borders(view: Any, props: Dict[str, Any]) -> None:
352
353
  if w > 0.0 and h > 0.0:
353
354
  _update_side_border_layers(view, w, h)
354
355
  except Exception:
355
- pass
356
+ diagnostics.swallowed("ios._apply_side_borders")
356
357
 
357
358
 
358
359
  def _update_side_border_layers(view: Any, width: float, height: float) -> None:
@@ -377,7 +378,7 @@ def _update_side_border_layers(view: Any, width: float, height: float) -> None:
377
378
  try:
378
379
  layer.removeFromSuperlayer()
379
380
  except Exception:
380
- pass
381
+ diagnostics.swallowed("ios._update_side_border_layers")
381
382
  layers[i] = None
382
383
  continue
383
384
  try:
@@ -388,7 +389,7 @@ def _update_side_border_layers(view: Any, width: float, height: float) -> None:
388
389
  layer.setBackgroundColor_(_cgcolor(entry["colors"][i]))
389
390
  layer.setFrame_(frames[i])
390
391
  except Exception:
391
- pass
392
+ diagnostics.swallowed("ios._update_side_border_layers")
392
393
 
393
394
 
394
395
  def _clamp_view_corner_radius(view: Any, width: float, height: float) -> None:
@@ -402,7 +403,7 @@ def _clamp_view_corner_radius(view: Any, width: float, height: float) -> None:
402
403
  try:
403
404
  view.layer.setCornerRadius_(min(float(requested), max_radius))
404
405
  except Exception:
405
- pass
406
+ diagnostics.swallowed("ios._clamp_view_corner_radius")
406
407
 
407
408
 
408
409
  def _clamp_layer_corner_radius(layer: Any, width: float, height: float) -> None:
@@ -418,7 +419,7 @@ def _clamp_layer_corner_radius(layer: Any, width: float, height: float) -> None:
418
419
  try:
419
420
  layer.setCornerRadius_(min(requested, max_radius))
420
421
  except Exception:
421
- pass
422
+ diagnostics.swallowed("ios._clamp_layer_corner_radius")
422
423
 
423
424
 
424
425
  def _apply_shadow(view: Any, props: Dict[str, Any]) -> None:
@@ -434,22 +435,22 @@ def _apply_shadow(view: Any, props: Dict[str, Any]) -> None:
434
435
  layer.setMasksToBounds_(False)
435
436
  view.setClipsToBounds_(False)
436
437
  except Exception:
437
- pass
438
+ diagnostics.swallowed("ios._apply_shadow")
438
439
  if "shadow_color" in props and props["shadow_color"] is not None:
439
440
  try:
440
441
  layer.setShadowColor_(_cgcolor(props["shadow_color"]))
441
442
  except Exception:
442
- pass
443
+ diagnostics.swallowed("ios._apply_shadow")
443
444
  if "shadow_opacity" in props and props["shadow_opacity"] is not None:
444
445
  try:
445
446
  layer.setShadowOpacity_(float(props["shadow_opacity"]))
446
447
  except Exception:
447
- pass
448
+ diagnostics.swallowed("ios._apply_shadow")
448
449
  if "shadow_radius" in props and props["shadow_radius"] is not None:
449
450
  try:
450
451
  layer.setShadowRadius_(float(props["shadow_radius"]))
451
452
  except Exception:
452
- pass
453
+ diagnostics.swallowed("ios._apply_shadow")
453
454
  if "shadow_offset" in props and props["shadow_offset"] is not None:
454
455
  offset = props["shadow_offset"]
455
456
  try:
@@ -460,7 +461,7 @@ def _apply_shadow(view: Any, props: Dict[str, Any]) -> None:
460
461
  w, h = float(offset[0]), float(offset[1])
461
462
  layer.setShadowOffset_((w, h))
462
463
  except Exception:
463
- pass
464
+ diagnostics.swallowed("ios._apply_shadow")
464
465
 
465
466
 
466
467
  def _make_transform(spec: Any) -> Any:
@@ -534,7 +535,7 @@ def _apply_transform(view: Any, props: Dict[str, Any]) -> None:
534
535
  try:
535
536
  view.setTransform_((1.0, 0.0, 0.0, 1.0, 0.0, 0.0))
536
537
  except Exception:
537
- pass
538
+ diagnostics.swallowed("ios._apply_transform")
538
539
  return
539
540
  try:
540
541
  transform = _make_transform(spec)
@@ -565,7 +566,7 @@ def _apply_transform(view: Any, props: Dict[str, Any]) -> None:
565
566
  # rubicon-objc accepts the C struct as a tuple of its fields.
566
567
  view.setTransform_((a, b, c, d, tx, ty))
567
568
  except Exception:
568
- pass
569
+ diagnostics.swallowed("ios._apply_transform")
569
570
 
570
571
 
571
572
  # UIAccessibilityTraits bitmask values (UIKit constants).
@@ -604,25 +605,25 @@ def _apply_accessibility(view: Any, props: Dict[str, Any]) -> None:
604
605
  try:
605
606
  view.setIsAccessibilityElement_(bool(props["accessible"]))
606
607
  except Exception:
607
- pass
608
+ diagnostics.swallowed("ios._apply_accessibility")
608
609
  if "accessibility_label" in props:
609
610
  v = props["accessibility_label"]
610
611
  try:
611
612
  view.setAccessibilityLabel_(str(v) if v is not None else "")
612
613
  except Exception:
613
- pass
614
+ diagnostics.swallowed("ios._apply_accessibility")
614
615
  if "accessibility_hint" in props:
615
616
  v = props["accessibility_hint"]
616
617
  try:
617
618
  view.setAccessibilityHint_(str(v) if v is not None else "")
618
619
  except Exception:
619
- pass
620
+ diagnostics.swallowed("ios._apply_accessibility")
620
621
  if "test_id" in props:
621
622
  v = props["test_id"]
622
623
  try:
623
624
  view.setAccessibilityIdentifier_(str(v) if v is not None else None)
624
625
  except Exception:
625
- pass
626
+ diagnostics.swallowed("ios._apply_accessibility")
626
627
 
627
628
  role = props.get("accessibility_role")
628
629
  state = props.get("accessibility_state")
@@ -642,11 +643,11 @@ def _apply_accessibility(view: Any, props: Dict[str, Any]) -> None:
642
643
  try:
643
644
  view.setAccessibilityValue_("expanded" if state["expanded"] else "collapsed")
644
645
  except Exception:
645
- pass
646
+ diagnostics.swallowed("ios._apply_accessibility")
646
647
  try:
647
648
  view.setAccessibilityTraits_(traits)
648
649
  except Exception:
649
- pass
650
+ diagnostics.swallowed("ios._apply_accessibility")
650
651
 
651
652
 
652
653
  def _apply_common_visual(view: Any, props: Dict[str, Any]) -> None:
@@ -657,14 +658,14 @@ def _apply_common_visual(view: Any, props: Dict[str, Any]) -> None:
657
658
  try:
658
659
  view.layer.setBackgroundColor_(color.CGColor)
659
660
  except Exception:
660
- pass
661
+ diagnostics.swallowed("ios._apply_common_visual")
661
662
  if "overflow" in props:
662
663
  view.setClipsToBounds_(props["overflow"] == "hidden")
663
664
  if "opacity" in props and props["opacity"] is not None:
664
665
  try:
665
666
  view.setAlpha_(float(props["opacity"]))
666
667
  except Exception:
667
- pass
668
+ diagnostics.swallowed("ios._apply_common_visual")
668
669
  _apply_view_border(view, props)
669
670
  _apply_side_borders(view, props)
670
671
  _apply_shadow(view, props)
@@ -733,7 +734,7 @@ def _set_recognizer_delegate(recognizer: Any) -> None:
733
734
  rec_ptr = recognizer.ptr if hasattr(recognizer, "ptr") else recognizer
734
735
  _objc_msgSend(rec_ptr, _SEL_SET_DELEGATE, delegate)
735
736
  except Exception:
736
- pass
737
+ diagnostics.swallowed("ios._set_recognizer_delegate")
737
738
 
738
739
 
739
740
  # ======================================================================
@@ -753,6 +754,7 @@ def _set_recognizer_delegate(recognizer: Any) -> None:
753
754
  _pn_action_handlers: Dict[int, Any] = {}
754
755
 
755
756
  _ACTION_IMP_TYPE = _ct.CFUNCTYPE(None, _ct.c_void_p, _ct.c_void_p, _ct.c_void_p)
757
+ _CONTROL_ACTION_IMP_TYPE = _ct.CFUNCTYPE(None, _ct.c_void_p, _ct.c_void_p, _ct.c_void_p, _ct.c_void_p)
756
758
 
757
759
 
758
760
  def _action_imp(_self_ptr: int, _cmd_ptr: int, sender_ptr: int) -> None:
@@ -763,12 +765,70 @@ def _action_imp(_self_ptr: int, _cmd_ptr: int, sender_ptr: int) -> None:
763
765
  try:
764
766
  handler()
765
767
  except Exception:
766
- pass
768
+ diagnostics.swallowed("ios._action_imp")
767
769
 
768
770
 
769
771
  _action_imp_ref = _ACTION_IMP_TYPE(_action_imp)
770
772
 
773
+ # ----------------------------------------------------------------------
774
+ # UIControl action dedupe
775
+ # ----------------------------------------------------------------------
776
+ #
777
+ # The simulator's XCTest event-injection stack sometimes delivers a
778
+ # single synthesized tap as *two* back-to-back "send control actions"
779
+ # bursts for the same UIEvent (~1ms apart; the unified log shows one
780
+ # UIEvent delivered to one window, then the doubled sends). Every
781
+ # non-idempotent ``on_press`` then fires twice: reducer counters jump
782
+ # by two, alerts present twice, pickers reopen after selecting. The
783
+ # control action selector therefore takes the ``forEvent:`` argument
784
+ # and drops a second send whose UIEvent timestamp matches the last one
785
+ # delivered to the same control. Distinct taps (even rapid ones) carry
786
+ # distinct event timestamps, and continuous ValueChanged streams
787
+ # (slider drags) tick the timestamp per touch move, so only the
788
+ # duplicated sends are filtered. Programmatic ``sendActions...`` calls
789
+ # pass a nil event and are never deduped.
790
+
791
+ # Maps control ptr -> UIEvent.timestamp of the last delivered action.
792
+ _pn_last_control_event_ts: Dict[int, float] = {}
793
+
794
+ _objc_msgSend_double = _ct.CFUNCTYPE(_ct.c_double, _ct.c_void_p, _ct.c_void_p)(("objc_msgSend", _libobjc))
795
+
796
+ _SEL_TIMESTAMP = _sel_reg(b"timestamp")
797
+
798
+
799
+ def _control_action_imp(_self_ptr: int, _cmd_ptr: int, sender_ptr: int, event_ptr: int) -> None:
800
+ """Raw C callback for every PythonNative UIControl event action."""
801
+ key = int(sender_ptr or 0)
802
+ handler = _pn_action_handlers.get(key)
803
+ if handler is None:
804
+ return
805
+ if event_ptr:
806
+ try:
807
+ timestamp = float(_objc_msgSend_double(event_ptr, _SEL_TIMESTAMP))
808
+ except Exception:
809
+ timestamp = None
810
+ if timestamp is not None:
811
+ if _pn_last_control_event_ts.get(key) == timestamp:
812
+ diagnostics.warn_once(
813
+ "ios._control_action_imp dropped a duplicated control action "
814
+ "(one UIEvent, two sends; a simulator event-injection quirk)",
815
+ key=f"control_action_dedupe:{key}",
816
+ )
817
+ return
818
+ _pn_last_control_event_ts[key] = timestamp
819
+ try:
820
+ handler()
821
+ except Exception:
822
+ diagnostics.swallowed("ios._control_action_imp")
823
+
824
+
825
+ _control_action_imp_ref = _CONTROL_ACTION_IMP_TYPE(_control_action_imp)
826
+
827
+ # Gesture recognizers only support zero- or one-argument action
828
+ # selectors, so recognizers keep the plain variant while controls use
829
+ # the two-argument ``forEvent:`` variant for the dedupe above.
771
830
  _SEL_ON_ACTION = _sel_reg(b"onPNAction:")
831
+ _SEL_ON_CONTROL_ACTION = _sel_reg(b"onPNControlAction:forEvent:")
772
832
 
773
833
  _PN_ACTION_TARGET_CLS = _alloc_cls(_NS_OBJECT_CLS, b"_PNActionTargetCTypes", 0)
774
834
  if _PN_ACTION_TARGET_CLS:
@@ -778,6 +838,12 @@ if _PN_ACTION_TARGET_CLS:
778
838
  _ct.cast(_action_imp_ref, _ct.c_void_p),
779
839
  b"v@:@",
780
840
  )
841
+ _add_method(
842
+ _PN_ACTION_TARGET_CLS,
843
+ _SEL_ON_CONTROL_ACTION,
844
+ _ct.cast(_control_action_imp_ref, _ct.c_void_p),
845
+ b"v@:@@",
846
+ )
781
847
  _reg_cls(_PN_ACTION_TARGET_CLS)
782
848
 
783
849
  _pn_action_target_ptr: Any = None
@@ -830,7 +896,7 @@ def _register_control_action(control: Any, events_mask: int, handler: Any) -> No
830
896
  _objc_msgSend.restype = None
831
897
  _objc_msgSend.argtypes = [_ct.c_void_p, _ct.c_void_p, _ct.c_void_p, _ct.c_ulong]
832
898
  ctl_ptr = control.ptr if hasattr(control, "ptr") else control
833
- _objc_msgSend(ctl_ptr, _SEL_ADD_TARGET_ACTION_EVENTS, target_ptr, _SEL_ON_ACTION, events_mask)
899
+ _objc_msgSend(ctl_ptr, _SEL_ADD_TARGET_ACTION_EVENTS, target_ptr, _SEL_ON_CONTROL_ACTION, events_mask)
834
900
  _pn_action_handlers[_recognizer_ptr(control)] = handler
835
901
 
836
902
 
@@ -864,11 +930,11 @@ def _make_gesture_handler(
864
930
  payload["x"] = float(location.x)
865
931
  payload["y"] = float(location.y)
866
932
  except Exception:
867
- pass
933
+ diagnostics.swallowed("ios._make_gesture_handler.handler")
868
934
  try:
869
935
  payload["pointer_count"] = int(rec.numberOfTouches)
870
936
  except Exception:
871
- pass
937
+ diagnostics.swallowed("ios._make_gesture_handler.handler")
872
938
 
873
939
  if kind == "pan":
874
940
  try:
@@ -879,17 +945,17 @@ def _make_gesture_handler(
879
945
  payload["velocity_x"] = float(velocity.x)
880
946
  payload["velocity_y"] = float(velocity.y)
881
947
  except Exception:
882
- pass
948
+ diagnostics.swallowed("ios._make_gesture_handler.handler")
883
949
  elif kind == "pinch":
884
950
  try:
885
951
  payload["scale"] = float(rec.scale)
886
952
  except Exception:
887
- pass
953
+ diagnostics.swallowed("ios._make_gesture_handler.handler")
888
954
  elif kind == "rotation":
889
955
  try:
890
956
  payload["rotation"] = float(rec.rotation)
891
957
  except Exception:
892
- pass
958
+ diagnostics.swallowed("ios._make_gesture_handler.handler")
893
959
  elif kind == "swipe":
894
960
  # Discrete: UIKit only calls us on recognition, and only the
895
961
  # recognizer whose direction matched fires, so the bound
@@ -917,7 +983,7 @@ def _make_recognizer(kind: str, spec: Dict[str, Any]) -> List[Tuple[Any, Optiona
917
983
  try:
918
984
  rec.setNumberOfTapsRequired_(max(1, int(spec.get("n_taps", 1))))
919
985
  except Exception:
920
- pass
986
+ diagnostics.swallowed("ios._make_recognizer")
921
987
  out.append((rec, None))
922
988
  elif kind == "long_press":
923
989
  rec = ObjCClass("UILongPressGestureRecognizer").alloc().init()
@@ -925,14 +991,14 @@ def _make_recognizer(kind: str, spec: Dict[str, Any]) -> List[Tuple[Any, Optiona
925
991
  rec.setMinimumPressDuration_(float(spec.get("min_duration_ms", 500.0)) / 1000.0)
926
992
  rec.setAllowableMovement_(float(spec.get("max_distance", 12.0)))
927
993
  except Exception:
928
- pass
994
+ diagnostics.swallowed("ios._make_recognizer")
929
995
  out.append((rec, None))
930
996
  elif kind == "pan":
931
997
  rec = ObjCClass("UIPanGestureRecognizer").alloc().init()
932
998
  try:
933
999
  rec.setMinimumNumberOfTouches_(max(1, int(spec.get("min_pointers", 1))))
934
1000
  except Exception:
935
- pass
1001
+ diagnostics.swallowed("ios._make_recognizer")
936
1002
  out.append((rec, None))
937
1003
  elif kind == "swipe":
938
1004
  direction = str(spec.get("direction", "any"))
@@ -942,7 +1008,7 @@ def _make_recognizer(kind: str, spec: Dict[str, Any]) -> List[Tuple[Any, Optiona
942
1008
  try:
943
1009
  rec.setDirection_(_SWIPE_DIRECTIONS[d])
944
1010
  except Exception:
945
- pass
1011
+ diagnostics.swallowed("ios._make_recognizer")
946
1012
  out.append((rec, d))
947
1013
  elif kind == "pinch":
948
1014
  out.append((ObjCClass("UIPinchGestureRecognizer").alloc().init(), None))
@@ -963,7 +1029,7 @@ def _wire_gestures(view: Any, specs: Any) -> None:
963
1029
  try:
964
1030
  view.removeGestureRecognizer_(rec)
965
1031
  except Exception:
966
- pass
1032
+ diagnostics.swallowed("ios._wire_gestures")
967
1033
  _pn_action_handlers.pop(_recognizer_ptr(rec), None)
968
1034
  state["gesture_recognizers"] = []
969
1035
  if not isinstance(specs, (list, tuple)) or not specs:
@@ -972,7 +1038,7 @@ def _wire_gestures(view: Any, specs: Any) -> None:
972
1038
  try:
973
1039
  view.setUserInteractionEnabled_(True)
974
1040
  except Exception:
975
- pass
1041
+ diagnostics.swallowed("ios._wire_gestures")
976
1042
 
977
1043
  recognizers: List[Any] = []
978
1044
  for i, spec in enumerate(specs):
@@ -985,7 +1051,7 @@ def _wire_gestures(view: Any, specs: Any) -> None:
985
1051
  try:
986
1052
  rec.setCancelsTouchesInView_(False)
987
1053
  except Exception:
988
- pass
1054
+ diagnostics.swallowed("ios._wire_gestures")
989
1055
  _set_recognizer_delegate(rec)
990
1056
  try:
991
1057
  view.addGestureRecognizer_(rec)
@@ -1082,13 +1148,13 @@ def _start_native_animation(view: Any, anim_id: int, prop_name: str, spec: Dict[
1082
1148
 
1083
1149
  native_animation_completed(anim_id, bool(finished))
1084
1150
  except Exception:
1085
- pass
1151
+ diagnostics.swallowed("ios._start_native_animation._completion")
1086
1152
 
1087
1153
  def _animations() -> None:
1088
1154
  try:
1089
1155
  applier_to(view)
1090
1156
  except Exception:
1091
- pass
1157
+ diagnostics.swallowed("ios._start_native_animation._animations")
1092
1158
 
1093
1159
  try:
1094
1160
  # Snap to the starting value so the animation covers the full
@@ -1166,12 +1232,13 @@ class IOSViewHandler(ViewHandler):
1166
1232
  # Controls register their own pointer as the action-handler key
1167
1233
  # (see _register_control_action); drop it with the view.
1168
1234
  _pn_action_handlers.pop(_recognizer_ptr(native_view), None)
1235
+ _pn_last_control_event_ts.pop(_recognizer_ptr(native_view), None)
1169
1236
  _pn_view_border_radius_map.pop(id(native_view), None)
1170
1237
  _pn_side_border_map.pop(id(native_view), None)
1171
1238
  try:
1172
1239
  native_view.removeFromSuperview()
1173
1240
  except Exception:
1174
- pass
1241
+ diagnostics.swallowed("ios.IOSViewHandler.destroy")
1175
1242
  _forget(native_view)
1176
1243
 
1177
1244
  def _build(self, props: Dict[str, Any]) -> Any:
@@ -1187,7 +1254,7 @@ class IOSViewHandler(ViewHandler):
1187
1254
  try:
1188
1255
  child.setTranslatesAutoresizingMaskIntoConstraints_(True)
1189
1256
  except Exception:
1190
- pass
1257
+ diagnostics.swallowed("ios.IOSViewHandler.insert_child")
1191
1258
  try:
1192
1259
  count = len(list(parent.subviews or []))
1193
1260
  except Exception:
@@ -1198,13 +1265,13 @@ class IOSViewHandler(ViewHandler):
1198
1265
  try:
1199
1266
  parent.addSubview_(child)
1200
1267
  except Exception:
1201
- pass
1268
+ diagnostics.swallowed("ios.IOSViewHandler.insert_child")
1202
1269
 
1203
1270
  def remove_child(self, parent: Any, child: Any) -> None:
1204
1271
  try:
1205
1272
  child.removeFromSuperview()
1206
1273
  except Exception:
1207
- pass
1274
+ diagnostics.swallowed("ios.IOSViewHandler.remove_child")
1208
1275
 
1209
1276
  def set_frame(self, native_view: Any, x: float, y: float, width: float, height: float) -> None:
1210
1277
  if native_view is None:
@@ -1221,7 +1288,7 @@ class IOSViewHandler(ViewHandler):
1221
1288
  try:
1222
1289
  _clamp_layer_corner_radius(native_view.layer, frame_w, frame_h)
1223
1290
  except Exception:
1224
- pass
1291
+ diagnostics.swallowed("ios.IOSViewHandler.set_frame")
1225
1292
  try:
1226
1293
  parent = native_view.superview
1227
1294
  parent_cls = ""
@@ -1238,9 +1305,9 @@ class IOSViewHandler(ViewHandler):
1238
1305
  content_h = max(float(bounds.size.height), frame_y + frame_h)
1239
1306
  parent.setContentSize_((content_w, content_h))
1240
1307
  except Exception:
1241
- pass
1308
+ diagnostics.swallowed("ios.IOSViewHandler.set_frame")
1242
1309
  except Exception:
1243
- pass
1310
+ diagnostics.swallowed("ios.IOSViewHandler.set_frame")
1244
1311
 
1245
1312
  def measure_intrinsic(
1246
1313
  self,
@@ -1273,7 +1340,7 @@ class IOSViewHandler(ViewHandler):
1273
1340
  try:
1274
1341
  applier(native_view)
1275
1342
  except Exception:
1276
- pass
1343
+ diagnostics.swallowed("ios.IOSViewHandler.set_animated_property")
1277
1344
 
1278
1345
  def start_animation(
1279
1346
  self,
@@ -1312,7 +1379,7 @@ class IOSViewHandler(ViewHandler):
1312
1379
  try:
1313
1380
  view.layer.removeAllAnimations()
1314
1381
  except Exception:
1315
- pass
1382
+ diagnostics.swallowed("ios.IOSViewHandler.cancel_animation")
1316
1383
  return value
1317
1384
 
1318
1385
 
@@ -1400,7 +1467,7 @@ class TextHandler(IOSViewHandler):
1400
1467
  if desc is not None:
1401
1468
  font = UIFont.fontWithDescriptor_size_(desc, size)
1402
1469
  except Exception:
1403
- pass
1470
+ diagnostics.swallowed("ios.TextHandler._font_for")
1404
1471
  return font
1405
1472
 
1406
1473
  _SPAN_REBUILD_KEYS = (
@@ -1463,7 +1530,7 @@ class TextHandler(IOSViewHandler):
1463
1530
  try:
1464
1531
  label.setAlpha_(float(props["opacity"]))
1465
1532
  except Exception:
1466
- pass
1533
+ diagnostics.swallowed("ios.TextHandler._apply")
1467
1534
 
1468
1535
  def _apply_spans(self, label: Any, merged: Dict[str, Any]) -> None:
1469
1536
  """Render a rich-text span list as one NSAttributedString.
@@ -1525,13 +1592,13 @@ class TextHandler(IOSViewHandler):
1525
1592
  elif span.get("text_decoration") == "line_through":
1526
1593
  attr.addAttribute_value_range_("NSStrikethrough", 1, rng)
1527
1594
  except Exception:
1528
- pass
1595
+ diagnostics.swallowed("ios.TextHandler._apply_spans")
1529
1596
  label.setAttributedText_(attr)
1530
1597
  except Exception:
1531
1598
  try:
1532
1599
  label.setText_(str(merged.get("text") or ""))
1533
1600
  except Exception:
1534
- pass
1601
+ diagnostics.swallowed("ios.TextHandler._apply_spans")
1535
1602
 
1536
1603
  def _apply_attributed(self, label: Any, props: Dict[str, Any]) -> None:
1537
1604
  """Re-render the label's text as an NSAttributedString.
@@ -1576,7 +1643,7 @@ class TextHandler(IOSViewHandler):
1576
1643
  attr.addAttribute_value_range_("NSStrikethrough", 1, full_range)
1577
1644
  label.setAttributedText_(attr)
1578
1645
  except Exception:
1579
- pass
1646
+ diagnostics.swallowed("ios.TextHandler._apply_attributed")
1580
1647
 
1581
1648
 
1582
1649
  class ButtonHandler(IOSViewHandler):
@@ -1632,7 +1699,7 @@ class ButtonHandler(IOSViewHandler):
1632
1699
  try:
1633
1700
  btn.setAlpha_(float(props["opacity"]))
1634
1701
  except Exception:
1635
- pass
1702
+ diagnostics.swallowed("ios.ButtonHandler._apply")
1636
1703
 
1637
1704
 
1638
1705
  # ``scrollViewDidScroll:`` hands the delegate a ``UIScrollView*``. rubicon's
@@ -1718,24 +1785,24 @@ class ScrollViewHandler(IOSViewHandler):
1718
1785
  sv.setShowsVerticalScrollIndicator_(visible)
1719
1786
  sv.setShowsHorizontalScrollIndicator_(visible)
1720
1787
  except Exception:
1721
- pass
1788
+ diagnostics.swallowed("ios.ScrollViewHandler._apply")
1722
1789
  if "paging_enabled" in props:
1723
1790
  try:
1724
1791
  sv.setPagingEnabled_(bool(props["paging_enabled"]))
1725
1792
  except Exception:
1726
- pass
1793
+ diagnostics.swallowed("ios.ScrollViewHandler._apply")
1727
1794
  if "bounces" in props:
1728
1795
  b = props["bounces"]
1729
1796
  try:
1730
1797
  sv.setBounces_(True if b is None else bool(b))
1731
1798
  except Exception:
1732
- pass
1799
+ diagnostics.swallowed("ios.ScrollViewHandler._apply")
1733
1800
  if "keyboard_dismiss_mode" in props and props["keyboard_dismiss_mode"] is not None:
1734
1801
  mapping = {"none": 0, "on_drag": 1, "interactive": 2}
1735
1802
  try:
1736
1803
  sv.setKeyboardDismissMode_(mapping.get(props["keyboard_dismiss_mode"], 0))
1737
1804
  except Exception:
1738
- pass
1805
+ diagnostics.swallowed("ios.ScrollViewHandler._apply")
1739
1806
 
1740
1807
  def command(self, native_view: Any, name: str, args: Dict[str, Any]) -> Any:
1741
1808
  if name == "scroll_to_offset":
@@ -1745,7 +1812,7 @@ class ScrollViewHandler(IOSViewHandler):
1745
1812
  try:
1746
1813
  native_view.setContentOffset_animated_((x, y), animated)
1747
1814
  except Exception:
1748
- pass
1815
+ diagnostics.swallowed("ios.ScrollViewHandler.command")
1749
1816
  return None
1750
1817
  if name == "scroll_to_end":
1751
1818
  animated = args.get("animated", True) is not False
@@ -1760,7 +1827,7 @@ class ScrollViewHandler(IOSViewHandler):
1760
1827
  offset = (target_x, 0.0) if horizontal else (0.0, target_y)
1761
1828
  native_view.setContentOffset_animated_(offset, animated)
1762
1829
  except Exception:
1763
- pass
1830
+ diagnostics.swallowed("ios.ScrollViewHandler.command")
1764
1831
  return None
1765
1832
  if name == "get_scroll_offset":
1766
1833
  try:
@@ -1796,10 +1863,11 @@ class ScrollViewHandler(IOSViewHandler):
1796
1863
  if existing is not None:
1797
1864
  existing.endRefreshing()
1798
1865
  _pn_action_handlers.pop(_recognizer_ptr(existing), None)
1866
+ _pn_last_control_event_ts.pop(_recognizer_ptr(existing), None)
1799
1867
  sv.setRefreshControl_(None)
1800
1868
  sv.setAlwaysBounceVertical_(False)
1801
1869
  except Exception:
1802
- pass
1870
+ diagnostics.swallowed("ios.ScrollViewHandler._apply_refresh")
1803
1871
  return
1804
1872
  try:
1805
1873
  existing = sv.refreshControl
@@ -1816,7 +1884,7 @@ class ScrollViewHandler(IOSViewHandler):
1816
1884
  try:
1817
1885
  sv.setAlwaysBounceVertical_(True)
1818
1886
  except Exception:
1819
- pass
1887
+ diagnostics.swallowed("ios.ScrollViewHandler._apply_refresh")
1820
1888
  existing = rc
1821
1889
  refreshing = bool(spec.get("refreshing")) if isinstance(spec, dict) else False
1822
1890
  if refreshing:
@@ -1824,7 +1892,7 @@ class ScrollViewHandler(IOSViewHandler):
1824
1892
  else:
1825
1893
  existing.endRefreshing()
1826
1894
  except Exception:
1827
- pass
1895
+ diagnostics.swallowed("ios.ScrollViewHandler._apply_refresh")
1828
1896
 
1829
1897
 
1830
1898
  class ImageHandler(IOSViewHandler):
@@ -1842,12 +1910,12 @@ class ImageHandler(IOSViewHandler):
1842
1910
  try:
1843
1911
  iv.setTintColor_(_uicolor(props["tint_color"]))
1844
1912
  except Exception:
1845
- pass
1913
+ diagnostics.swallowed("ios.ImageHandler._apply")
1846
1914
  if "placeholder_color" in props and props["placeholder_color"] is not None:
1847
1915
  try:
1848
1916
  iv.setBackgroundColor_(_uicolor(props["placeholder_color"]))
1849
1917
  except Exception:
1850
- pass
1918
+ diagnostics.swallowed("ios.ImageHandler._apply")
1851
1919
  if "source" in props and props["source"]:
1852
1920
  self._load_source(iv, str(props["source"]))
1853
1921
  if "scale_type" in props and props["scale_type"]:
@@ -1873,7 +1941,7 @@ class ImageHandler(IOSViewHandler):
1873
1941
  w, h = max_width, h * scale
1874
1942
  return (w, h)
1875
1943
  except Exception:
1876
- pass
1944
+ diagnostics.swallowed("ios.ImageHandler.measure_intrinsic")
1877
1945
  return (0.0, 0.0)
1878
1946
 
1879
1947
  def _load_source(self, iv: Any, source: str) -> None:
@@ -1894,7 +1962,7 @@ class ImageHandler(IOSViewHandler):
1894
1962
  else:
1895
1963
  _fire(iv, "on_error", f"image {source!r} not found")
1896
1964
  except Exception:
1897
- pass
1965
+ diagnostics.swallowed("ios.ImageHandler._load_source")
1898
1966
 
1899
1967
  def _load_data_uri(self, iv: Any, source: str) -> None:
1900
1968
  """Decode an inline ``data:`` URI (base64 payload) into the view."""
@@ -1942,7 +2010,7 @@ class ImageHandler(IOSViewHandler):
1942
2010
  iv.setImage_(image)
1943
2011
  _fire(iv, "on_load")
1944
2012
  except Exception:
1945
- pass
2013
+ diagnostics.swallowed("ios.ImageHandler._load_async.on_ready")
1946
2014
 
1947
2015
  def on_error(message: str) -> None:
1948
2016
  if _state_of(iv).get("pending_uri") == source:
@@ -1950,7 +2018,7 @@ class ImageHandler(IOSViewHandler):
1950
2018
 
1951
2019
  fetch(source, on_ready, on_error)
1952
2020
  except Exception:
1953
- pass
2021
+ diagnostics.swallowed("ios.ImageHandler._load_async")
1954
2022
 
1955
2023
  def _decode_downsampled(self, iv: Any, path: str) -> Any:
1956
2024
  """Decode a file, scaling down to the view's bounds if oversized.
@@ -2052,7 +2120,7 @@ def _tf_on_change_imp(self_ptr: int, _cmd: int, sender_ptr: int) -> None:
2052
2120
  try:
2053
2121
  view.setText_(text)
2054
2122
  except Exception:
2055
- pass
2123
+ diagnostics.swallowed("ios._tf_on_change_imp")
2056
2124
  finally:
2057
2125
  state["suppress"] = False
2058
2126
  _fire(view, "on_change", text)
@@ -2077,7 +2145,7 @@ def _tf_should_return_imp(self_ptr: int, _cmd: int, tf_ptr: int) -> bool:
2077
2145
  _objc_msgSend.argtypes = [_ct.c_void_p, _ct.c_void_p]
2078
2146
  _objc_msgSend(_ct.c_void_p(int(tf_ptr or 0)), _SEL_RESIGN_FIRST_RESPONDER)
2079
2147
  except Exception:
2080
- pass
2148
+ diagnostics.swallowed("ios._tf_should_return_imp")
2081
2149
  return True
2082
2150
 
2083
2151
 
@@ -2244,7 +2312,7 @@ class TextInputHandler(IOSViewHandler):
2244
2312
  )
2245
2313
  view.setAttributedPlaceholder_(attr)
2246
2314
  except Exception:
2247
- pass
2315
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2248
2316
  if "font_size" in props and props["font_size"] is not None:
2249
2317
  view.setFont_(UIFont.systemFontOfSize_(float(props["font_size"])))
2250
2318
  if "color" in props and props["color"] is not None:
@@ -2255,7 +2323,7 @@ class TextInputHandler(IOSViewHandler):
2255
2323
  try:
2256
2324
  view.setSecureTextEntry_(bool(props["secure"]))
2257
2325
  except Exception:
2258
- pass
2326
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2259
2327
  if "keyboard_type" in props and props["keyboard_type"] is not None:
2260
2328
  mapping = {
2261
2329
  "default": 0,
@@ -2270,19 +2338,19 @@ class TextInputHandler(IOSViewHandler):
2270
2338
  try:
2271
2339
  view.setKeyboardType_(mapping.get(props["keyboard_type"], 0))
2272
2340
  except Exception:
2273
- pass
2341
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2274
2342
  if "auto_capitalize" in props and props["auto_capitalize"] is not None:
2275
2343
  # UITextAutocapitalizationType: none=0, words=1, sentences=2, all=3.
2276
2344
  mapping = {"none": 0, "words": 1, "sentences": 2, "characters": 3}
2277
2345
  try:
2278
2346
  view.setAutocapitalizationType_(mapping.get(props["auto_capitalize"], 2))
2279
2347
  except Exception:
2280
- pass
2348
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2281
2349
  if "auto_correct" in props:
2282
2350
  try:
2283
2351
  view.setAutocorrectionType_(1 if props["auto_correct"] else 0)
2284
2352
  except Exception:
2285
- pass
2353
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2286
2354
  if "return_key_type" in props and props["return_key_type"] is not None:
2287
2355
  mapping = {
2288
2356
  "default": 0,
@@ -2299,23 +2367,23 @@ class TextInputHandler(IOSViewHandler):
2299
2367
  try:
2300
2368
  view.setReturnKeyType_(mapping.get(props["return_key_type"], 0))
2301
2369
  except Exception:
2302
- pass
2370
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2303
2371
  if "selection_color" in props and props["selection_color"] is not None:
2304
2372
  try:
2305
2373
  view.setTintColor_(_uicolor(props["selection_color"]))
2306
2374
  except Exception:
2307
- pass
2375
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2308
2376
  if "text_content_type" in props:
2309
2377
  tct = props["text_content_type"]
2310
2378
  try:
2311
2379
  view.setTextContentType_(_ui_text_content_type(str(tct)) if tct is not None else None)
2312
2380
  except Exception:
2313
- pass
2381
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2314
2382
  if "clear_button" in props and is_field:
2315
2383
  try:
2316
2384
  view.setClearButtonMode_(1 if props["clear_button"] else 0) # 1 = WhileEditing
2317
2385
  except Exception:
2318
- pass
2386
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2319
2387
  # ``editable`` is present only when False (read-only). A removed
2320
2388
  # prop arrives as None on update, which means "editable again".
2321
2389
  if "editable" in props:
@@ -2327,12 +2395,12 @@ class TextInputHandler(IOSViewHandler):
2327
2395
  else:
2328
2396
  view.setEditable_(resolved)
2329
2397
  except Exception:
2330
- pass
2398
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2331
2399
  if "auto_focus" in props and props["auto_focus"]:
2332
2400
  try:
2333
2401
  view.becomeFirstResponder()
2334
2402
  except Exception:
2335
- pass
2403
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2336
2404
  _apply_view_border(view, props)
2337
2405
  _apply_shadow(view, props)
2338
2406
  _apply_transform(view, props)
@@ -2341,7 +2409,7 @@ class TextInputHandler(IOSViewHandler):
2341
2409
  try:
2342
2410
  view.setAlpha_(float(props["opacity"]))
2343
2411
  except Exception:
2344
- pass
2412
+ diagnostics.swallowed("ios.TextInputHandler._apply")
2345
2413
 
2346
2414
  def measure_intrinsic(
2347
2415
  self,
@@ -2357,13 +2425,13 @@ class TextInputHandler(IOSViewHandler):
2357
2425
  try:
2358
2426
  native_view.becomeFirstResponder()
2359
2427
  except Exception:
2360
- pass
2428
+ diagnostics.swallowed("ios.TextInputHandler.command")
2361
2429
  return None
2362
2430
  if name == "blur":
2363
2431
  try:
2364
2432
  native_view.resignFirstResponder()
2365
2433
  except Exception:
2366
- pass
2434
+ diagnostics.swallowed("ios.TextInputHandler.command")
2367
2435
  return None
2368
2436
  if name == "clear":
2369
2437
  state = _state_of(native_view)
@@ -2481,7 +2549,7 @@ class ActivityIndicatorHandler(IOSViewHandler):
2481
2549
  try:
2482
2550
  ai.setActivityIndicatorViewStyle_(style)
2483
2551
  except Exception:
2484
- pass
2552
+ diagnostics.swallowed("ios.ActivityIndicatorHandler._apply")
2485
2553
  if "color" in props and props["color"] is not None:
2486
2554
  ai.setColor_(_uicolor(props["color"]))
2487
2555
  animating = props.get("animating")
@@ -2563,7 +2631,7 @@ class PressableHandler(IOSViewHandler):
2563
2631
  try:
2564
2632
  rec.setCancelsTouchesInView_(False)
2565
2633
  except Exception:
2566
- pass
2634
+ diagnostics.swallowed("ios.PressableHandler._wire_press")
2567
2635
  _set_recognizer_delegate(rec)
2568
2636
  view.addGestureRecognizer_(rec)
2569
2637
  rec.retain()
@@ -2643,7 +2711,7 @@ def _press_feedback(view: Any, pressed: bool) -> None:
2643
2711
  UIView = ObjCClass("UIView")
2644
2712
  UIView.animateWithDuration_animations_(duration, lambda: view.setAlpha_(opacity))
2645
2713
  except Exception:
2646
- pass
2714
+ diagnostics.swallowed("ios._press_feedback")
2647
2715
 
2648
2716
 
2649
2717
  # ======================================================================
@@ -2668,7 +2736,7 @@ class ProgressBarHandler(IOSViewHandler):
2668
2736
  try:
2669
2737
  ai.startAnimating()
2670
2738
  except Exception:
2671
- pass
2739
+ diagnostics.swallowed("ios.ProgressBarHandler._build")
2672
2740
  return ai
2673
2741
  pv = ObjCClass("UIProgressView").alloc().init()
2674
2742
  pv.setTranslatesAutoresizingMaskIntoConstraints_(True)
@@ -2684,27 +2752,27 @@ class ProgressBarHandler(IOSViewHandler):
2684
2752
  try:
2685
2753
  view.setColor_(_uicolor(props["color"]))
2686
2754
  except Exception:
2687
- pass
2755
+ diagnostics.swallowed("ios.ProgressBarHandler._apply")
2688
2756
  try:
2689
2757
  view.startAnimating()
2690
2758
  except Exception:
2691
- pass
2759
+ diagnostics.swallowed("ios.ProgressBarHandler._apply")
2692
2760
  else:
2693
2761
  if "value" in props and props["value"] is not None:
2694
2762
  try:
2695
2763
  view.setProgress_(float(props["value"]))
2696
2764
  except Exception:
2697
- pass
2765
+ diagnostics.swallowed("ios.ProgressBarHandler._apply")
2698
2766
  if "color" in props and props["color"] is not None:
2699
2767
  try:
2700
2768
  view.setProgressTintColor_(_uicolor(props["color"]))
2701
2769
  except Exception:
2702
- pass
2770
+ diagnostics.swallowed("ios.ProgressBarHandler._apply")
2703
2771
  if "track_color" in props and props["track_color"] is not None:
2704
2772
  try:
2705
2773
  view.setTrackTintColor_(_uicolor(props["track_color"]))
2706
2774
  except Exception:
2707
- pass
2775
+ diagnostics.swallowed("ios.ProgressBarHandler._apply")
2708
2776
  _apply_accessibility(view, props)
2709
2777
 
2710
2778
  def measure_intrinsic(
@@ -2771,7 +2839,7 @@ def _webview_did_finish_imp(self_ptr: int, _cmd_ptr: int, _webview_ptr: int, _na
2771
2839
  try:
2772
2840
  wv.evaluateJavaScript_completionHandler_(str(js), None)
2773
2841
  except Exception:
2774
- pass
2842
+ diagnostics.swallowed("ios._webview_did_finish_imp")
2775
2843
  _fire(wv, "on_load", _webview_url(wv))
2776
2844
 
2777
2845
 
@@ -2855,7 +2923,7 @@ class WebViewHandler(IOSViewHandler):
2855
2923
  try:
2856
2924
  cls.declare_property(prop)
2857
2925
  except Exception:
2858
- pass
2926
+ diagnostics.swallowed("ios.WebViewHandler._build")
2859
2927
  _pn_wkwebview_declared = True
2860
2928
  config = WKWebViewConfiguration.alloc().init()
2861
2929
  delegate_ptr = _new_webview_delegate_ptr()
@@ -2867,14 +2935,14 @@ class WebViewHandler(IOSViewHandler):
2867
2935
  try:
2868
2936
  config.userContentController.addScriptMessageHandler_name_(delegate, "pythonnative")
2869
2937
  except Exception:
2870
- pass
2938
+ diagnostics.swallowed("ios.WebViewHandler._build")
2871
2939
  wv = WKWebView.alloc().initWithFrame_configuration_(((0, 0), (0, 0)), config)
2872
2940
  wv.setTranslatesAutoresizingMaskIntoConstraints_(True)
2873
2941
  if delegate is not None:
2874
2942
  try:
2875
2943
  wv.setNavigationDelegate_(delegate)
2876
2944
  except Exception:
2877
- pass
2945
+ diagnostics.swallowed("ios.WebViewHandler._build")
2878
2946
  if delegate_ptr:
2879
2947
  _pn_webview_state[delegate_ptr] = {"view": wv, "inject_js": None}
2880
2948
  self._delegate_ids[_objc_ptr(wv) or 0] = delegate_ptr
@@ -2907,7 +2975,7 @@ class WebViewHandler(IOSViewHandler):
2907
2975
  try:
2908
2976
  wv.loadHTMLString_baseURL_(str(props["html"]), None)
2909
2977
  except Exception:
2910
- pass
2978
+ diagnostics.swallowed("ios.WebViewHandler._apply")
2911
2979
  elif "url" in props and props["url"]:
2912
2980
  try:
2913
2981
  NSURL = ObjCClass("NSURL")
@@ -2915,38 +2983,38 @@ class WebViewHandler(IOSViewHandler):
2915
2983
  url_obj = NSURL.URLWithString_(str(props["url"]))
2916
2984
  wv.loadRequest_(NSURLRequest.requestWithURL_(url_obj))
2917
2985
  except Exception:
2918
- pass
2986
+ diagnostics.swallowed("ios.WebViewHandler._apply")
2919
2987
  if "scroll_enabled" in props:
2920
2988
  enabled = props["scroll_enabled"]
2921
2989
  try:
2922
2990
  wv.scrollView.setScrollEnabled_(True if enabled is None else bool(enabled))
2923
2991
  except Exception:
2924
- pass
2992
+ diagnostics.swallowed("ios.WebViewHandler._apply")
2925
2993
 
2926
2994
  def command(self, native_view: Any, name: str, args: Dict[str, Any]) -> Any:
2927
2995
  if name == "eval_js":
2928
2996
  try:
2929
2997
  native_view.evaluateJavaScript_completionHandler_(str(args.get("source", "")), None)
2930
2998
  except Exception:
2931
- pass
2999
+ diagnostics.swallowed("ios.WebViewHandler.command")
2932
3000
  return None
2933
3001
  if name == "reload":
2934
3002
  try:
2935
3003
  native_view.reload()
2936
3004
  except Exception:
2937
- pass
3005
+ diagnostics.swallowed("ios.WebViewHandler.command")
2938
3006
  return None
2939
3007
  if name == "go_back":
2940
3008
  try:
2941
3009
  native_view.goBack()
2942
3010
  except Exception:
2943
- pass
3011
+ diagnostics.swallowed("ios.WebViewHandler.command")
2944
3012
  return None
2945
3013
  if name == "go_forward":
2946
3014
  try:
2947
3015
  native_view.goForward()
2948
3016
  except Exception:
2949
- pass
3017
+ diagnostics.swallowed("ios.WebViewHandler.command")
2950
3018
  return None
2951
3019
  return None
2952
3020
 
@@ -3026,13 +3094,13 @@ class ModalHandler(IOSViewHandler):
3026
3094
  try:
3027
3095
  child.setTranslatesAutoresizingMaskIntoConstraints_(True)
3028
3096
  except Exception:
3029
- pass
3097
+ diagnostics.swallowed("ios.ModalHandler.insert_child")
3030
3098
  try:
3031
3099
  content = modal["content_view"]
3032
3100
  count = len(list(content.subviews or []))
3033
3101
  content.insertSubview_atIndex_(child, max(0, min(index, count)))
3034
3102
  except Exception:
3035
- pass
3103
+ diagnostics.swallowed("ios.ModalHandler.insert_child")
3036
3104
  else:
3037
3105
  buf = state.setdefault("pending_children", [])
3038
3106
  buf.insert(max(0, min(index, len(buf))), child)
@@ -3041,7 +3109,7 @@ class ModalHandler(IOSViewHandler):
3041
3109
  try:
3042
3110
  child.removeFromSuperview()
3043
3111
  except Exception:
3044
- pass
3112
+ diagnostics.swallowed("ios.ModalHandler.remove_child")
3045
3113
  buf = _state_of(parent).get("pending_children")
3046
3114
  if buf and child in buf:
3047
3115
  buf.remove(child)
@@ -3082,7 +3150,7 @@ class ModalHandler(IOSViewHandler):
3082
3150
  content.setFrame_(((0, 0), (bounds.size.width, bounds.size.height)))
3083
3151
  content.setAutoresizingMask_(2 | 16) # FlexibleWidth | FlexibleHeight
3084
3152
  except Exception:
3085
- pass
3153
+ diagnostics.swallowed("ios.ModalHandler._present")
3086
3154
 
3087
3155
  # UIModalPresentationStyle: fullScreen=0, pageSheet=1,
3088
3156
  # formSheet=2, overCurrentContext=6 (the dimmed overlay).
@@ -3091,7 +3159,7 @@ class ModalHandler(IOSViewHandler):
3091
3159
  try:
3092
3160
  controller.setModalPresentationStyle_(style_int)
3093
3161
  except Exception:
3094
- pass
3162
+ diagnostics.swallowed("ios.ModalHandler._present")
3095
3163
  # For sheet styles, ``dismiss_on_backdrop=False`` locks
3096
3164
  # interactive (swipe / outside-tap) dismissal so the modal
3097
3165
  # stays put until ``visible`` is driven back to False.
@@ -3099,7 +3167,7 @@ class ModalHandler(IOSViewHandler):
3099
3167
  try:
3100
3168
  controller.setModalInPresentation_(True)
3101
3169
  except Exception:
3102
- pass
3170
+ diagnostics.swallowed("ios.ModalHandler._present")
3103
3171
 
3104
3172
  def _on_present_complete() -> None:
3105
3173
  _fire(placeholder, "on_show")
@@ -3115,7 +3183,7 @@ class ModalHandler(IOSViewHandler):
3115
3183
  child.setTranslatesAutoresizingMaskIntoConstraints_(True)
3116
3184
  content.addSubview_(child)
3117
3185
  except Exception:
3118
- pass
3186
+ diagnostics.swallowed("ios.ModalHandler._present")
3119
3187
 
3120
3188
  top = _top_view_controller_for_alert(UIApplication.sharedApplication)
3121
3189
  if top is not None:
@@ -3133,7 +3201,7 @@ class ModalHandler(IOSViewHandler):
3133
3201
  try:
3134
3202
  controller.dismissViewControllerAnimated_completion_(True, None)
3135
3203
  except Exception:
3136
- pass
3204
+ diagnostics.swallowed("ios.ModalHandler._dismiss")
3137
3205
  _fire(placeholder, "on_dismiss")
3138
3206
 
3139
3207
 
@@ -3251,7 +3319,18 @@ class PortalHandler(IOSViewHandler):
3251
3319
  return
3252
3320
  current = overlay.superview
3253
3321
  if current is not None and _objc_ptr(current) == _objc_ptr(host):
3254
- host.bringSubviewToFront_(overlay)
3322
+ # Already attached to the right host. Only reorder when
3323
+ # the overlay is not frontmost: reordering the subview
3324
+ # array during an active touch sequence (a re-render can
3325
+ # flush between touch-down and touch-up) can cancel the
3326
+ # touch and leave the portal's controls stuck
3327
+ # highlighted with their action never fired.
3328
+ try:
3329
+ front = host.subviews.lastObject()
3330
+ if front is None or _objc_ptr(front) != _objc_ptr(overlay):
3331
+ host.bringSubviewToFront_(overlay)
3332
+ except Exception:
3333
+ host.bringSubviewToFront_(overlay)
3255
3334
  self._sync_overlay_frame(overlay, host)
3256
3335
  return
3257
3336
  if current is not None:
@@ -3259,7 +3338,7 @@ class PortalHandler(IOSViewHandler):
3259
3338
  host.addSubview_(overlay)
3260
3339
  self._sync_overlay_frame(overlay, host)
3261
3340
  except Exception:
3262
- pass
3341
+ diagnostics.swallowed("ios.PortalHandler._ensure_attached")
3263
3342
 
3264
3343
  @staticmethod
3265
3344
  def _sync_overlay_frame(overlay: Any, host: Any) -> None:
@@ -3275,7 +3354,7 @@ class PortalHandler(IOSViewHandler):
3275
3354
  overlay.setFrame_(((left, top), (w, h)))
3276
3355
  overlay.setAutoresizingMask_(2 | 16) # FlexibleWidth | FlexibleHeight
3277
3356
  except Exception:
3278
- pass
3357
+ diagnostics.swallowed("ios.PortalHandler._sync_overlay_frame")
3279
3358
 
3280
3359
  def insert_child(self, parent: Any, child: Any, index: int) -> None:
3281
3360
  self._ensure_attached(parent)
@@ -3302,7 +3381,7 @@ class PortalHandler(IOSViewHandler):
3302
3381
  try:
3303
3382
  overlay.release()
3304
3383
  except Exception:
3305
- pass
3384
+ diagnostics.swallowed("ios.PortalHandler._teardown")
3306
3385
 
3307
3386
 
3308
3387
  # ======================================================================
@@ -3336,7 +3415,7 @@ class StatusBarHandler(IOSViewHandler):
3336
3415
  mapping = {"default": 3, "light": 1, "dark": 3}
3337
3416
  app.setStatusBarStyle_animated_(mapping.get(props["bar_style"], 0), True)
3338
3417
  except Exception:
3339
- pass
3418
+ diagnostics.swallowed("ios.StatusBarHandler._apply")
3340
3419
 
3341
3420
  def set_frame(self, native_view: Any, x: float, y: float, width: float, height: float) -> None:
3342
3421
  return
@@ -3402,7 +3481,7 @@ def _ensure_keyboard_observer() -> None:
3402
3481
  None,
3403
3482
  )
3404
3483
  except Exception:
3405
- pass
3484
+ diagnostics.swallowed("ios._ensure_keyboard_observer")
3406
3485
 
3407
3486
 
3408
3487
  class KeyboardAvoidingViewHandler(IOSViewHandler):
@@ -3446,7 +3525,7 @@ def _tabbar_did_select_imp(self_ptr: int, cmd_ptr: int, tabbar_ptr: int, item_pt
3446
3525
  if 0 <= index < len(items):
3447
3526
  _fire_ptr(int(tabbar_ptr), "on_tab_select", items[index].get("name", ""))
3448
3527
  except Exception:
3449
- pass
3528
+ diagnostics.swallowed("ios._tabbar_did_select_imp")
3450
3529
 
3451
3530
 
3452
3531
  _tabbar_imp_ref = _DELEGATE_IMP_TYPE(_tabbar_did_select_imp)
@@ -3532,7 +3611,7 @@ class TabBarHandler(IOSViewHandler):
3532
3611
  try:
3533
3612
  tab_bar.setItems_animated_(bar_items, False)
3534
3613
  except Exception:
3535
- pass
3614
+ diagnostics.swallowed("ios.TabBarHandler._set_bar_items")
3536
3615
 
3537
3616
  def _resolve_icon(self, UIImage: Any, icon: Any) -> Any:
3538
3617
  """Resolve a tab icon spec to a UIImage, or return None.
@@ -3566,7 +3645,7 @@ class TabBarHandler(IOSViewHandler):
3566
3645
  if i < len(all_items):
3567
3646
  tab_bar.setSelectedItem_(all_items[i])
3568
3647
  except Exception:
3569
- pass
3648
+ diagnostics.swallowed("ios.TabBarHandler._set_active")
3570
3649
  break
3571
3650
 
3572
3651
 
@@ -3589,7 +3668,7 @@ def _top_view_controller_for_alert(app: Any) -> Any:
3589
3668
  try:
3590
3669
  window = app.keyWindow
3591
3670
  except Exception:
3592
- pass
3671
+ diagnostics.swallowed("ios._top_view_controller_for_alert")
3593
3672
 
3594
3673
  if window is None:
3595
3674
  try:
@@ -3601,7 +3680,7 @@ def _top_view_controller_for_alert(app: Any) -> Any:
3601
3680
  if window is None and windows:
3602
3681
  window = windows[0]
3603
3682
  except Exception:
3604
- pass
3683
+ diagnostics.swallowed("ios._top_view_controller_for_alert")
3605
3684
 
3606
3685
  if window is None:
3607
3686
  return None
@@ -3618,7 +3697,7 @@ def _top_view_controller_for_alert(app: Any) -> Any:
3618
3697
  if visible is not None:
3619
3698
  top = visible
3620
3699
  except Exception:
3621
- pass
3700
+ diagnostics.swallowed("ios._top_view_controller_for_alert")
3622
3701
 
3623
3702
  while top is not None:
3624
3703
  try:
@@ -3666,7 +3745,7 @@ def _present_alert(
3666
3745
  try:
3667
3746
  on_result(index)
3668
3747
  except Exception:
3669
- pass
3748
+ diagnostics.swallowed("ios._present_alert._deliver")
3670
3749
 
3671
3750
  def _present_on_main() -> None:
3672
3751
  try:
@@ -3767,7 +3846,7 @@ class PickerHandler(IOSViewHandler):
3767
3846
  try:
3768
3847
  btn.setTitle_forState_(_picker_button_title(merged), 0)
3769
3848
  except Exception:
3770
- pass
3849
+ diagnostics.swallowed("ios.PickerHandler._apply")
3771
3850
  _apply_accessibility(btn, props)
3772
3851
 
3773
3852
  def measure_intrinsic(self, native_view: Any, max_width: float, max_height: float) -> Tuple[float, float]:
@@ -3803,10 +3882,10 @@ def _checkbox_set_image(btn: Any) -> None:
3803
3882
  if tinted is not None:
3804
3883
  image = tinted
3805
3884
  except Exception:
3806
- pass
3885
+ diagnostics.swallowed("ios._checkbox_set_image")
3807
3886
  btn.setImage_forState_(image, 0)
3808
3887
  except Exception:
3809
- pass
3888
+ diagnostics.swallowed("ios._checkbox_set_image")
3810
3889
 
3811
3890
 
3812
3891
  def _checkbox_toggle(btn: Any) -> None:
@@ -3844,7 +3923,7 @@ class CheckboxHandler(IOSViewHandler):
3844
3923
  btn.setTitleColor_forState_(_uicolor("#111111"), 0)
3845
3924
  btn.setTintColor_(_uicolor("#111111"))
3846
3925
  except Exception:
3847
- pass
3926
+ diagnostics.swallowed("ios.CheckboxHandler._apply")
3848
3927
  if "value" in props:
3849
3928
  state["value"] = bool(props["value"])
3850
3929
  if "label" in props:
@@ -3852,14 +3931,14 @@ class CheckboxHandler(IOSViewHandler):
3852
3931
  try:
3853
3932
  btn.setTitle_forState_(str(label) if label is not None else "", 0)
3854
3933
  except Exception:
3855
- pass
3934
+ diagnostics.swallowed("ios.CheckboxHandler._apply")
3856
3935
  # An image-bearing custom button is not exposed to the
3857
3936
  # accessibility tree by title alone; mirror the label
3858
3937
  # explicitly (an accessibility_label prop still wins below).
3859
3938
  try:
3860
3939
  btn.setAccessibilityLabel_(str(label) if label is not None else "")
3861
3940
  except Exception:
3862
- pass
3941
+ diagnostics.swallowed("ios.CheckboxHandler._apply")
3863
3942
  if "disabled" in props:
3864
3943
  # ``disabled`` is present only when True; a removed prop
3865
3944
  # (None) re-enables the control.
@@ -3867,7 +3946,7 @@ class CheckboxHandler(IOSViewHandler):
3867
3946
  try:
3868
3947
  btn.setEnabled_(not disabled)
3869
3948
  except Exception:
3870
- pass
3949
+ diagnostics.swallowed("ios.CheckboxHandler._apply")
3871
3950
  _checkbox_set_image(btn)
3872
3951
  _apply_accessibility(btn, props)
3873
3952
 
@@ -3924,7 +4003,7 @@ class SegmentedControlHandler(IOSViewHandler):
3924
4003
  for i, title in enumerate(new_segments):
3925
4004
  control.insertSegmentWithTitle_atIndex_animated_(title, i, False)
3926
4005
  except Exception:
3927
- pass
4006
+ diagnostics.swallowed("ios.SegmentedControlHandler._apply")
3928
4007
  finally:
3929
4008
  state["suppress"] = False
3930
4009
  rebuilt = True
@@ -3937,7 +4016,7 @@ class SegmentedControlHandler(IOSViewHandler):
3937
4016
  try:
3938
4017
  control.setSelectedSegmentIndex_(int(merged.get("selected_index", 0) or 0))
3939
4018
  except Exception:
3940
- pass
4019
+ diagnostics.swallowed("ios.SegmentedControlHandler._apply")
3941
4020
  finally:
3942
4021
  state["suppress"] = False
3943
4022
  if "tint_color" in props and props["tint_color"] is not None:
@@ -3945,11 +4024,11 @@ class SegmentedControlHandler(IOSViewHandler):
3945
4024
  try:
3946
4025
  control.setSelectedSegmentTintColor_(color) # iOS 13+
3947
4026
  except Exception:
3948
- pass
4027
+ diagnostics.swallowed("ios.SegmentedControlHandler._apply")
3949
4028
  try:
3950
4029
  control.setTintColor_(color)
3951
4030
  except Exception:
3952
- pass
4031
+ diagnostics.swallowed("ios.SegmentedControlHandler._apply")
3953
4032
  if "enabled" in props:
3954
4033
  # ``enabled`` is present only when False; a removed prop
3955
4034
  # (None) re-enables the control.
@@ -3957,7 +4036,7 @@ class SegmentedControlHandler(IOSViewHandler):
3957
4036
  try:
3958
4037
  control.setEnabled_(True if enabled is None else bool(enabled))
3959
4038
  except Exception:
3960
- pass
4039
+ diagnostics.swallowed("ios.SegmentedControlHandler._apply")
3961
4040
  _apply_accessibility(control, props)
3962
4041
 
3963
4042
  def measure_intrinsic(self, native_view: Any, max_width: float, max_height: float) -> Tuple[float, float]:
@@ -3996,7 +4075,7 @@ def _date_formatter(mode: str) -> Any:
3996
4075
  NSLocale = ObjCClass("NSLocale")
3997
4076
  formatter.setLocale_(NSLocale.alloc().initWithLocaleIdentifier_("en_US_POSIX"))
3998
4077
  except Exception:
3999
- pass
4078
+ diagnostics.swallowed("ios._date_formatter")
4000
4079
  formatter.retain()
4001
4080
  _pn_date_formatters[fmt] = formatter
4002
4081
  return formatter
@@ -4013,7 +4092,7 @@ class DatePickerHandler(IOSViewHandler):
4013
4092
  try:
4014
4093
  picker.setPreferredDatePickerStyle_(2) # UIDatePickerStyleCompact
4015
4094
  except Exception:
4016
- pass
4095
+ diagnostics.swallowed("ios.DatePickerHandler._build")
4017
4096
 
4018
4097
  def _on_change() -> None:
4019
4098
  state = _state_of(picker)
@@ -4038,7 +4117,7 @@ class DatePickerHandler(IOSViewHandler):
4038
4117
  try:
4039
4118
  picker.setDatePickerMode_(mode_map.get(mode, 1))
4040
4119
  except Exception:
4041
- pass
4120
+ diagnostics.swallowed("ios.DatePickerHandler._apply")
4042
4121
  if "minimum" in props:
4043
4122
  self._set_bound(picker, "setMinimumDate_", props["minimum"], mode)
4044
4123
  if "maximum" in props:
@@ -4053,7 +4132,7 @@ class DatePickerHandler(IOSViewHandler):
4053
4132
  try:
4054
4133
  picker.setDate_animated_(date, False)
4055
4134
  except Exception:
4056
- pass
4135
+ diagnostics.swallowed("ios.DatePickerHandler._apply")
4057
4136
  finally:
4058
4137
  state["suppress"] = False
4059
4138
  if "enabled" in props:
@@ -4063,7 +4142,7 @@ class DatePickerHandler(IOSViewHandler):
4063
4142
  try:
4064
4143
  picker.setEnabled_(True if enabled is None else bool(enabled))
4065
4144
  except Exception:
4066
- pass
4145
+ diagnostics.swallowed("ios.DatePickerHandler._apply")
4067
4146
  _apply_accessibility(picker, props)
4068
4147
 
4069
4148
  def _set_bound(self, picker: Any, selector: str, value: Any, mode: str) -> None:
@@ -4074,7 +4153,7 @@ class DatePickerHandler(IOSViewHandler):
4074
4153
  date = _date_formatter(mode).dateFromString_(str(value))
4075
4154
  getattr(picker, selector)(date)
4076
4155
  except Exception:
4077
- pass
4156
+ diagnostics.swallowed("ios.DatePickerHandler._set_bound")
4078
4157
 
4079
4158
  def measure_intrinsic(self, native_view: Any, max_width: float, max_height: float) -> Tuple[float, float]:
4080
4159
  try:
@@ -4219,7 +4298,7 @@ def _table_cell_imp(self_ptr: int, cmd_ptr: int, tv_ptr: int, ip_ptr: int) -> in
4219
4298
  cell.setFrame_(((0, 0), (cell_w, row_h)))
4220
4299
  cell.contentView.setFrame_(((0, 0), (cell_w, row_h)))
4221
4300
  except Exception:
4222
- pass
4301
+ diagnostics.swallowed("ios._table_cell_imp")
4223
4302
 
4224
4303
  content = cell.contentView
4225
4304
  try:
@@ -4229,7 +4308,7 @@ def _table_cell_imp(self_ptr: int, cmd_ptr: int, tv_ptr: int, ip_ptr: int) -> in
4229
4308
  for sub in list(existing_subs or []):
4230
4309
  sub.removeFromSuperview()
4231
4310
  except Exception:
4232
- pass
4311
+ diagnostics.swallowed("ios._table_cell_imp")
4233
4312
 
4234
4313
  # Only rows in (or near) the visible content window get a
4235
4314
  # mounted subtree. UITableView's accessibility container calls
@@ -4259,7 +4338,7 @@ def _table_cell_imp(self_ptr: int, cmd_ptr: int, tv_ptr: int, ip_ptr: int) -> in
4259
4338
  try:
4260
4339
  cell.setAccessibilityElementsHidden_(not in_window)
4261
4340
  except Exception:
4262
- pass
4341
+ diagnostics.swallowed("ios._table_cell_imp")
4263
4342
 
4264
4343
  if in_window and info is not None:
4265
4344
  render_row = info.get("render_row")
@@ -4278,7 +4357,7 @@ def _table_cell_imp(self_ptr: int, cmd_ptr: int, tv_ptr: int, ip_ptr: int) -> in
4278
4357
  try:
4279
4358
  root.setFrame_(((0, 0), (cell_w, row_h)))
4280
4359
  except Exception:
4281
- pass
4360
+ diagnostics.swallowed("ios._table_cell_imp")
4282
4361
  except Exception:
4283
4362
  print(f"[VirtualList][iOS] mount for row={row} raised:")
4284
4363
  _tb.print_exc()
@@ -4303,7 +4382,7 @@ def _table_did_select_imp(self_ptr: int, cmd_ptr: int, tv_ptr: int, ip_ptr: int)
4303
4382
  _objc_msgSend.argtypes = [_ct.c_void_p, _ct.c_void_p, _ct.c_void_p, _ct.c_bool]
4304
4383
  _objc_msgSend(_ct.c_void_p(tv_ptr), _SEL_DESELECT_ROW, _ct.c_void_p(ip_ptr), True)
4305
4384
  except Exception:
4306
- pass
4385
+ diagnostics.swallowed("ios._table_did_select_imp")
4307
4386
  _fire_ptr(int(tv_ptr or 0), "on_row_press", row)
4308
4387
 
4309
4388
 
@@ -4429,7 +4508,7 @@ class VirtualListHandler(IOSViewHandler):
4429
4508
  estimate = float(props.get("row_height", 44.0) or 44.0)
4430
4509
  tv.setEstimatedRowHeight_(max(1.0, estimate))
4431
4510
  except Exception:
4432
- pass
4511
+ diagnostics.swallowed("ios.VirtualListHandler._build")
4433
4512
  tv.retain()
4434
4513
  _pn_retained_views.append(tv)
4435
4514
 
@@ -4487,12 +4566,12 @@ class VirtualListHandler(IOSViewHandler):
4487
4566
  try:
4488
4567
  tv.setShowsVerticalScrollIndicator_(visible)
4489
4568
  except Exception:
4490
- pass
4569
+ diagnostics.swallowed("ios.VirtualListHandler._apply")
4491
4570
  if data_changed and not initial:
4492
4571
  try:
4493
4572
  tv.reloadData()
4494
4573
  except Exception:
4495
- pass
4574
+ diagnostics.swallowed("ios.VirtualListHandler._apply")
4496
4575
 
4497
4576
  def _teardown(self, tv: Any) -> None:
4498
4577
  info = self._source_state(tv)
@@ -4501,7 +4580,7 @@ class VirtualListHandler(IOSViewHandler):
4501
4580
  try:
4502
4581
  info["pool"].release_all()
4503
4582
  except Exception:
4504
- pass
4583
+ diagnostics.swallowed("ios.VirtualListHandler._teardown")
4505
4584
  info["tv"] = None
4506
4585
  _pn_table_state.pop(info.get("source_ptr"), None)
4507
4586
 
@@ -4525,7 +4604,7 @@ class VirtualListHandler(IOSViewHandler):
4525
4604
  try:
4526
4605
  native_view.setContentOffset_animated_((0.0, y), animated)
4527
4606
  except Exception:
4528
- pass
4607
+ diagnostics.swallowed("ios.VirtualListHandler.command")
4529
4608
  return None
4530
4609
  if name == "scroll_to_index":
4531
4610
  index = int(args.get("index", 0) or 0)
@@ -4538,7 +4617,7 @@ class VirtualListHandler(IOSViewHandler):
4538
4617
  # 1 = UITableViewScrollPositionTop
4539
4618
  native_view.scrollToRowAtIndexPath_atScrollPosition_animated_(path, 1, animated)
4540
4619
  except Exception:
4541
- pass
4620
+ diagnostics.swallowed("ios.VirtualListHandler.command")
4542
4621
  return None
4543
4622
  if name == "scroll_to_end":
4544
4623
  animated = args.get("animated", True) is not False
@@ -4548,7 +4627,7 @@ class VirtualListHandler(IOSViewHandler):
4548
4627
  target = max(0.0, content_h - bounds_h)
4549
4628
  native_view.setContentOffset_animated_((0.0, target), animated)
4550
4629
  except Exception:
4551
- pass
4630
+ diagnostics.swallowed("ios.VirtualListHandler.command")
4552
4631
  return None
4553
4632
  if name == "get_scroll_offset":
4554
4633
  try: