module
UI::Native::Populator
Overview
SwiftKit override populator.
Phase 3's renderer migration moves overrides population OUT of each
visit method and INTO this module so the populator can be exercised
in plain crystal spec runs (no -Dmacos / -Dios flag, no
AppKit / UIKit / SwiftKit frameworks linked).
The contract:
Populator.populate_button(overrides_ptr, view) walks the
UI::Button's common-view properties (background, corner_radius,
padding, opacity, hidden, border_, shadow_, min/max_*,
accessibility_label, test_id) PLUS the button-specific properties
(role, style, disabled, symbol) and ONLY sends the setter when
the property differs from its type default. This is the
"default-detection" invariant — implementation.md §11 calls it
"the single most important behavioral invariant of phase 3."
The populator does not call objc_msgSend directly. It calls
into an injected Sender interface. In production builds the
sender is LibObjCBridge-backed (renderer integration); in spec
builds it is FakeLibObjCBridge-backed and merely records each
setter invocation.
This indirection lets the renderer's visit method shrink from
~230 lines to ~5: populate_button(ovr, view); make_button(...).
Defined in:
ui/native/swiftkit_overrides.crClass Method Summary
-
.objc_setter_selector(setter : Symbol) : String
Symbol-to-ObjC-selector helper.
- .populate_alert(target : String, view : UI::Alert, sender : Sender)
-
.populate_button(target : String, view : UI::Button, sender : Sender)
Populate an
APSKButtonOverridesinstance from aUI::Button. - .populate_card(target : String, view : UI::Card, sender : Sender)
- .populate_checkbox(target : String, view : UI::Checkbox, sender : Sender)
- .populate_color_picker(target : String, view : UI::ColorPicker, sender : Sender)
- .populate_confirmation_dialog(target : String, view : UI::ConfirmationDialog, sender : Sender)
- .populate_date_picker(target : String, view : UI::DatePicker, sender : Sender)
- .populate_divider(target : String, view : UI::Divider, sender : Sender)
- .populate_form(target : String, view : UI::Form, sender : Sender)
-
.populate_glass_background(target : String, view : UI::GlassBackground, sender : Sender, apple_step : Symbol = view.material)
--------------------------------------------------------------- Glass — the Phase 3 "headline visual differentiator".
- .populate_grid(target : String, view : UI::Grid, sender : Sender)
- .populate_icon_button(target : String, view : UI::IconButton, sender : Sender)
- .populate_image(target : String, view : UI::Image, sender : Sender)
- .populate_label(target : String, view : UI::Label, sender : Sender)
- .populate_link_button(target : String, view : UI::LinkButton, sender : Sender)
-
.populate_list_view(target : String, view : UI::ListView, sender : Sender)
UI::ListView(§6 #25). - .populate_menu_button(target : String, view : UI::MenuButton, sender : Sender)
- .populate_navigation_link(target : String, view : UI::NavigationLink, sender : Sender)
- .populate_navigation_split_view(target : String, view : UI::NavigationSplitView, sender : Sender)
- .populate_navigation_stack(target : String, view : UI::NavigationStack, sender : Sender)
- .populate_picker(target : String, view : UI::Picker, sender : Sender)
- .populate_popover(target : String, view : UI::Popover, sender : Sender)
- .populate_radio_group(target : String, view : UI::RadioGroup, sender : Sender)
- .populate_search_field(target : String, view : UI::SearchField, sender : Sender)
- .populate_secure_field(target : String, view : UI::SecureField, sender : Sender)
- .populate_segmented_control(target : String, view : UI::SegmentedControl, sender : Sender)
- .populate_sheet(target : String, view : UI::Sheet, sender : Sender)
- .populate_slider(target : String, view : UI::Slider, sender : Sender)
- .populate_spacer(target : String, view : UI::Spacer, sender : Sender)
- .populate_stepper(target : String, view : UI::Stepper, sender : Sender)
- .populate_surface(target : String, view : UI::Surface, sender : Sender)
- .populate_tab_view(target : String, view : UI::TabView, sender : Sender)
- .populate_text_area(target : String, view : UI::TextArea, sender : Sender)
- .populate_text_editor(target : String, view : UI::TextEditor, sender : Sender)
- .populate_text_field(target : String, view : UI::TextField, sender : Sender)
- .populate_time_picker(target : String, view : UI::TimePicker, sender : Sender)
- .populate_toggle(target : String, view : UI::Toggle, sender : Sender)
- .populate_toggle_button(target : String, view : UI::ToggleButton, sender : Sender)
- .populate_toolbar(target : String, view : UI::Toolbar, sender : Sender)
-
.populate_view_common(target : String, view : UI::View, sender : Sender)
Populate the common
APSKViewOverridesfields from anyUI::View. -
.swiftui_font_weight_rawvalue(weight : Symbol) : Int32
Map a Crystal
UI::Font.weightSymbol to the SwiftUIFont.WeightrawValue Int the Swift facade init reads.
Class Method Detail
Symbol-to-ObjC-selector helper. The Populator emits setter
symbols without a trailing colon (:setStyle,
:setBackgroundColor) because that's the shape the spec
recording sender asserts against. ObjC selectors for single-
argument setters need the colon; the production
SwiftKitObjCSender routes through this helper at the boundary
so the populator contract and the spec recording remain
symmetric.
Idempotent: passing a Symbol that already ends in : returns
its String form unchanged so a future caller that knows the
ObjC convention can pass the canonical Symbol directly.
Populate an APSKButtonOverrides instance from a UI::Button.
The target parameter is the Crystal-side identifier for the
overrides object (in production it's a stringified pointer; in
specs it's the sentinel from FakeLibObjCBridge.next_sentinel_pointer).
Glass — the Phase 3 "headline visual differentiator". On iOS 26 /
macOS 26 the facade routes through .glassEffect() for real
Liquid Glass; on pre-26 OSes it falls back to .background(<Material>).
material mirrors the Crystal UI::GlassBackground.material
symbol (:regular | :thin | :ultra_thin | :thick | :chrome). The
facade switch normalises camelCase keys (ultraThin) on the
Swift side; we emit them in the same shape so the facade
dispatch stays simple.
Glass populator. apple_step is the Apple-quantized step Symbol the
renderer has resolved via tokens.material.apple_step(view.material).
The populator emits the facade setMaterial: key derived from the
resolved Symbol, not from view.material directly, so brand
intensity overrides cascade onto the SwiftUI Material enum case per
the Phase 5 brief's adapter_cardinality row 1 contract.
apple_step defaults to view.material so spec-level callers that
don't have a renderer can still exercise the populator without
threading tokens through the test fixtures.
UI::ListView (§6 #25). The Crystal side carries a sectioned list
(Array(Section)) with optional headers / footers per section and
a list-wide ListStyle enum. The Swift facade rebuilds the
SwiftUI List { Section { ... } } hierarchy from a flat
childViews array sliced by setSectionItemCounts.
selection_mode is not a Crystal-side property today (verified
against src/ui/views/list_view.cr); the facade therefore uses
SwiftUI's default selection model. The on_item_tap Proc lives
on the view itself and is wired by the renderer's visit method
via callback-token registration (not through overrides).
Populate the common APSKViewOverrides fields from any UI::View.
Every per-widget populator calls this helper first, then layers widget-specific setters on top. Keeps each per-widget populator to the minimal "what is unique about THIS widget?" surface.
All setters are skipped when the matching property is at its type default — that is the default-detection invariant in §11.
Map a Crystal UI::Font.weight Symbol to the SwiftUI
Font.Weight rawValue Int the Swift facade init reads. The
mapping mirrors ButtonFacade.swift's private Font.Weight
extension. ultraLight = -3, thin = -2, light = -1, regular = 0,
medium = 1, semibold = 2, bold = 3, heavy = 4, black = 5.