Bifrost turns 1! Get 30% off annual plans with code
HAPPYBIRTHDAY
Blog

NativePHP Mobile 4.7.0 and mobile-ui 0.9.0

NativePHP for Mobile 4.7.0 is out, along with mobile-ui 0.9.0. You get shared-element morph transitions, opt-in Laravel validation for native screens, route middleware on in-app navigation, your own SVG icons, a microphone that records WAV, and faster builds that skip steps whose inputs haven't changed. mobile-ui 0.9.0 adds <native:popover>, <native:emoji-picker>, sticky list headers, long-press context menus, and a bottom sheet detent that fits its content.

Upgrade with:

Copied!
composer update nativephp/mobile nativephp/mobile-ui
php artisan native:install --force
php artisan native:run

composer update leaves the native iOS and Android projects alone, so a lot of 4.7.0 only reaches your app after native:install --force. mobile-ui 0.9.0 requires nativephp/mobile ^4.7 (#169).

Shared-element morph transitions arrive with ref and @navigate.morph (#372). Native route middleware now runs on in-app navigation (#348). navigate(), replace() and exitToWeb() accept route() and external URLs (#578), and the query string survives an exit to a web route (#583). NativeComponent::query() reads a screen's query string (#401).

Native navigation reconciles by router stack depth, with direction-aware stack transitions on Android (#439). Native routes opened from the php webview land on the native stack (#600), and the iOS back history menu pops every level it shows (#616). Custom native tab bar presentations are supported (#560).

On iOS, cold deep links to native screens boot natively (#591). Unrouted app links go back to the browser, warm and cold (#558). On Android a deep link path ending in * claims a prefix (#557), queries are forwarded still percent-encoded (#581), and off-host redirects open outside the app (#599). External deep link paths are no longer double-encoded (#605).

Validation, events and APIs#

Native screens can opt in to Laravel validation (#614), and mobile-ui shows validation errors on bound form controls (mobile-ui #176). BroadcastsGlobally events reach native screens, and #[On] gets a when filter (#539). Native events reach nested child components (#344), and QR scans and other device events reach web view pages on iOS (#611) and Android (#613).

The microphone gains wav(), maxDuration() and level() (#626). Device.GetInfo includes the phone's timezone (#566). HasVirtualListWindow::scrollVirtualListTo() opens a virtual list at a row (#608). Image fit values are named by an ImageFit enum (#572). The platform is read from NATIVEPHP_PLATFORM, and Platform is deprecated (#596). Headless build/verify commands, durable fatal capture, and report() for EDGE exceptions land in #305. iOS plugins can declare scene configurations (#541), and Home Screen quick actions are delivered through a scene delegate (#530).

Styling and layout#

Apps can use their own SVG icons (#544, mobile-ui #132). Dashed, dotted and per-side borders are drawn natively (#548). text-outline draws hollow outlined text (#549, mobile-ui #142). font-rounded selects the rounded system font, drawn in SF Pro Rounded on iOS (#529, mobile-ui #121). Custom fonts draw their real weights for font-medium, font-semibold and font-bold (mobile-ui #155). Top-bar actions get an opt-in image prop (#537) and show-label (#527). Arbitrary percent widths and heights parse as percentages (#499).

Flex layout got several fixes: rows size from their flexed children's real height (#571), each flex child is measured once per proposal (#564), and Android column children stretch like iOS (#451).

mobile-ui 0.9.0#

New elements and features:

  • <native:popover>, a bubble beside the spot it's laid over (#184)
  • <native:emoji-picker>, the platform's own emoji chooser (#180)
  • A sticky header that pins to the top of a list (#138)
  • A pressable's :context-menu opens on a long press (#179, mobile-air #621)
  • A fit bottom sheet detent that sizes the sheet to its content (#186)
  • Lists and virtual lists open at a row with auto-scroll-to (#174), and ScrollView::autoScrollTo() works in both renderers (#67)
  • Lists report their visible rows (#145) and tell the screen when a scroll comes to rest (#177)
  • Gesture area pans snap to points (#183), and bounded gesture areas keep their content on screen (#173)
  • Horizontal pagers get a header that scrolls with the page showing (#154) and report their page once a swipe settles (#135)
  • Button variants can name a theme colour (#137)

Text inputs: submit-label sets the return key / IME action (#69), next-focus chains keyboard focus between inputs (#70), inputs can declare a content type for AutoFill (#112), PHP can place the caret when it changes the text (#189), and stale model echoes no longer overwrite newer text (#110).

Lists: opt-in trailingTextStyle and trailingAlign (#66) and leadingImageFit (#116) on list items. On iOS, list rows line up with the system row layout (#118), grouped lists show their cards (#117), and only changed rows redraw (#152). The iOS button group is now the system segmented control (#123).

Performance and builds#

Build steps whose inputs haven't changed are skipped (#513), and the Gradle build and configuration caches are on (#514). iOS no longer re-extracts the bundle on every launch (#543). The version and build number decide re-extraction (#584). iOS keeps unchanged list rows when a list gains or loses rows (#575), and loads local images off the main thread (mobile-ui #149). Memory autoreleased by bridge calls and render passes is freed (#547). A native session that PHP dies in restarts, and the memory limit is raised (#561).

PHP binaries are bumped to 4.1.0 (#531). native:run warns when the installed binaries aren't the pinned release (#556). Gradle is no longer stopped after 600 seconds (#500). native:validate knows about plugin elements (#501), and unrenderable element tags fail loudly (#502). iOS builds stop guessing a build number when App Store Connect fails (#619).

Security#

4.7.0 includes the web view bridge hardening from 4.6.1 (#609) and keeps bridge parameters and results out of release logcat (#523). Composer's auth.json is stripped from bundled apps (#542), and .claude is no longer bundled (#622). PHP binary downloads are checked against the manifest's checksums and signature (#573), and the CA files are kept up to date (#570). The extracted Laravel bundle stays out of Android backups (#579). Crash details only show in debug builds (#592, #597).

Accessibility#

a11y_label is honoured on top-bar actions, and MaterialIcon honours contentDescription (#550). a11y-label works on pressable and layout containers (#580). On Android, a single-line text input submits from a hardware Enter key or the accessibility IME action (mobile-ui #163).

Fixes worth calling out#

  • Android no longer crashes when the phone rotates while the app is starting (#525) or when a positional node ID changes type (#436)
  • The runloop no longer wakes a sleeping phone (#511)
  • On iOS, swipes that start on press-* elements scroll (#524), and keyboard-dismiss taps no longer cancel UIKit controls (#545)
  • A started biometric prompt reports as started (#522)
  • A leaving screen closes its sheets and modals in its farewell frame (#617)
  • A screen's bg-* reaches the edges in landscape on Android (#528)
  • Async completions survive screen transitions (#555, #615)
  • A gesture area's swipe wins over iOS 26's swipe back from mid-screen (mobile-ui #181), and on Android no longer also presses a pressable (mobile-ui #182)
  • A slider's thumb stays under the finger while dragged (mobile-ui #160), and an Android bottom sheet slides away when PHP closes it (mobile-ui #170)

Full changelog#

Mobile 4.7.0 full changelog · Mobile 4.7.0 release · mobile-ui 0.9.0 full changelog · mobile-ui 0.9.0 release

Thanks to Simon Hamp, Shane Rosenthal, Willem Leuverink, SRWieZ, Sander (@sandermj), wojt-janowski, bkuhl, prosenthal828, thelemon2020, PrestaEdit, martin-ro, nlo-tronet, victorycodedev, MhAhmadAli, jeremyfrancis55, nrobates, fkrzski, pstephan1187, and jubianchi. Welcome to everyone who made their first contribution in this release.