Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ux Native Cli Laravel Package

akyos/ux-native-cli

Bundle Symfony exposant des commandes console pour générer des coques Hotwire Native minimales (Android et iOS) depuis des modèles, puis lancer Gradle et xcodebuild depuis la racine d’un projet PHP. Configuration via native.yaml (URL, application_id, bundle_id, chemins).

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the CLI and the Hotwire Native runtime:

    composer require akyos/ux-native-cli symfony/ux-native
    

    The CLI ships as a Symfony bundle (Akyos\UxNativeCliBundle\AkyosUxNativeCliBundle), so it runs through a bin/console binary rather than php artisan. In a Laravel-only repo, keep a thin tooling console (or a small sibling Symfony app) that points at your Laravel server URL — the app it wraps is still your Laravel app.

  2. Create the config file (native.yaml) in the directory the console runs from:

    native:
        app_name: "MyApp"
        url: "http://192.168.1.20:8000"   # LAN IP, not 127.0.0.1, if you test on a device
        application_id: "com.example.myapp"
        bundle_id: "com.example.myapp"
        ios_development_team: "ABCDE12345" # required for real iOS devices
        icon: "resources/images/app-icon.png" # new in 0.5.0 — generates iOS + Android icons
    

    As of 0.5.0 the CLI also drives the top bar (via top_bar in path-configuration rules) and the app icon from this file.

  3. Generate the native apps:

    php bin/console native:init
    

    This scaffolds Hotwire Native iOS (native/ios) and Android (native/android).

  4. Add bridges you didn't enable at init (new in 0.5.0 — no need to re-init):

    php bin/console native:update:config --notification --barcode-scanner --admob
    

    native:update:config reinjects native.yaml values (URL, name, icon, top bar) between markers and adds any missing bridge wiring, leaving the rest of the generated code untouched.

  5. First build and install:

    php bin/console native:build --platform=android --install
    php bin/console native:build --platform=ios --install
    

    native:build now runs the config update first, so native.yaml edits are picked up automatically on every build.

  6. Where to look first: native.yaml (single source of truth), then native/ios / native/android for the generated shells, then your Laravel routes — the shell renders whatever your app serves at native.url.

First Use Case

Point the shell at php artisan serve (port 8000 is the default native.yaml expects) and confirm your existing Blade/Turbo pages render inside the native shell. Drop a 1024×1024 PNG at the icon: path, rebuild, and confirm the launcher icon changed — that's the fastest sanity check that the 0.5.0 config injection is actually running.


Implementation Patterns

Daily Development Loop

  1. Backend: php artisan serve --host=0.0.0.0 (bind to all interfaces so a physical device can reach it).
  2. Rebuild + install after native-side changes only — web changes just need a refresh since the shell loads your Laravel URLs.
  3. Config changes no longer need a rebuild command of their own — edit native.yaml (URL, name, icon, top bar), then native:build. Use native:update:config alone when you only want to sync the generated projects without compiling.
  4. Test on a physical device without cable:
    php bin/console native:qr-install --platform=android --build
    

Keeping native.yaml in Sync (0.5.0)

  • native:update:config writes between markers inside the generated native projects. Treat everything between those markers as generated code.
  • Typical flows:
    # pulled a teammate's URL/top-bar change — apply without rebuilding
    php bin/console native:update:config
    
    # add a bridge to an existing app after the fact
    php bin/console native:update:config --barcode-scanner
    
    # normal build already does the update step
    php bin/console native:build --platform=android --install
    
  • Point different environments at different native.yaml files and let native:build re-inject the URL — a staging build can't accidentally ship production URLs if the file is right.

Push Notifications

  1. Enable at init or later: native:init --notification, or native:update:config --notification on an existing project. This wires Firebase (Android) and APNs (iOS) and installs the notification-token JS bridge.
  2. Capture the token in Laravel: the bridge fires when the device obtains a token. Handle it in your JS and POST it to a Laravel endpoint:
    document.addEventListener('notification-token', (e) =>
      fetch('/api/device-tokens', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrf },
        body: JSON.stringify({ token: e.detail.token, platform: e.detail.platform ?? 'unknown' })
      })
    );
    
    Store per user + device in a device_tokens table (unique on token), with platform, last_seen_at.
  3. Server-side NotificationTrait: 0.5.0 ships a Symfony UX Live Component trait. In Laravel, port the same behaviour into a Livewire trait or a queued notification channel — don't try to use the Symfony trait directly.
  4. Send from Laravel with the companion package:
    composer require akyos/native-push
    
    Use its server-side API inside a queued job / notification channel so a slow FCM/APNs call never blocks a request.
  5. Deep link on tap: opening a specified page when tapped, app open or closed. Put the target path in the payload and make sure it's reachable through your native.url route set — no auth wall a cold start can't clear.
  6. Authenticate before subscribing: the token bridge usually fires before a session exists; queue the token locally and flush it after login.

