ios-uikit is a Tier 1 target for iPhone and iPad apps. Day connects shared Rust UI code to UIKit controls. Building for this target needs Apple’s tools; device distribution also requires the relevant signing and provisioning setup.

Getting started

Install full Xcode 27 or newer to open the generated JSON5 project.xcproj host.

sudo xcode-select -s /Applications/Xcode.app # Xcode 27+, with an iOS Simulator runtime
cargo install --git https://github.com/daybrite/day day-cli
day new app my-app --toolkit ios-uikit
cd my-app
day launch -t ios-uikit          # boots a Simulator and installs

day doctor --toolkit uikit verifies Xcode 27 or newer, the Simulator runtimes and the Rust iOS targets. day open -t ios-uikit prepares and opens the generated project in Xcode; ⌘R builds the same Rust library through the project’s build phase.

day build also generates a local SwiftPM package at build/day/ios/DayPieces (the Swift shims pieces contribute, their SwiftPM dependencies, and the generated SwiftUI embedding glue), which the generated (or optionally checked-in) Xcode project links. A piece’s platform metadata key raises the deployment floor: day build prints a Raising status and passes IPHONEOS_DEPLOYMENT_TARGET=<floor> to xcodebuild. That setting does not reach ⌘R builds inside Xcode, so for IDE work raise the value in platform/ios/DayApp.xcconfig.

Packaging produces a device .ipa:

day pack -t ios-uikit

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

Caveats

  • 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, and day lint fails the build when one is missing, so the mistake surfaces in CI.
  • day-piece-combobox has no UIKit arm, because iOS has no native combo-box control; it renders as a placeholder. Use picker().menu().
  • 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

The generated coverage matrix, checked in CI, covers every backend; this table adds links and notes.

Day pieceUIKit class
column / row / nav_stack / sectionUIView
labelUILabel
buttonUIButton (system type; bordered/prominent use UIButtonConfiguration)
toggleUISwitch
sliderUISlider
text_fieldUITextField
secure_fieldUITextField with isSecureTextEntry
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
vectorUIImageView — the SVG ships in a DayPieces imageset with preserves-vector-representation, so it renders at display size
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)

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

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