1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! [`NativeCtx`] — the scoped call context every Apple
//! [`NativeWidget`](crate::runtime::NativeWidget) builds its views through:
//! the iOS mirror of `crate::android::ctx`'s JNI wrapper, with almost nothing
//! left in it, because UIKit needs almost nothing.
//!
//! # What it carries, and why that is all
//!
//! A [`MainThreadMarker`] — nothing else. Where the Android context has to
//! thread a live `Env`, an `Activity`/`Context` pair, a classloader cache and
//! a local-frame wrapper, the Apple arm's equivalents are all either global or
//! automatic:
//!
//! | Android needs | Apple equivalent |
//! |---|---|
//! | a live `Env` per call | the ObjC runtime is globally reachable (`objc2`) |
//! | the app classloader (`FindClass` can't see app classes) | `NSClassFromString`/`objc2`'s linked classes need no loader |
//! | `PushLocalFrame` around hierarchy loops | ARC + `Retained`'s own `Drop` |
//! | `NewGlobalRef` to retain past the call | `Retained<T>` *is* the retain |
//! | a `Context` to construct a view against | `UIView::new(mtm)` needs only main-thread proof |
//!
//! So the one thing a UIKit call genuinely cannot do without is **proof that
//! it is on the main thread**, and that is exactly what this type carries: a
//! compile-time main-thread proof. A control's `create`/
//! `update`/`dispose` takes `&mut NativeCtx<'_, '_>`, gets a
//! [`MainThreadMarker`] out of it, and every `objc2-ui-kit` constructor that
//! demands one is then callable — with the check done once, by the type
//! system, instead of a `debug_assert` per export the way the Android arm has
//! to.
//!
//! # No `CATransaction` batch helper here — the host already opened one
//!
//! A `CATransaction` batch (implicit animations disabled) around multi-view
//! updates is what this arm would otherwise need. That batch **already exists, one layer up**:
//! `FrustViewHost.applyCommands` (`platform/ios/FrustEmbedding/Sources/
//! FrustEmbedding/FrustViewHost.swift`) wraps its whole per-poll command loop
//! in `CATransaction.begin()` / `CATransaction.setDisableActions(true)` /
//! `CATransaction.commit()`, and every one of this plugin's three entry points
//! (`createView`, `updateParams`, `disposeView`) is called from inside that
//! loop. A second, nested transaction opened from Rust would batch nothing the
//! host is not already batching and would cost this crate a whole new
//! dependency (`objc2-quartz-core`) for it — so this arm deliberately ships
//! none. If a future path ever calls into UIKit *outside* the host's poll (a
//! target-action handler mutating sibling views directly, say), that is the
//! moment to add one, and this comment is the reason it wasn't added sooner.
//!
//! # Frame-setting layout only
//!
//! Nothing here (or in `crate::apple::factory`) touches
//! `translatesAutoresizingMaskIntoConstraints` or any constraint API: the host
//! positions a slot's content view by assigning `frame` from the differ's rect
//! (`FrustViewHost.applyUpdate`), exactly as this contract specifies. A control
//! that installed constraints would fight it.
//!
//! # Hierarchy: `addSubview`, and nothing else
//!
//! [`NativeCtx::add_child`] is this arm's whole subtree surface — the mirror
//! of `crate::android::ctx`'s JNI `addView` wrapper, and the piece that did
//! not exist here before. **The platform lays the subtree out**, not
//! frust: a component positions its children with explicit frames or a
//! `UIStackView` of its own making, and frust keeps seeing one opaque slot
//! with one rect.
//!
//! There is deliberately **no local-frame wrapper on this arm**: the
//! discipline `crate::android::ctx`'s `with_frame` enforces (fifty children
//! must not pin fifty-plus local references for the whole call) has no
//! analogue under ARC, where a `Retained`'s own `Drop` is the release. The
//! public `ComponentCtx::with_local_frame` (`crate::component`) still exists
//! on this arm — it simply runs its closure — so a component's `create` reads
//! the same on both platforms.
// Mirrors `crate::android::ctx`'s own module-level allow: this is the helper
// surface the built-in Apple controls and their target-action objects
// will build on, and this lands the type before its consumers
// exist. The attribute goes away once those consumers land, rather than
// growing per-item `allow`s in the meantime.
use PhantomData;
use MainThreadMarker;
use UIView;
/// The scoped call context handed to every Apple
/// [`NativeWidget`](crate::runtime::NativeWidget) method — see the module doc.
///
/// The two lifetime parameters exist to match the shape
/// `crate::runtime`'s trait signatures were written against (`NativeCtx<'local,
/// 'env>`, where the Android arm binds them to a JNI stack frame and the
/// borrow of its `Env`). The Apple arm borrows nothing — ARC owns every
/// reference a control creates — so both are phantom here rather than a second
/// spelling of the trait for one platform.
pub