Barcode / QR Scanning (0.5.0)

  • Add the bridge with native:update:config --barcode-scanner; it ships a Stimulus controller plus a ScanQRTrait for Symfony Live Components.
  • The scanner bridge is JS-first: trigger it from your front-end and route the decoded payload to a Laravel endpoint (or a Livewire action) that performs the lookup. Check the shipped Stimulus controller in the package for the exact event/action names before wiring listeners.
  • Permissions are native: camera permission must be granted, and it can't be exercised from the browser dev loop — always verify on a device.

Ads (AdMob, 0.5.0)

  • native:update:config --admob adds the ads bridge to the generated projects. Ad unit IDs are per-app/per-platform; keep them out of native.yaml if you don't want them committed, and inject per environment.
  • Test with Google's test unit IDs first — real units silently return no ads until the account/app is approved.

Path Configuration: Tabs, Modals, Top Bar, Offline

Map URL patterns to native presentation instead of building separate screens:

{
  "settings": { "screenshots_enabled": true },
  "rules": [
    { "patterns": ["/home$"], "properties": { "context": "default", "uri": "hotwire://fragment/web/home" } },
    { "patterns": ["/new$", "/edit$"], "properties": { "context": "modal", "top_bar": false } },
    { "patterns": ["/account$"], "properties": { "context": "default", "pull_to_refresh_enabled": false } }
  ]
}
  • top_bar is new in 0.5.0 and applies on both iOS and Android. It is evaluated per rule, so a global "hide the bar" means adding it to every rule that matches, not one top-level setting.
  • Serve the file from a stable Laravel route (e.g. /native/path-configuration.json) and point the shell at it.
  • Enable offline mode for content that survives a dead network; pair it with cache headers on your controllers so the shell's cached copy goes stale predictably.
  • Use tabs for top-level destinations only — each tab keeps its own navigation stack.

iOS Signing for Real Devices

  • Set ios_development_team in native.yaml; builds pass -allowProvisioningUpdates so Xcode can create/refresh profiles without opening the GUI.
  • Simulator builds need no team; CI: inject the team ID via env var rather than committing it.

CI/CD

  1. Artifacts only:
    php bin/console native:build --platform=android --no-install
    
    Outputs: native/android/app/build/outputs/apk/debug/app-debug.apk and native/ios/build/DerivedDataCli/Build/Products/Debug-iphoneos/MyApp.app.
  2. Non-interactive iOS: --ios-destination="simulator:UDID".
  3. Release builds: override the Gradle task:
    native:
        android_gradle_task: "assembleRelease"
    
  4. Commit the icon source to the repo — the build regenerates platform icons from native.icon and will fail or produce stale assets if the path is missing on the runner.

Gotchas and Tips

