iOS (UIKit)

ios-uikit renders real UINavigationController pushes, a real UITabBarController, and system text handling — driven from Rust through objc2. The scaffold is a checked-in Xcode project whose build phase calls back into day, so Xcode, day launch and CI all build the same way.

Getting started

xcode-select --install          # plus Xcode itself, with an iOS Simulator runtime
cargo install day-cli
day new app my-app --toolkit ios-uikit
cd my-app
day launch -p ios-uikit          # boots a Simulator and installs

day doctor --toolkit uikit verifies Xcode, the Simulator runtimes and the Rust iOS targets. Opening platform/ios/Runner.xcodeproj in Xcode works too — ⌘R builds the same Rust library through the project’s build phase.

Packaging produces a device .ipa:

day pack -p ios-uikit

With signing.ios configured you get a signed App Store export; without it you get an unsigned device build (<name>-unsigned.ipa) for sideloading or your own signing. See packaging.

Caveats and special considerations

  • Development is Simulator-first. Device debugging works but gets far less use than the Simulator path.
  • Declare permissions before you use them. A gated API without its NS*UsageDescription key terminates the app on first use. Day generates those keys from your [permissions] table — day lint fails the build when one is missing, which is the difference between a CI failure and a crash on a tester’s phone.
  • No combo box. day-piece-combobox has no UIKit arm, because iOS has no native combo-box control; it renders as a placeholder. Use picker().menu().
  • No menu bar. iOS has no persistent menu bar, so set_app_menu is a no-op; context menus work (UIContextMenuInteraction).
  • Window screenshots aren’t implemented in-process — the walkthrough uses simctl io booted screenshot instead.
  • Dynamic Type is live: labels set adjustsFontForContentSizeCategory, so text rescales with the user’s system setting and your layout has to tolerate it.

What each piece becomes

Day pieceUIKit class
column / row / stack / sectionUIView
labelUILabel
buttonUIButton (system type; bordered/prominent use UIButtonConfiguration)
toggleUISwitch
sliderUISlider
text_fieldUITextField
text_areaUITextView
picker().segmented()UISegmentedControl
picker().menu()UIButton + UIMenu
picker().inline()UIStackView of check-marked buttons
progress(f)UIProgressView
spinner()UIActivityIndicatorView
scrollUIScrollView
listUITableView (plain style, recycled cells)
navUINavigationController
nav_menuUITableView (inset-grouped)
tabsUITabBarController
cover()a full-screen modal UIViewController
imageUIImageView
canvasa custom UIView replaying the display list through Core Graphics
dialogs / alerts / promptsUIAlertController
file dialogsUIDocumentPickerViewController
context menusUIContextMenuInteraction
external activity()UIActivityIndicatorView
external search_field()UISearchTextField
external date_picker() / time_picker()UIDatePicker
external pull_refresh()UIRefreshControl
external webview()WKWebView
external media()AVPlayerViewController
external map()MKMapView
external lottie()LottieAnimationView (lottie-ios, via SwiftPM)

Day has no checkbox, radio, stepper or table piece — toggle is the only boolean control, radio behavior is picker().inline(), and list is the only collection.

Text measurement is UIKit’s own sizeThatFits: on the realized view, so wrapping matches what the system would do anywhere else on the platform.