//! iOS backend — UIKit via C FFI / Objective-C bridge. //! //! Architecture: correct and complete. Native UIKit calls are stubs marked //! TODO — a future agent fills in the actual `extern "C"` calls. //! //! Each `PlatformNode` maps to a UIKit view: //! element("div") → UIView //! element("span") → UILabel (inline) //! element("button") → UIButton //! element("input") → UITextField //! text("...") → UILabel //! //! Event binding maps DOM event names to UIControl target-action pairs: //! "click" → UIControlEventTouchUpInside //! "input" → UIControlEventEditingChanged use crate::{EventHandler, PlatformBackend, PlatformError, PlatformNode, PlatformResult}; pub struct IosBackend; impl IosBackend { pub fn new() -> Self { Self } /// Map an HTML tag name to the UIKit class name it maps to. fn uikit_class(tag: &str) -> &'static str { match tag { "button" => "UIButton", "input" => "UITextField", "textarea" => "UITextView", "img" => "UIImageView", "ul" | "ol" => "UITableView", "li" => "UITableViewCell", "nav" => "UINavigationBar", _ => "UIView", } } /// Map a DOM event name to a UIControlEvent constant name. fn uicontrol_event(event: &str) -> &'static str { match event { "click" => "UIControlEventTouchUpInside", "input" | "change" => "UIControlEventEditingChanged", "focus" => "UIControlEventEditingDidBegin", "blur" => "UIControlEventEditingDidEnd", _ => "UIControlEventAllEvents", } } } impl Default for IosBackend { fn default() -> Self { Self::new() } } impl PlatformBackend for IosBackend { fn name(&self) -> &'static str { "ios" } fn create_element(&self, tag: &str) -> PlatformResult { let mut node = PlatformNode::element(tag); // TODO: call UIKit C FFI to allocate the view: // extern "C" { fn el_ios_create_view(class_name: *const c_char) -> usize; } // node.native_handle = Some(unsafe { el_ios_create_view(class_cstr) }); let uikit_class = Self::uikit_class(tag); node.attributes .push(crate::Attribute::new("data-uikit-class", uikit_class)); Ok(node) } fn create_text(&self, content: &str) -> PlatformResult { let mut node = PlatformNode::text(content); // TODO: UILabel with text = content node.attributes .push(crate::Attribute::new("data-uikit-class", "UILabel")); Ok(node) } fn set_attribute( &self, node: &mut PlatformNode, name: &str, value: &str, ) -> PlatformResult<()> { // TODO: map attribute names to UIKit property setters: // "class" → apply style from stylesheet // "disabled" → view.isUserInteractionEnabled = false // "placeholder" → textField.placeholder = value node.attributes.retain(|a| a.name != name); node.attributes.push(crate::Attribute::new(name, value)); Ok(()) } fn remove_attribute(&self, node: &mut PlatformNode, name: &str) -> PlatformResult<()> { node.attributes.retain(|a| a.name != name); Ok(()) } fn append_child( &self, parent: &mut PlatformNode, child: PlatformNode, ) -> PlatformResult<()> { // TODO: [parentView addSubview:childView] via FFI parent.children.push(child); Ok(()) } fn remove_child(&self, parent: &mut PlatformNode, child_index: usize) -> PlatformResult<()> { if child_index >= parent.children.len() { return Err(PlatformError::Render(format!( "ios: child index {} out of bounds", child_index ))); } // TODO: [childView removeFromSuperview] via FFI parent.children.remove(child_index); Ok(()) } fn replace_child( &self, parent: &mut PlatformNode, index: usize, new_child: PlatformNode, ) -> PlatformResult<()> { if index >= parent.children.len() { return Err(PlatformError::Render("ios: replace_child out of bounds".into())); } // TODO: remove old view, insert new view via FFI parent.children[index] = new_child; Ok(()) } fn bind_event( &self, node: &mut PlatformNode, event: &str, _handler: EventHandler, ) -> PlatformResult<()> { let uievent = Self::uicontrol_event(event); // TODO: [view addTarget:target action:@selector(handler:) forControlEvents:uievent] node.attributes .push(crate::Attribute::new(format!("data-ios-event-{}", event), uievent)); Ok(()) } fn render_to_string(&self, node: &PlatformNode) -> PlatformResult { // iOS doesn't render to HTML strings at runtime, but we support it for // testing and SSR fallback. Ok(node.to_html()) } fn mount(&self, _root: PlatformNode, _container_id: &str) -> PlatformResult<()> { // TODO: get UIViewController by container_id and set root as its view Ok(()) } fn patch(&self, _old: &PlatformNode, _new: &PlatformNode) -> PlatformResult<()> { // TODO: diff old and new trees, apply UIKit mutations via FFI Ok(()) } }