Common Pitfalls

  1. Symfony bundle in a Laravel repo: Laravel has no config/bundles.php and won't auto-register the bundle. Run the commands from a dedicated tooling console, or vendor the command classes into app/Console/Commands and adapt them — the artifacts it produces (native/) are framework-agnostic.
  2. Asset Mapper doesn't exist in Laravel (0.5.0): the bridge Stimulus controllers are now served from the package via Asset Mapper as @akyos/ux-native-cli instead of being copied into your project. Laravel uses Vite, not importmap. You must either add the vendor path to Vite's resolve/alias config or copy the controllers into resources/js/controllers and register them yourself. Symptom of missing this: notification-token / scanner / ads bridges never fire.
  3. NotificationTrait / ScanQRTrait are Symfony UX Live Component traits. There is no Laravel equivalent — reimplement them as Livewire traits or plain services. Composer will install them happily; they just won't do anything in a Livewire component.
  4. Don't delete the injection markers in native/. native:update:config — and therefore native:build, which calls it — cannot find its anchors without them. Hand-edits inside the marked regions are overwritten on every build; put customisations outside.
  5. native:init --force still wipes hand-edits. Use native:update:config for config/bridge changes; reserve --force for genuine re-scaffolds.
  6. 127.0.0.1 is not reachable from a device. Always use the LAN IP or a tunnel.
  7. Cleartext HTTP is blocked by default: Android needs usesCleartextTraffic, iOS needs an ATS exception for http:// dev URLs. Prefer HTTPS or local tunnels before editing manifests.
  8. APNs tokens don't exist on the simulator — iOS push is real-device only. Firebase on iOS also needs a valid GoogleService-Info.plist matching bundle_id.
  9. Android 13+ runtime permission: POST_NOTIFICATIONS must be requested; a token can be issued while notifications are silently dropped.
  10. Notification deep links into auth-gated pages: on a cold start the session cookie may not be loaded yet. Allowlist target routes or render a login-then-redirect flow.
  11. iOS signature failures: usually a wrong/missing ios_development_team, or a bundle ID that doesn't exist in the Apple portal.
  12. iOS ... is not installed: install the runtime via Xcode → Settings → Components.
  13. No devices/simulators found:
    xcrun simctl boot "iPhone 15"
    
  14. QR install: IPA cannot be installed over plain HTTP — use Xcode, TestFlight, or an OTA manifest. Default server port is 9876; open it in your firewall. Android will still ask for "Unknown Sources".

Debugging Tips

  1. Verbose CLI output: php bin/console native:build -vv.
  2. Dry run before destroying anything: php bin/console native:init --app-name="Test" --dry-run.
  3. The WebView cache is disabled in debug builds (0.5.0). Cache/offline bugs therefore only reproduce on a release build — verify offline-mode behaviour there before filing anything.
  4. Native logs: adb logcat | grep -i hotwire for Android; Xcode console / xcrun simctl spawn booted log stream for iOS.
  5. Inspect the web layer first — most "native" bugs are your Laravel HTML/JS behaving differently without browser chrome (no address bar, different viewport, no target=_blank).
  6. Manual builds when the CLI output is ambiguous:
    cd native/android && ./gradlew assembleDebug
    xcodebuild -project NativeApp.xcodeproj -scheme NativeApp \
      -destination 'platform=iOS Simulator,name=iPhone 15'
    
  7. List simulator UDIDs: xcrun simctl list devices booted -j.

Extension Points

  1. Custom templates: copy from vendor/akyos/ux-native-cli/Resources/templates into config/native/templates and edit there — keeps upgrades clean.
  2. Post-build hooks: a wrapper command that runs native:build and then adds signing/upload steps:
    $this->getApplication()->find('native:build')->run($input, $output);
    // then: sign, upload to S3, notify Slack
    
  3. Env vars for machine-specific paths (.env.local / CI secrets): ANDROID_HOME, JAVA_HOME.
  4. Bridge controllers: rather than copying package controllers into resources/js, alias the vendor path in vite.config.js so @akyos/ux-native-cli resolves from vendor/ — you get upgrades for free and stay aligned with the Asset Mapper layout.

Configuration Quirks

  1. Path resolution: defaults are {project}/native/android and {project}/native/ios; override with android_path / ios_path.
  2. JDK mismatches: Gradle failing on JAVA_HOME — set java_home in native.yaml or export it; AGP versions in generated projects pin to specific JDK majors (17 is the safe default).
  3. android_home: set it explicitly if adb: command not found:
    native:
        android_home: "/Users/you/Library/Android/sdk"
    
  4. native.yaml is the single source of truth, and since 0.5.0 native:build re-injects it automatically — config drift between environments is a file-diff problem, not a code problem. Keep it version-controlled and override secrets via env.
  5. top_bar lives in path-configuration rules, not native.yaml. If the bar won't hide, confirm the URL actually matches the rule you edited.
  6. Icons are generated artifacts. Changing the source PNG takes effect on the next build; editing the generated Assets.xcassets / mipmap files by hand will be overwritten.
  7. Offline mode caches aggressively: after changing a path or a cached page, uninstall/reinstall the app — clearing your Laravel cache won't clear the shell's.
  8. bundle_id and application_id are baked into the generated projects; changing them later requires re-running native:init --force or editing the native projects by hand.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast