From 593fde185334662f9bd0fea56689bf7969ab3657 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Sun, 1 Jun 2025 19:14:01 -0400 Subject: [PATCH 01/16] Updates some of the docs for mobile --- .../docs/mobile/1/the-basics/app-assets.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 resources/views/docs/mobile/1/the-basics/app-assets.md diff --git a/resources/views/docs/mobile/1/the-basics/app-assets.md b/resources/views/docs/mobile/1/the-basics/app-assets.md new file mode 100644 index 00000000..47d4eafb --- /dev/null +++ b/resources/views/docs/mobile/1/the-basics/app-assets.md @@ -0,0 +1,69 @@ +--- +title: App Assets +order: 400 +--- + +## Customizing Your App Icon + +NativePHP makes it easy to apply a custom app icon to your iOS and Android builds. + +### Step 1: Provide Your Icon + +Place a single high-resolution icon file at: `public/icon.png` + + +### Requirements: +- Format: PNG +- Size: 1024 × 1024 pixels +- Shape: Square +- Background: Transparent or solid — your choice + +Note: This image will be automatically resized for all Android densities and used as the base iOS app icon. + +--- + +## Compiling CSS and JavaScript + +Your device behaves like a server, so assets must be compiled before deployment. + +To ensure your latest styles and JavaScript are included, run: `npm run build` before running: `php artisan native:run`. + +If you’ve made changes to your frontend, this step is required to see them reflected in the app. + +--- + +## Using the --watch Flag (Experimental) + +NativePHP includes an experimental `--watch` flag that enables automatic file syncing while the app is running: + +php artisan native:run --watch + +This is useful during development for quickly testing changes without rebuilding the entire app. + +### Caveats + +- This feature is currently best suited for **Blade** and **Livewire** applications. +- It does **not** currently detect or sync **compiled frontend assets**, such as those built with Vite or used by **Inertia.js**. +- If you're working with a JavaScript-heavy stack (Vue, React, Inertia), you should continue using `npm run build` before launching the app with `native:run`. + +### Recommendation + +Use `--watch` when you're iterating on Blade views or Livewire components. For all other use cases, treat this flag as experimental and optional. + +--- + +## Optional: Installing with ICU Support + +By default, NativePHP installs a smaller PHP runtime without ICU (International Components for Unicode) to keep app size minimal. + +If your Laravel app uses features that rely on `intl` (such as number formatting, localized date handling, or advanced string collation), you’ll need ICU support enabled. + +To include ICU during installation, select it when running: `php artisan native:install`. + +This will install a version of PHP with full ICU support. Note that it increases the PHP binary size significantly (typically from ~16MB to ~44MB). + +**Important:** If you plan to use [Filament](https://filamentphp.com/) in your app, you must enable this option. Filament relies on the `intl` extension for formatting and localization features. + + + + From 9fefd45e31b1198d6335dffc7e648e5d1f06b175 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Sun, 1 Jun 2025 19:27:51 -0400 Subject: [PATCH 02/16] Updates ordering for docs --- resources/views/docs/mobile/1/the-basics/app-assets.md | 2 +- resources/views/docs/mobile/1/the-basics/deep-links.md | 2 +- resources/views/docs/mobile/1/the-basics/dialogs.md | 2 +- resources/views/docs/mobile/1/the-basics/native-functions.md | 2 +- resources/views/docs/mobile/1/the-basics/system.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/views/docs/mobile/1/the-basics/app-assets.md b/resources/views/docs/mobile/1/the-basics/app-assets.md index 47d4eafb..b8346f00 100644 --- a/resources/views/docs/mobile/1/the-basics/app-assets.md +++ b/resources/views/docs/mobile/1/the-basics/app-assets.md @@ -1,6 +1,6 @@ --- title: App Assets -order: 400 +order: 200 --- ## Customizing Your App Icon diff --git a/resources/views/docs/mobile/1/the-basics/deep-links.md b/resources/views/docs/mobile/1/the-basics/deep-links.md index 7be701b6..804bb0b1 100644 --- a/resources/views/docs/mobile/1/the-basics/deep-links.md +++ b/resources/views/docs/mobile/1/the-basics/deep-links.md @@ -1,6 +1,6 @@ --- title: Deep, Universal, App Links and NFC -order: 900 +order: 500 --- ## Overview diff --git a/resources/views/docs/mobile/1/the-basics/dialogs.md b/resources/views/docs/mobile/1/the-basics/dialogs.md index a7e35836..4ccba9c4 100644 --- a/resources/views/docs/mobile/1/the-basics/dialogs.md +++ b/resources/views/docs/mobile/1/the-basics/dialogs.md @@ -1,6 +1,6 @@ --- title: Dialogs -order: 400 +order: 300 --- ## Native Dialogs diff --git a/resources/views/docs/mobile/1/the-basics/native-functions.md b/resources/views/docs/mobile/1/the-basics/native-functions.md index 630d184b..3260b32f 100644 --- a/resources/views/docs/mobile/1/the-basics/native-functions.md +++ b/resources/views/docs/mobile/1/the-basics/native-functions.md @@ -1,6 +1,6 @@ --- title: Native Functions -order: 1 +order: 100 --- Nearly any basic Laravel app will work as a mobile app with NativePHP for Mobile. However, what makes NativePHP diff --git a/resources/views/docs/mobile/1/the-basics/system.md b/resources/views/docs/mobile/1/the-basics/system.md index cc3236d3..8e87a747 100644 --- a/resources/views/docs/mobile/1/the-basics/system.md +++ b/resources/views/docs/mobile/1/the-basics/system.md @@ -1,6 +1,6 @@ --- title: System -order: 800 +order: 400 --- ## Native System From a699a3086226bedcf70393cc05989df5e64f2353 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Sat, 14 Jun 2025 22:37:32 -0400 Subject: [PATCH 03/16] updates doc --- resources/views/docs/mobile/1/getting-started/installation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/views/docs/mobile/1/getting-started/installation.md b/resources/views/docs/mobile/1/getting-started/installation.md index 3ba83b09..6f930534 100644 --- a/resources/views/docs/mobile/1/getting-started/installation.md +++ b/resources/views/docs/mobile/1/getting-started/installation.md @@ -76,6 +76,7 @@ Then run: ```shell composer require nativephp/mobile ``` +*If you experience a cURL error when running this command make sure you are running PHP v8.3+ in your CLI.* If this is the first time you're installing the package, you will be prompted to authenticate. Your username is the email address you used when purchasing your license. Your password is your license key. From bdcac7b575a90036afcc7d2f5c9661244bbe6880 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Sun, 6 Jul 2025 22:32:17 -0400 Subject: [PATCH 04/16] =?UTF-8?q?=F0=9F=93=9A=20Update=20mobile=20v1.1=20d?= =?UTF-8?q?ocumentation=20structure=20and=20content?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Reorganize v1.1 documentation with new APIs and concepts sections - Add comprehensive database security guidance covering API-first architecture, Laravel Sanctum integration, and SecureStorage best practices - Simplify Haptics API documentation to essential examples and usage patterns - Add Windows performance tip for faster composer installs during compilation - Improve geolocation API documentation with detailed event handling - Update installation guide with platform-specific optimizations - Restructure v1.1 content hierarchy for better developer experience --- .../ShowDocumentationController.php | 5 +- resources/views/docs/mobile/1/_index.md | 99 ++++ resources/views/docs/mobile/1/apis/_index.md | 140 ++++++ .../views/docs/mobile/1/apis/biometrics.md | 93 ++++ resources/views/docs/mobile/1/apis/camera.md | 161 +++++++ resources/views/docs/mobile/1/apis/dialog.md | 249 ++++++++++ .../views/docs/mobile/1/apis/geolocation.md | 438 ++++++++++++++++++ resources/views/docs/mobile/1/apis/haptics.md | 91 ++++ .../docs/mobile/1/apis/push-notifications.md | 411 ++++++++++++++++ .../docs/mobile/1/apis/secure-storage.md | 288 ++++++++++++ resources/views/docs/mobile/1/apis/system.md | 221 +++++++++ .../views/docs/mobile/1/concepts/_index.md | 46 ++ .../views/docs/mobile/1/concepts/ci-cd.md | 167 +++++++ .../views/docs/mobile/1/concepts/databases.md | 272 +++++++++++ .../docs/mobile/1/concepts/deep-links.md | 108 +++++ .../push-notifications.md | 6 +- .../{digging-deeper => concepts}/security.md | 2 +- .../mobile/1/concepts/splash-screen-icons.md | 359 ++++++++++++++ .../docs/mobile/1/concepts/versioning.md | 291 ++++++++++++ .../docs/mobile/1/digging-deeper/_index.md | 4 - .../docs/mobile/1/digging-deeper/databases.md | 33 -- .../docs/mobile/1/getting-started/_index.md | 2 +- .../mobile/1/getting-started/configuration.md | 4 +- .../1/getting-started/environment-setup.md | 94 ++++ .../mobile/1/getting-started/installation.md | 61 +-- .../mobile/1/getting-started/introduction.md | 26 +- .../docs/mobile/1/getting-started/roadmap.md | 8 +- .../views/docs/mobile/1/the-basics/_index.md | 2 +- .../docs/mobile/1/the-basics/app-assets.md | 20 +- .../1/the-basics/asynchronous-methods.md | 308 ++++++++++++ .../views/docs/mobile/1/the-basics/dialogs.md | 39 -- .../docs/mobile/1/the-basics/icu-support.md | 376 +++++++++++++++ .../mobile/1/the-basics/native-functions.md | 66 +-- .../views/docs/mobile/1/the-basics/system.md | 134 ------ 34 files changed, 4262 insertions(+), 362 deletions(-) create mode 100644 resources/views/docs/mobile/1/apis/_index.md create mode 100644 resources/views/docs/mobile/1/apis/biometrics.md create mode 100644 resources/views/docs/mobile/1/apis/camera.md create mode 100644 resources/views/docs/mobile/1/apis/dialog.md create mode 100644 resources/views/docs/mobile/1/apis/geolocation.md create mode 100644 resources/views/docs/mobile/1/apis/haptics.md create mode 100644 resources/views/docs/mobile/1/apis/push-notifications.md create mode 100644 resources/views/docs/mobile/1/apis/secure-storage.md create mode 100644 resources/views/docs/mobile/1/apis/system.md create mode 100644 resources/views/docs/mobile/1/concepts/_index.md create mode 100644 resources/views/docs/mobile/1/concepts/ci-cd.md create mode 100644 resources/views/docs/mobile/1/concepts/databases.md create mode 100644 resources/views/docs/mobile/1/concepts/deep-links.md rename resources/views/docs/mobile/1/{digging-deeper => concepts}/push-notifications.md (97%) rename resources/views/docs/mobile/1/{digging-deeper => concepts}/security.md (98%) create mode 100644 resources/views/docs/mobile/1/concepts/splash-screen-icons.md create mode 100644 resources/views/docs/mobile/1/concepts/versioning.md delete mode 100644 resources/views/docs/mobile/1/digging-deeper/_index.md delete mode 100644 resources/views/docs/mobile/1/digging-deeper/databases.md create mode 100644 resources/views/docs/mobile/1/getting-started/environment-setup.md create mode 100644 resources/views/docs/mobile/1/the-basics/asynchronous-methods.md delete mode 100644 resources/views/docs/mobile/1/the-basics/dialogs.md create mode 100644 resources/views/docs/mobile/1/the-basics/icu-support.md delete mode 100644 resources/views/docs/mobile/1/the-basics/system.md diff --git a/app/Http/Controllers/ShowDocumentationController.php b/app/Http/Controllers/ShowDocumentationController.php index bae2ce42..d051f302 100644 --- a/app/Http/Controllers/ShowDocumentationController.php +++ b/app/Http/Controllers/ShowDocumentationController.php @@ -46,7 +46,6 @@ public function __invoke(Request $request, string $platform, string $version, ?s } catch (InvalidArgumentException $e) { return $this->redirectToFirstNavigationPage($navigation, $page); } - SEOTools::setTitle($pageProperties['title'].' - NativePHP '.$platform.' v'.$version); SEOTools::setDescription(Arr::exists($pageProperties, 'description') ? $pageProperties['description'] : ''); @@ -65,7 +64,8 @@ protected function getPageProperties($platform, $version, $page = null): array $pageProperties = $document->matter(); $versionProperties = YamlFrontMatter::parseFile(resource_path("views/docs/{$platform}/{$version}/_index.md")); - $pageProperties = array_merge($pageProperties, $versionProperties->matter()); + + $pageProperties = array_merge($versionProperties->matter(), $pageProperties); $pageProperties['platform'] = $platform; $pageProperties['version'] = $version; @@ -143,6 +143,7 @@ protected function getPageProperties($platform, $version, $page = null): array } } + return $pageProperties; } diff --git a/resources/views/docs/mobile/1/_index.md b/resources/views/docs/mobile/1/_index.md index e69de29b..a6e453c2 100644 --- a/resources/views/docs/mobile/1/_index.md +++ b/resources/views/docs/mobile/1/_index.md @@ -0,0 +1,99 @@ +--- +title: Mobile +order: 1 +--- + +# NativePHP for Mobile + +**Build native iOS and Android apps with PHP and Laravel** + +NativePHP for Mobile revolutionizes mobile development by allowing PHP developers to create native mobile applications using the languages and frameworks they already know and love. No need to learn Swift, Kotlin, or React Native - just pure PHP and Laravel. + +## What Makes NativePHP Mobile Special? + +=� **Native Performance** - Your app runs natively on device with embedded PHP runtime +=� **True Mobile APIs** - Access camera, biometrics, push notifications, and more +� **Laravel Powered** - Use your existing Laravel skills and ecosystem +=' **No Web Server** - Your app runs entirely on-device +<� **Cross Platform** - Single codebase for iOS and Android + +## Quick Start + +Get your first mobile app running in minutes: + +```bash +# Install NativePHP Mobile +composer require nativephp/mobile + +# Configure your app +php artisan native:install + +# Run your app +php artisan native:run +``` + +## Current Features (v1.1) + +**Available now:** +- =� Camera & Gallery access +- = Biometric authentication (Face ID, Touch ID, Fingerprint) +- = Push notifications via Firebase +- =� Native dialogs & toasts +- = Deep links & universal links +- =� NFC support +- =� Haptic feedback & vibration +- =& Flashlight control +- =� Native sharing +- = Secure storage (Keychain/Keystore) +- =� Location services + +[See the complete roadmap �](/docs/mobile/1/getting-started/roadmap) + +## Documentation Sections + +### [Getting Started](/docs/mobile/1/getting-started) +Everything you need to start building mobile apps with PHP: +- [Introduction](/docs/mobile/1/getting-started/introduction) - Learn how NativePHP Mobile works +- [Installation](/docs/mobile/1/getting-started/installation) - Set up your development environment +- [Environment Setup](/docs/mobile/1/getting-started/environment-setup) - Configure iOS and Android tools +- [Configuration](/docs/mobile/1/getting-started/configuration) - App settings and permissions +- [Development](/docs/mobile/1/getting-started/development) - Development workflow +- [Roadmap](/docs/mobile/1/getting-started/roadmap) - Current and planned features + +### [The Basics](/docs/mobile/1/the-basics) +Core concepts and fundamental knowledge: +- [Native Functions](/docs/mobile/1/the-basics/native-functions) - Understanding sync vs async APIs +- [Asynchronous Methods](/docs/mobile/1/the-basics/asynchronous-methods) - Event-driven mobile development +- [ICU Support](/docs/mobile/1/the-basics/icu-support) - International components for Unicode + +### [Concepts](/docs/mobile/1/concepts) +Important concepts for mobile app development: +- [CI/CD](/docs/mobile/1/concepts/ci-cd) - Continuous integration and deployment +- [Deep Links](/docs/mobile/1/concepts/deep-links) - Universal links, app links, and NFC +- [Push Notifications](/docs/mobile/1/concepts/push-notifications) - Firebase Cloud Messaging setup +- [Splash Screen/Icons](/docs/mobile/1/concepts/splash-screen-icons) - App branding and assets +- [Versioning](/docs/mobile/1/concepts/versioning) - App version management + +### [APIs](/docs/mobile/1/apis) +Complete API reference for all native features: +- [Biometrics](/docs/mobile/1/apis/biometrics) - Face ID, Touch ID, fingerprint authentication +- [Camera](/docs/mobile/1/apis/camera) - Photo capture and gallery access +- [Dialog](/docs/mobile/1/apis/dialog) - Alerts, toasts, and sharing +- [Geolocation](/docs/mobile/1/apis/geolocation) - GPS and location services +- [Haptics](/docs/mobile/1/apis/haptics) - Vibration and tactile feedback +- [PushNotifications](/docs/mobile/1/apis/push-notifications) - FCM token management +- [SecureStorage](/docs/mobile/1/apis/secure-storage) - Keychain and keystore operations +- [System](/docs/mobile/1/apis/system) - Flashlight and legacy methods + +### [Digging Deeper](/docs/mobile/1/digging-deeper) +Advanced topics for production apps: +- [Databases](/docs/mobile/1/digging-deeper/databases) - SQLite and data management +- [Security](/docs/mobile/1/digging-deeper/security) - Best practices and secure storage + +## Need Help? + +- **License Required** - [Purchase your license](https://nativephp.com/mobile) to get started +- **Community** - Join our Discord for support and discussions +- **Examples** - Check out the Kitchen Sink demo app (coming soon to app stores) + +Ready to build your first mobile app with PHP? [Let's get started! �](/docs/mobile/1/getting-started/introduction) \ No newline at end of file diff --git a/resources/views/docs/mobile/1/apis/_index.md b/resources/views/docs/mobile/1/apis/_index.md new file mode 100644 index 00000000..c2df782c --- /dev/null +++ b/resources/views/docs/mobile/1/apis/_index.md @@ -0,0 +1,140 @@ +--- +title: APIs +order: 4 +--- + +# API Reference + +Complete documentation for all NativePHP Mobile APIs. Each API provides access to native device capabilities through familiar PHP facades. + +## Available APIs + +### [Biometrics](/docs/mobile/1/apis/biometrics) +**Face ID, Touch ID, Fingerprint Authentication** +```php +Biometrics::promptForBiometricID(); +``` +Secure user authentication using device biometric sensors. Supports Face ID on iOS, Touch ID, and fingerprint readers on Android. + +### [Camera](/docs/mobile/1/apis/camera) +**Photo Capture & Gallery Access** +```php +Camera::getPhoto(); +Camera::pickImages('images', true, 5); +``` +Take photos with the device camera or select images from the photo gallery. Supports both single and multiple image selection. + +### [Dialog](/docs/mobile/1/apis/dialog) +**Native UI Elements** +```php +Dialog::alert('Title', 'Message', $buttons, $callback); +Dialog::toast('Success message'); +Dialog::share('Title', 'Text', 'https://example.com'); +``` +Display native alerts, toast notifications, and sharing interfaces that match platform design guidelines. + +### [Geolocation](/docs/mobile/1/apis/geolocation) ⭐ New in v1.1 +**GPS & Location Services** +```php +Geolocation::getCurrentPosition(true); // High accuracy +Geolocation::checkPermissions(); +Geolocation::requestPermissions(); +``` +Access device location services with configurable accuracy levels and proper permission handling. + +### [Haptics](/docs/mobile/1/apis/haptics) +**Vibration & Tactile Feedback** +```php +Haptics::vibrate(); +``` +Provide tactile feedback for user interactions, form validation, and important events. + +### [PushNotifications](/docs/mobile/1/apis/push-notifications) +**Firebase Cloud Messaging** +```php +PushNotifications::enrollForPushNotifications(); +PushNotifications::getPushNotificationsToken(); +``` +Register devices for push notifications and manage FCM tokens for server-side notification delivery. + +### [SecureStorage](/docs/mobile/1/apis/secure-storage) ⭐ New in v1.1 +**Keychain & Keystore Operations** +```php +SecureStorage::set('api_token', $token); +$token = SecureStorage::get('api_token'); +SecureStorage::delete('api_token'); +``` +Store sensitive data securely using iOS Keychain and Android Keystore with automatic encryption. + +### [System](/docs/mobile/1/apis/system) +**System Functions & Legacy API** +```php +System::flashlight(); // Toggle flashlight +``` +Control system functions like the flashlight. Also provides deprecated methods that have moved to dedicated facades. + +## API Patterns + +### Synchronous APIs +Execute immediately and return results: +- `Haptics::vibrate()` +- `System::flashlight()` +- `Dialog::toast()` +- `SecureStorage::set()` / `get()` + +### Asynchronous APIs +Trigger operations and fire events when complete: +- `Camera::getPhoto()` → `PhotoTaken` event +- `Biometrics::promptForBiometricID()` → `Completed` event +- `PushNotifications::enrollForPushNotifications()` → `TokenGenerated` event +- `Geolocation::getCurrentPosition()` → `LocationReceived` event + +### Event Handling +All async APIs use Laravel events with Livewire integration: + +```php +use Livewire\Attributes\On; +use Native\Mobile\Events\Camera\PhotoTaken; + +#[On('native:' . PhotoTaken::class)] +public function handlePhotoTaken(string $path) +{ + // Process the captured photo +} +``` + +## Migration from System Facade + +Many methods have moved from the `System` facade to dedicated facades in v1.1: + +| Old (Deprecated) | New (Recommended) | +|------------------|-------------------| +| `System::camera()` | `Camera::getPhoto()` | +| `System::vibrate()` | `Haptics::vibrate()` | +| `System::promptForBiometricID()` | `Biometrics::promptForBiometricID()` | +| `System::enrollForPushNotifications()` | `PushNotifications::enrollForPushNotifications()` | +| `System::secureSet()` / `secureGet()` | `SecureStorage::set()` / `get()` | + +The old methods still work but are deprecated. See the [System API documentation](/docs/mobile/1/apis/system) for complete migration guidance. + +## Platform Support + +All APIs work on both iOS and Android with platform-appropriate implementations: +- **iOS**: Uses native iOS frameworks and APIs +- **Android**: Uses Android SDK and native libraries +- **Permissions**: Automatically handled with user prompts when required +- **Fallbacks**: Graceful degradation when features aren't available + +## Error Handling + +APIs provide both success and error events for proper error handling: + +```php +#[On('native:' . PhotoTaken::class)] +public function handleSuccess($data) { /* ... */ } + +#[On('native:' . PermissionDenied::class)] +public function handleError($error) { /* ... */ } +``` + +Each API documentation includes complete error handling examples and best practices. \ No newline at end of file diff --git a/resources/views/docs/mobile/1/apis/biometrics.md b/resources/views/docs/mobile/1/apis/biometrics.md new file mode 100644 index 00000000..4260d24d --- /dev/null +++ b/resources/views/docs/mobile/1/apis/biometrics.md @@ -0,0 +1,93 @@ +--- +title: Biometrics +order: 100 +--- + +## Overview + +The Biometrics API allows you to authenticate users using their device's biometric sensors like Face ID, Touch ID, or fingerprint scanners. + +```php +use Native\Mobile\Facades\Biometrics; +``` + +## Methods + +### `promptForBiometricID()` + +Prompts the user for biometric authentication. + +## Events + +### `Native\Mobile\Events\Biometric\Completed` + +Fired when biometric authentication completes (success or failure). + +```php +use Livewire\Attributes\On; +use Native\Mobile\Events\Biometric\Completed; + +#[On('native:' . Completed::class)] +public function handleBiometricAuth(bool $success) +{ + if ($success) { + // User authenticated successfully + $this->unlockSecureFeature(); + } else { + // Authentication failed + $this->showErrorMessage(); + } +} +``` + +## Example Usage + +```php +use Livewire\Component; +use Livewire\Attributes\On; +use Native\Mobile\Facades\Biometrics; +use Native\Mobile\Events\Biometric\Completed; + +class SecureArea extends Component +{ + public bool $isUnlocked = false; + public bool $isAuthenticating = false; + + public function authenticate() + { + $this->isAuthenticating = true; + Biometrics::promptForBiometricID(); + } + + #[On('native:' . Completed::class)] + public function handleBiometricAuth(bool $success) + { + $this->isAuthenticating = false; + + if ($success) { + $this->isUnlocked = true; + session(['biometric_authenticated' => true]); + } else { + $this->addError('auth', 'Biometric authentication failed'); + } + } + + public function render() + { + return view('livewire.secure-area'); + } +} +``` + +## Platform Support + +- **iOS:** Face ID, Touch ID +- **Android:** Fingerprint, Face unlock, other biometric methods +- **Fallback:** System authentication (PIN, password, pattern) + +## Security Notes + +- Biometric authentication provides **convenience**, not absolute security +- Always combine with other authentication factors for sensitive operations +- Consider implementing session timeouts for unlocked states +- Users can potentially bypass biometrics if their device is compromised diff --git a/resources/views/docs/mobile/1/apis/camera.md b/resources/views/docs/mobile/1/apis/camera.md new file mode 100644 index 00000000..3b26c383 --- /dev/null +++ b/resources/views/docs/mobile/1/apis/camera.md @@ -0,0 +1,161 @@ +--- +title: Camera +order: 200 +--- + +## Overview + +The Camera API provides access to the device's camera for taking photos and selecting images from the gallery. + +```php +use Native\Mobile\Facades\Camera; +``` + +## Methods + +### `getPhoto()` + +Opens the camera interface to take a photo. + +```php +Camera::getPhoto(); +``` + +### `pickImages()` + +Opens the gallery/photo picker to select existing images. + +**Parameters:** +- `string $media_type` - Type of media to pick: `'all'`, `'images'`, `'videos'` (default: `'all'`) +- `bool $multiple` - Allow multiple selection (default: `false`) + +**Returns:** `bool` - `true` if picker opened successfully + +```php +// Pick a single image +Camera::pickImages('images', false); + +// Pick multiple images +Camera::pickImages('images', true); + +// Pick any media type +Camera::pickImages('all', true); +``` + +## Events + +### `Native\Mobile\Events\Camera\PhotoTaken` + +Fired when a photo is taken with the camera. + +**Payload:** `string $path` - File path to the captured photo + +```php +use Livewire\Attributes\On; +use Native\Mobile\Events\Camera\PhotoTaken; + +#[On('native:' . PhotoTaken::class)] +public function handlePhotoTaken(string $path) +{ + // Process the captured photo + $this->processPhoto($path); +} +``` + +### `Native\Mobile\Events\Gallery\MediaSelected` + +Fired when media is selected from the gallery. + +**Payload:** `array $media` - Array of selected media items + +```php +use Livewire\Attributes\On; +use Native\Mobile\Events\Gallery\MediaSelected; + +#[On('native:' . MediaSelected::class)] +public function handleMediaSelected($success, $files, $count) +{ + foreach ($files as $file) { + // Process each selected media item + $this->processMedia($file); + } +} +``` + +## Example Usage + +```php +use Livewire\Component; +use Livewire\Attributes\On; +use Native\Mobile\Facades\Camera; +use Native\Mobile\Events\Camera\PhotoTaken; +use Native\Mobile\Events\Gallery\MediaSelected; + +class PhotoManager extends Component +{ + public array $photos = []; + public bool $isCapturing = false; + + public function takePhoto() + { + $this->isCapturing = true; + Camera::getPhoto(); + } + + public function pickFromGallery() + { + Camera::pickImages('images', true, 5); + } + + #[On('native:' . PhotoTaken::class)] + public function handlePhotoTaken(string $path) + { + $this->isCapturing = false; + $this->addPhoto($path); + } + + #[On('native:' . MediaSelected::class)] + public function handleMediaSelected($success, $files, $count) + { + foreach ($files as $file) { + $this->addPhoto($file); + } + } + + private function addPhoto(string $path) + { + $this->photos[] = [ + 'path' => $path, + 'data_url' => $this->createDataUrl($path), + 'timestamp' => now() + ]; + } + + private function createDataUrl(string $path): string + { + $data = base64_encode(file_get_contents($path)); + $mime = mime_content_type($path); + return "data:$mime;base64,$data"; + } + + public function render() + { + return view('livewire.photo-manager'); + } +} +``` + +## Platform Support + +- **iOS:** Uses UIImagePickerController and Photos framework +- **Android:** Uses Intent.ACTION_IMAGE_CAPTURE and gallery intents +- **Permissions:** Camera permission required for photo capture +- **File Location:** Photos saved to app's temporary directory + +## Notes + +- The first time your app requests camera access, users will be prompted for permission +- If permission is denied, camera functions will fail silently +- Captured photos are stored in the app's temporary directory +- Consider implementing cleanup for old temporary photos +- File formats are platform-dependent (typically JPEG) diff --git a/resources/views/docs/mobile/1/apis/dialog.md b/resources/views/docs/mobile/1/apis/dialog.md new file mode 100644 index 00000000..b75420f4 --- /dev/null +++ b/resources/views/docs/mobile/1/apis/dialog.md @@ -0,0 +1,249 @@ +--- +title: Dialog +order: 300 +--- + +## Overview + +The Dialog API provides access to native UI elements like alerts, toasts, and sharing interfaces. + +```php +use Native\Mobile\Facades\Dialog; +``` + +## Methods + +### `alert()` + +Displays a native alert dialog with customizable buttons. + +**Parameters:** +- `string $title` - The alert title +- `string $message` - The alert message +- `array $buttons` - Array of button configurations +- `callable $callback` - Callback function for button presses + +```php +Dialog::alert( + 'Confirm Action', + 'Are you sure you want to delete this item?', + [ + ['text' => 'Cancel', 'style' => 'cancel'], + ['text' => 'Delete', 'style' => 'destructive'] + ], + function($buttonIndex) { + // Handle button press + } +); +``` + +### `toast()` + +Displays a brief toast notification message. + +**Parameters:** +- `string $message` - The message to display + +```php +Dialog::toast('Item saved successfully!'); +``` + +### `share()` + +Opens the native sharing interface. + +**Parameters:** +- `string $title` - The share dialog title +- `string $text` - Text content to share +- `string $url` - URL to share + +```php +Dialog::share( + 'Check this out!', + 'I found this amazing Laravel package for mobile development', + 'https://nativephp.com' +); +``` + +## Events + +### `Native\Mobile\Events\Alert\ButtonPressed` + +Fired when a button is pressed in an alert dialog. + +**Payload:** `int $buttonIndex` - Index of the pressed button (0-based) + +```php +use Livewire\Attributes\On; +use Native\Mobile\Events\Alert\ButtonPressed; + +#[On('native:' . ButtonPressed::class)] +public function handleAlertButton(int $buttonIndex) +{ + switch ($buttonIndex) { + case 0: + // First button (usually Cancel) + break; + case 1: + // Second button (usually OK/Confirm) + $this->performAction(); + break; + } +} +``` + +## Example Usage + +```php +use Livewire\Component; +use Livewire\Attributes\On; +use Native\Mobile\Facades\Dialog; +use Native\Mobile\Events\Alert\ButtonPressed; + +class ItemManager extends Component +{ + public array $items = []; + public ?int $itemToDelete = null; + + public function deleteItem(int $itemId) + { + $this->itemToDelete = $itemId; + + Dialog::alert( + 'Delete Item', + 'This action cannot be undone. Are you sure?', + [ + ['text' => 'Cancel', 'style' => 'cancel'], + ['text' => 'Delete', 'style' => 'destructive'] + ], + null + ); + } + + #[On('native:' . ButtonPressed::class)] + public function handleDeleteConfirmation(int $buttonIndex) + { + if ($buttonIndex === 1 && $this->itemToDelete) { + // User confirmed deletion + $this->performDelete($this->itemToDelete); + Dialog::toast('Item deleted successfully'); + $this->itemToDelete = null; + } else { + // User cancelled + $this->itemToDelete = null; + } + } + + public function shareItem(array $item) + { + Dialog::share( + 'Share Item', + "Check out this item: {$item['name']}", + "https://myapp.com/items/{$item['id']}" + ); + } + + public function showSuccess(string $message) + { + Dialog::toast($message); + } + + private function performDelete(int $itemId) + { + $this->items = array_filter( + $this->items, + fn($item) => $item['id'] !== $itemId + ); + } + + public function render() + { + return view('livewire.item-manager'); + } +} +``` + +## Alert Button Styles + +### iOS Button Styles +- `'default'` - Standard blue button +- `'cancel'` - Bold cancel button (usually on the left) +- `'destructive'` - Red destructive action button + +### Android Button Styles +- `'positive'` - Primary action button +- `'negative'` - Cancel/dismiss button +- `'neutral'` - Additional option button + +```php +// Cross-platform alert with proper styling +Dialog::alert( + 'Delete Account', + 'This will permanently delete your account and all data.', + [ + ['text' => 'Cancel', 'style' => 'cancel'], + ['text' => 'Delete', 'style' => 'destructive'] + ], + null +); +``` + +## Toast Guidelines + +### Best Practices +- Keep messages short and clear +- Use for confirmations and status updates +- Don't rely on toasts for critical information +- Avoid showing multiple toasts in quick succession + +```php +// Good toast messages +Dialog::toast('Saved!'); +Dialog::toast('Photo uploaded'); +Dialog::toast('Settings updated'); + +// Avoid long messages +Dialog::toast('Your photo has been successfully uploaded to the server and will be processed shortly'); +``` + +## Sharing Content + +### Supported Content Types +- Plain text +- URLs +- Images (when sharing files) +- Mixed content + +```php +// Share just text +Dialog::share('', 'Check out this amazing app!', ''); + +// Share a URL +Dialog::share('', '', 'https://nativephp.com'); + +// Share text and URL together +Dialog::share( + 'NativePHP for Mobile', + 'Build mobile apps with PHP and Laravel!', + 'https://nativephp.com' +); +``` + +## Platform Differences + +### iOS +- Alerts use UIAlertController +- Toasts use custom overlay views +- Sharing uses UIActivityViewController + +### Android +- Alerts use AlertDialog +- Toasts use native Toast system +- Sharing uses Intent.ACTION_SEND + +## Accessibility + +- All dialogs automatically support screen readers +- Button text should be descriptive +- Toast messages are announced by accessibility services +- Consider users with motor disabilities when designing button layouts \ No newline at end of file diff --git a/resources/views/docs/mobile/1/apis/geolocation.md b/resources/views/docs/mobile/1/apis/geolocation.md new file mode 100644 index 00000000..e7d7f631 --- /dev/null +++ b/resources/views/docs/mobile/1/apis/geolocation.md @@ -0,0 +1,438 @@ +--- +title: Geolocation +order: 400 +--- + +## Overview + +The Geolocation API provides access to the device's GPS and location services to determine the user's current position. + +```php +use Native\Mobile\Facades\Geolocation; +``` + +## Methods + +### `getCurrentPosition()` + +Gets the current GPS location of the device. + +**Parameters:** +- `bool $fineAccuracy` - Whether to use high accuracy mode (GPS vs network) (default: `false`) + +**Returns:** Location data via events + +```php +// Get location using network positioning (faster, less accurate) +Geolocation::getCurrentPosition(); + +// Get location using GPS (slower, more accurate) +Geolocation::getCurrentPosition(true); +``` + +### `checkPermissions()` + +Checks the current location permissions status. + +**Returns:** Permission status via events + +```php +Geolocation::checkPermissions(); +``` + +### `requestPermissions()` + +Requests location permissions from the user. + +**Returns:** Permission status after request via events + +```php +Geolocation::requestPermissions(); +``` + +## Events + +### `Geolocation\LocationReceived` + +Fired when location data is requested (success or failure). + +**Event Parameters:** +- `bool $success` - Whether location was successfully retrieved +- `float $latitude` - Latitude coordinate (when successful) +- `float $longitude` - Longitude coordinate (when successful) +- `float $accuracy` - Accuracy in meters (when successful) +- `int $timestamp` - Unix timestamp of location fix +- `string $provider` - Location provider used (GPS, network, etc.) +- `string $error` - Error message (when unsuccessful) + +```php +use Livewire\Attributes\On; +use Native\Mobile\Events\Geolocation\LocationReceived; + +#[On('native:' . LocationReceived::class)] +public function handleLocationReceived($success = null, $latitude = null, $longitude = null, $accuracy = null, $timestamp = null, $provider = null, $error = null) +{ + if ($success) { + // Location successfully retrieved + $this->latitude = $latitude; + $this->longitude = $longitude; + $this->accuracy = $accuracy; + $this->provider = $provider; + + Log::info('Location received', [ + 'lat' => $latitude, + 'lng' => $longitude, + 'accuracy' => $accuracy, + 'provider' => $provider + ]); + } else { + // Location request failed + $this->error = $error ?? 'Failed to get location'; + Log::warning('Location request failed', ['error' => $error]); + } +} +``` + +### `Geolocation\PermissionStatusReceived` + +Fired when permission status is checked. + +**Event Parameters:** +- `string $location` - Overall location permission status +- `string $coarseLocation` - Coarse location permission status +- `string $fineLocation` - Fine location permission status + +**Permission Values:** +- `'granted'` - Permission is granted +- `'denied'` - Permission is denied +- `'not_determined'` - Permission not yet requested + +```php +use Livewire\Attributes\On; +use Native\Mobile\Events\Geolocation\PermissionStatusReceived; + +#[On('native:' . PermissionStatusReceived::class)] +public function handlePermissionStatus($location, $coarseLocation, $fineLocation) +{ + $this->locationPermission = $location; + $this->coarsePermission = $coarseLocation; + $this->finePermission = $fineLocation; + + if ($coarseLocation === 'granted' || $fineLocation === 'granted') { + // At least some location permission is granted + $this->canRequestLocation = true; + } else { + // No location permissions granted + $this->showPermissionExplanation(); + } +} +``` + +### `Geolocation\PermissionRequestResult` + +Fired when a permission request completes. + +**Event Parameters:** +- `string $location` - Overall location permission result +- `string $coarseLocation` - Coarse location permission result +- `string $fineLocation` - Fine location permission result +- `string $message` - Optional message (for permanently denied) +- `bool $needsSettings` - Whether user needs to go to Settings + +**Special Values:** +- `'permanently_denied'` - User has permanently denied permission + +```php +use Livewire\Attributes\On; +use Native\Mobile\Events\Geolocation\PermissionRequestResult; + +#[On('native:' . PermissionRequestResult::class)] +public function handlePermissionRequest($location, $coarseLocation, $fineLocation, $message = null, $needsSettings = null) +{ + if ($location === 'permanently_denied') { + // Permission permanently denied - must go to Settings + $this->error = $message ?? 'Location permission permanently denied. Please enable in Settings.'; + $this->showSettingsPrompt = true; + } elseif ($coarseLocation === 'granted' || $fineLocation === 'granted') { + // Permission granted - can now request location + $this->permissionGranted = true; + $this->getCurrentLocation(); + } else { + // Permission denied but can ask again + $this->error = 'Location permission is required for this feature.'; + } +} +``` + +## Complete Example + +```php +use Livewire\Component; +use Livewire\Attributes\On; +use Native\Mobile\Facades\Geolocation; +use Native\Mobile\Events\Geolocation\LocationReceived; +use Native\Mobile\Events\Geolocation\PermissionStatusReceived; +use Native\Mobile\Events\Geolocation\PermissionRequestResult; + +class LocationTracker extends Component +{ + public ?float $latitude = null; + public ?float $longitude = null; + public ?float $accuracy = null; + public ?string $provider = null; + public bool $isLoading = false; + public string $error = ''; + public bool $showSettingsPrompt = false; + + // Permission states + public string $locationPermission = 'unknown'; + public string $coarsePermission = 'unknown'; + public string $finePermission = 'unknown'; + + public function mount() + { + // Check current permissions on load + $this->checkPermissions(); + } + + public function checkPermissions() + { + $this->error = ''; + Geolocation::checkPermissions(); + } + + public function requestPermissions() + { + $this->error = ''; + $this->isLoading = true; + Geolocation::requestPermissions(); + } + + public function getCurrentLocation() + { + $this->isLoading = true; + $this->error = ''; + + // Use high accuracy GPS + Geolocation::getCurrentPosition(true); + } + + #[On('native:' . PermissionStatusReceived::class)] + public function handlePermissionStatus($location, $coarseLocation, $fineLocation) + { + $this->locationPermission = $location; + $this->coarsePermission = $coarseLocation; + $this->finePermission = $fineLocation; + + if ($coarseLocation === 'granted' || $fineLocation === 'granted') { + // Has some level of location permission + $this->showLocationButton = true; + } elseif ($location === 'denied') { + // Permission denied - can request again + $this->showRequestButton = true; + } else { + // Permission not determined - can request + $this->showRequestButton = true; + } + } + + #[On('native:' . PermissionRequestResult::class)] + public function handlePermissionRequest($location, $coarseLocation, $fineLocation, $message = null, $needsSettings = null) + { + $this->isLoading = false; + + if ($location === 'permanently_denied') { + $this->error = $message ?? 'Location access permanently denied. Please enable location services in your device Settings app.'; + $this->showSettingsPrompt = true; + } elseif ($coarseLocation === 'granted' || $fineLocation === 'granted') { + // Permission granted - automatically get location + $this->getCurrentLocation(); + } else { + $this->error = 'Location permission is required to use this feature.'; + } + } + + #[On('native:' . LocationReceived::class)] + public function handleLocationReceived($success = null, $latitude = null, $longitude = null, $accuracy = null, $timestamp = null, $provider = null, $error = null) + { + $this->isLoading = false; + + if ($success) { + $this->latitude = $latitude; + $this->longitude = $longitude; + $this->accuracy = $accuracy; + $this->provider = $provider; + $this->error = ''; + + // Store for later use + session([ + 'last_location' => [ + 'lat' => $latitude, + 'lng' => $longitude, + 'accuracy' => $accuracy, + 'timestamp' => $timestamp, + 'provider' => $provider + ] + ]); + + Log::info('Location updated', [ + 'lat' => $latitude, + 'lng' => $longitude, + 'accuracy' => $accuracy + ]); + + } else { + $this->error = $error ?? 'Failed to get current location'; + Log::warning('Location request failed', ['error' => $error]); + } + } + + public function openSettings() + { + // You might want to show instructions or deep link to settings + $this->dispatch('show-settings-instructions'); + } + + public function render() + { + return view('livewire.location-tracker'); + } +} +``` + +## Understanding Permission States + +### Permission Types + +**Coarse Location (`ACCESS_COARSE_LOCATION` on Android)** +- Network-based location (WiFi, cellular towers) +- Lower accuracy (~100-1000 meters) +- Less battery usage +- Faster location fixes + +**Fine Location (`ACCESS_FINE_LOCATION` on Android, Location Services on iOS)** +- GPS-based location +- Higher accuracy (~5-50 meters) +- More battery usage +- Slower initial location fix + +### Permission Flow + +```php +class PermissionFlowExample extends Component +{ + public function handleLocationFlow() + { + // 1. Check current permissions + Geolocation::checkPermissions(); + } + + #[On('native:' . PermissionStatusReceived::class)] + public function handleCheck($location, $coarseLocation, $fineLocation) + { + if ($coarseLocation === 'granted' || $fineLocation === 'granted') { + // 2a. Permission already granted - get location + Geolocation::getCurrentPosition(true); + } else { + // 2b. Need to request permission + Geolocation::requestPermissions(); + } + } + + #[On('native:' . PermissionRequestResult::class)] + public function handleRequest($location, $coarseLocation, $fineLocation, $message = null, $needsSettings = null) + { + if ($location === 'permanently_denied') { + // 3a. User must enable in Settings + $this->showSettingsInstructions($message); + } elseif ($coarseLocation === 'granted' || $fineLocation === 'granted') { + // 3b. Permission granted - get location + Geolocation::getCurrentPosition(true); + } else { + // 3c. Permission denied - show explanation + $this->showPermissionExplanation(); + } + } +} +``` + +## Platform Support + +### iOS +- Uses Core Location framework +- Requires location usage description in Info.plist +- Supports both "When in Use" and "Always" permissions +- Automatic permission prompts + +### Android +- Uses FusedLocationProviderClient (Google Play Services) +- Requires location permissions in AndroidManifest.xml +- Supports coarse and fine location permissions +- Runtime permission requests (Android 6+) + +## Privacy Considerations + +### Best Practices +- **Explain why** you need location access before requesting +- **Request at the right time** - when the feature is actually needed +- **Respect denials** - provide alternative functionality when possible +- **Use appropriate accuracy** - don't request fine location if coarse is sufficient +- **Limit frequency** - don't request location updates constantly + +### User Experience Tips + +```php +class LocationUX extends Component +{ + public function requestLocationWithExplanation() + { + // Show explanation first + $this->showExplanation = true; + } + + public function proceedWithLocationRequest() + { + $this->showExplanation = false; + + // Now request permission + Geolocation::requestPermissions(); + } + + public function handleDeniedGracefully($location, $coarseLocation, $fineLocation) + { + if ($location === 'permanently_denied') { + // Offer manual location entry + $this->showManualLocationEntry = true; + } else { + // Show benefit of enabling location + $this->showLocationBenefits = true; + } + } +} +``` + +## Accuracy and Performance + +### Choosing Accuracy Level + +```php +// For general location (city-level) +Geolocation::getCurrentPosition(false); // ~100-1000m accuracy + +// For precise location (navigation, delivery) +Geolocation::getCurrentPosition(true); // ~5-50m accuracy +``` + +### Performance Considerations +- **Battery Usage** - GPS uses more battery than network location +- **Time to Fix** - GPS takes longer for initial position +- **Indoor Accuracy** - GPS may not work well indoors +- **Caching** - Consider caching recent locations for better UX + +### Error Handling +- Always handle both success and failure cases +- Provide meaningful error messages to users +- Implement fallback strategies (manual entry, saved locations) +- Log errors for debugging but don't expose sensitive details + +The Geolocation API provides powerful location capabilities while respecting user privacy and platform requirements. Always handle permissions gracefully and provide clear value propositions for why location access is needed. diff --git a/resources/views/docs/mobile/1/apis/haptics.md b/resources/views/docs/mobile/1/apis/haptics.md new file mode 100644 index 00000000..5ec54ef3 --- /dev/null +++ b/resources/views/docs/mobile/1/apis/haptics.md @@ -0,0 +1,91 @@ +--- +title: Haptics +order: 500 +--- + +## Overview + +The Haptics API provides access to the device's vibration and haptic feedback system for tactile user interactions. + +```php +use Native\Mobile\Facades\Haptics; +``` + +## Methods + +### `vibrate()` + +Triggers device vibration for tactile feedback. + +**Returns:** `void` + +```php +Haptics::vibrate(); +``` + +## Example Usage + +### Basic Form Feedback +```php +use Livewire\Component; +use Native\Mobile\Facades\Haptics; + +class FormComponent extends Component +{ + public function save() + { + if ($this->hasErrors()) { + // Haptic feedback for errors + Haptics::vibrate(); + return; + } + + $this->saveData(); + + // Success haptic feedback + Haptics::vibrate(); + } + + public function deleteItem() + { + // Haptic feedback for important actions + Haptics::vibrate(); + $this->performDelete(); + } +} +``` + +### Best Practices +```php +class HapticsExample extends Component +{ + // ✅ Good: Button presses, form errors, important actions + public function onButtonPress() + { + Haptics::vibrate(); + $this->processAction(); + } + + // ❌ Avoid: Frequent events like scrolling + public function onScroll() + { + // Don't vibrate on every scroll - too annoying! + // Haptics::vibrate(); + } +} +``` + +**Use haptics for:** Button presses, form validation, important notifications, game events +**Avoid haptics for:** Frequent events, background processes, minor updates + +## Migration from System Facade + +```php +// Old way (deprecated) +use Native\Mobile\Facades\System; +System::vibrate(); + +// New way (recommended) +use Native\Mobile\Facades\Haptics; +Haptics::vibrate(); +``` \ No newline at end of file diff --git a/resources/views/docs/mobile/1/apis/push-notifications.md b/resources/views/docs/mobile/1/apis/push-notifications.md new file mode 100644 index 00000000..1040a457 --- /dev/null +++ b/resources/views/docs/mobile/1/apis/push-notifications.md @@ -0,0 +1,411 @@ +--- +title: PushNotifications +order: 600 +--- + +## Overview + +The PushNotifications API handles device registration for Firebase Cloud Messaging to receive push notifications. + +```php +use Native\Mobile\Facades\PushNotifications; +``` + +## Methods + +### `enrollForPushNotifications()` + +Requests permission and enrolls the device for push notifications. + +**Returns:** `void` + +```php +PushNotifications::enrollForPushNotifications(); +``` + +### `getPushNotificationsToken()` + +Retrieves the current FCM token for this device. + +**Returns:** `string|null` - The FCM token, or `null` if not available + +```php +$token = PushNotifications::getPushNotificationsToken(); + +if ($token) { + // Send token to your server + $this->registerTokenWithServer($token); +} else { + // Token not available, enrollment may have failed +} +``` + +## Events + +#### `Native\Mobile\Events\PushNotification\TokenGenerated` + +Fired when a push notification token is successfully generated. + +**Payload:** `string $token` - The FCM token for this device + +```php +use Livewire\Attributes\On; +use Native\Mobile\Events\PushNotification\TokenGenerated; + +#[On('native:' . TokenGenerated::class)] +public function handlePushToken(string $token) +{ + // Send token to your backend + $this->sendTokenToServer($token); +} +``` + +## Example Usage + +```php +use Livewire\Component; +use Livewire\Attributes\On; +use Native\Mobile\Facades\PushNotifications; +use Native\Mobile\Events\PushNotification\TokenGenerated; + +class NotificationManager extends Component +{ + public bool $isRegistered = false; + public bool $isRegistering = false; + public string $error = ''; + + public function mount() + { + // Check if already registered + $this->checkExistingRegistration(); + } + + public function enableNotifications() + { + $this->isRegistering = true; + $this->error = ''; + + // Request permission and get token + PushNotifications::enrollForPushNotifications(); + } + + #[On('native:' . TokenGenerated::class)] + public function handleTokenGenerated(string $token) + { + $this->isRegistering = false; + + try { + // Send token to your backend API + $response = Http::withToken(session('api_token')) + ->post('/api/push-tokens', [ + 'token' => $token, + 'device_id' => $this->getDeviceId(), + 'platform' => $this->getPlatform(), + 'user_id' => auth()->id() + ]); + + if ($response->successful()) { + $this->isRegistered = true; + session(['push_token' => $token]); + + Log::info('Push notification token registered', [ + 'user_id' => auth()->id(), + 'token_preview' => substr($token, 0, 10) . '...' + ]); + } else { + throw new Exception('Server rejected token registration'); + } + + } catch (Exception $e) { + $this->error = 'Failed to register for notifications: ' . $e->getMessage(); + + Log::error('Push token registration failed', [ + 'error' => $e->getMessage(), + 'user_id' => auth()->id() + ]); + } + } + + public function disableNotifications() + { + $token = session('push_token'); + + if ($token) { + try { + // Remove token from server + Http::withToken(session('api_token')) + ->delete("/api/push-tokens/{$token}"); + + session()->forget('push_token'); + $this->isRegistered = false; + + } catch (Exception $e) { + $this->error = 'Failed to disable notifications'; + } + } + } + + private function checkExistingRegistration() + { + $existingToken = session('push_token'); + + if ($existingToken) { + // Verify token is still valid + $currentToken = PushNotifications::getPushNotificationsToken(); + + if ($currentToken === $existingToken) { + $this->isRegistered = true; + } else { + // Token changed, need to re-register + session()->forget('push_token'); + $this->isRegistered = false; + } + } + } + + private function getDeviceId(): string + { + if (!session()->has('device_id')) { + session(['device_id' => Str::uuid()]); + } + + return session('device_id'); + } + + private function getPlatform(): string + { + // Detect platform from user agent or environment + return request()->header('X-Platform', 'unknown'); + } + + public function render() + { + return view('livewire.notification-manager'); + } +} +``` + +## Backend Integration + +### Database Schema + +```php +// Migration for storing push tokens +Schema::create('push_tokens', function (Blueprint $table) { + $table->id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->string('token')->unique(); + $table->string('device_id')->nullable(); + $table->enum('platform', ['ios', 'android', 'unknown']); + $table->timestamp('last_used_at')->nullable(); + $table->timestamps(); + + $table->index(['user_id', 'platform']); +}); +``` + +### API Controller + +```php +namespace App\Http\Controllers\Api; + +use App\Models\PushToken; +use Illuminate\Http\Request; + +class PushTokenController extends Controller +{ + public function store(Request $request) + { + $validated = $request->validate([ + 'token' => 'required|string|max:255', + 'device_id' => 'nullable|string|max:255', + 'platform' => 'required|in:ios,android,unknown' + ]); + + PushToken::updateOrCreate( + [ + 'user_id' => $request->user()->id, + 'device_id' => $validated['device_id'] + ], + [ + 'token' => $validated['token'], + 'platform' => $validated['platform'], + 'last_used_at' => now() + ] + ); + + return response()->json(['message' => 'Token registered successfully']); + } + + public function destroy(Request $request, string $token) + { + PushToken::where('user_id', $request->user()->id) + ->where('token', $token) + ->delete(); + + return response()->json(['message' => 'Token removed successfully']); + } +} +``` + +### Sending Notifications + +```php +namespace App\Services; + +use Google\Client as GoogleClient; +use Google\Service\FirebaseCloudMessaging; + +class PushNotificationService +{ + public function sendToUser(int $userId, array $notification, array $data = []) + { + $tokens = PushToken::where('user_id', $userId) + ->pluck('token') + ->toArray(); + + if (empty($tokens)) { + throw new Exception('No push tokens found for user'); + } + + return $this->sendToTokens($tokens, $notification, $data); + } + + private function sendToTokens(array $tokens, array $notification, array $data = []) + { + $client = new GoogleClient(); + $client->setAuthConfig(base_path('google-services.json')); + $client->addScope('https://www.googleapis.com/auth/firebase.messaging'); + + $fcm = new FirebaseCloudMessaging($client); + $projectId = config('services.firebase.project_id'); + + $results = []; + + foreach ($tokens as $token) { + try { + $message = [ + 'token' => $token, + 'notification' => $notification, + 'data' => array_map('strval', $data) + ]; + + $response = $fcm->projects_messages->send($projectId, [ + 'message' => $message + ]); + + $results[] = [ + 'token' => substr($token, 0, 10) . '...', + 'success' => true, + 'message_id' => $response->getName() + ]; + + } catch (Exception $e) { + $results[] = [ + 'token' => substr($token, 0, 10) . '...', + 'success' => false, + 'error' => $e->getMessage() + ]; + + // Remove invalid tokens + if (str_contains($e->getMessage(), 'registration-token-not-registered')) { + PushToken::where('token', $token)->delete(); + } + } + } + + return $results; + } +} +``` + +## Configuration Requirements + +### Firebase Setup + +1. Create a Firebase project at [Firebase Console](https://console.firebase.google.com/) +2. Add your mobile app to the project +3. Download `google-services.json` (Android) and `GoogleService-Info.plist` (iOS) +4. Place these files in your Laravel project root +5. Enable push notifications in your NativePHP config: + +```php +// config/nativephp.php +return [ + 'permissions' => [ + 'push_notifications' => true, + ], +]; +``` + +### Environment Variables + +```bash +NATIVEPHP_APP_ID=com.yourcompany.yourapp +FIREBASE_PROJECT_ID=your-firebase-project-id +``` + +## Permission Flow + +1. User taps "Enable Notifications" +2. App calls `enrollForPushNotifications()` +3. System shows permission dialog +4. If granted, FCM generates token +5. `TokenGenerated` event fires with token +6. App sends token to backend +7. Backend stores token for user +8. Server can now send notifications to this device + +## Error Handling + +```php +public function handleRegistrationFailure() +{ + // Common failure scenarios: + + // 1. User denied permission + if (!$this->hasNotificationPermission()) { + $this->showPermissionExplanation(); + return; + } + + // 2. Network error + if (!$this->hasNetworkConnection()) { + $this->showNetworkError(); + return; + } + + // 3. Firebase configuration missing + if (!$this->hasFirebaseConfig()) { + Log::error('Firebase configuration missing'); + return; + } + + // 4. Backend API error + $this->showGenericError(); +} +``` + +## Migration from System Facade + +```php +// Old way (deprecated) +use Native\Mobile\Facades\System; +System::enrollForPushNotifications(); +$token = System::getPushNotificationsToken(); + +// New way (recommended) +use Native\Mobile\Facades\PushNotifications; +PushNotifications::enrollForPushNotifications(); +$token = PushNotifications::getPushNotificationsToken(); +``` + +## Best Practices + +- Request permission at the right time (not immediately on app launch) +- Explain the value of notifications to users +- Handle permission denial gracefully +- Clean up invalid tokens on your backend +- Implement retry logic for network failures +- Log registration events for debugging +- Respect user preferences and provide opt-out diff --git a/resources/views/docs/mobile/1/apis/secure-storage.md b/resources/views/docs/mobile/1/apis/secure-storage.md new file mode 100644 index 00000000..74f38826 --- /dev/null +++ b/resources/views/docs/mobile/1/apis/secure-storage.md @@ -0,0 +1,288 @@ +--- +title: SecureStorage +order: 700 +--- + +## Overview + +The SecureStorage API provides secure storage using the device's native keychain (iOS) or keystore (Android) for sensitive data like tokens, passwords, and user credentials. + +```php +use Native\Mobile\Facades\SecureStorage; +``` + +## Methods + +### `set()` + +Stores a secure value in the native keychain or keystore. + +**Parameters:** +- `string $key` - The key to store the value under +- `string|null $value` - The value to store securely + +**Returns:** `bool` - `true` if successfully stored, `false` otherwise + +```php +$success = SecureStorage::set('api_token', 'abc123xyz'); + +if ($success) { + // Token stored securely +} else { + // Storage failed +} +``` + +### `get()` + +Retrieves a secure value from the native keychain or keystore. + +**Parameters:** +- `string $key` - The key to retrieve the value for + +**Returns:** `string|null` - The stored value or `null` if not found + +```php +$token = SecureStorage::get('api_token'); + +if ($token) { + // Use the retrieved token + $this->authenticateWithToken($token); +} else { + // Token not found, user needs to login + $this->redirectToLogin(); +} +``` + +### `delete()` + +Deletes a secure value from the native keychain or keystore. + +**Parameters:** +- `string $key` - The key to delete the value for + +**Returns:** `bool` - `true` if successfully deleted, `false` otherwise + +```php +$deleted = SecureStorage::delete('api_token'); + +if ($deleted) { + // Token removed successfully +} else { + // Deletion failed or key didn't exist +} +``` + +## Example Usage + +```php +use Livewire\Component; +use Native\Mobile\Facades\SecureStorage; + +class AuthManager extends Component +{ + public bool $isLoggedIn = false; + public string $error = ''; + + public function mount() + { + // Check if user has stored credentials + $this->checkStoredAuth(); + } + + public function login(string $username, string $password) + { + try { + // Authenticate with your API + $response = Http::post('/api/login', [ + 'username' => $username, + 'password' => $password + ]); + + if ($response->successful()) { + $data = $response->json(); + + // Store tokens securely + SecureStorage::set('access_token', $data['access_token']); + SecureStorage::set('refresh_token', $data['refresh_token']); + SecureStorage::set('user_id', (string) $data['user']['id']); + + $this->isLoggedIn = true; + } else { + $this->error = 'Login failed'; + } + } catch (Exception $e) { + $this->error = 'Network error: ' . $e->getMessage(); + } + } + + public function logout() + { + // Clear stored credentials + SecureStorage::delete('access_token'); + SecureStorage::delete('refresh_token'); + SecureStorage::delete('user_id'); + + $this->isLoggedIn = false; + } + + private function checkStoredAuth() + { + $accessToken = SecureStorage::get('access_token'); + + if ($accessToken) { + // Verify token is still valid + $response = Http::withToken($accessToken) + ->get('/api/user'); + + if ($response->successful()) { + $this->isLoggedIn = true; + } else { + // Token expired, try refresh + $this->refreshToken(); + } + } + } + + private function refreshToken() + { + $refreshToken = SecureStorage::get('refresh_token'); + + if ($refreshToken) { + $response = Http::post('/api/refresh', [ + 'refresh_token' => $refreshToken + ]); + + if ($response->successful()) { + $data = $response->json(); + SecureStorage::set('access_token', $data['access_token']); + $this->isLoggedIn = true; + } else { + // Refresh failed, clear everything + $this->logout(); + } + } + } + + public function render() + { + return view('livewire.auth-manager'); + } +} +``` + +## Platform Implementation + +### iOS - Keychain Services +- Uses the iOS Keychain Services API +- Data is encrypted and tied to your app's bundle ID +- Survives app deletion and reinstallation if iCloud Keychain is enabled +- Protected by device passcode/biometrics + +### Android - Keystore +- Uses Android Keystore system +- Hardware-backed encryption when available +- Data is automatically deleted when app is uninstalled +- Protected by device lock screen + +## Security Features + +- **Encryption:** All data is automatically encrypted +- **App Isolation:** Data is only accessible by your app +- **System Protection:** Protected by device authentication +- **Tamper Resistance:** Hardware-backed security when available + +## Best Practices + +### What to Store +- API tokens and refresh tokens +- User credentials (if necessary) +- Encryption keys +- Sensitive user preferences +- Two-factor authentication secrets + +### What NOT to Store +- Large amounts of data (use encrypted database instead) +- Non-sensitive configuration +- Temporary data +- Cached content + +### Implementation Tips + +```php +class SecureSettings +{ + public function storeUserCredentials(string $userId, string $token) + { + // Use prefixed keys for organization + SecureStorage::set("user_{$userId}_token", $token); + SecureStorage::set("user_{$userId}_last_login", now()->toISOString()); + } + + public function getUserToken(string $userId): ?string + { + return SecureStorage::get("user_{$userId}_token"); + } + + public function clearUserData(string $userId) + { + // Clean up all user-related secure data + SecureStorage::delete("user_{$userId}_token"); + SecureStorage::delete("user_{$userId}_last_login"); + SecureStorage::delete("user_{$userId}_preferences"); + } + + public function rotateToken(string $userId, string $newToken) + { + // Atomic token rotation + $oldToken = $this->getUserToken($userId); + + if (SecureStorage::set("user_{$userId}_token", $newToken)) { + // New token stored successfully + Log::info("Token rotated for user {$userId}"); + } else { + // Rotation failed, keep old token + Log::error("Token rotation failed for user {$userId}"); + } + } +} +``` + +## Error Handling + +```php +public function storeSecurely(string $key, string $value) +{ + $attempts = 0; + $maxAttempts = 3; + + while ($attempts < $maxAttempts) { + if (SecureStorage::set($key, $value)) { + return true; + } + + $attempts++; + usleep(100000); // Wait 100ms before retry + } + + Log::error("Failed to store secure value after {$maxAttempts} attempts", [ + 'key' => $key + ]); + + return false; +} +``` + +## Migration from System Facade + +If you were using the deprecated `System::secureSet()` and `System::secureGet()` methods: + +```php +// Old way (deprecated) +System::secureSet('key', 'value'); +$value = System::secureGet('key'); + +// New way (recommended) +SecureStorage::set('key', 'value'); +$value = SecureStorage::get('key'); +``` \ No newline at end of file diff --git a/resources/views/docs/mobile/1/apis/system.md b/resources/views/docs/mobile/1/apis/system.md new file mode 100644 index 00000000..3d677fee --- /dev/null +++ b/resources/views/docs/mobile/1/apis/system.md @@ -0,0 +1,221 @@ +--- +title: System +order: 800 +--- + +## Overview + +The System API provides access to basic system functions and serves as a legacy interface for methods that have been moved to dedicated facades in v1.1. + +```php +use Native\Mobile\Facades\System; +``` + +## Current Methods + +### `flashlight()` + +Toggles the device flashlight (camera flash LED) on and off. + +**Returns:** `void` + +```php +System::flashlight(); // Toggle flashlight state +``` + +## Deprecated Methods (v1.1+) + +The following methods are deprecated and have been moved to dedicated facades for better organization: + +### ~~`camera()`~~ → Use `Camera::getPhoto()` + +```php +// ❌ Deprecated (still works but not recommended) +$path = System::camera(); + +// ✅ Use instead +use Native\Mobile\Facades\Camera; +$path = Camera::getPhoto(); +``` + +### ~~`vibrate()`~~ → Use `Haptics::vibrate()` + +```php +// ❌ Deprecated (still works but not recommended) +System::vibrate(); + +// ✅ Use instead +use Native\Mobile\Facades\Haptics; +Haptics::vibrate(); +``` + +### ~~`promptForBiometricID()`~~ → Use `Biometrics::promptForBiometricID()` + +```php +// ❌ Deprecated (still works but not recommended) +$result = System::promptForBiometricID(); + +// ✅ Use instead +use Native\Mobile\Facades\Biometrics; +$result = Biometrics::promptForBiometricID(); +``` + +### ~~`enrollForPushNotifications()`~~ → Use `PushNotifications::enrollForPushNotifications()` + +```php +// ❌ Deprecated (still works but not recommended) +System::enrollForPushNotifications(); + +// ✅ Use instead +use Native\Mobile\Facades\PushNotifications; +PushNotifications::enrollForPushNotifications(); +``` + +### ~~`getPushNotificationsToken()`~~ → Use `PushNotifications::getPushNotificationsToken()` + +```php +// ❌ Deprecated (still works but not recommended) +$token = System::getPushNotificationsToken(); + +// ✅ Use instead +use Native\Mobile\Facades\PushNotifications; +$token = PushNotifications::getPushNotificationsToken(); +``` + +### ~~`secureSet()` / `secureGet()`~~ → Use `SecureStorage` + +```php +// ❌ Deprecated (still works but not recommended) +System::secureSet('key', 'value'); +$value = System::secureGet('key'); + +// ✅ Use instead +use Native\Mobile\Facades\SecureStorage; +SecureStorage::set('key', 'value'); +$value = SecureStorage::get('key'); +``` + +## Example Usage + +```php +use Livewire\Component; +use Native\Mobile\Facades\System; + +class FlashlightController extends Component +{ + public bool $isFlashlightOn = false; + + public function toggleFlashlight() + { + System::flashlight(); + $this->isFlashlightOn = !$this->isFlashlightOn; + } + + public function render() + { + return view('livewire.flashlight-controller'); + } +} +``` + +## Migration Guide + +If you're upgrading from an earlier version of NativePHP Mobile, here's how to migrate your code: + +### Step 1: Update Import Statements + +```php +// Before +use Native\Mobile\Facades\System; + +// After (add the specific facades you need) +use Native\Mobile\Facades\System; // Still needed for flashlight +use Native\Mobile\Facades\Camera; // For camera operations +use Native\Mobile\Facades\Haptics; // For vibration +use Native\Mobile\Facades\Biometrics; // For biometric auth +use Native\Mobile\Facades\PushNotifications; // For push notifications +use Native\Mobile\Facades\SecureStorage; // For secure storage +``` + +### Step 2: Replace Method Calls + +```php +class MigratedComponent extends Component +{ + public function oldWay() + { + // ❌ Old approach + System::vibrate(); + $photo = System::camera(); + $biometric = System::promptForBiometricID(); + System::enrollForPushNotifications(); + $token = System::getPushNotificationsToken(); + System::secureSet('key', 'value'); + $value = System::secureGet('key'); + } + + public function newWay() + { + // ✅ New approach + Haptics::vibrate(); + $photo = Camera::getPhoto(); + $biometric = Biometrics::promptForBiometricID(); + PushNotifications::enrollForPushNotifications(); + $token = PushNotifications::getPushNotificationsToken(); + SecureStorage::set('key', 'value'); + $value = SecureStorage::get('key'); + } +} +``` + +### Step 3: Update Event Listeners + +Event names remain the same, but you may want to update your code organization: + +```php +use Native\Mobile\Events\Camera\PhotoTaken; +use Native\Mobile\Events\Biometric\Completed; +use Native\Mobile\Events\PushNotification\TokenGenerated; + +class ModernComponent extends Component +{ + // Events work the same way, just organized better + #[On('native:' . PhotoTaken::class)] + public function handlePhoto($path) { /* ... */ } + + #[On('native:' . Completed::class)] + public function handleBiometric($success) { /* ... */ } + + #[On('native:' . TokenGenerated::class)] + public function handleToken($token) { /* ... */ } +} +``` + +## Platform Support + +### Flashlight +- **iOS:** Controls camera flash LED +- **Android:** Controls camera flash LED +- **Permissions:** None required +- **Limitations:** May not work if camera is currently in use + +## Future Deprecation Notice + +The System facade will continue to exist for backward compatibility, but new features will be added to the dedicated facades. We recommend migrating to the new facades when convenient to take advantage of: + +- Better code organization +- Clearer API surface +- Enhanced features in dedicated facades +- Better IDE autocompletion and documentation + +## Why the Change? + +The original System facade became too large and mixed different concerns. The new structure provides: + +- **Better organization:** Related methods grouped together +- **Clearer purpose:** Each facade has a single responsibility +- **Enhanced features:** New facades can offer richer APIs +- **Better maintenance:** Easier to add features and fix bugs +- **Improved documentation:** Each API can be documented thoroughly + +The migration improves code clarity and makes the NativePHP Mobile API more intuitive for new developers. \ No newline at end of file diff --git a/resources/views/docs/mobile/1/concepts/_index.md b/resources/views/docs/mobile/1/concepts/_index.md new file mode 100644 index 00000000..8499a7d4 --- /dev/null +++ b/resources/views/docs/mobile/1/concepts/_index.md @@ -0,0 +1,46 @@ +--- +title: Concepts +order: 3 +--- + +# Concepts + +Understanding these key concepts will help you build better mobile applications with NativePHP. This section covers important topics that apply across different APIs and features. + +## Mobile-Specific Concepts + +### [CI/CD](/docs/mobile/1/concepts/ci-cd) +**Continuous Integration & Deployment** - Automate your mobile app build and release process with GitHub Actions, GitLab CI, and other platforms. Learn how to set up automated testing, building, and app store deployment. + +### [Deep Links](/docs/mobile/1/concepts/deep-links) +**App Integration** - Connect your app with the mobile ecosystem using deep links, universal links (iOS), app links (Android), and NFC. Enable users to open your app from URLs, other apps, and NFC tags. + +### [Push Notifications](/docs/mobile/1/concepts/push-notifications) +**Real-time Engagement** - Set up Firebase Cloud Messaging to send push notifications to your users. Learn about token management, server-side integration, and notification best practices. + +### [Splash Screen/Icons](/docs/mobile/1/concepts/splash-screen-icons) +**App Branding** - Create professional app icons and splash screens. Understand platform requirements, asset optimization, and how to provide a great first impression. + +### [Versioning](/docs/mobile/1/concepts/versioning) +**Release Management** - Manage app versions across development, staging, and production environments. Learn about semantic versioning, app store requirements, and automated version management. + +## Development Workflow + +These concepts help you establish efficient development practices: + +- **Automated Building** - Set up CI/CD pipelines for consistent builds +- **Asset Management** - Optimize icons, images, and compiled assets +- **Version Control** - Track releases and manage updates +- **User Engagement** - Keep users connected with push notifications +- **App Discovery** - Make your app accessible through links and NFC + +## Cross-Platform Considerations + +NativePHP abstracts many platform differences, but understanding these concepts helps you: + +- Design for both iOS and Android users +- Handle platform-specific requirements +- Optimize for different screen sizes and capabilities +- Meet app store guidelines and requirements + +Each concept includes practical examples, best practices, and real-world implementation guidance to help you build professional mobile applications. \ No newline at end of file diff --git a/resources/views/docs/mobile/1/concepts/ci-cd.md b/resources/views/docs/mobile/1/concepts/ci-cd.md new file mode 100644 index 00000000..e2279813 --- /dev/null +++ b/resources/views/docs/mobile/1/concepts/ci-cd.md @@ -0,0 +1,167 @@ +--- +title: CI/CD Integration +order: 500 +--- + +## Overview + +NativePHP Mobile provides robust CLI commands designed for automated CI/CD environments. With proper configuration, you can build, package, and deploy mobile apps without manual intervention. + +## Key Commands for CI/CD + +### Installation Command + +Install NativePHP dependencies in automated environments: + +```bash +# Install Android platform, overwriting existing files +php artisan native:install android --force --no-tty + +# Install with ICU support for Filament/intl features +php artisan native:install android --force --with-icu + +# Install both platforms +php artisan native:install both --force +``` + +### Build Commands + +Build your app for different environments: + +```bash +# Build debug version (development) +php artisan native:run android --build=debug --no-tty + +# Build release version (production) +php artisan native:run android --build=release --no-tty + +# Build app bundle for Play Store +php artisan native:run android --build=bundle --no-tty +``` + +### Packaging Command + +Package signed releases for distribution: + +```bash +# Package signed APK using environment variables +php artisan native:package android --build-type=release --output=/artifacts --no-tty + +# Package signed App Bundle for Play Store +php artisan native:package android --build-type=bundle --output=/artifacts --no-tty +``` + +## Environment Variables + +Store sensitive signing information in environment variables: + +```bash +# Android Signing +ANDROID_KEYSTORE_FILE="/path/to/keystore.jks" +ANDROID_KEYSTORE_PASSWORD="your-keystore-password" +ANDROID_KEY_ALIAS="your-key-alias" +ANDROID_KEY_PASSWORD="your-key-password" + +# Push Notifications (optional) +FCM_SERVER_KEY="your-fcm-server-key" +GOOGLE_SERVICE_ACCOUNT_KEY="/path/to/service-account.json" + +# App Configuration +NATIVEPHP_APP_ID="com.yourcompany.yourapp" +NATIVEPHP_APP_VERSION="1.0.0" +NATIVEPHP_APP_VERSION_CODE="1" +``` + +## Command Line Options + +### `--no-tty` Flag +Essential for CI/CD environments where TTY is not available: +- Disables interactive prompts +- Provides non-interactive output +- Shows build progress without real-time updates +- Required for most automated environments + +### `--force` Flag +Overwrites existing files and directories: +- Useful for clean builds in CI +- Ensures fresh installation of NativePHP scaffolding +- Prevents build failures from existing files + +### Build Types +- `--build=debug`: Development builds with debugging enabled +- `--build=release`: Production builds optimized for distribution +- `--build=bundle`: App bundles for Play Store distribution + +## Signing Configuration + +### Using Command Line Options +```bash +php artisan native:package android \ + --build-type=release \ + --keystore=/path/to/keystore.jks \ + --keystore-password=your-password \ + --key-alias=your-alias \ + --key-password=your-key-password \ + --output=./artifacts \ + --no-tty +``` + +### Using Environment Variables (Recommended) +```bash +# Set environment variables in CI +export ANDROID_KEYSTORE_FILE="/path/to/keystore.jks" +export ANDROID_KEYSTORE_PASSWORD="your-password" +export ANDROID_KEY_ALIAS="your-alias" +export ANDROID_KEY_PASSWORD="your-key-password" + +# Run packaging command +php artisan native:package android --build-type=release --output=./artifacts --no-tty +``` + +## Common CI/CD Workflows + +### Development Pipeline +1. Install dependencies: `composer install` +2. Setup environment: copy `.env`, generate key +3. Install NativePHP: `native:install android --force` +4. Build debug: `native:run android --build=debug --no-tty` + +### Release Pipeline +1. Install dependencies: `composer install --no-dev --optimize-autoloader` +2. Setup environment with production settings +3. Install NativePHP: `native:install android --force --with-icu` +4. Package release: `native:package android --build-type=release --no-tty` + +### Play Store Pipeline +1. Same as release pipeline through step 3 +2. Package bundle: `native:package android --build-type=bundle --no-tty` +3. Upload to Play Console via API + +## Error Handling + +NativePHP commands provide proper exit codes for CI/CD: +- `0`: Success +- `1`: General error +- Build errors are logged and reported + +Monitor build logs for: +- Compilation errors +- Signing failures +- Missing dependencies +- Permission issues + +## Performance Tips + +### Caching +Cache these directories in CI for faster builds: +- `vendor/` (Composer dependencies) +- `nativephp/android/` (Android project) +- Android SDK components + + +### Optimization +- Use `--no-dev` for production Composer installs +- Enable Composer autoloader optimization +- Minimize included files with cleanup configuration + +The `--no-tty` flag and environment variable support make NativePHP Mobile well-suited for modern CI/CD pipelines, enabling fully automated mobile app builds and deployments. diff --git a/resources/views/docs/mobile/1/concepts/databases.md b/resources/views/docs/mobile/1/concepts/databases.md new file mode 100644 index 00000000..0d485d51 --- /dev/null +++ b/resources/views/docs/mobile/1/concepts/databases.md @@ -0,0 +1,272 @@ +--- +title: Databases +order: 200 +--- + +## Working with Databases + +You'll almost certainly want your application to persist structured data. For this, NativePHP supports +[SQLite](https://sqlite.org/), which works on both iOS and Android devices. + +You can interact with SQLite from PHP in whichever way you're used to. + +## Configuration + +You do not need to do anything special to configure your application to use SQLite. NativePHP will automatically: +- Switch to using SQLite when building your application. +- Create the database for you in the app container. +- Run your migrations each time your app starts, as needed. + +## Migrations + +When writing migrations, you need to consider any special recommendations for working with SQLite. + +For example, prior to Laravel 11, SQLite foreign key constraints are turned off by default. If your application relies +upon foreign key constraints, [you need to enable SQLite support for them](https://laravel.com/docs/database#configuration) before running your migrations. + +**It's important to test your migrations on prod builds before releasing updates!** You don't want to accidentally +delete your user's data when they update your app. + +## Things to note + +- As your app is installed on a separate device, you do not have remote access to the database. +- If a user deletes your application from their device, any databases are also deleted. + +## Database Security & Remote Data + +### Why No MySQL/PostgreSQL Support? + +NativePHP for Mobile intentionally does not include MySQL, PostgreSQL, or other remote database drivers. This is a deliberate security decision to prevent developers from accidentally embedding production database credentials directly in mobile applications. + +**Key security concerns:** +- Mobile apps are distributed to user devices and can be reverse-engineered +- Database credentials embedded in apps are accessible to anyone with the app binary +- Direct database connections bypass important security layers like rate limiting and access controls +- Network connectivity issues make direct database connections unreliable on mobile + +### The API-First Approach + +Instead of direct database connections, we strongly recommend building a secure API backend that your mobile app communicates with. This provides multiple security and architectural benefits: + +**Security Benefits:** +- Database credentials never leave your server +- Implement proper authentication and authorization +- Rate limiting and request validation +- Audit logs for all data access +- Ability to revoke access instantly + +**Technical Benefits:** +- Better error handling and offline support +- Easier to scale and maintain +- Version your API for backward compatibility +- Transform data specifically for mobile consumption + +### Recommended Architecture + +```php +// ❌ NEVER DO THIS - Direct database in mobile app +DB::connection('production')->table('users')->get(); // Credentials exposed! + +// ✅ DO THIS - Secure API communication +$response = Http::withToken($this->getStoredToken()) + ->get('https://your-api.com/api/users'); +``` + +### Laravel Sanctum Integration + +[Laravel Sanctum](https://laravel.com/docs/sanctum) is the perfect solution for API authentication between your mobile app and Laravel backend. It provides secure, token-based authentication without the complexity of OAuth. + +**Backend Setup:** +```php +// Install Sanctum in your API backend +composer require laravel/sanctum + +// Create login endpoint +Route::post('/login', function (Request $request) { + $credentials = $request->validate([ + 'email' => 'required|email', + 'password' => 'required' + ]); + + if (Auth::attempt($credentials)) { + $user = Auth::user(); + $token = $user->createToken('mobile-app')->plainTextToken; + + return response()->json([ + 'token' => $token, + 'user' => $user + ]); + } + + return response()->json(['error' => 'Invalid credentials'], 401); +}); + +// Protected API routes +Route::middleware('auth:sanctum')->group(function () { + Route::get('/user', function (Request $request) { + return $request->user(); + }); + + Route::get('/data', function (Request $request) { + // Your protected data endpoints + return Data::where('user_id', $request->user()->id)->get(); + }); +}); +``` + +### Secure Token Storage + +Use the [SecureStorage API](/docs/mobile/1/apis/secure-storage) to securely store authentication tokens on the device: + +```php +use Native\Mobile\Facades\SecureStorage; +use Illuminate\Support\Facades\Http; + +class ApiAuthManager extends Component +{ + public bool $isAuthenticated = false; + public string $error = ''; + + public function mount() + { + $this->checkStoredAuthentication(); + } + + public function login(string $email, string $password) + { + try { + $response = Http::post('https://your-api.com/api/login', [ + 'email' => $email, + 'password' => $password + ]); + + if ($response->successful()) { + $data = $response->json(); + + // Store token securely in device keychain/keystore + SecureStorage::set('api_token', $data['token']); + SecureStorage::set('user_data', json_encode($data['user'])); + + $this->isAuthenticated = true; + $this->error = ''; + } else { + $this->error = 'Invalid credentials'; + } + } catch (Exception $e) { + $this->error = 'Network error: ' . $e->getMessage(); + } + } + + public function logout() + { + // Revoke token on server + $token = SecureStorage::get('api_token'); + if ($token) { + Http::withToken($token)->post('https://your-api.com/api/logout'); + } + + // Clear local storage + SecureStorage::delete('api_token'); + SecureStorage::delete('user_data'); + + $this->isAuthenticated = false; + } + + private function checkStoredAuthentication() + { + $token = SecureStorage::get('api_token'); + + if ($token) { + // Verify token is still valid + $response = Http::withToken($token) + ->get('https://your-api.com/api/user'); + + if ($response->successful()) { + $this->isAuthenticated = true; + } else { + // Token expired or invalid + SecureStorage::delete('api_token'); + SecureStorage::delete('user_data'); + } + } + } + + public function makeAuthenticatedRequest(string $endpoint) + { + $token = SecureStorage::get('api_token'); + + if (!$token) { + throw new Exception('No authentication token available'); + } + + $response = Http::withToken($token) + ->get("https://your-api.com/api/{$endpoint}"); + + if ($response->status() === 401) { + // Token expired + $this->logout(); + throw new Exception('Authentication expired'); + } + + return $response->json(); + } +} +``` + +### Best Practices + +**For Mobile Apps:** +- Always store API tokens in SecureStorage +- Implement proper error handling for network requests +- Cache data locally using SQLite for offline functionality +- Use HTTPS for all API communications +- Implement retry logic with exponential backoff + +**For API Backends:** +- Use Laravel Sanctum or similar for token-based authentication +- Implement rate limiting to prevent abuse +- Validate and sanitize all input data +- Use HTTPS with proper SSL certificates +- Log all authentication attempts and API access + +**Token Management:** +```php +class TokenManager +{ + public function refreshTokenIfNeeded(): bool + { + $token = SecureStorage::get('api_token'); + $tokenExpiry = SecureStorage::get('token_expiry'); + + if (!$token || ($tokenExpiry && now()->timestamp > $tokenExpiry)) { + return $this->refreshToken(); + } + + return true; + } + + private function refreshToken(): bool + { + $refreshToken = SecureStorage::get('refresh_token'); + + if (!$refreshToken) { + return false; + } + + $response = Http::post('https://your-api.com/api/refresh', [ + 'refresh_token' => $refreshToken + ]); + + if ($response->successful()) { + $data = $response->json(); + SecureStorage::set('api_token', $data['access_token']); + SecureStorage::set('token_expiry', $data['expires_at']); + return true; + } + + return false; + } +} +``` + +This approach ensures your mobile app remains secure while providing seamless access to your backend data through a well-designed API layer. diff --git a/resources/views/docs/mobile/1/concepts/deep-links.md b/resources/views/docs/mobile/1/concepts/deep-links.md new file mode 100644 index 00000000..6a0f91ec --- /dev/null +++ b/resources/views/docs/mobile/1/concepts/deep-links.md @@ -0,0 +1,108 @@ +--- +title: Deep, Universal, App Links and NFC +order: 300 +--- + +## Overview + +NativePHP for Mobile supports both **deep linking** and **web-based linking** into your mobile apps. + +There are two types of link integrations you can configure: + +- **Deep Links** (myapp://some/path) +- **Universal Links (iOS)** and **App Links (Android)** (https://example.net/some/path) + +Each method has its use case, and NativePHP allows you to configure and handle both easily. + +--- + +## Deep Links + +Deep links use a **custom URL scheme** to open your app. + +For example: + +``` +myapp://profile/123 +``` + + +When a user taps a deep link, the mobile operating system detects the custom scheme and opens your app directly. + +### Configuration + +To enable deep linking, you must define: + +- **Scheme**: The protocol (e.g., myapp) +- **Host**: An optional domain-like segment (e.g., open) + +These are configured in your .env: + +```dotenv +NATIVEPHP_DEEPLINK_SCHEME=myapp +NATIVEPHP_DEEPLINK_HOST=open +``` + +## Universal Links (iOS) and App Links (Android) + +Universal Links and App Links allow real HTTPS URLs to open your app instead of a web browser, if the app is installed. + +For example: +```dotenv +https://example.net/property/456 +``` + +When a user taps this link: + + - If your app is installed, it opens directly into the app. + - If not, it opens normally in the browser. + +This provides a seamless user experience without needing a custom scheme. + +### How It Works +1. You must prove to iOS and Android that you own the domain by hosting a special file: + - .well-known/apple-app-site-association (for iOS) + - .well-known/assetlinks.json (for Android) +2. The mobile OS reads these files to verify the link association. +3. Once verified, tapping a real URL will open your app instead of Safari or Chrome. + +NativePHP for Mobile handles all of this for you. + +### Configuration + +To enable Universal Links and App Links, you must define: + +- **Host**: The domain name (e.g., example.net) + +These are configured in your .env: + +```dotenv +NATIVEPHP_DEEPLINK_HOST=example.net +``` + +## Handling Universal/App Links + +Once you've configured your deep link settings, you can handle the link in your app. + +Simply set up a route in your web.php file and the deeplink will redirect to your route. + +```dotenv +https://example.net/profile/123 +``` + +```php +Route::get('/profile/{id}', function ($id) { + // Handle the deep link +}); +``` + +## NFC +NFC is a technology that allows you to read and write NFC tags. + +NativePHP handles NFC tag "bumping" just like a Universal/App Link. +You can use a tool like [NFC Tools](https://www.wakdev.com/en/) to test write NFC tags. + +Set the url to a Universal/App Link and the tag will be written to the NFC tag. +"Bumping" the tag will open the app. + + diff --git a/resources/views/docs/mobile/1/digging-deeper/push-notifications.md b/resources/views/docs/mobile/1/concepts/push-notifications.md similarity index 97% rename from resources/views/docs/mobile/1/digging-deeper/push-notifications.md rename to resources/views/docs/mobile/1/concepts/push-notifications.md index a1b47a3a..fd534659 100644 --- a/resources/views/docs/mobile/1/digging-deeper/push-notifications.md +++ b/resources/views/docs/mobile/1/concepts/push-notifications.md @@ -1,5 +1,5 @@ --- -title: Push Notifications +title: Push Notifications - Firebase order: 400 --- @@ -26,7 +26,7 @@ take a look at how easy it is to listen for a `TokenGenerated` event in Livewire ```php use Livewire\Attributes\On; use Livewire\Component; -use Native\Mobile\Facades\System; +use Native\Mobile\Facades\PushNotifications; use Native\Mobile\Events\PushNotification\TokenGenerated; class PushNotifications extends Component @@ -93,4 +93,4 @@ class PushNotificationController extends Controller SendPushNotification::dispatch($token)->delay(now()->addMinutes(1)); } } -``` +``` \ No newline at end of file diff --git a/resources/views/docs/mobile/1/digging-deeper/security.md b/resources/views/docs/mobile/1/concepts/security.md similarity index 98% rename from resources/views/docs/mobile/1/digging-deeper/security.md rename to resources/views/docs/mobile/1/concepts/security.md index 6b8254b3..2abb9acf 100644 --- a/resources/views/docs/mobile/1/digging-deeper/security.md +++ b/resources/views/docs/mobile/1/concepts/security.md @@ -27,4 +27,4 @@ level of entropy, as this makes them hard to guess and hard to abuse. If your application allows users to connect _their own_ API keys for a service, you should treat these keys with great care. If you choose to store them anywhere (either in a [File](files) or [Database](databases)), make sure you store them -[encrypted](../the-basics/system#encryption-decryption) and decrypt them only when needed. +[encrypted](../the-basics/system#encryption-decryption) and decrypt them only when needed. \ No newline at end of file diff --git a/resources/views/docs/mobile/1/concepts/splash-screen-icons.md b/resources/views/docs/mobile/1/concepts/splash-screen-icons.md new file mode 100644 index 00000000..1a46bed6 --- /dev/null +++ b/resources/views/docs/mobile/1/concepts/splash-screen-icons.md @@ -0,0 +1,359 @@ +--- +title: Splash Screen/Icons +order: 500 +--- + +## Overview + +App icons and splash screens are the first things users see when interacting with your mobile app. NativePHP Mobile makes it easy to customize both elements to create a professional, branded experience. + +## App Icons + +### Basic Icon Setup + +Place a single high-resolution icon file at: `public/icon.png` + +### Requirements +- **Format:** PNG +- **Size:** 1024 × 1024 pixels +- **Shape:** Square +- **Background:** Transparent or solid (your choice) +- **Content:** Should work well at small sizes + +``` +your-laravel-app/ +├── public/ +│ └── icon.png ← Place your 1024x1024 icon here +├── app/ +└── ... +``` + +### Automatic Icon Generation + +NativePHP automatically generates all required icon sizes for both platforms: + +#### iOS Icon Sizes (Generated Automatically) +- App Store: 1024x1024 +- iPhone: 60x60, 120x120, 180x180 +- iPad: 76x76, 152x152, 167x167 +- Settings: 29x29, 58x58, 87x87 +- Spotlight: 40x40, 80x80, 120x120 + +#### Android Icon Sizes (Generated Automatically) +- mdpi: 48x48 +- hdpi: 72x72 +- xhdpi: 96x96 +- xxhdpi: 144x144 +- xxxhdpi: 192x192 + +### Icon Design Best Practices + +```php +// Example of checking if custom icon exists +class IconValidator +{ + public function validateIcon(): array + { + $iconPath = public_path('icon.png'); + + if (!file_exists($iconPath)) { + return ['valid' => false, 'message' => 'Icon file not found']; + } + + $imageInfo = getimagesize($iconPath); + + if (!$imageInfo) { + return ['valid' => false, 'message' => 'Invalid image file']; + } + + [$width, $height] = $imageInfo; + + if ($width !== 1024 || $height !== 1024) { + return [ + 'valid' => false, + 'message' => "Icon must be 1024x1024px, got {$width}x{$height}px" + ]; + } + + if ($imageInfo['mime'] !== 'image/png') { + return ['valid' => false, 'message' => 'Icon must be PNG format']; + } + + return ['valid' => true, 'message' => 'Icon is valid']; + } +} +``` + +#### Design Guidelines +1. **Keep it simple** - Icons must be recognizable at 16x16 pixels +2. **Avoid text** - Text becomes unreadable at small sizes +3. **Use strong contrast** - Ensure visibility on various backgrounds +4. **Make it memorable** - Unique shape or color helps recognition +5. **Test at multiple sizes** - Check how it looks when scaled down +6. **Consider platform conventions** - iOS prefers rounded corners (applied automatically) + +## Splash Screens + +### Splash Screen Configuration + +Splash screens are shown while your app loads. NativePHP provides built-in splash screen support that you can customize. + +### Default Splash Screen + +By default, NativePHP creates a simple splash screen using your app icon and name. No additional configuration required. + +### Custom Splash Screen + +Create custom splash screen assets: + +``` +your-laravel-app/ +├── public/ +│ ├── icon.png +│ ├── splash-logo.png ← Optional custom splash logo +│ └── splash-background.png ← Optional background image +├── app/ +└── ... +``` + +### Splash Screen Specifications + +#### iOS Launch Images +- **iPhone:** Various sizes for different devices +- **iPad:** Portrait and landscape orientations +- **Safe Areas:** Account for notches and home indicators + +#### Android Splash Screens +- **Centered Logo:** Displayed on colored background +- **Responsive:** Adapts to different screen sizes and orientations +- **Theme-aware:** Can adapt to light/dark themes + +### Configuration Options + +```php +// config/nativephp.php +return [ + 'app' => [ + 'name' => 'Your App Name', + 'splash' => [ + 'background_color' => '#ffffff', + 'logo_path' => 'splash-logo.png', + 'show_loading' => true, + ], + ], +]; +``` + +### Dynamic Splash Screens + +```php +class SplashScreenManager +{ + public function configureSplashScreen(): void + { + $isDarkMode = $this->getUserPreference('dark_mode'); + + $splashConfig = [ + 'background_color' => $isDarkMode ? '#000000' : '#ffffff', + 'logo_path' => $isDarkMode ? 'logo-dark.png' : 'logo-light.png', + 'show_loading' => true, + ]; + + $this->updateSplashConfiguration($splashConfig); + } + + private function getUserPreference(string $key): bool + { + return session($key, false); + } + + private function updateSplashConfiguration(array $config): void + { + // Update runtime splash configuration + config(['nativephp.app.splash' => $config]); + } +} +``` + +## Asset Compilation + +### CSS and JavaScript Assets + +Your mobile app runs locally, so all assets must be compiled before deployment. + +```bash +# Always compile assets before building +npm run build + +# Then build your mobile app +php artisan native:run +``` + +### Build Process Integration + +```json +{ + "scripts": { + "dev": "vite", + "build": "vite build", + "build:mobile": "vite build --mode mobile", + "mobile:prepare": "npm run build:mobile && php artisan optimize" + } +} +``` + +### Asset Optimization for Mobile + +```javascript +// vite.config.js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + input: ['resources/css/app.css', 'resources/js/app.js'], + refresh: true, + }), + ], + build: { + rollupOptions: { + output: { + manualChunks: { + vendor: ['lodash', 'axios'], + ui: ['@headlessui/vue', '@heroicons/vue'], + } + } + }, + chunkSizeWarningLimit: 1000, + }, +}); +``` + +## Development Workflow + +### Hot Reload (Experimental) + +For development, you can use the experimental watch flag: + +```bash +php artisan native:run --watch +``` + +### Watch Limitations + +Currently supports: +- ✅ Blade templates +- ✅ Livewire components +- ✅ PHP files +- ❌ Compiled CSS/JS assets +- ❌ Vite builds +- ❌ Inertia.js apps + +### Recommended Development Flow + +```bash +# For Blade/Livewire apps +php artisan native:run --watch + +# For apps with compiled assets (Vue, React, Inertia) +npm run dev & # Run in background +npm run build # Build assets +php artisan native:run # Launch app +``` + +## Testing Icons and Splash Screens + +### Icon Testing Checklist + +```php +class AssetTestSuite +{ + public function testIconExists(): void + { + $this->assertFileExists(public_path('icon.png')); + } + + public function testIconDimensions(): void + { + $iconPath = public_path('icon.png'); + [$width, $height] = getimagesize($iconPath); + + $this->assertEquals(1024, $width); + $this->assertEquals(1024, $height); + } + + public function testIconFormat(): void + { + $iconPath = public_path('icon.png'); + $imageInfo = getimagesize($iconPath); + + $this->assertEquals('image/png', $imageInfo['mime']); + } + + public function testSplashConfiguration(): void + { + $splashConfig = config('nativephp.app.splash'); + + $this->assertIsArray($splashConfig); + $this->assertArrayHasKey('background_color', $splashConfig); + } +} +``` + +### Visual Testing + +1. **Test on multiple devices** - Check how icons look on different screen sizes +2. **Test both orientations** - Portrait and landscape modes +3. **Test theme variations** - Light and dark modes +4. **Test loading states** - Ensure splash screens display properly +5. **Performance testing** - Monitor app launch times + +## Platform-Specific Considerations + +### iOS +- Icons automatically get rounded corners +- Supports Dynamic Type for accessibility +- Requires specific launch image sizes +- Splash screens adapt to safe areas + +### Android +- Icons can be adaptive (foreground + background) +- Supports vector drawables for splash screens +- Material Design guidelines apply +- Supports theme-aware splash screens + +### Cross-Platform Assets + +``` +public/ +├── icon.png # Universal app icon +├── icon-android.png # Android-specific (optional) +├── icon-ios.png # iOS-specific (optional) +├── splash-logo.png # Universal splash logo +├── splash-android.png # Android splash (optional) +└── splash-ios.png # iOS splash (optional) +``` + +## Best Practices + +### Icon Design +1. **Start with vector graphics** - Use SVG or AI files for source +2. **Export high quality** - Use 1024x1024 PNG with no compression +3. **Test readability** - Check visibility at 16x16 pixels +4. **Maintain brand consistency** - Match your web/desktop icons +5. **Consider accessibility** - Ensure sufficient contrast + +### Splash Screen Design +1. **Keep it simple** - Splash screens should load quickly +2. **Match your brand** - Use consistent colors and typography +3. **Don't include text** - Text may not scale properly +4. **Consider loading states** - Show progress if app takes time to load +5. **Test performance** - Long splash screens hurt user experience + +### Asset Management +1. **Optimize file sizes** - Compress images without quality loss +2. **Use appropriate formats** - PNG for icons, WebP for photos +3. **Version your assets** - Track changes to visual elements +4. **Automate generation** - Script the creation of multiple sizes +5. **Test regularly** - Verify assets display correctly after changes \ No newline at end of file diff --git a/resources/views/docs/mobile/1/concepts/versioning.md b/resources/views/docs/mobile/1/concepts/versioning.md new file mode 100644 index 00000000..bf6e403c --- /dev/null +++ b/resources/views/docs/mobile/1/concepts/versioning.md @@ -0,0 +1,291 @@ +--- +title: Versioning +order: 600 +--- + +## Overview + +Proper versioning is crucial for mobile app development, affecting app store submissions, user updates, and feature compatibility. NativePHP Mobile provides flexible versioning strategies for both development and production environments. + +## Version Configuration + +### Environment Variables + +Configure your app version in `.env`: + +```bash +# App identifier (must match across all platforms) +NATIVEPHP_APP_ID=com.yourcompany.yourapp + +# Version string (shown to users) +NATIVEPHP_APP_VERSION=1.2.3 + +# Version code (integer, must increase with each release) +NATIVEPHP_APP_VERSION_CODE=123 +``` + +### Dynamic Versioning + +```php +// Set version programmatically +config(['nativephp.app.version' => '1.2.3']); +config(['nativephp.app.version_code' => 123]); +``` + +## Version Types + +### Semantic Versioning (Recommended) + +Follow [Semantic Versioning](https://semver.org/) principles: + +``` +MAJOR.MINOR.PATCH +``` + +- **MAJOR** - Breaking changes or significant new features +- **MINOR** - New features, backward compatible +- **PATCH** - Bug fixes, backward compatible + +Examples: +```bash +NATIVEPHP_APP_VERSION=1.0.0 # Initial release +NATIVEPHP_APP_VERSION=1.1.0 # New features added +NATIVEPHP_APP_VERSION=1.1.1 # Bug fixes +NATIVEPHP_APP_VERSION=2.0.0 # Breaking changes +``` + +### Version Codes + +Version codes are integers that must increase with each release: + +```bash +# v1.0.0 +NATIVEPHP_APP_VERSION_CODE=100 + +# v1.1.0 +NATIVEPHP_APP_VERSION_CODE=110 + +# v1.1.1 +NATIVEPHP_APP_VERSION_CODE=111 + +# v2.0.0 +NATIVEPHP_APP_VERSION_CODE=200 +``` + +## Environment-Specific Versioning + +### Development + +```bash +# .env.development +NATIVEPHP_APP_VERSION=1.2.3-dev +NATIVEPHP_APP_VERSION_CODE=99999 +``` + +### Staging + +```bash +# .env.staging +NATIVEPHP_APP_VERSION=1.2.3-beta +NATIVEPHP_APP_VERSION_CODE=99998 +``` + +### Production + +```bash +# .env.production +NATIVEPHP_APP_VERSION=1.2.3 +NATIVEPHP_APP_VERSION_CODE=123 +``` + +## Automated Versioning + +### Git Tag-Based Versioning + +```bash +#!/bin/bash +# scripts/set-version.sh + +# Get version from git tag +VERSION=$(git describe --tags --exact-match 2>/dev/null || echo "dev") + +# Remove 'v' prefix if present +VERSION=${VERSION#v} + +# Set in environment +export NATIVEPHP_APP_VERSION=$VERSION + +# Generate version code from semantic version +MAJOR=$(echo $VERSION | cut -d. -f1) +MINOR=$(echo $VERSION | cut -d. -f2) +PATCH=$(echo $VERSION | cut -d. -f3) +VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH)) + +export NATIVEPHP_APP_VERSION_CODE=$VERSION_CODE +``` + +### CI/CD Integration + +```yaml +# GitHub Actions +- name: Set version from tag + run: | + if [[ $GITHUB_REF == refs/tags/* ]]; then + VERSION=${GITHUB_REF#refs/tags/v} + echo "NATIVEPHP_APP_VERSION=$VERSION" >> .env + + # Generate version code + MAJOR=$(echo $VERSION | cut -d. -f1) + MINOR=$(echo $VERSION | cut -d. -f2) + PATCH=$(echo $VERSION | cut -d. -f3) + VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH)) + echo "NATIVEPHP_APP_VERSION_CODE=$VERSION_CODE" >> .env + fi +``` + +### Build Number Integration + +```bash +# Use CI build number for development versions +NATIVEPHP_APP_VERSION=1.2.3-build.${BUILD_NUMBER} +NATIVEPHP_APP_VERSION_CODE=${BUILD_NUMBER} +``` + +## Version Management in Code + +### Accessing Current Version + +```php +use Livewire\Component; + +class VersionDisplay extends Component +{ + public function mount() + { + $this->version = config('nativephp.app.version'); + $this->versionCode = config('nativephp.app.version_code'); + } + + public function render() + { + return view('livewire.version-display', [ + 'version' => $this->version, + 'versionCode' => $this->versionCode + ]); + } +} +``` + +### Version Comparison + +```php +class VersionManager +{ + public function isNewerVersion(string $current, string $new): bool + { + return version_compare($new, $current, '>'); + } + + public function getCurrentVersion(): string + { + return config('nativephp.app.version'); + } + + public function checkForUpdates(): array + { + $currentVersion = $this->getCurrentVersion(); + $latestVersion = $this->getLatestVersionFromApi(); + + return [ + 'has_update' => $this->isNewerVersion($currentVersion, $latestVersion), + 'current_version' => $currentVersion, + 'latest_version' => $latestVersion + ]; + } + + private function getLatestVersionFromApi(): string + { + // Check your API for latest version + $response = Http::get('https://api.yourapp.com/version/latest'); + return $response->json('version'); + } +} +``` + +## App Store Considerations + +### iOS App Store + +- **CFBundleShortVersionString** - User-facing version (1.2.3) +- **CFBundleVersion** - Build number (123) +- Version must be incremented for each submission +- Can skip version numbers (1.0 → 1.2 is allowed) + +### Google Play Store + +- **versionName** - User-facing version string +- **versionCode** - Integer that must increase with every release +- Cannot decrease version code +- Can reuse version names with different codes +- **Important**: `NATIVEPHP_APP_VERSION_CODE` must be incremented for each Google Play Store release, even for minor updates + +### Configuration Example + +```php +// config/nativephp.php +return [ + 'app' => [ + 'id' => env('NATIVEPHP_APP_ID', 'com.example.app'), + 'version' => env('NATIVEPHP_APP_VERSION', '1.0.0'), + 'version_code' => env('NATIVEPHP_APP_VERSION_CODE', 1), + ], +]; +``` + +## Best Practices + +### Version Strategy +1. **Use semantic versioning** for consistency +2. **Increment version codes** for every build +3. **Test version upgrades** thoroughly +4. **Document breaking changes** clearly +5. **Plan update strategies** in advance + +### Development Workflow +1. **Branch naming** - Include version in branch names (`feature/v1.2.0-new-ui`) +2. **Tag releases** - Use git tags for version tracking +3. **Automate versioning** - Reduce manual errors +4. **Test backwards compatibility** - Ensure smooth upgrades +5. **Maintain changelog** - Document all changes + +### Release Management +1. **Staged rollouts** - Release to small groups first +2. **Feature flags** - Control feature availability by version +3. **Emergency updates** - Have a fast-track process for critical fixes +4. **Version analytics** - Track version adoption rates +5. **Sunset planning** - Plan when to stop supporting old versions + +### Common Pitfalls +- **Don't decrease version codes** - Google Play Store will reject decreasing `NATIVEPHP_APP_VERSION_CODE` +- **Don't reuse version codes** - Each release must have a unique, higher version code +- **Don't skip testing upgrade paths** - Test how users transition between versions +- **Don't forget to update all platform configs** - Keep versions synchronized +- **Don't ignore app store requirements** - Each platform has specific versioning rules + +### Google Play Store Version Code Requirements + +The `NATIVEPHP_APP_VERSION_CODE` is critical for Google Play Store submissions: + +```bash +# ❌ WRONG - Cannot decrease or reuse version codes +NATIVEPHP_APP_VERSION_CODE=100 # v1.0.0 +NATIVEPHP_APP_VERSION_CODE=99 # v1.0.1 - REJECTED! + +# ✅ CORRECT - Always increase version code +NATIVEPHP_APP_VERSION_CODE=100 # v1.0.0 +NATIVEPHP_APP_VERSION_CODE=101 # v1.0.1 - Accepted +NATIVEPHP_APP_VERSION_CODE=102 # v1.0.2 - Accepted +NATIVEPHP_APP_VERSION_CODE=200 # v2.0.0 - Accepted +``` + +**Remember**: Even for hotfixes or minor updates, the version code must always increase. diff --git a/resources/views/docs/mobile/1/digging-deeper/_index.md b/resources/views/docs/mobile/1/digging-deeper/_index.md deleted file mode 100644 index 7d67168d..00000000 --- a/resources/views/docs/mobile/1/digging-deeper/_index.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Digging Deeper -order: 3 ---- diff --git a/resources/views/docs/mobile/1/digging-deeper/databases.md b/resources/views/docs/mobile/1/digging-deeper/databases.md deleted file mode 100644 index fdccaf8f..00000000 --- a/resources/views/docs/mobile/1/digging-deeper/databases.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Databases -order: 200 ---- - -## Working with Databases - -You'll almost certainly want your application to persist structured data. For this, NativePHP supports -[SQLite](https://sqlite.org/), which works on both iOS and Android devices. - -You can interact with SQLite from PHP in whichever way you're used to. - -## Configuration - -You do not need to do anything special to configure your application to use SQLite. NativePHP will automatically: -- Switch to using SQLite when building your application. -- Create the database for you in the app container. -- Run your migrations each time your app starts, as needed. - -## Migrations - -When writing migrations, you need to consider any special recommendations for working with SQLite. - -For example, prior to Laravel 11, SQLite foreign key constraints are turned off by default. If your application relies -upon foreign key constraints, [you need to enable SQLite support for them](https://laravel.com/docs/database#configuration) before running your migrations. - -**It's important to test your migrations on prod builds before releasing updates!** You don't want to accidentally -delete your user's data when they update your app. - -## Things to note - -- As your app is installed on a separate device, you do not have remote access to the database. -- If a user deletes your application from their device, any databases are also deleted. diff --git a/resources/views/docs/mobile/1/getting-started/_index.md b/resources/views/docs/mobile/1/getting-started/_index.md index 7f67f577..5c684744 100644 --- a/resources/views/docs/mobile/1/getting-started/_index.md +++ b/resources/views/docs/mobile/1/getting-started/_index.md @@ -1,4 +1,4 @@ --- title: Getting Started order: 1 ---- +--- \ No newline at end of file diff --git a/resources/views/docs/mobile/1/getting-started/configuration.md b/resources/views/docs/mobile/1/getting-started/configuration.md index c6cf39b2..2b385d19 100644 --- a/resources/views/docs/mobile/1/getting-started/configuration.md +++ b/resources/views/docs/mobile/1/getting-started/configuration.md @@ -42,11 +42,11 @@ This is useful for removing files like logs or other temporary files. ## Permissions In general, the app stores don't want apps to request permissions that they don't need. -To enable some permissions your app needs you simply need to change their values in the permissions section. +To enable some permissions your app needs, you simply need to change their values in the permissions section. ```dotenv biometric camera nfc -push_notifications +location ``` diff --git a/resources/views/docs/mobile/1/getting-started/environment-setup.md b/resources/views/docs/mobile/1/getting-started/environment-setup.md new file mode 100644 index 00000000..2f955a04 --- /dev/null +++ b/resources/views/docs/mobile/1/getting-started/environment-setup.md @@ -0,0 +1,94 @@ +--- +title: Environment Setup +order: 100 +--- + +## iOS Requirements + +### For iOS +1. macOS (required - iOS development is only possible on Mac) +2. [Xcode 16.0 or later](https://apps.apple.com/app/xcode/id497799835) +3. Xcode Command Line Tools +4. Homebrew (for dependency management) +5. CocoaPods +6. _Optional_ iOS device for testing + +#### Setting up iOS Development Environment + +1. **Install Xcode** + - Download from the [Mac App Store](https://apps.apple.com/app/xcode/id497799835) + - Minimum version: Xcode 16.0 + +2. **Install Xcode Command Line Tools** + ```shell + xcode-select --install + ``` + Verify installation: + ```shell + xcode-select -p + ``` + +3. **Install Homebrew** (if not already installed) + ```shell + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + ``` + +4. **Install CocoaPods** + ```shell + brew install cocoapods + ``` + Verify installation: + ```shell + pod --version + ``` + +#### Apple Developer Account +You **do not** need to enroll in the [Apple Developer Program](https://developer.apple.com/programs/enroll/) ($99/year) +to develop and test your apps on a Simulator. However, you will need to enroll when you want to: +- Test your apps on real devices +- Distribute your apps via the App Store + +> **Note** You cannot build iOS apps on Windows or Linux + +## Android Requirements + +### For Android +1. [Android Studio 2024.2.1 or later](https://developer.android.com/studio) +2. Android SDK with API 23 or higher +3. **Windows only**: You must have [7zip](https://www.7-zip.org/) installed. + +> **Note** You do not need to separately install the Java Development Kit (JDK). Android Studio will automatically install the proper JDK for you. + +#### Setting up Android Studio and SDK + +1. **Download and Install Android Studio** + - Download from the [Android Studio download page](https://developer.android.com/studio) + - Minimum version required: Android Studio 2024.2.1 + +2. **Install Android SDK** + - Open Android Studio + - Navigate to **Tools → SDK Manager** + - In the **SDK Platforms** tab, install at least one Android SDK platform for API 23 or higher + - Latest stable version: Android 15 (API 35) + - You only need to install one API version to get started + - In the **SDK Tools** tab, ensure **Android SDK Build-Tools** and **Android SDK Platform-Tools** are installed + +That's it! Android Studio handles all the necessary configuration automatically. + +## Testing on Real Devices + +You don't _need_ a physical iOS/Android device to compile and test your application, as NativePHP for Mobile supports +the iOS Simulator and Android emulators. However, we highly recommend that you test your application on a real device before submitting to the +Apple App Store and Google Play Store. + +### Running on a real device + +#### On iOS +If you want to run your app on a real iOS device, you need to make sure it is in +[Developer Mode](https://developer.apple.com/documentation/xcode/enabling-developer-mode-on-a-device) +and that it's been added to your Apple Developer account as +[a registered device](https://developer.apple.com/account/resources/devices/list). + +#### On Android +On Android you need to [enable developer options](https://developer.android.com/studio/debug/dev-options#enable) +and have USB debugging (ADB) enabled. diff --git a/resources/views/docs/mobile/1/getting-started/installation.md b/resources/views/docs/mobile/1/getting-started/installation.md index 6f930534..15ce23bd 100644 --- a/resources/views/docs/mobile/1/getting-started/installation.md +++ b/resources/views/docs/mobile/1/getting-started/installation.md @@ -1,6 +1,6 @@ --- title: Installation -order: 100 +order: 150 --- ## Requirements @@ -9,42 +9,7 @@ order: 100 2. Laravel 10 or higher 3. [A NativePHP for Mobile license](https://nativephp.com/mobile) -### For iOS -1. An Apple Silicon Mac running macOS 12+ with [Xcode 16+](https://apps.apple.com/app/xcode/id497799835) -2. An active [Apple Developer account](https://developer.apple.com/) -3. _Optional_ iOS device - -You **do not** need to enroll in the [Apple Developer Program](https://developer.apple.com/programs/enroll/) ($99/year) -to develop and test your apps on a Simulator. But you will need to when you want to test your apps on real devices -and start distributing them to your users via the App Store. - -> **Note** You cannot build iOS apps on Windows or Linux - -### For Android -1. [Android Studio Giraffe (or later)](https://developer.android.com/studio) -2. The following environment variables set. -3. You should be able to successfully run `java -v` and `adb devices` from the terminal. -4. **Windows only**: You must have [7zip](https://www.7-zip.org/) installed. - -#### On macOS -```shell -export JAVA_HOME=$(/usr/libexec/java_home -v 17) // This isn't required if JAVA_HOME is already set in your environment variables (check using `printenv | grep JAVA_HOME`) -export ANDROID_HOME=$HOME/Library/Android/sdk -export PATH=$PATH:$JAVA_HOME/bin:$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools -``` - -#### On Windows -```shell -set ANDROID_HOME=C:\Users\yourname\AppData\Local\Android\Sdk -set PATH=%PATH%;%JAVA_HOME%\bin;%ANDROID_HOME%\platform-tools - -# This isn't required if JAVA_HOME is already set in the Windows Env Variables -set JAVA_HOME=C:\Program Files\Microsoft\jdk-17.0.8.7-hotspot -``` - -You don't _need_ a physical iOS/Android device to compile and test your application, as NativePHP for Mobile supports -the iOS Simulator and Android emulators. However, we highly recommend that you test your application on a real device before submitting to the -Apple App Store and Google Play Store. +For platform-specific development environment setup (iOS and Android), see the [Environment Setup](/docs/mobile/1/getting-started/environment-setup) page. ## Laravel @@ -78,6 +43,8 @@ composer require nativephp/mobile ``` *If you experience a cURL error when running this command make sure you are running PHP v8.3+ in your CLI.* +**Windows Performance Tip:** Add `C:\temp` to your Windows Defender exclusions list to significantly speed up composer installs during app compilation. This prevents real-time scanning from slowing down the many temporary files created during the build process. + If this is the first time you're installing the package, you will be prompted to authenticate. Your username is the email address you used when purchasing your license. Your password is your license key. @@ -92,6 +59,8 @@ NATIVEPHP_APP_VERSION="DEBUG" NATIVEPHP_APP_VERSION_CODE="1" ``` +**Important: the NATIVEPHP_APP_ID must not contain any special characters or spaces** + ## Run the NativePHP installer ```shell @@ -100,6 +69,10 @@ php artisan native:install The NativePHP installer takes care of setting up and configuring your Laravel application to work with iOS and Android. +## ICU Support (Android Only) + +If you are wanting to run [Filament](https://filamentphp.com) or use some of the Number or I18n methods within Laravel you will need to install NativePHP with the optional ICU supported binaries, more on that [here](/docs/mobile/1/the-basics/icu-support). + ## Start your app **Heads up!** Before starting your app in a native context, try running it in the browser. You may bump into exceptions @@ -113,16 +86,6 @@ php artisan native:run This will start compiling your application and boot it on whichever device you select. -### Running on a real device - -#### On iOS -If you want to run your app on a real iOS device, you need to make sure it is in -[Developer Mode](https://developer.apple.com/documentation/xcode/enabling-developer-mode-on-a-device) -and that it's been added to your Apple Developer account as -[a registered device](https://developer.apple.com/account/resources/devices/list). - -#### On Android -On Android you need to [enable developer options](https://developer.android.com/studio/debug/dev-options#enable) -and have USB debugging (ADB) enabled. - And that's it! You should now see your Laravel application running as a native app! 🎉 + +For information about running on real devices, see the [Environment Setup](/docs/mobile/1/getting-started/environment-setup) page. diff --git a/resources/views/docs/mobile/1/getting-started/introduction.md b/resources/views/docs/mobile/1/getting-started/introduction.md index c313a037..7957c6a1 100644 --- a/resources/views/docs/mobile/1/getting-started/introduction.md +++ b/resources/views/docs/mobile/1/getting-started/introduction.md @@ -5,22 +5,24 @@ order: 001 ## Welcome to the revolution! -NativePHP for Mobile is a first of its kind library that allows PHP developers to run PHP applications natively on -all sorts of mobile devices _without a web server_. +NativePHP for Mobile is the first library of its kind that lets you run full PHP applications natively on mobile +devices — no web server required. -We've combined the statically compiling PHP as an embeddable C library with the flexibility of Laravel and the rich -native APIs of each support platform, unlocking the power and convenience of Laravel for building performant, native -_mobile_ applications using PHP. +By embedding a statically compiled PHP runtime alongside Laravel, and bridging directly into each platform’s native +APIs, NativePHP brings the power of modern PHP to truly native mobile apps. Build performant, offline-capable +experiences using the tools you already know. **It's never been this easy to build beautiful, local-first apps for iOS and Android.** ## Old tools, new tricks -With NativePHP for Mobile, you don't have to learn any new languages or ecosystems. Stop fighting with other package -managers and build tools. Stay in the comfort of PHP and Composer! +With NativePHP for Mobile, you don’t need to learn Swift, Kotlin, or anything new. +No new languages. No unfamiliar build tools. No fighting with Gradle or Xcode. -PHP developers all over the world are building incredible mobile experiences with the skills they already possess. -In just a few hours you can build an app and have it submitted to the app stores for review. +Just PHP. + +Developers around the world are using the skills they already have to build and ship real mobile apps — faster than +ever. In just a few hours, you can go from code to app store submission. ## How does it work? @@ -28,8 +30,8 @@ On the simplest level: 1. A statically-compiled version of PHP is bundled with your code into a Swift/Kotlin shell application. 2. NativePHP's custom Swift/Kotlin bridges manage the PHP environment, running your PHP code directly. -3. A custom PHP extension is compiled into PHP, that exposes PHP interfaces to native functions. -4. Your app renders in a native web view, so you can continue developing your UI the way you're used to. +3. A custom PHP extension is compiled into PHP, that exposes PHP interfaces to native functions. +4. Your app renders in a native web view, so you can continue developing your UI the way you're used to. You simply interact with an easy-to-use set of functions from PHP and everything just works! @@ -38,7 +40,7 @@ You simply interact with an easy-to-use set of functions from PHP and everything NativePHP for Mobile is way more than just a web view wrapper for your server-based application. Your application lives _on device_ and is shipped with each installation. -Thanks to our custom PHP extension, you can interact with many native APIs today, with more coming each week, including: +Thanks to our custom PHP extension, you can interact with many native APIs today, with more coming, including: - Camera & Microphone - Biometric ID diff --git a/resources/views/docs/mobile/1/getting-started/roadmap.md b/resources/views/docs/mobile/1/getting-started/roadmap.md index 4899aa01..26e6110a 100644 --- a/resources/views/docs/mobile/1/getting-started/roadmap.md +++ b/resources/views/docs/mobile/1/getting-started/roadmap.md @@ -21,11 +21,13 @@ Presently, NativePHP for Mobile offers the following "native" functionality: - Push Notifications - Deep Links - NFC +- Secure Storage +- Location +- Native image picker +- Splash screen We're working on adding more and more features, including (in no particular order): - - Secure Storage - Microphone access - - Location - Bluetooth - SMS (Android only) - File picker @@ -33,7 +35,6 @@ We're working on adding more and more features, including (in no particular orde - Document scanner - Background tasks - Geofencing - - Native image picker - Calendar access - Local notifications, scheduled notifications - Clipboard API @@ -51,5 +52,4 @@ We're working on adding more and more features, including (in no particular orde - CPU information - Ads - In-app billing - - Splash screen diff --git a/resources/views/docs/mobile/1/the-basics/_index.md b/resources/views/docs/mobile/1/the-basics/_index.md index e8a6458a..373920fa 100644 --- a/resources/views/docs/mobile/1/the-basics/_index.md +++ b/resources/views/docs/mobile/1/the-basics/_index.md @@ -1,4 +1,4 @@ --- title: The Basics order: 2 ---- +--- \ No newline at end of file diff --git a/resources/views/docs/mobile/1/the-basics/app-assets.md b/resources/views/docs/mobile/1/the-basics/app-assets.md index b8346f00..041012df 100644 --- a/resources/views/docs/mobile/1/the-basics/app-assets.md +++ b/resources/views/docs/mobile/1/the-basics/app-assets.md @@ -18,7 +18,7 @@ Place a single high-resolution icon file at: `public/icon.png` - Shape: Square - Background: Transparent or solid — your choice -Note: This image will be automatically resized for all Android densities and used as the base iOS app icon. +Note: This image will be automatically resized for all Android densities and used as the base iOS app icon. You must have the GD extension installed and active in your local PHP environment for this to work. --- @@ -49,21 +49,3 @@ This is useful during development for quickly testing changes without rebuilding ### Recommendation Use `--watch` when you're iterating on Blade views or Livewire components. For all other use cases, treat this flag as experimental and optional. - ---- - -## Optional: Installing with ICU Support - -By default, NativePHP installs a smaller PHP runtime without ICU (International Components for Unicode) to keep app size minimal. - -If your Laravel app uses features that rely on `intl` (such as number formatting, localized date handling, or advanced string collation), you’ll need ICU support enabled. - -To include ICU during installation, select it when running: `php artisan native:install`. - -This will install a version of PHP with full ICU support. Note that it increases the PHP binary size significantly (typically from ~16MB to ~44MB). - -**Important:** If you plan to use [Filament](https://filamentphp.com/) in your app, you must enable this option. Filament relies on the `intl` extension for formatting and localization features. - - - - diff --git a/resources/views/docs/mobile/1/the-basics/asynchronous-methods.md b/resources/views/docs/mobile/1/the-basics/asynchronous-methods.md new file mode 100644 index 00000000..4cc36a87 --- /dev/null +++ b/resources/views/docs/mobile/1/the-basics/asynchronous-methods.md @@ -0,0 +1,308 @@ +--- +title: Asynchronous Methods +order: 200 +--- + +## Overview + +Many native mobile operations take time to complete and require user interaction. NativePHP Mobile handles these through asynchronous methods that use Laravel's event system to notify your app when operations complete. + +## Understanding Async vs Sync + +### Synchronous Methods ⚡ +Execute immediately and return results directly. + +```php +// These complete instantly +Haptics::vibrate(); +System::flashlight(); +Dialog::toast('Hello!'); +``` + +### Asynchronous Methods 🔄 +Trigger operations that complete later and fire events when done. + +```php +// These trigger operations and fire events when complete +Camera::getPhoto(); // → PhotoTaken event +Biometrics::promptForBiometricID(); // → Completed event +PushNotifications::enrollForPushNotifications(); // → TokenGenerated event +``` + +## Event Handling Pattern + +All asynchronous methods follow the same pattern: + +1. **Call the method** to trigger the operation +2. **Listen for events** to handle the result +3. **Update your UI** based on the outcome + +### Basic Event Structure + +```php +use Livewire\Component; +use Livewire\Attributes\On; +use Native\Mobile\Facades\Camera; +use Native\Mobile\Events\Camera\PhotoTaken; + +class PhotoComponent extends Component +{ + public bool $isCapturing = false; + public ?string $photoPath = null; + + // Step 1: Trigger the async operation + public function takePhoto() + { + $this->isCapturing = true; + Camera::getPhoto(); + } + + // Step 2: Handle the result event + #[On('native:' . PhotoTaken::class)] + public function handlePhotoTaken(string $path) + { + $this->isCapturing = false; + $this->photoPath = $path; + } + + public function render() + { + return view('livewire.photo-component'); + } +} +``` + +## Event Naming Convention + +All frontend events use the `native:` prefix to prevent naming collisions: + +```php +// Backend event class +Native\Mobile\Events\Camera\PhotoTaken + +// Frontend Livewire event (with prefix) +native:Native\Mobile\Events\Camera\PhotoTaken +``` + +## Common Async Operations + +### Camera Operations + +```php +use Native\Mobile\Events\Camera\PhotoTaken; +use Native\Mobile\Events\Gallery\MediaSelected; + +class MediaManager extends Component +{ + public function capturePhoto() + { + Camera::getPhoto(); + } + + public function selectFromGallery() + { + Camera::pickImages('images', true); + } + + #[On('native:' . PhotoTaken::class)] + public function handlePhoto(string $path) + { + // Handle captured photo + } + + #[On('native:' . MediaSelected::class)] + public function handleGallerySelection($success, $files, $count) + { + // Handle selected media + } +} +``` + +### Biometric Authentication + +```php +use Native\Mobile\Events\Biometric\Completed; + +class SecureFeature extends Component +{ + public function authenticate() + { + Biometrics::promptForBiometricID(); + } + + #[On('native:' . Completed::class)] + public function handleBiometric(bool $success) + { + if ($success) { + $this->unlockFeature(); + } else { + $this->showAuthError(); + } + } +} +``` + +### Push Notification Registration + +```php +use Native\Mobile\Events\PushNotification\TokenGenerated; + +class NotificationSetup extends Component +{ + public function enableNotifications() + { + PushNotifications::enrollForPushNotifications(); + } + + #[On('native:' . TokenGenerated::class)] + public function handleToken(string $token) + { + // Send token to your backend + $this->registerToken($token); + } +} +``` + +### Location Services + +```php +use Native\Mobile\Events\Geolocation\LocationReceived; +use Native\Mobile\Events\Geolocation\PermissionStatusReceived; + +class LocationTracker extends Component +{ + public function getCurrentLocation() + { + Geolocation::getCurrentPosition(true); // High accuracy + } + + #[On('native:' . LocationReceived::class)] + public function handleLocation($success = null, $latitude = null, $longitude = null, $accuracy = null, $timestamp = null, $provider = null, $error = null) + { + $this->latitude = $latitude; + $this->longitude = $longitude; + } + + #[On('native:' . PermissionStatusReceived::class)] + public function handlePermissionStatus($location, $coarseLocation, $fineLocation) + { + if (!$coarseLocation == 'granted') { + $this->showLocationPermissionRequest(); + } + } +} +``` + +## Loading States + +Provide visual feedback during async operations: + +```php +class LoadingStatesExample extends Component +{ + public bool $isLoading = false; + public string $loadingMessage = ''; + + public function performAsyncOperation() + { + $this->isLoading = true; + $this->loadingMessage = 'Taking photo...'; + + Camera::getPhoto(); + } + + #[On('native:' . PhotoTaken::class)] + public function handleComplete($path) + { + $this->isLoading = false; + $this->loadingMessage = ''; + + // Process result + } + + public function render() + { + return view('livewire.loading-states-example'); + } +} +``` + +## Advanced Patterns + +### Chaining Async Operations + +```php +class ChainedOperations extends Component +{ + public function authenticateAndCapture() + { + // Step 1: Authenticate first + Biometrics::promptForBiometricID(); + } + + #[On('native:' . Completed::class)] + public function handleAuthComplete(bool $success) + { + if ($success) { + // Step 2: Then capture photo + Camera::getPhoto(); + } + } + + #[On('native:' . PhotoTaken::class)] + public function handlePhotoComplete(string $path) + { + // Step 3: Process the authenticated photo + $this->processSecurePhoto($path); + } +} +``` + +### Multiple Event Listeners + +```php +class MultiEventComponent extends Component +{ + public function performMultipleOperations() + { + Camera::getPhoto(); + PushNotifications::enrollForPushNotifications(); + Geolocation::getCurrentPosition(); + } + + #[On('native:' . PhotoTaken::class)] + public function handlePhoto(string $path) { /* ... */ } + + #[On('native:' . TokenGenerated::class)] + public function handlePushToken(string $token) { /* ... */ } + + #[On('native:' . LocationReceived::class)] + public function handleLocation($success = null, $latitude = null, $longitude = null, $accuracy = null) { /* ... */ } +} +``` + +## Troubleshooting + +### Debugging Async Operations + +```php +class DebuggingComponent extends Component +{ + public function startDebugOperation() + { + Log::info('Starting async operation'); + $this->isLoading = true; + + Camera::getPhoto(); + } + + #[On('native:' . PhotoTaken::class)] + public function handleResult($path) + { + Log::info('Async operation completed', ['result' => $result]); + $this->isLoading = false; + } +} +``` + +Understanding asynchronous methods is crucial for building responsive mobile apps with NativePHP. The event-driven pattern ensures your UI stays responsive while native operations complete in the background. diff --git a/resources/views/docs/mobile/1/the-basics/dialogs.md b/resources/views/docs/mobile/1/the-basics/dialogs.md deleted file mode 100644 index 4ccba9c4..00000000 --- a/resources/views/docs/mobile/1/the-basics/dialogs.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: Dialogs -order: 300 ---- - -## Native Dialogs - -NativePHP allows you to trigger many native dialogs. - -Dialogs are created using the `Dialog` facade. - -```php -use Native\Mobile\Facades\Dialog; -``` - -### The Share Dialog - -You may open the native share dialog by using the `Dialog::share()` method. - -```php -Dialog::share('Title', 'Description', 'URL'); -``` - -### The Alert Dialog - -You may open a native alert dialog by using the `Dialog::alert()` method. - -```php -Dialog::alert('Title', 'Message'); -``` - -### The Toast Dialog - -You may open a native toast dialog by using the `Dialog::toast()` method. There is not a toast dialog on iOS, -on iOS we will simply show an Alert Dialog with just an `OK` button. - -```php -Dialog::toast('Message'); -``` diff --git a/resources/views/docs/mobile/1/the-basics/icu-support.md b/resources/views/docs/mobile/1/the-basics/icu-support.md new file mode 100644 index 00000000..2b96e8b7 --- /dev/null +++ b/resources/views/docs/mobile/1/the-basics/icu-support.md @@ -0,0 +1,376 @@ +--- +title: ICU Support +order: 300 +--- + +## Overview + +ICU (International Components for Unicode) is a library that provides robust Unicode and locale support for applications. While NativePHP Mobile includes a lightweight PHP runtime by default, you can optionally enable ICU support for applications that require advanced internationalization features. + +## What is ICU? + +ICU provides: +- **Unicode support** - Full Unicode text processing +- **Localization** - Number, date, and currency formatting +- **Collation** - Language-sensitive string comparison +- **Text boundaries** - Word, sentence, and line breaking +- **Transliteration** - Script conversion between languages + +## When Do You Need ICU? + +### Required for: +- **Number formatting** with locale-specific rules +- **Date/time formatting** with localized patterns +- **Currency formatting** with proper symbols and rules +- **String collation** for sorting in different languages +- **Text normalization** and case conversion +- **Complex text rendering** for right-to-left languages + +### Specifically Required for: +- **[Filament](https://filamentphp.com/)** - Uses `intl` extension extensively +- **Laravel's localization helpers** that depend on `intl` +- **Third-party packages** that require `intl` extension +- **Multi-language applications** with complex formatting needs + +## Installation + +### During Initial Setup + +When running `php artisan native:install`, you'll be prompted: + +```bash +php artisan native:install + +# You'll see: +? ➕ Include ICU-enabled PHP binary for Filament/intl requirements? (~30MB extra) + > No (default - smaller app size) + Yes (required for Filament and advanced i18n) +``` + +Select **"Yes"** if you need ICU support. + +### After Installation + +If you already installed without ICU and need to add it: + +```bash +# Re-run the installer and select ICU support +php artisan native:install --force +``` + +## Size Considerations + +| PHP Runtime | Size | Use Case | +|-------------|-------|----------| +| Standard (no ICU) | ~7MB | Basic apps, simple localization | +| With ICU | ~34MB | Filament, complex i18n, number formatting | + +The ICU-enabled runtime is approximately **5x larger**, so only enable it if you specifically need these features. + +## Feature Comparison + +### Without ICU (Default) + +```php +// ✅ Works - Basic functionality +$date = now()->format('Y-m-d'); +$number = number_format(1234.56, 2); + +// ❌ Won't work - Requires intl extension +$formatter = new NumberFormatter('en_US', NumberFormatter::CURRENCY); +$collator = new Collator('en_US'); +``` + +### With ICU Enabled + +```php +// ✅ All basic functionality works +$date = now()->format('Y-m-d'); +$number = number_format(1234.56, 2); + +// ✅ Advanced internationalization works +$formatter = new NumberFormatter('en_US', NumberFormatter::CURRENCY); +$price = $formatter->formatCurrency(1234.56, 'USD'); // $1,234.56 + +$collator = new Collator('en_US'); +$result = $collator->compare('apple', 'äpple'); // Proper Unicode comparison +``` + +## Code Examples + +### Number Formatting + +```php +use NumberFormatter; + +class LocalizedNumbers +{ + public function formatCurrency(float $amount, string $locale, string $currency): string + { + if (!extension_loaded('intl')) { + // Fallback for non-ICU builds + return $currency . ' ' . number_format($amount, 2); + } + + $formatter = new NumberFormatter($locale, NumberFormatter::CURRENCY); + return $formatter->formatCurrency($amount, $currency); + } + + public function formatPercent(float $value, string $locale): string + { + if (!extension_loaded('intl')) { + return number_format($value * 100, 1) . '%'; + } + + $formatter = new NumberFormatter($locale, NumberFormatter::PERCENT); + return $formatter->format($value); + } +} + +// Usage +$numbers = new LocalizedNumbers(); + +echo $numbers->formatCurrency(1234.56, 'en_US', 'USD'); // $1,234.56 +echo $numbers->formatCurrency(1234.56, 'de_DE', 'EUR'); // 1.234,56 € +echo $numbers->formatPercent(0.1234, 'en_US'); // 12.3% +``` + +### Date Formatting + +```php +use IntlDateFormatter; + +class LocalizedDates +{ + public function formatDate(\DateTime $date, string $locale): string + { + if (!extension_loaded('intl')) { + return $date->format('M j, Y'); + } + + $formatter = new IntlDateFormatter( + $locale, + IntlDateFormatter::LONG, + IntlDateFormatter::NONE + ); + + return $formatter->format($date); + } +} + +// Usage +$dates = new LocalizedDates(); +$date = new DateTime('2024-03-15'); + +echo $dates->formatDate($date, 'en_US'); // March 15, 2024 +echo $dates->formatDate($date, 'de_DE'); // 15. März 2024 +echo $dates->formatDate($date, 'ja_JP'); // 2024年3月15日 +``` + +### String Collation + +```php +use Collator; + +class LocalizedSorting +{ + public function sortNames(array $names, string $locale): array + { + if (!extension_loaded('intl')) { + // Simple ASCII sort fallback + sort($names); + return $names; + } + + $collator = new Collator($locale); + $collator->sort($names); + return $names; + } +} + +// Usage +$sorter = new LocalizedSorting(); +$names = ['Müller', 'Mueller', 'Miller', 'Möller']; + +$sorted = $sorter->sortNames($names, 'de_DE'); +// Proper German sorting with umlauts +``` + +## Framework Integration + +### Laravel Localization + +```php +// config/app.php +return [ + 'locale' => 'en', + 'available_locales' => ['en', 'es', 'fr', 'de', 'ja'], +]; + +// With ICU support, you can use advanced formatters +class LocalizedContent +{ + public function getLocalizedPrice(float $price): string + { + $locale = app()->getLocale(); + $currency = config('app.currency.' . $locale, 'USD'); + + if (extension_loaded('intl')) { + $formatter = new NumberFormatter($locale, NumberFormatter::CURRENCY); + return $formatter->formatCurrency($price, $currency); + } + + return $currency . ' ' . number_format($price, 2); + } +} +``` + +### Filament Integration + +```php +// Filament requires ICU for proper operation +use Filament\Forms\Components\TextInput; + +TextInput::make('price') + ->numeric() + ->formatStateUsing(function ($state) { + // This formatting requires ICU + $formatter = new NumberFormatter(app()->getLocale(), NumberFormatter::CURRENCY); + return $formatter->formatCurrency($state, 'USD'); + }); +``` + +## Graceful Degradation + +Design your app to work with or without ICU: + +```php +class InternationalizationHelper +{ + public static function hasICU(): bool + { + return extension_loaded('intl'); + } + + public static function formatNumber(float $number, string $locale = 'en_US'): string + { + if (self::hasICU()) { + $formatter = new NumberFormatter($locale, NumberFormatter::DECIMAL); + return $formatter->format($number); + } + + // Fallback formatting + return number_format($number, 2); + } + + public static function compareStrings(string $a, string $b, string $locale = 'en_US'): int + { + if (self::hasICU()) { + $collator = new Collator($locale); + return $collator->compare($a, $b); + } + + // Fallback to simple comparison + return strcmp($a, $b); + } +} +``` + +## Performance Considerations + +### Memory Usage +- ICU adds ~28MB to your app size +- Runtime memory usage increases slightly +- Complex formatting operations are slower + +### Optimization Tips + +```php +class OptimizedFormatting +{ + private static array $formatters = []; + + public static function getCachedFormatter(string $locale, int $style): NumberFormatter + { + $key = $locale . '_' . $style; + + if (!isset(self::$formatters[$key])) { + self::$formatters[$key] = new NumberFormatter($locale, $style); + } + + return self::$formatters[$key]; + } + + public static function formatCurrency(float $amount, string $locale, string $currency): string + { + $formatter = self::getCachedFormatter($locale, NumberFormatter::CURRENCY); + return $formatter->formatCurrency($amount, $currency); + } +} +``` + +## Testing ICU Features + +```php +// tests/Feature/ICUSupportTest.php +namespace Tests\Feature; + +use Tests\TestCase; + +class ICUSupportTest extends TestCase +{ + public function test_icu_extension_loaded() + { + if (config('app.requires_icu')) { + $this->assertTrue(extension_loaded('intl'), 'ICU extension is required but not loaded'); + } + } + + public function test_number_formatting_works() + { + if (!extension_loaded('intl')) { + $this->markTestSkipped('ICU not available'); + } + + $formatter = new \NumberFormatter('en_US', \NumberFormatter::CURRENCY); + $result = $formatter->formatCurrency(1234.56, 'USD'); + + $this->assertEquals('$1,234.56', $result); + } + + public function test_fallback_formatting_works() + { + $helper = new InternationalizationHelper(); + $result = $helper->formatNumber(1234.56); + + $this->assertIsString($result); + $this->assertStringContains('1234', $result); + } +} +``` + +## Decision Matrix + +Use this matrix to decide if you need ICU support: + +| Feature Needed | ICU Required | Alternative | +|----------------|--------------|-------------| +| Basic number formatting | ❌ | `number_format()` | +| Locale-specific currency | ✅ | Manual formatting | +| Date localization | ✅ | Carbon with locales | +| String sorting (non-ASCII) | ✅ | Basic `sort()` | +| Filament admin panel | ✅ | Custom admin | +| Multi-language text processing | ✅ | Limited alternatives | +| Unicode normalization | ✅ | Basic string functions | + +## Best Practices + +1. **Evaluate early** - Decide on ICU support before building your app +2. **Design for fallbacks** - Always provide non-ICU alternatives +3. **Cache formatters** - Reuse NumberFormatter and Collator instances +4. **Test both scenarios** - Test your app with and without ICU +5. **Document requirements** - Clearly state if your app requires ICU +6. **Monitor app size** - Consider the trade-off between features and size +7. **Profile performance** - ICU operations can be slower than simple alternatives + +Choose ICU support if you're building a truly international app, using Filament, or need advanced text processing. For simpler apps, the default lightweight runtime is usually sufficient. diff --git a/resources/views/docs/mobile/1/the-basics/native-functions.md b/resources/views/docs/mobile/1/the-basics/native-functions.md index 3260b32f..f8ec6a08 100644 --- a/resources/views/docs/mobile/1/the-basics/native-functions.md +++ b/resources/views/docs/mobile/1/the-basics/native-functions.md @@ -8,63 +8,13 @@ unique is that it allows you to call native functions from your PHP code. These functions are called from your PHP code using one of an ever-growing list of facades. -Currently, there are two facades available: +All native functionality is namespaced into its own Facade, they are: -- `Native\Mobile\Facades\System` +- `Native\Mobile\Facades\Biometrics` +- `Native\Mobile\Facades\Camera` - `Native\Mobile\Facades\Dialog` - -## System - -The `System` facade is used to call native functions that access system resources. - -For example, you may use the `System::camera()` method to request access to the device's camera. - - -## Synchronous vs. Asynchronous Methods - -It is important to understand the difference between synchronous and asynchronous methods. Some methods -like `flashlight` and `vibrate` are synchronous, meaning that they will block the current thread until the -operation is complete. - -Other methods like `camera` and `biometric` are asynchronous, meaning that they -will return immediately and the operation will be performed in the background. When the operation is -complete, the method will `broadcast an event` to your frontend via an injected javascript event as well -as a traditional [Laravel Event](https://laravel.com/docs/12.x/events#main-content) that you can listen for within your app. - -In order to receive these events, you must register a listener for the event. For example, -take a look at how easy it is to listen for a `PhotoTaken` event in Livewire: - -```php -use Livewire\Attributes\On; -use Livewire\Component; -use Native\Mobile\Facades\System; -use Native\Mobile\Events\Camera\PhotoTaken; - -class Camera extends Component -{ - public string $photoDataUrl = ''; - - public function camera() - { - System::camera(); - } - - #[On('native:' . PhotoTaken::class)] - public function handleCamera($path) - { - $data = base64_encode(file_get_contents($path)); - $mime = mime_content_type($path); - - $this->photoDataUrl = "data:$mime;base64,$data"; - } - - public function render() - { - return view('livewire.system.camera'); - } -} -``` - -All Livewire/front end events are prefixed with `native:` to avoid collisions with other events. -In the following pages we will highlight the available native functions, whether they are asynchronous or not -and which events they fire. +- `Native\Mobile\Facades\Geolocation` +- `Native\Mobile\Facades\Haptics` +- `Native\Mobile\Facades\PushNotifications` +- `Native\Mobile\Facades\SecureStorage` +- `Native\Mobile\Facades\System` diff --git a/resources/views/docs/mobile/1/the-basics/system.md b/resources/views/docs/mobile/1/the-basics/system.md deleted file mode 100644 index 8e87a747..00000000 --- a/resources/views/docs/mobile/1/the-basics/system.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -title: System -order: 400 ---- - -## Native System - -NativePHP allows you to trigger many native system functions. - -System functions are called using the `System` facade. - -```php -use Native\Mobile\Facades\System; -``` ---- - -# Synchronous Functions ---- - -### Vibration - -You may vibrate the user's device by calling the `vibrate` method: - -```php -System::vibrate() -``` ---- -### Flashlight - -You may toggle the device flashlight (on/off) by calling the `flashlight` method: - -```php -System::flashlight() -``` ---- - -# Asynchronous Functions ---- - -### Camera -```php -Front End Event: `native:Native\Mobile\Events\Camera\PhotoTaken` -Back End Event: `Native\Mobile\Events\Camera\PhotoTaken` -``` - -You may request the native camera interface to take a photograph by calling the `System::camera()` method: - -When the user takes a photograph the event is fired with a payload array that contains one item: `path` -which is a string containing the path to the photo. - -```php -use Native\Mobile\Events\Camera\PhotoTaken; - -System::camera(); - -// Later... -#[On('native:' . PhotoTaken::class)] -public function handlePhotoTaken($path) -{ - $data = base64_encode(file_get_contents($path)); - $mime = mime_content_type($path); - - $this->photoDataUrl = "data:$mime;base64,$data"; -} -``` - -**Note: The first time your application asks to use the camera, the user will be prompted to grant permission. If they -decline, triggering the camera API will silently fail.** - ---- - -### Push Notifications -```php -Front End Event: `native:Native\Mobile\Events\PushNotification\TokenGenerated` -Back End Event: `Native\Mobile\Events\PushNotification\TokenGenerated` -``` -Currently, NativePHP uses [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) to send push notifications to your users. - -Simply use the `enrollForPushNotifications` method to trigger enrolment. If this is the first time that your app tries -to enrol this device for push notifications, the user will be presented with a native alert, allowing them to opt-in. - -Then use the `getPushNotificationsToken` method to retrieve the token. If enrolment was unsuccessful for some reason, -this method will return `null`. - -```php -use Native\Mobile\Events\PushNotification\TokenGenerated; - -System::enrollForPushNotifications(); - -// Later... -#[On('native:' . TokenGenerated::class)] -public function handlePushNotifications(string $token) -{ - // Do something with the token... -} -``` -Once you have the token, you may use it from your server-based applications to trigger Push Notifications directly to -your user's device. - -> Learn more about [what to do with push tokens here](/docs/mobile/1/digging-deeper/push-notifications). - ---- - -### Biometric ID -```php -Front End Event: `native:Native\Mobile\Events\Biometric\Completed` -Back End Event: `Native\Mobile\Events\Biometric\Completed` -``` - -For devices that support some form of biometric identification, you can use this to protect and unlock various parts -of your application. - -```php -use Native\Mobile\Events\Biometric\Completed; - -System::promptForBiometricID() - -// Later... -#[On('native:' . Completed::class)] -public function handleBiometricAuth(boolean $success) -{ - if ($success) { - // Do your super secret activity here - } -} -``` - -Using this, you can gate certain parts of your app, allowing you to offer an extra layer of protection for your user's -data. - -**Note: Despite the name, Biometric identification only gives you *greater confidence* that the person using your app -is *someone* who has the capacity to unlock the device your app is installed on. It does not allow you to *identify* -that user or prove that they are willingly taking this action.** - From 678349033935320ac9eb36300edefaab2c9fb477 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Sun, 6 Jul 2025 22:38:23 -0400 Subject: [PATCH 05/16] Resolve merge conflicts with main branch - Merge installation.md: preserve platform requirements from main + Windows performance tip - Update ShowDocumentationController.php with latest changes --- .../ShowDocumentationController.php | 1 - .../mobile/1/getting-started/installation.md | 59 +++++++++++++++---- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/ShowDocumentationController.php b/app/Http/Controllers/ShowDocumentationController.php index d051f302..d4faeba2 100644 --- a/app/Http/Controllers/ShowDocumentationController.php +++ b/app/Http/Controllers/ShowDocumentationController.php @@ -143,7 +143,6 @@ protected function getPageProperties($platform, $version, $page = null): array } } - return $pageProperties; } diff --git a/resources/views/docs/mobile/1/getting-started/installation.md b/resources/views/docs/mobile/1/getting-started/installation.md index 15ce23bd..cf99593e 100644 --- a/resources/views/docs/mobile/1/getting-started/installation.md +++ b/resources/views/docs/mobile/1/getting-started/installation.md @@ -1,6 +1,6 @@ --- title: Installation -order: 150 +order: 100 --- ## Requirements @@ -9,7 +9,42 @@ order: 150 2. Laravel 10 or higher 3. [A NativePHP for Mobile license](https://nativephp.com/mobile) -For platform-specific development environment setup (iOS and Android), see the [Environment Setup](/docs/mobile/1/getting-started/environment-setup) page. +### For iOS +1. An Apple Silicon Mac running macOS 12+ with [Xcode 16+](https://apps.apple.com/app/xcode/id497799835) +2. An active [Apple Developer account](https://developer.apple.com/) +3. _Optional_ iOS device + +You **do not** need to enroll in the [Apple Developer Program](https://developer.apple.com/programs/enroll/) ($99/year) +to develop and test your apps on a Simulator. But you will need to when you want to test your apps on real devices +and start distributing them to your users via the App Store. + +> **Note** You cannot build iOS apps on Windows or Linux + +### For Android +1. [Android Studio Giraffe (or later)](https://developer.android.com/studio) +2. The following environment variables set. +3. You should be able to successfully run `java -v` and `adb devices` from the terminal. +4. **Windows only**: You must have [7zip](https://www.7-zip.org/) installed. + +#### On macOS +```shell +export JAVA_HOME=$(/usr/libexec/java_home -v 17) // This isn't required if JAVA_HOME is already set in your environment variables (check using `printenv | grep JAVA_HOME`) +export ANDROID_HOME=$HOME/Library/Android/sdk +export PATH=$PATH:$JAVA_HOME/bin:$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools +``` + +#### On Windows +```shell +set ANDROID_HOME=C:\Users\yourname\AppData\Local\Android\Sdk +set PATH=%PATH%;%JAVA_HOME%\bin;%ANDROID_HOME%\platform-tools + +# This isn't required if JAVA_HOME is already set in the Windows Env Variables +set JAVA_HOME=C:\Program Files\Microsoft\jdk-17.0.8.7-hotspot +``` + +You don't _need_ a physical iOS/Android device to compile and test your application, as NativePHP for Mobile supports +the iOS Simulator and Android emulators. However, we highly recommend that you test your application on a real device before submitting to the +Apple App Store and Google Play Store. ## Laravel @@ -59,8 +94,6 @@ NATIVEPHP_APP_VERSION="DEBUG" NATIVEPHP_APP_VERSION_CODE="1" ``` -**Important: the NATIVEPHP_APP_ID must not contain any special characters or spaces** - ## Run the NativePHP installer ```shell @@ -69,10 +102,6 @@ php artisan native:install The NativePHP installer takes care of setting up and configuring your Laravel application to work with iOS and Android. -## ICU Support (Android Only) - -If you are wanting to run [Filament](https://filamentphp.com) or use some of the Number or I18n methods within Laravel you will need to install NativePHP with the optional ICU supported binaries, more on that [here](/docs/mobile/1/the-basics/icu-support). - ## Start your app **Heads up!** Before starting your app in a native context, try running it in the browser. You may bump into exceptions @@ -86,6 +115,16 @@ php artisan native:run This will start compiling your application and boot it on whichever device you select. -And that's it! You should now see your Laravel application running as a native app! 🎉 +### Running on a real device + +#### On iOS +If you want to run your app on a real iOS device, you need to make sure it is in +[Developer Mode](https://developer.apple.com/documentation/xcode/enabling-developer-mode-on-a-device) +and that it's been added to your Apple Developer account as +[a registered device](https://developer.apple.com/account/resources/devices/list). + +#### On Android +On Android you need to [enable developer options](https://developer.android.com/studio/debug/dev-options#enable) +and have USB debugging (ADB) enabled. -For information about running on real devices, see the [Environment Setup](/docs/mobile/1/getting-started/environment-setup) page. +And that's it! You should now see your Laravel application running as a native app! 🎉 \ No newline at end of file From cd6f1d0fcee5a3170032d208381938c5e538b416 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Mon, 7 Jul 2025 09:38:14 -0400 Subject: [PATCH 06/16] Update resources/views/docs/mobile/1/apis/camera.md Co-authored-by: Peter Bishop <9081809+PeteBishwhip@users.noreply.github.com> --- resources/views/docs/mobile/1/apis/camera.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/docs/mobile/1/apis/camera.md b/resources/views/docs/mobile/1/apis/camera.md index 3b26c383..cd454df6 100644 --- a/resources/views/docs/mobile/1/apis/camera.md +++ b/resources/views/docs/mobile/1/apis/camera.md @@ -148,7 +148,7 @@ class PhotoManager extends Component ## Platform Support - **iOS:** Uses UIImagePickerController and Photos framework -- **Android:** Uses Intent.ACTION_IMAGE_CAPTURE and gallery intents +- **Android:** Uses `Intent.ACTION_IMAGE_CAPTURE` and gallery intents - **Permissions:** Camera permission required for photo capture - **File Location:** Photos saved to app's temporary directory From 1c0ad4784e25b07e78b4a838c641f9c2f27c3a31 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Mon, 7 Jul 2025 23:16:24 -0400 Subject: [PATCH 07/16] More docs updates --- resources/views/docs/mobile/1/_index.md | 38 ++-- resources/views/docs/mobile/1/apis/_index.md | 136 -------------- .../views/docs/mobile/1/apis/biometrics.md | 2 +- resources/views/docs/mobile/1/apis/camera.md | 4 +- resources/views/docs/mobile/1/apis/dialog.md | 4 +- .../views/docs/mobile/1/apis/geolocation.md | 6 +- resources/views/docs/mobile/1/apis/haptics.md | 11 -- .../views/docs/mobile/1/apis/overview.md | 130 +++++++++++++ .../docs/mobile/1/apis/push-notifications.md | 15 +- .../docs/mobile/1/apis/secure-storage.md | 13 -- resources/views/docs/mobile/1/apis/system.md | 172 +----------------- .../views/docs/mobile/1/concepts/_index.md | 42 ----- 12 files changed, 161 insertions(+), 412 deletions(-) create mode 100644 resources/views/docs/mobile/1/apis/overview.md diff --git a/resources/views/docs/mobile/1/_index.md b/resources/views/docs/mobile/1/_index.md index a6e453c2..68ac888e 100644 --- a/resources/views/docs/mobile/1/_index.md +++ b/resources/views/docs/mobile/1/_index.md @@ -11,11 +11,11 @@ NativePHP for Mobile revolutionizes mobile development by allowing PHP developer ## What Makes NativePHP Mobile Special? -=� **Native Performance** - Your app runs natively on device with embedded PHP runtime -=� **True Mobile APIs** - Access camera, biometrics, push notifications, and more -� **Laravel Powered** - Use your existing Laravel skills and ecosystem -=' **No Web Server** - Your app runs entirely on-device -<� **Cross Platform** - Single codebase for iOS and Android +📱**Native Performance** - Your app runs natively on device with embedded PHP runtime +🔥**True Mobile APIs** - Access camera, biometrics, push notifications, and more +⚡ **Laravel Powered** - Use your existing Laravel skills and ecosystem +🚫**No Web Server** - Your app runs entirely on-device +🔄**Cross Platform** - Single codebase for iOS and Android ## Quick Start @@ -35,19 +35,19 @@ php artisan native:run ## Current Features (v1.1) **Available now:** -- =� Camera & Gallery access -- = Biometric authentication (Face ID, Touch ID, Fingerprint) -- = Push notifications via Firebase -- =� Native dialogs & toasts -- = Deep links & universal links -- =� NFC support -- =� Haptic feedback & vibration -- =& Flashlight control -- =� Native sharing -- = Secure storage (Keychain/Keystore) -- =� Location services - -[See the complete roadmap �](/docs/mobile/1/getting-started/roadmap) +- 📷 Camera & Gallery access +- 🔐 Biometric authentication (Face ID, Touch ID, Fingerprint) +- 🔔 Push notifications via Firebase +- 💬 Native dialogs & toasts +- 🔗 Deep links & universal links +- 📱 NFC support +- 📳 Haptic feedback & vibration +- 🔦 Flashlight control +- 📤 Native sharing +- 🔒 Secure storage (Keychain/Keystore) +- 📍 Location services + +[See the complete roadmap 🗺️](/docs/mobile/1/getting-started/roadmap) ## Documentation Sections @@ -96,4 +96,4 @@ Advanced topics for production apps: - **Community** - Join our Discord for support and discussions - **Examples** - Check out the Kitchen Sink demo app (coming soon to app stores) -Ready to build your first mobile app with PHP? [Let's get started! �](/docs/mobile/1/getting-started/introduction) \ No newline at end of file +Ready to build your first mobile app with PHP? [Let's get started! 🚀](/docs/mobile/1/getting-started/introduction) diff --git a/resources/views/docs/mobile/1/apis/_index.md b/resources/views/docs/mobile/1/apis/_index.md index c2df782c..31293365 100644 --- a/resources/views/docs/mobile/1/apis/_index.md +++ b/resources/views/docs/mobile/1/apis/_index.md @@ -2,139 +2,3 @@ title: APIs order: 4 --- - -# API Reference - -Complete documentation for all NativePHP Mobile APIs. Each API provides access to native device capabilities through familiar PHP facades. - -## Available APIs - -### [Biometrics](/docs/mobile/1/apis/biometrics) -**Face ID, Touch ID, Fingerprint Authentication** -```php -Biometrics::promptForBiometricID(); -``` -Secure user authentication using device biometric sensors. Supports Face ID on iOS, Touch ID, and fingerprint readers on Android. - -### [Camera](/docs/mobile/1/apis/camera) -**Photo Capture & Gallery Access** -```php -Camera::getPhoto(); -Camera::pickImages('images', true, 5); -``` -Take photos with the device camera or select images from the photo gallery. Supports both single and multiple image selection. - -### [Dialog](/docs/mobile/1/apis/dialog) -**Native UI Elements** -```php -Dialog::alert('Title', 'Message', $buttons, $callback); -Dialog::toast('Success message'); -Dialog::share('Title', 'Text', 'https://example.com'); -``` -Display native alerts, toast notifications, and sharing interfaces that match platform design guidelines. - -### [Geolocation](/docs/mobile/1/apis/geolocation) ⭐ New in v1.1 -**GPS & Location Services** -```php -Geolocation::getCurrentPosition(true); // High accuracy -Geolocation::checkPermissions(); -Geolocation::requestPermissions(); -``` -Access device location services with configurable accuracy levels and proper permission handling. - -### [Haptics](/docs/mobile/1/apis/haptics) -**Vibration & Tactile Feedback** -```php -Haptics::vibrate(); -``` -Provide tactile feedback for user interactions, form validation, and important events. - -### [PushNotifications](/docs/mobile/1/apis/push-notifications) -**Firebase Cloud Messaging** -```php -PushNotifications::enrollForPushNotifications(); -PushNotifications::getPushNotificationsToken(); -``` -Register devices for push notifications and manage FCM tokens for server-side notification delivery. - -### [SecureStorage](/docs/mobile/1/apis/secure-storage) ⭐ New in v1.1 -**Keychain & Keystore Operations** -```php -SecureStorage::set('api_token', $token); -$token = SecureStorage::get('api_token'); -SecureStorage::delete('api_token'); -``` -Store sensitive data securely using iOS Keychain and Android Keystore with automatic encryption. - -### [System](/docs/mobile/1/apis/system) -**System Functions & Legacy API** -```php -System::flashlight(); // Toggle flashlight -``` -Control system functions like the flashlight. Also provides deprecated methods that have moved to dedicated facades. - -## API Patterns - -### Synchronous APIs -Execute immediately and return results: -- `Haptics::vibrate()` -- `System::flashlight()` -- `Dialog::toast()` -- `SecureStorage::set()` / `get()` - -### Asynchronous APIs -Trigger operations and fire events when complete: -- `Camera::getPhoto()` → `PhotoTaken` event -- `Biometrics::promptForBiometricID()` → `Completed` event -- `PushNotifications::enrollForPushNotifications()` → `TokenGenerated` event -- `Geolocation::getCurrentPosition()` → `LocationReceived` event - -### Event Handling -All async APIs use Laravel events with Livewire integration: - -```php -use Livewire\Attributes\On; -use Native\Mobile\Events\Camera\PhotoTaken; - -#[On('native:' . PhotoTaken::class)] -public function handlePhotoTaken(string $path) -{ - // Process the captured photo -} -``` - -## Migration from System Facade - -Many methods have moved from the `System` facade to dedicated facades in v1.1: - -| Old (Deprecated) | New (Recommended) | -|------------------|-------------------| -| `System::camera()` | `Camera::getPhoto()` | -| `System::vibrate()` | `Haptics::vibrate()` | -| `System::promptForBiometricID()` | `Biometrics::promptForBiometricID()` | -| `System::enrollForPushNotifications()` | `PushNotifications::enrollForPushNotifications()` | -| `System::secureSet()` / `secureGet()` | `SecureStorage::set()` / `get()` | - -The old methods still work but are deprecated. See the [System API documentation](/docs/mobile/1/apis/system) for complete migration guidance. - -## Platform Support - -All APIs work on both iOS and Android with platform-appropriate implementations: -- **iOS**: Uses native iOS frameworks and APIs -- **Android**: Uses Android SDK and native libraries -- **Permissions**: Automatically handled with user prompts when required -- **Fallbacks**: Graceful degradation when features aren't available - -## Error Handling - -APIs provide both success and error events for proper error handling: - -```php -#[On('native:' . PhotoTaken::class)] -public function handleSuccess($data) { /* ... */ } - -#[On('native:' . PermissionDenied::class)] -public function handleError($error) { /* ... */ } -``` - -Each API documentation includes complete error handling examples and best practices. \ No newline at end of file diff --git a/resources/views/docs/mobile/1/apis/biometrics.md b/resources/views/docs/mobile/1/apis/biometrics.md index 4260d24d..e3f4ed2e 100644 --- a/resources/views/docs/mobile/1/apis/biometrics.md +++ b/resources/views/docs/mobile/1/apis/biometrics.md @@ -19,7 +19,7 @@ Prompts the user for biometric authentication. ## Events -### `Native\Mobile\Events\Biometric\Completed` +### `Completed` Fired when biometric authentication completes (success or failure). diff --git a/resources/views/docs/mobile/1/apis/camera.md b/resources/views/docs/mobile/1/apis/camera.md index cd454df6..da2f750c 100644 --- a/resources/views/docs/mobile/1/apis/camera.md +++ b/resources/views/docs/mobile/1/apis/camera.md @@ -44,7 +44,7 @@ Camera::pickImages('all', true); ## Events -### `Native\Mobile\Events\Camera\PhotoTaken` +### `PhotoTaken` Fired when a photo is taken with the camera. @@ -62,7 +62,7 @@ public function handlePhotoTaken(string $path) } ``` -### `Native\Mobile\Events\Gallery\MediaSelected` +### `MediaSelected` Fired when media is selected from the gallery. diff --git a/resources/views/docs/mobile/1/apis/dialog.md b/resources/views/docs/mobile/1/apis/dialog.md index b75420f4..59b958a5 100644 --- a/resources/views/docs/mobile/1/apis/dialog.md +++ b/resources/views/docs/mobile/1/apis/dialog.md @@ -67,7 +67,7 @@ Dialog::share( ## Events -### `Native\Mobile\Events\Alert\ButtonPressed` +### `ButtonPressed` Fired when a button is pressed in an alert dialog. @@ -246,4 +246,4 @@ Dialog::share( - All dialogs automatically support screen readers - Button text should be descriptive - Toast messages are announced by accessibility services -- Consider users with motor disabilities when designing button layouts \ No newline at end of file +- Consider users with motor disabilities when designing button layouts diff --git a/resources/views/docs/mobile/1/apis/geolocation.md b/resources/views/docs/mobile/1/apis/geolocation.md index e7d7f631..308ac08c 100644 --- a/resources/views/docs/mobile/1/apis/geolocation.md +++ b/resources/views/docs/mobile/1/apis/geolocation.md @@ -52,7 +52,7 @@ Geolocation::requestPermissions(); ## Events -### `Geolocation\LocationReceived` +### `LocationReceived` Fired when location data is requested (success or failure). @@ -93,7 +93,7 @@ public function handleLocationReceived($success = null, $latitude = null, $longi } ``` -### `Geolocation\PermissionStatusReceived` +### `PermissionStatusReceived` Fired when permission status is checked. @@ -128,7 +128,7 @@ public function handlePermissionStatus($location, $coarseLocation, $fineLocation } ``` -### `Geolocation\PermissionRequestResult` +### `PermissionRequestResult` Fired when a permission request completes. diff --git a/resources/views/docs/mobile/1/apis/haptics.md b/resources/views/docs/mobile/1/apis/haptics.md index 5ec54ef3..6cc1e290 100644 --- a/resources/views/docs/mobile/1/apis/haptics.md +++ b/resources/views/docs/mobile/1/apis/haptics.md @@ -78,14 +78,3 @@ class HapticsExample extends Component **Use haptics for:** Button presses, form validation, important notifications, game events **Avoid haptics for:** Frequent events, background processes, minor updates -## Migration from System Facade - -```php -// Old way (deprecated) -use Native\Mobile\Facades\System; -System::vibrate(); - -// New way (recommended) -use Native\Mobile\Facades\Haptics; -Haptics::vibrate(); -``` \ No newline at end of file diff --git a/resources/views/docs/mobile/1/apis/overview.md b/resources/views/docs/mobile/1/apis/overview.md new file mode 100644 index 00000000..234fdbdb --- /dev/null +++ b/resources/views/docs/mobile/1/apis/overview.md @@ -0,0 +1,130 @@ +--- +title: Overview +order: 1 +--- +# API Reference + +Complete documentation for all NativePHP Mobile APIs. Each API provides access to native device capabilities through familiar PHP facades. + +## Available APIs + +### Biometrics +**Face ID, Touch ID, Fingerprint Authentication** +```php +Biometrics::promptForBiometricID(); +``` +Secure user authentication using device biometric sensors. Supports Face ID on iOS, Touch ID, and fingerprint readers on Android. + +### Camera +**Photo Capture & Gallery Access** +```php +Camera::getPhoto(); +Camera::pickImages('images', true, 5); +``` +Take photos with the device camera or select images from the photo gallery. Supports both single and multiple image selection. + +### Dialog +**Native UI Elements** +```php +Dialog::alert('Title', 'Message', $buttons, $callback); +Dialog::toast('Success message'); +Dialog::share('Title', 'Text', 'https://example.com'); +``` +Display native alerts, toast notifications, and sharing interfaces that match platform design guidelines. + +### Geolocation +**GPS & Location Services** +```php +Geolocation::getCurrentPosition(true); // High accuracy +Geolocation::checkPermissions(); +Geolocation::requestPermissions(); +``` +Access device location services with configurable accuracy levels and proper permission handling. + +### Haptics +**Vibration & Tactile Feedback** +```php +Haptics::vibrate(); +``` +Provide tactile feedback for user interactions, form validation, and important events. + +### PushNotifications +**Firebase Cloud Messaging** +```php +PushNotifications::enrollForPushNotifications(); +PushNotifications::getPushNotificationsToken(); +``` +Register devices for push notifications and manage FCM tokens for server-side notification delivery. + +### SecureStorage +**Keychain & Keystore Operations** +```php +SecureStorage::set('api_token', $token); +$token = SecureStorage::get('api_token'); +SecureStorage::delete('api_token'); +``` +Store sensitive data securely using iOS Keychain and Android Keystore with automatic encryption. + +### System +**System Functions & Legacy API** +```php +System::flashlight(); // Toggle flashlight +``` +Control system functions like the flashlight. Also provides deprecated methods that have moved to dedicated facades. + +## API Patterns + +### Synchronous APIs +Execute immediately and return results: +- `Haptics::vibrate()` +- `System::flashlight()` +- `Dialog::toast()` +- `SecureStorage::set()` / `get()` + +### Asynchronous APIs +Trigger operations and fire events when complete: +- `Camera::getPhoto()` → `PhotoTaken` event +- `Camera::pickImages()` → `MediaSelected` event +- `Biometrics::promptForBiometricID()` → `Completed` event +- `PushNotifications::enrollForPushNotifications()` → `TokenGenerated` event +- `Geolocation::getCurrentPosition()` → `LocationReceived` event +- `Geolocation::checkPermissions()` → `PermissionStatusReceived` event +- `Geolocation::requestPermissions()` → `PermissionRequestResult` event +- `Dialog::alert()` → `ButtonPressed` event + +### Event Handling +All async APIs use Laravel events with Livewire integration: + +```php +use Livewire\Attributes\On; +use Native\Mobile\Events\Camera\PhotoTaken; + +#[On('native:' . PhotoTaken::class)] +public function handlePhotoTaken(string $path) +{ + // Process the captured photo +} +``` + + +## Platform Support + +All APIs work on both iOS and Android with platform-appropriate implementations: +- **iOS**: Uses native iOS frameworks and APIs +- **Android**: Uses Android SDK and native libraries +- **Permissions**: Automatically handled with user prompts when required +- **Fallbacks**: Graceful degradation when features aren't available + +## Error Handling + +APIs provide both success and error events for proper error handling: + +```php +#[On('native:' . PhotoTaken::class)] +public function handleSuccess($data) { /* ... */ } + +#[On('native:' . PermissionDenied::class)] +public function handleError($error) { /* ... */ } +``` + +Each API documentation includes complete error handling examples and best practices. diff --git a/resources/views/docs/mobile/1/apis/push-notifications.md b/resources/views/docs/mobile/1/apis/push-notifications.md index 1040a457..504e7124 100644 --- a/resources/views/docs/mobile/1/apis/push-notifications.md +++ b/resources/views/docs/mobile/1/apis/push-notifications.md @@ -42,7 +42,7 @@ if ($token) { ## Events -#### `Native\Mobile\Events\PushNotification\TokenGenerated` +### `TokenGenerated` Fired when a push notification token is successfully generated. @@ -386,19 +386,6 @@ public function handleRegistrationFailure() } ``` -## Migration from System Facade - -```php -// Old way (deprecated) -use Native\Mobile\Facades\System; -System::enrollForPushNotifications(); -$token = System::getPushNotificationsToken(); - -// New way (recommended) -use Native\Mobile\Facades\PushNotifications; -PushNotifications::enrollForPushNotifications(); -$token = PushNotifications::getPushNotificationsToken(); -``` ## Best Practices diff --git a/resources/views/docs/mobile/1/apis/secure-storage.md b/resources/views/docs/mobile/1/apis/secure-storage.md index 74f38826..497fbdc5 100644 --- a/resources/views/docs/mobile/1/apis/secure-storage.md +++ b/resources/views/docs/mobile/1/apis/secure-storage.md @@ -273,16 +273,3 @@ public function storeSecurely(string $key, string $value) } ``` -## Migration from System Facade - -If you were using the deprecated `System::secureSet()` and `System::secureGet()` methods: - -```php -// Old way (deprecated) -System::secureSet('key', 'value'); -$value = System::secureGet('key'); - -// New way (recommended) -SecureStorage::set('key', 'value'); -$value = SecureStorage::get('key'); -``` \ No newline at end of file diff --git a/resources/views/docs/mobile/1/apis/system.md b/resources/views/docs/mobile/1/apis/system.md index 3d677fee..aec383fd 100644 --- a/resources/views/docs/mobile/1/apis/system.md +++ b/resources/views/docs/mobile/1/apis/system.md @@ -5,13 +5,13 @@ order: 800 ## Overview -The System API provides access to basic system functions and serves as a legacy interface for methods that have been moved to dedicated facades in v1.1. +The System API provides access to basic system functions like flashlight control. ```php use Native\Mobile\Facades\System; ``` -## Current Methods +## Methods ### `flashlight()` @@ -23,78 +23,6 @@ Toggles the device flashlight (camera flash LED) on and off. System::flashlight(); // Toggle flashlight state ``` -## Deprecated Methods (v1.1+) - -The following methods are deprecated and have been moved to dedicated facades for better organization: - -### ~~`camera()`~~ → Use `Camera::getPhoto()` - -```php -// ❌ Deprecated (still works but not recommended) -$path = System::camera(); - -// ✅ Use instead -use Native\Mobile\Facades\Camera; -$path = Camera::getPhoto(); -``` - -### ~~`vibrate()`~~ → Use `Haptics::vibrate()` - -```php -// ❌ Deprecated (still works but not recommended) -System::vibrate(); - -// ✅ Use instead -use Native\Mobile\Facades\Haptics; -Haptics::vibrate(); -``` - -### ~~`promptForBiometricID()`~~ → Use `Biometrics::promptForBiometricID()` - -```php -// ❌ Deprecated (still works but not recommended) -$result = System::promptForBiometricID(); - -// ✅ Use instead -use Native\Mobile\Facades\Biometrics; -$result = Biometrics::promptForBiometricID(); -``` - -### ~~`enrollForPushNotifications()`~~ → Use `PushNotifications::enrollForPushNotifications()` - -```php -// ❌ Deprecated (still works but not recommended) -System::enrollForPushNotifications(); - -// ✅ Use instead -use Native\Mobile\Facades\PushNotifications; -PushNotifications::enrollForPushNotifications(); -``` - -### ~~`getPushNotificationsToken()`~~ → Use `PushNotifications::getPushNotificationsToken()` - -```php -// ❌ Deprecated (still works but not recommended) -$token = System::getPushNotificationsToken(); - -// ✅ Use instead -use Native\Mobile\Facades\PushNotifications; -$token = PushNotifications::getPushNotificationsToken(); -``` - -### ~~`secureSet()` / `secureGet()`~~ → Use `SecureStorage` - -```php -// ❌ Deprecated (still works but not recommended) -System::secureSet('key', 'value'); -$value = System::secureGet('key'); - -// ✅ Use instead -use Native\Mobile\Facades\SecureStorage; -SecureStorage::set('key', 'value'); -$value = SecureStorage::get('key'); -``` - ## Example Usage ```php @@ -118,104 +46,10 @@ class FlashlightController extends Component } ``` -## Migration Guide - -If you're upgrading from an earlier version of NativePHP Mobile, here's how to migrate your code: - -### Step 1: Update Import Statements - -```php -// Before -use Native\Mobile\Facades\System; - -// After (add the specific facades you need) -use Native\Mobile\Facades\System; // Still needed for flashlight -use Native\Mobile\Facades\Camera; // For camera operations -use Native\Mobile\Facades\Haptics; // For vibration -use Native\Mobile\Facades\Biometrics; // For biometric auth -use Native\Mobile\Facades\PushNotifications; // For push notifications -use Native\Mobile\Facades\SecureStorage; // For secure storage -``` - -### Step 2: Replace Method Calls - -```php -class MigratedComponent extends Component -{ - public function oldWay() - { - // ❌ Old approach - System::vibrate(); - $photo = System::camera(); - $biometric = System::promptForBiometricID(); - System::enrollForPushNotifications(); - $token = System::getPushNotificationsToken(); - System::secureSet('key', 'value'); - $value = System::secureGet('key'); - } - - public function newWay() - { - // ✅ New approach - Haptics::vibrate(); - $photo = Camera::getPhoto(); - $biometric = Biometrics::promptForBiometricID(); - PushNotifications::enrollForPushNotifications(); - $token = PushNotifications::getPushNotificationsToken(); - SecureStorage::set('key', 'value'); - $value = SecureStorage::get('key'); - } -} -``` - -### Step 3: Update Event Listeners - -Event names remain the same, but you may want to update your code organization: - -```php -use Native\Mobile\Events\Camera\PhotoTaken; -use Native\Mobile\Events\Biometric\Completed; -use Native\Mobile\Events\PushNotification\TokenGenerated; - -class ModernComponent extends Component -{ - // Events work the same way, just organized better - #[On('native:' . PhotoTaken::class)] - public function handlePhoto($path) { /* ... */ } - - #[On('native:' . Completed::class)] - public function handleBiometric($success) { /* ... */ } - - #[On('native:' . TokenGenerated::class)] - public function handleToken($token) { /* ... */ } -} -``` - ## Platform Support ### Flashlight - **iOS:** Controls camera flash LED - **Android:** Controls camera flash LED - **Permissions:** None required -- **Limitations:** May not work if camera is currently in use - -## Future Deprecation Notice - -The System facade will continue to exist for backward compatibility, but new features will be added to the dedicated facades. We recommend migrating to the new facades when convenient to take advantage of: - -- Better code organization -- Clearer API surface -- Enhanced features in dedicated facades -- Better IDE autocompletion and documentation - -## Why the Change? - -The original System facade became too large and mixed different concerns. The new structure provides: - -- **Better organization:** Related methods grouped together -- **Clearer purpose:** Each facade has a single responsibility -- **Enhanced features:** New facades can offer richer APIs -- **Better maintenance:** Easier to add features and fix bugs -- **Improved documentation:** Each API can be documented thoroughly - -The migration improves code clarity and makes the NativePHP Mobile API more intuitive for new developers. \ No newline at end of file +- **Limitations:** May not work if camera is currently in use \ No newline at end of file diff --git a/resources/views/docs/mobile/1/concepts/_index.md b/resources/views/docs/mobile/1/concepts/_index.md index 8499a7d4..97e3b3a0 100644 --- a/resources/views/docs/mobile/1/concepts/_index.md +++ b/resources/views/docs/mobile/1/concepts/_index.md @@ -2,45 +2,3 @@ title: Concepts order: 3 --- - -# Concepts - -Understanding these key concepts will help you build better mobile applications with NativePHP. This section covers important topics that apply across different APIs and features. - -## Mobile-Specific Concepts - -### [CI/CD](/docs/mobile/1/concepts/ci-cd) -**Continuous Integration & Deployment** - Automate your mobile app build and release process with GitHub Actions, GitLab CI, and other platforms. Learn how to set up automated testing, building, and app store deployment. - -### [Deep Links](/docs/mobile/1/concepts/deep-links) -**App Integration** - Connect your app with the mobile ecosystem using deep links, universal links (iOS), app links (Android), and NFC. Enable users to open your app from URLs, other apps, and NFC tags. - -### [Push Notifications](/docs/mobile/1/concepts/push-notifications) -**Real-time Engagement** - Set up Firebase Cloud Messaging to send push notifications to your users. Learn about token management, server-side integration, and notification best practices. - -### [Splash Screen/Icons](/docs/mobile/1/concepts/splash-screen-icons) -**App Branding** - Create professional app icons and splash screens. Understand platform requirements, asset optimization, and how to provide a great first impression. - -### [Versioning](/docs/mobile/1/concepts/versioning) -**Release Management** - Manage app versions across development, staging, and production environments. Learn about semantic versioning, app store requirements, and automated version management. - -## Development Workflow - -These concepts help you establish efficient development practices: - -- **Automated Building** - Set up CI/CD pipelines for consistent builds -- **Asset Management** - Optimize icons, images, and compiled assets -- **Version Control** - Track releases and manage updates -- **User Engagement** - Keep users connected with push notifications -- **App Discovery** - Make your app accessible through links and NFC - -## Cross-Platform Considerations - -NativePHP abstracts many platform differences, but understanding these concepts helps you: - -- Design for both iOS and Android users -- Handle platform-specific requirements -- Optimize for different screen sizes and capabilities -- Meet app store guidelines and requirements - -Each concept includes practical examples, best practices, and real-world implementation guidance to help you build professional mobile applications. \ No newline at end of file From 033fd9741df9e2c7e452e382dc77167045ccb829 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Wed, 9 Jul 2025 09:00:10 -0400 Subject: [PATCH 08/16] Update mobile documentation with enhanced deep links, push notifications, and database seeding - Enhanced deep links documentation with comprehensive platform-specific setup, domain verification, and NFC integration - Expanded push notifications docs with FCM implementation, platform-specific configurations, and data payload handling - Added database seeding section explaining how to use migrations for initial data setup - Updated various documentation files with improved clarity and additional configuration examples - Fixed broken links and improved navigation structure --- .../views/docs/mobile/1/concepts/databases.md | 52 ++++ .../docs/mobile/1/concepts/deep-links.md | 163 +++++++++- .../mobile/1/concepts/push-notifications.md | 217 ++++++++++++- .../docs/mobile/1/concepts/versioning.md | 291 ------------------ .../docs/mobile/1/getting-started/roadmap.md | 4 +- .../mobile/1/getting-started/versioning.md | 171 ++++++++++ .../docs/mobile/1/the-basics/deep-links.md | 109 ------- .../docs/mobile/1/the-basics/icu-support.md | 117 ------- .../mobile/1/the-basics/native-functions.md | 3 +- 9 files changed, 578 insertions(+), 549 deletions(-) delete mode 100644 resources/views/docs/mobile/1/concepts/versioning.md create mode 100644 resources/views/docs/mobile/1/getting-started/versioning.md delete mode 100644 resources/views/docs/mobile/1/the-basics/deep-links.md diff --git a/resources/views/docs/mobile/1/concepts/databases.md b/resources/views/docs/mobile/1/concepts/databases.md index 0d485d51..df70557b 100644 --- a/resources/views/docs/mobile/1/concepts/databases.md +++ b/resources/views/docs/mobile/1/concepts/databases.md @@ -27,6 +27,58 @@ upon foreign key constraints, [you need to enable SQLite support for them](https **It's important to test your migrations on prod builds before releasing updates!** You don't want to accidentally delete your user's data when they update your app. +## Seeding Data with Migrations + +Migrations are the perfect mechanism for seeding data in mobile applications. They provide the natural behavior you want for data seeding: + +- **Run once**: Each migration runs exactly once per device +- **Tracked**: Laravel tracks which migrations have been executed +- **Versioned**: New app versions can include new data seeding migrations +- **Reversible**: You can create migrations to remove or update seed data + +### Creating Seed Migrations + +Create dedicated migrations for seeding data: + +```bash +php artisan make:migration seed_default_categories +php artisan make:migration seed_app_settings +php artisan make:migration seed_initial_user_data +``` + +### Example: Seeding Default Categories + +```php +use Illuminate\Database\Migrations\Migration; +use Illuminate\Support\Facades\DB; + +return new class extends Migration +{ + public function up() + { + DB::table('categories')->insert([ + ['name' => 'Work', 'color' => '#3B82F6'], + ['name' => 'Personal', 'color' => '#10B981'], + ]); + } + + public function down() + { + // Rollback/fresh migrations never run + } +}; +``` + +### Best Practices for Seed Migrations + +1. **Use specific timestamps**: Name migrations with dates to ensure proper ordering +2. **Check before inserting**: Prevent duplicate data with updateOrCreate() or firstOrCreate() +3. **Handle conflicts gracefully**: Check if data already exists before seeding +4. **Use realistic data**: Seed with data that matches your production environment +5. **Test thoroughly**: Verify seed migrations work on fresh installs and updates + +This approach ensures your mobile app has the initial data it needs while maintaining consistency across different app versions and user devices. + ## Things to note - As your app is installed on a separate device, you do not have remote access to the database. diff --git a/resources/views/docs/mobile/1/concepts/deep-links.md b/resources/views/docs/mobile/1/concepts/deep-links.md index 6a0f91ec..12912676 100644 --- a/resources/views/docs/mobile/1/concepts/deep-links.md +++ b/resources/views/docs/mobile/1/concepts/deep-links.md @@ -12,7 +12,7 @@ There are two types of link integrations you can configure: - **Deep Links** (myapp://some/path) - **Universal Links (iOS)** and **App Links (Android)** (https://example.net/some/path) -Each method has its use case, and NativePHP allows you to configure and handle both easily. +Each method has its use case, and NativePHP handles all the platform-specific configuration automatically when you provide the proper environment variables. --- @@ -26,7 +26,6 @@ For example: myapp://profile/123 ``` - When a user taps a deep link, the mobile operating system detects the custom scheme and opens your app directly. ### Configuration @@ -43,6 +42,12 @@ NATIVEPHP_DEEPLINK_SCHEME=myapp NATIVEPHP_DEEPLINK_HOST=open ``` +### Platform Behavior + +- **iOS**: Deep links work immediately after installation +- **Android**: Deep links work immediately after installation +- **Both**: Apps will open directly when deep links are tapped + ## Universal Links (iOS) and App Links (Android) Universal Links and App Links allow real HTTPS URLs to open your app instead of a web browser, if the app is installed. @@ -52,21 +57,25 @@ For example: https://example.net/property/456 ``` +### User Experience + When a user taps this link: - - If your app is installed, it opens directly into the app. - - If not, it opens normally in the browser. +- **If your app is installed**: It opens directly into the app +- **If not installed**: It opens normally in the browser +- **Seamless fallback**: No broken experience for users without the app This provides a seamless user experience without needing a custom scheme. ### How It Works -1. You must prove to iOS and Android that you own the domain by hosting a special file: - - .well-known/apple-app-site-association (for iOS) - - .well-known/assetlinks.json (for Android) -2. The mobile OS reads these files to verify the link association. -3. Once verified, tapping a real URL will open your app instead of Safari or Chrome. -NativePHP for Mobile handles all of this for you. +1. You must prove to iOS and Android that you own the domain by hosting special files: + - `.well-known/apple-app-site-association` (for iOS) + - `.well-known/assetlinks.json` (for Android) +2. The mobile OS reads these files to verify the link association +3. Once verified, tapping a real URL will open your app instead of Safari or Chrome + +**NativePHP handles all the technical setup automatically** - you just need to host the verification files and configure your domain. ### Configuration @@ -80,6 +89,87 @@ These are configured in your .env: NATIVEPHP_DEEPLINK_HOST=example.net ``` +#### Complete Configuration Example + +```dotenv +# For both deep links and universal/app links +NATIVEPHP_DEEPLINK_SCHEME=myapp +NATIVEPHP_DEEPLINK_HOST=example.net + +# Your app will respond to: +# myapp://profile/123 (deep link) +# https://example.net/profile/123 (universal/app link) +``` + +## Domain Verification + +### Required Files + +The app stores generate the content for these files, but you must host them on your domain: + +#### iOS - `.well-known/apple-app-site-association` + +```json +{ + "applinks": { + "details": [ + { + "appIDs": ["TEAM_ID.com.yourcompany.yourapp"], + "components": [ + { + "*": "*" + } + ] + } + ] + } +} +``` + +#### Android - `.well-known/assetlinks.json` + +```json +[{ + "relation": ["delegate_permission/common.handle_all_urls"], + "target": { + "namespace": "android_app", + "package_name": "com.yourcompany.yourapp", + "sha256_cert_fingerprints": ["SHA256_FINGERPRINT"] + } +}] +``` + +### Verification Process + +1. **iOS**: Apple's servers periodically check the `apple-app-site-association` file +2. **Android**: Google Play Services verifies the `assetlinks.json` file +3. **Both**: Files must be accessible via HTTPS without redirects +4. **Content-Type**: Serve files as `application/json` + +### Automatic Configuration + +NativePHP automatically: +- Configures iOS `associatedDomains` in your app +- Sets up Android `intentFilters` for your domain +- Generates the correct fingerprints and app IDs +- Handles platform-specific URL routing + +## Platform-Specific Behavior + +### iOS Universal Links + +- **Immediate**: Work as soon as the app is installed +- **Smart Banner**: iOS can display an install banner if app isn't installed +- **Fallback**: Opens in Safari if app isn't installed +- **Cross-app**: Work from any app, not just Safari + +### Android App Links + +- **Verification**: Requires domain verification before working +- **Default**: Can be set as default handler for domain links +- **Fallback**: Opens in Chrome if app isn't installed +- **Intent**: Uses Android's Intent system for routing + ## Handling Universal/App Links Once you've configured your deep link settings, you can handle the link in your app. @@ -93,16 +183,67 @@ https://example.net/profile/123 ```php Route::get('/profile/{id}', function ($id) { // Handle the deep link + // This works for both deep links and universal/app links }); ``` +## Testing and Development + +### Testing Limitations + +- **Development builds**: Universal/App Links may not work in development +- **Production required**: Full testing requires production builds and domain verification +- **Simulator**: iOS Simulator may not handle Universal Links correctly + +### Best Practices + +1. **Test both link types** - Ensure deep links and universal/app links work +2. **Verify domain files** - Check that .well-known files are accessible +3. **Production testing** - Test universal/app links with production builds +4. **Fallback handling** - Ensure your website handles users without the app +5. **Analytics tracking** - Monitor which link types are most effective + ## NFC + NFC is a technology that allows you to read and write NFC tags. NativePHP handles NFC tag "bumping" just like a Universal/App Link. -You can use a tool like [NFC Tools](https://www.wakdev.com/en/) to test write NFC tags. +You can use a tool like [NFC Tools](https://www.wakdev.com/en/) to write NFC tags. Set the url to a Universal/App Link and the tag will be written to the NFC tag. "Bumping" the tag will open the app. +### NFC Configuration + +NFC tags work best with Universal/App Links because: +- They provide fallback to website if app isn't installed +- They work across different devices and platforms +- They provide a better user experience than custom schemes + +```bash +# Write this URL to an NFC tag +https://example.net/product/456 + +# When "bumped": +# - Opens your app if installed +# - Opens website if not installed +``` + +## Troubleshooting + +### Common Issues + +1. **Universal Links not working**: Check domain verification files +2. **Deep links not opening**: Verify URL scheme configuration +3. **Wrong app opening**: Check for conflicting URL schemes +4. **iOS Smart Banner**: Ensure proper app store metadata + +### Debug Steps + +1. **Verify .env configuration** - Check scheme and host values +2. **Test deep links first** - Easier to debug than universal links +3. **Check domain files** - Ensure .well-known files are accessible +4. **Use production builds** - Development builds may not work correctly +5. **Monitor app logs** - Check for link handling errors +Remember that NativePHP handles all the complex platform-specific setup automatically - you just need to configure your domain and environment variables correctly. diff --git a/resources/views/docs/mobile/1/concepts/push-notifications.md b/resources/views/docs/mobile/1/concepts/push-notifications.md index fd534659..ef65a38e 100644 --- a/resources/views/docs/mobile/1/concepts/push-notifications.md +++ b/resources/views/docs/mobile/1/concepts/push-notifications.md @@ -1,27 +1,53 @@ --- -title: Push Notifications - Firebase +title: Push Notifications order: 400 --- - ## Overview -NativePHP for Mobile uses [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) to send push notifications to your users. +NativePHP for Mobile uses Firebase Cloud Messaging (FCM) to send push notifications to your users on both iOS and Android devices. + +**Cross-Platform Support**: FCM is the unified push notification service for both platforms. For iOS devices, FCM automatically routes notifications through Apple Push Notification Service (APNS) behind the scenes - you don't need to configure APNS separately. + +**Supported Services**: NativePHP only supports FCM. Other push notification services are not supported. ## Setting up your app -Once you create a Firebase account and create a project you will be offered to download a `google-services.json` file. +### Firebase Project Setup + +1. Create a Firebase account and create a project +2. You will be offered to download a `google-services.json` file for your app configuration +3. This file contains the configuration for your app and is used by the Firebase SDK to retrieve tokens for each device -This file contains the configuration for your app and is used by the Firebase SDK to retrieve tokens for each device using your app. +### Service Account Setup -Simply drag this file into the root of your Laravel project, enable `push_notifications` in the [config](/docs/mobile/1/getting-started/configuration) it will be used automatically. +For server-side notifications, you'll also need a Firebase service account: + +1. Go to your Firebase Console → Project Settings → Service Accounts +2. Click "Generate New Private Key" to download the service account JSON file +3. Save this file as `fcm-service-account.json` in your Laravel project's `public` directory + +### NativePHP Configuration + +Simply drag the `google-services.json` file into the root of your Laravel project, enable `push_notifications` in the config and it will be used automatically. You will see more instructions on how to configure your app in the Firebase documentation, you can ignore all of those, NativePHP handles all of that for you. ## Receiving Push Tokens -To receive push notifications, you must register a listener for the event. For example, -take a look at how easy it is to listen for a `TokenGenerated` event in Livewire: +### Token Management + +FCM tokens are unique identifiers for each app installation. These tokens can change when: +- The app is restored on a new device +- The app data is restored from backup +- The app is updated on Android +- Other internal FCM operations + +You should store both the FCM token and platform information for each user device. + +### Listening for Tokens + +To receive push notifications, you must register a listener for the event. For example, take a look at how easy it is to listen for a `TokenGenerated` event in Livewire: ```php use Livewire\Attributes\On; @@ -44,10 +70,9 @@ class PushNotifications extends Component } ``` -Because of the nature of mobile applications you need an api server to handle these tokens. You can use Laravel's built-in `Http` facade to -`POST` the token to your server, on the server side you need to associate the token with the "user" that owns the device. +Because of the nature of mobile applications you need an api server to handle these tokens. You can use Laravel's built-in `Http` facade to `POST` the token to your server, on the server side you need to associate the token with the "user" that owns the device. -We **strongly** recommend using [Sanctum](https://laravel.com/docs/12.x/sanctum#main-content) to handle this for you. +We **strongly** recommend using [Sanctum](https://laravel.com/docs/sanctum) to handle this for you. ## The flow @@ -57,16 +82,17 @@ The token is stored in your apps `session` and is used on subsequent requests to When a push notification is received, the token is sent to your api server and the server stores it for the user who sent it. -> Optionally, you can have a `HasMany` relationship between your users and devices, -> this allows you to associate a device with a user and then use the device's token -> to send push notifications to that users devices. +> Optionally, you can have a `HasMany` relationship between your users and devices, this allows you to associate a device with a user and then use the device's token to send push notifications to that users devices. ## Sending Push Notifications -Once you have the token, you may use it from your server-based applications to trigger Push Notifications directly to -your user's device. We use a package like [google/apiclient](https://github.com/googleapis/google-api-php-client) to send the notifications. +Once you have the token, you may use it from your server-based applications to trigger Push Notifications directly to your user's device. We use the `google/apiclient` package to send the notifications. + +```bash +composer require google/apiclient +``` -This is the exact code used by the NativePHP Kitchen Sink App (available soon on all app stores and GitHub): +This is the exact code used by the NativePHP Kitchen Sink App: ```php Route::group(['middleware' => 'auth:sanctum'], function () { @@ -93,4 +119,161 @@ class PushNotificationController extends Controller SendPushNotification::dispatch($token)->delay(now()->addMinutes(1)); } } +``` + +## SendPushNotification Job + +Create a job file at `app/Jobs/SendPushNotification.php` with the following structure: + +**Key Components:** +- Uses `Google_Client` to authenticate with Firebase +- Retrieves access token from service account JSON file +- Sends HTTP POST request to FCM API endpoint +- Includes notification title and body in the message payload + +**Required Setup:** +1. Place your `fcm-service-account.json` file in the `public` directory +2. Configure `services.fcm.project_id` in your config +3. Install the `google/apiclient` package + +The job handles token authentication and sends notifications to specific device tokens using Firebase Cloud Messaging. + +**Core Implementation:** + +```php +private function sendFcmPush(string $token): void +{ + $fcmToken = $token; + + $client = new Google_Client(); + $client->setAuthConfig(public_path('fcm-service-account.json')); + $client->addScope('https://www.googleapis.com/auth/firebase.messaging'); + $token = $client->fetchAccessTokenWithAssertion()['access_token']; + + $projectId = config('services.fcm.project_id'); + + Http::withToken($token)->post( + "https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send", + [ + 'message' => [ + 'token' => $fcmToken, + 'notification' => [ + 'title' => 'NativePHP', + 'body' => 'Thanks for testing NativePHP!', + ], + ], + ] + ); +} +``` + +## Configuration + +### Environment Variables + +Add these to your `.env` file: + +```bash +FIREBASE_PROJECT_ID=your-firebase-project-id +FIREBASE_CLIENT_EMAIL=your-service-account-email +FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n" +``` + +### Config File + +Add FCM configuration to your `config/services.php`: + +```php +'fcm' => [ + 'project_id' => env('FIREBASE_PROJECT_ID'), + 'client_email' => env('FIREBASE_CLIENT_EMAIL'), + 'private_key' => env('FIREBASE_PRIVATE_KEY'), +], +``` + +## Platform-Specific Options + +### Android-Specific Configuration + +You can customize Android notifications with additional options: + +```php +'message' => [ + 'token' => $fcmToken, + 'notification' => [ + 'title' => 'NativePHP', + 'body' => 'Thanks for testing NativePHP!', + ], + 'android' => [ + 'notification' => [ + 'channel_id' => 'default', + 'priority' => 'high', + 'sound' => 'default', + ], + ], +], +``` + +### iOS-Specific Configuration + +For iOS devices, you can add APNS-specific options: + +```php +'message' => [ + 'token' => $fcmToken, + 'notification' => [ + 'title' => 'NativePHP', + 'body' => 'Thanks for testing NativePHP!', + ], + 'apns' => [ + 'payload' => [ + 'aps' => [ + 'badge' => 1, + 'sound' => 'default', + 'content-available' => 1, + ], + ], + ], +], + +## Data Payloads + +You can send custom data with your notifications by adding a `data` field to your message payload: + +```php +'message' => [ + 'token' => $fcmToken, + 'notification' => [ + 'title' => 'New Message', + 'body' => 'You have a new message from John', + ], + 'data' => [ + 'user_id' => '123', + 'message_id' => '456', + 'action' => 'view_message', + 'deep_link' => 'myapp://messages/456', + ], +], +``` + +### Handling Data in Your App + +The data payload is available in your app when the notification is received: + +```php +// In your notification event handler +#[On('native:' . PushNotificationReceived::class)] +public function handleNotificationData(array $data) +{ + $userId = $data['user_id'] ?? null; + $messageId = $data['message_id'] ?? null; + $action = $data['action'] ?? null; + + // Handle the data accordingly + if ($action === 'view_message') { + // Navigate to message view + $this->redirectRoute('messages.show', ['id' => $messageId]); + } +} +``` ``` \ No newline at end of file diff --git a/resources/views/docs/mobile/1/concepts/versioning.md b/resources/views/docs/mobile/1/concepts/versioning.md deleted file mode 100644 index bf6e403c..00000000 --- a/resources/views/docs/mobile/1/concepts/versioning.md +++ /dev/null @@ -1,291 +0,0 @@ ---- -title: Versioning -order: 600 ---- - -## Overview - -Proper versioning is crucial for mobile app development, affecting app store submissions, user updates, and feature compatibility. NativePHP Mobile provides flexible versioning strategies for both development and production environments. - -## Version Configuration - -### Environment Variables - -Configure your app version in `.env`: - -```bash -# App identifier (must match across all platforms) -NATIVEPHP_APP_ID=com.yourcompany.yourapp - -# Version string (shown to users) -NATIVEPHP_APP_VERSION=1.2.3 - -# Version code (integer, must increase with each release) -NATIVEPHP_APP_VERSION_CODE=123 -``` - -### Dynamic Versioning - -```php -// Set version programmatically -config(['nativephp.app.version' => '1.2.3']); -config(['nativephp.app.version_code' => 123]); -``` - -## Version Types - -### Semantic Versioning (Recommended) - -Follow [Semantic Versioning](https://semver.org/) principles: - -``` -MAJOR.MINOR.PATCH -``` - -- **MAJOR** - Breaking changes or significant new features -- **MINOR** - New features, backward compatible -- **PATCH** - Bug fixes, backward compatible - -Examples: -```bash -NATIVEPHP_APP_VERSION=1.0.0 # Initial release -NATIVEPHP_APP_VERSION=1.1.0 # New features added -NATIVEPHP_APP_VERSION=1.1.1 # Bug fixes -NATIVEPHP_APP_VERSION=2.0.0 # Breaking changes -``` - -### Version Codes - -Version codes are integers that must increase with each release: - -```bash -# v1.0.0 -NATIVEPHP_APP_VERSION_CODE=100 - -# v1.1.0 -NATIVEPHP_APP_VERSION_CODE=110 - -# v1.1.1 -NATIVEPHP_APP_VERSION_CODE=111 - -# v2.0.0 -NATIVEPHP_APP_VERSION_CODE=200 -``` - -## Environment-Specific Versioning - -### Development - -```bash -# .env.development -NATIVEPHP_APP_VERSION=1.2.3-dev -NATIVEPHP_APP_VERSION_CODE=99999 -``` - -### Staging - -```bash -# .env.staging -NATIVEPHP_APP_VERSION=1.2.3-beta -NATIVEPHP_APP_VERSION_CODE=99998 -``` - -### Production - -```bash -# .env.production -NATIVEPHP_APP_VERSION=1.2.3 -NATIVEPHP_APP_VERSION_CODE=123 -``` - -## Automated Versioning - -### Git Tag-Based Versioning - -```bash -#!/bin/bash -# scripts/set-version.sh - -# Get version from git tag -VERSION=$(git describe --tags --exact-match 2>/dev/null || echo "dev") - -# Remove 'v' prefix if present -VERSION=${VERSION#v} - -# Set in environment -export NATIVEPHP_APP_VERSION=$VERSION - -# Generate version code from semantic version -MAJOR=$(echo $VERSION | cut -d. -f1) -MINOR=$(echo $VERSION | cut -d. -f2) -PATCH=$(echo $VERSION | cut -d. -f3) -VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH)) - -export NATIVEPHP_APP_VERSION_CODE=$VERSION_CODE -``` - -### CI/CD Integration - -```yaml -# GitHub Actions -- name: Set version from tag - run: | - if [[ $GITHUB_REF == refs/tags/* ]]; then - VERSION=${GITHUB_REF#refs/tags/v} - echo "NATIVEPHP_APP_VERSION=$VERSION" >> .env - - # Generate version code - MAJOR=$(echo $VERSION | cut -d. -f1) - MINOR=$(echo $VERSION | cut -d. -f2) - PATCH=$(echo $VERSION | cut -d. -f3) - VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH)) - echo "NATIVEPHP_APP_VERSION_CODE=$VERSION_CODE" >> .env - fi -``` - -### Build Number Integration - -```bash -# Use CI build number for development versions -NATIVEPHP_APP_VERSION=1.2.3-build.${BUILD_NUMBER} -NATIVEPHP_APP_VERSION_CODE=${BUILD_NUMBER} -``` - -## Version Management in Code - -### Accessing Current Version - -```php -use Livewire\Component; - -class VersionDisplay extends Component -{ - public function mount() - { - $this->version = config('nativephp.app.version'); - $this->versionCode = config('nativephp.app.version_code'); - } - - public function render() - { - return view('livewire.version-display', [ - 'version' => $this->version, - 'versionCode' => $this->versionCode - ]); - } -} -``` - -### Version Comparison - -```php -class VersionManager -{ - public function isNewerVersion(string $current, string $new): bool - { - return version_compare($new, $current, '>'); - } - - public function getCurrentVersion(): string - { - return config('nativephp.app.version'); - } - - public function checkForUpdates(): array - { - $currentVersion = $this->getCurrentVersion(); - $latestVersion = $this->getLatestVersionFromApi(); - - return [ - 'has_update' => $this->isNewerVersion($currentVersion, $latestVersion), - 'current_version' => $currentVersion, - 'latest_version' => $latestVersion - ]; - } - - private function getLatestVersionFromApi(): string - { - // Check your API for latest version - $response = Http::get('https://api.yourapp.com/version/latest'); - return $response->json('version'); - } -} -``` - -## App Store Considerations - -### iOS App Store - -- **CFBundleShortVersionString** - User-facing version (1.2.3) -- **CFBundleVersion** - Build number (123) -- Version must be incremented for each submission -- Can skip version numbers (1.0 → 1.2 is allowed) - -### Google Play Store - -- **versionName** - User-facing version string -- **versionCode** - Integer that must increase with every release -- Cannot decrease version code -- Can reuse version names with different codes -- **Important**: `NATIVEPHP_APP_VERSION_CODE` must be incremented for each Google Play Store release, even for minor updates - -### Configuration Example - -```php -// config/nativephp.php -return [ - 'app' => [ - 'id' => env('NATIVEPHP_APP_ID', 'com.example.app'), - 'version' => env('NATIVEPHP_APP_VERSION', '1.0.0'), - 'version_code' => env('NATIVEPHP_APP_VERSION_CODE', 1), - ], -]; -``` - -## Best Practices - -### Version Strategy -1. **Use semantic versioning** for consistency -2. **Increment version codes** for every build -3. **Test version upgrades** thoroughly -4. **Document breaking changes** clearly -5. **Plan update strategies** in advance - -### Development Workflow -1. **Branch naming** - Include version in branch names (`feature/v1.2.0-new-ui`) -2. **Tag releases** - Use git tags for version tracking -3. **Automate versioning** - Reduce manual errors -4. **Test backwards compatibility** - Ensure smooth upgrades -5. **Maintain changelog** - Document all changes - -### Release Management -1. **Staged rollouts** - Release to small groups first -2. **Feature flags** - Control feature availability by version -3. **Emergency updates** - Have a fast-track process for critical fixes -4. **Version analytics** - Track version adoption rates -5. **Sunset planning** - Plan when to stop supporting old versions - -### Common Pitfalls -- **Don't decrease version codes** - Google Play Store will reject decreasing `NATIVEPHP_APP_VERSION_CODE` -- **Don't reuse version codes** - Each release must have a unique, higher version code -- **Don't skip testing upgrade paths** - Test how users transition between versions -- **Don't forget to update all platform configs** - Keep versions synchronized -- **Don't ignore app store requirements** - Each platform has specific versioning rules - -### Google Play Store Version Code Requirements - -The `NATIVEPHP_APP_VERSION_CODE` is critical for Google Play Store submissions: - -```bash -# ❌ WRONG - Cannot decrease or reuse version codes -NATIVEPHP_APP_VERSION_CODE=100 # v1.0.0 -NATIVEPHP_APP_VERSION_CODE=99 # v1.0.1 - REJECTED! - -# ✅ CORRECT - Always increase version code -NATIVEPHP_APP_VERSION_CODE=100 # v1.0.0 -NATIVEPHP_APP_VERSION_CODE=101 # v1.0.1 - Accepted -NATIVEPHP_APP_VERSION_CODE=102 # v1.0.2 - Accepted -NATIVEPHP_APP_VERSION_CODE=200 # v2.0.0 - Accepted -``` - -**Remember**: Even for hotfixes or minor updates, the version code must always increase. diff --git a/resources/views/docs/mobile/1/getting-started/roadmap.md b/resources/views/docs/mobile/1/getting-started/roadmap.md index 26e6110a..cc8c7fc7 100644 --- a/resources/views/docs/mobile/1/getting-started/roadmap.md +++ b/resources/views/docs/mobile/1/getting-started/roadmap.md @@ -46,10 +46,10 @@ We're working on adding more and more features, including (in no particular orde - Gyroscope - Accelerometer - Screen brightness - - Haptic feedback + - More Haptic feedback - Network info access - Battery status - - CPU information + - CPU/Device information - Ads - In-app billing diff --git a/resources/views/docs/mobile/1/getting-started/versioning.md b/resources/views/docs/mobile/1/getting-started/versioning.md new file mode 100644 index 00000000..5ed474ef --- /dev/null +++ b/resources/views/docs/mobile/1/getting-started/versioning.md @@ -0,0 +1,171 @@ +--- +title: Release Notes +order: 600 +--- + +## NativePHP Mobile Versioning Strategy + +This document outlines how NativePHP Mobile itself will be versioned moving forward. As we develop tools to assist with OTA updates and full app bundles, our versioning strategy ensures predictable release cycles and update compatibility. + +**Note**: You can version your own applications however you prefer - including codenames, custom schemes, or any versioning pattern that works for your project. This document specifically covers how NativePHP Mobile releases will be versioned. + +## Our Release Types + +### Patch Releases (Laravel-Only Changes) + +When NativePHP Mobile updates contain only Laravel/PHP code changes, we will increment the patch version: + +- **Laravel code improvements** - Core functionality, API enhancements, bug fixes +- **Configuration updates** - Default settings, environment handling +- **Documentation updates** - README, guides, examples +- **Dependency updates** - Composer package updates that don't affect native code + +**Version Pattern**: `X.Y.Z` → `X.Y.Z+1` (patch increment) + +```bash +# Example: Laravel bug fix in NativePHP Mobile +1.2.3 → 1.2.4 +``` + +**Deployment**: These updates can be consumed immediately via Composer without requiring developers to rebuild their native applications. + +### Minor Releases (Native Code Changes) + +When NativePHP Mobile updates require changes to Kotlin/Swift code, we will increment the minor version: + +- **Kotlin/Swift code changes** - New native functionality, native bug fixes +- **New native features** - Camera integration, location services, push notifications +- **Native dependency updates** - Changes to native SDKs or libraries +- **Platform-specific implementations** - iOS or Android specific code changes + +**Version Pattern**: `X.Y.Z` → `X.Y+1.0` (minor increment, reset patch to 0) + +```bash +# Example: New camera feature requiring native code updates +1.2.3 → 1.3.0 +``` + +**Deployment**: These updates require developers to rebuild their applications and submit new versions to app stores. + +## Why This Versioning Strategy Matters + +By following this consistent versioning pattern, we guarantee: + +1. **Predictable Update Impact** - Patch versions are safe to update without rebuilding apps +2. **Clear Breaking Change Communication** - Minor versions signal when native rebuilds are required +3. **OTA Update Compatibility** - Our tools can safely determine which updates can be deployed over-the-air +4. **Developer Confidence** - You know exactly what each version bump means for your development workflow + +## Version Examples + +### Patch Version Scenarios + +```bash +# Bug fix in package command +1.2.3 → 1.2.4 + +# New Artisan command added +1.2.4 → 1.2.5 + +# Change the way we move files around on Windows +1.2.5 → 1.2.6 + +# Composer dependency security update +1.2.6 → 1.2.7 +``` + +### Minor Version Scenarios + +```bash +# New camera API integration +1.2.7 → 1.3.0 + +# iOS-specific push notification changes +1.3.0 → 1.4.0 + +# Android permissions handling update +1.4.0 → 1.5.0 + +# New native file system access +1.5.0 → 1.6.0 +``` + +## Impact on Your Development + +### For Patch Releases + +When we release a patch version: +- **Update immediately** - Run `composer update` to get the latest improvements +- **No rebuild required** - Your existing native apps continue working +- **OTA compatible** - Any improvements are immediately available to your users + +### For Minor Releases + +When we release a minor version: +- **Update package** - Run `composer update` to get the latest version +- **Reinstall native code** - Run `php artisan native:install --force` to update native components +- **Review changelog** - Check what new native features or changes are included +- **Test thoroughly** - Ensure compatibility with your existing application code +- **Submit to app stores** - New native code requires store approval + +## Release Communication + +### Patch Releases +- **Immediate availability** - Available via Composer as soon as released +- **Minimal disruption** - No impact on your release schedule +- **Automatic compatibility** - Works with existing native builds + +### Minor Releases +- **Advance notice** - Announced ahead of time to allow planning +- **Migration guides** - Documentation for any required changes +- **Testing period** - Beta releases available for early testing + +## Implementation Details + +### Version Code Management + +Our version codes follow this pattern: + +```bash +# Version 1.2.3 = Version Code 123 +# Version 1.2.4 = Version Code 124 +# Version 1.3.0 = Version Code 130 +``` + +### Composer Integration + +```json +{ + "require": { + "nativephp/mobile": "^1.2.3" + } +} +``` + +Using semantic versioning constraints ensures you receive patch updates automatically while controlling when to adopt minor releases. + +## Your Application Versioning + +While NativePHP Mobile follows this structured approach, **you have complete freedom** in how you version your own applications: + +- **Semantic versioning** - `1.2.3`, `2.0.0`, etc. +- **Codenames** - `"Falcon"`, `"Eagle"`, `"Hawk"` +- **Date-based** - `2024.01.15`, `2024.02.01` +- **Build numbers** - `Build 1234`, `Version 5678` +- **Custom schemes** - Whatever works for your project + +The key is that regardless of how you version your app, NativePHP Mobile's consistent versioning ensures you always know the impact of framework updates on your development process. + +## Benefits of This Approach + +### For Developers +- **Predictable updates** - Always know what to expect from version bumps +- **Reduced friction** - Patch updates don't disrupt your release cycle +- **Clear upgrade paths** - Minor versions provide structured upgrade opportunities + +### For End Users +- **Faster improvements** - Laravel enhancements reach users immediately +- **Stable experience** - Native changes are properly tested before release +- **Reliable updates** - Consistent versioning prevents compatibility issues + +This versioning strategy enables us to deliver improvements quickly while maintaining the stability and predictability you need for production applications. diff --git a/resources/views/docs/mobile/1/the-basics/deep-links.md b/resources/views/docs/mobile/1/the-basics/deep-links.md deleted file mode 100644 index 804bb0b1..00000000 --- a/resources/views/docs/mobile/1/the-basics/deep-links.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: Deep, Universal, App Links and NFC -order: 500 ---- - -## Overview - -NativePHP for Mobile supports both **deep linking** and **web-based linking** into your mobile apps. - -There are two types of link integrations you can configure: - -- **Deep Links** (myapp://some/path) -- **Universal Links (iOS)** and **App Links (Android)** (https://example.net/some/path) - -Each method has its use case, and NativePHP allows you to configure and handle both easily. - ---- - -## Deep Links - -Deep links use a **custom URL scheme** to open your app. - -For example: - -``` -myapp://profile/123 -``` - - -When a user taps a deep link, the mobile operating system detects the custom scheme and opens your app directly. - -### Configuration - -To enable deep linking, you must define: - -- **Scheme**: The protocol (e.g., myapp) -- **Host**: An optional domain-like segment (e.g., open) - -These are configured in your .env: - -```dotenv -NATIVEPHP_DEEPLINK_SCHEME=myapp -NATIVEPHP_DEEPLINK_HOST=open -``` - -## Universal Links (iOS) and App Links (Android) - -Universal Links and App Links allow real HTTPS URLs to open your app instead of a web browser, if the app is installed. - -For example: -```dotenv -https://example.net/property/456 -``` - -When a user taps this link: - - - If your app is installed, it opens directly into the app. - - If not, it opens normally in the browser. - -This provides a seamless user experience without needing a custom scheme. - -### How It Works -1. You must prove to iOS and Android that you own the domain by hosting a special file: - - .well-known/apple-app-site-association (for iOS) - - .well-known/assetlinks.json (for Android) -2. The mobile OS reads these files to verify the link association. -3. Once verified, tapping a real URL will open your app instead of Safari or Chrome. - -NativePHP for Mobile handles all of this for you. - -### Configuration - -To enable Universal Links and App Links, you must define: - -- **Host**: The domain name (e.g., example.net) - -These are configured in your .env: - -```dotenv -NATIVEPHP_DEEPLINK_HOST=example.net -``` - -## Handling Universal/App Links - -Once you've configured your deep link settings, you can handle the link in your app. - -Simply set up a route in your web.php file and the deeplink will redirect to your route. - -```dotenv -https://example.net/profile/123 -``` - -```php -Route::get('/profile/{id}', function ($id) { - // Handle the deep link -}); -``` - -## NFC -NFC is a technology that allows you to read and write NFC tags. - -NativePHP handles NFC tag "bumping" just like a Universal/App Link. -You can use a tool like [NFC Tools](https://www.wakdev.com/en/) to test write NFC tags. - -Set the url to a Universal/App Link and the tag will be written to the NFC tag. -"Bumping" the tag will open the app. - - - diff --git a/resources/views/docs/mobile/1/the-basics/icu-support.md b/resources/views/docs/mobile/1/the-basics/icu-support.md index 2b96e8b7..a92fd01f 100644 --- a/resources/views/docs/mobile/1/the-basics/icu-support.md +++ b/resources/views/docs/mobile/1/the-basics/icu-support.md @@ -225,57 +225,6 @@ class LocalizedContent } ``` -### Filament Integration - -```php -// Filament requires ICU for proper operation -use Filament\Forms\Components\TextInput; - -TextInput::make('price') - ->numeric() - ->formatStateUsing(function ($state) { - // This formatting requires ICU - $formatter = new NumberFormatter(app()->getLocale(), NumberFormatter::CURRENCY); - return $formatter->formatCurrency($state, 'USD'); - }); -``` - -## Graceful Degradation - -Design your app to work with or without ICU: - -```php -class InternationalizationHelper -{ - public static function hasICU(): bool - { - return extension_loaded('intl'); - } - - public static function formatNumber(float $number, string $locale = 'en_US'): string - { - if (self::hasICU()) { - $formatter = new NumberFormatter($locale, NumberFormatter::DECIMAL); - return $formatter->format($number); - } - - // Fallback formatting - return number_format($number, 2); - } - - public static function compareStrings(string $a, string $b, string $locale = 'en_US'): int - { - if (self::hasICU()) { - $collator = new Collator($locale); - return $collator->compare($a, $b); - } - - // Fallback to simple comparison - return strcmp($a, $b); - } -} -``` - ## Performance Considerations ### Memory Usage @@ -283,72 +232,6 @@ class InternationalizationHelper - Runtime memory usage increases slightly - Complex formatting operations are slower -### Optimization Tips - -```php -class OptimizedFormatting -{ - private static array $formatters = []; - - public static function getCachedFormatter(string $locale, int $style): NumberFormatter - { - $key = $locale . '_' . $style; - - if (!isset(self::$formatters[$key])) { - self::$formatters[$key] = new NumberFormatter($locale, $style); - } - - return self::$formatters[$key]; - } - - public static function formatCurrency(float $amount, string $locale, string $currency): string - { - $formatter = self::getCachedFormatter($locale, NumberFormatter::CURRENCY); - return $formatter->formatCurrency($amount, $currency); - } -} -``` - -## Testing ICU Features - -```php -// tests/Feature/ICUSupportTest.php -namespace Tests\Feature; - -use Tests\TestCase; - -class ICUSupportTest extends TestCase -{ - public function test_icu_extension_loaded() - { - if (config('app.requires_icu')) { - $this->assertTrue(extension_loaded('intl'), 'ICU extension is required but not loaded'); - } - } - - public function test_number_formatting_works() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ICU not available'); - } - - $formatter = new \NumberFormatter('en_US', \NumberFormatter::CURRENCY); - $result = $formatter->formatCurrency(1234.56, 'USD'); - - $this->assertEquals('$1,234.56', $result); - } - - public function test_fallback_formatting_works() - { - $helper = new InternationalizationHelper(); - $result = $helper->formatNumber(1234.56); - - $this->assertIsString($result); - $this->assertStringContains('1234', $result); - } -} -``` - ## Decision Matrix Use this matrix to decide if you need ICU support: diff --git a/resources/views/docs/mobile/1/the-basics/native-functions.md b/resources/views/docs/mobile/1/the-basics/native-functions.md index f8ec6a08..c90fee2f 100644 --- a/resources/views/docs/mobile/1/the-basics/native-functions.md +++ b/resources/views/docs/mobile/1/the-basics/native-functions.md @@ -3,8 +3,7 @@ title: Native Functions order: 100 --- -Nearly any basic Laravel app will work as a mobile app with NativePHP for Mobile. However, what makes NativePHP -unique is that it allows you to call native functions from your PHP code. +NativePHP for Mobile enables you to build powerful mobile applications using Laravel and PHP. What makes this approach unique is the ability to call native device functions directly from your PHP code, giving you access to platform-specific features while maintaining the productivity and familiarity of Laravel development. These functions are called from your PHP code using one of an ever-growing list of facades. From 69832bf41b8db2173c0a8a8d1dab29ddd15e543f Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Wed, 9 Jul 2025 20:07:18 -0400 Subject: [PATCH 09/16] Update Dialog API documentation for new alert implementation - Updated alert() method signature to use 3 parameters (title, message, buttons array) - Changed buttons parameter from complex objects to simple string arrays - Updated ButtonPressed event to pass both index and label parameters - Replaced all example code to use new simplified API - Added button positioning documentation (negative, neutral, positive) - Removed old button style configurations - Added practical examples for 1, 2, and 3 button scenarios --- resources/views/docs/mobile/1/apis/dialog.md | 76 ++++++++++---------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/resources/views/docs/mobile/1/apis/dialog.md b/resources/views/docs/mobile/1/apis/dialog.md index 59b958a5..8a98c1b8 100644 --- a/resources/views/docs/mobile/1/apis/dialog.md +++ b/resources/views/docs/mobile/1/apis/dialog.md @@ -20,20 +20,18 @@ Displays a native alert dialog with customizable buttons. **Parameters:** - `string $title` - The alert title - `string $message` - The alert message -- `array $buttons` - Array of button configurations -- `callable $callback` - Callback function for button presses +- `array $buttons` - Array of button labels (max 3 buttons) + +**Button Positioning:** +- **1 button** - Positive (OK/Confirm) +- **2 buttons** - Negative (Cancel) + Positive (OK/Confirm) +- **3 buttons** - Negative (Cancel) + Neutral (Maybe) + Positive (OK/Confirm) ```php Dialog::alert( 'Confirm Action', 'Are you sure you want to delete this item?', - [ - ['text' => 'Cancel', 'style' => 'cancel'], - ['text' => 'Delete', 'style' => 'destructive'] - ], - function($buttonIndex) { - // Handle button press - } + ['Cancel', 'Delete'] ); ``` @@ -71,22 +69,26 @@ Dialog::share( Fired when a button is pressed in an alert dialog. -**Payload:** `int $buttonIndex` - Index of the pressed button (0-based) +**Payload:** +- `int $index` - Index of the pressed button (0-based) +- `string $label` - Label/text of the pressed button ```php use Livewire\Attributes\On; use Native\Mobile\Events\Alert\ButtonPressed; #[On('native:' . ButtonPressed::class)] -public function handleAlertButton(int $buttonIndex) +public function handleAlertButton($index, $label) { - switch ($buttonIndex) { + switch ($index) { case 0: // First button (usually Cancel) + Dialog::toast("You pressed '{$label}'"); break; case 1: // Second button (usually OK/Confirm) $this->performAction(); + Dialog::toast("You pressed '{$label}'"); break; } } @@ -112,24 +114,21 @@ class ItemManager extends Component Dialog::alert( 'Delete Item', 'This action cannot be undone. Are you sure?', - [ - ['text' => 'Cancel', 'style' => 'cancel'], - ['text' => 'Delete', 'style' => 'destructive'] - ], - null + ['Cancel', 'Delete'] ); } #[On('native:' . ButtonPressed::class)] - public function handleDeleteConfirmation(int $buttonIndex) + public function handleDeleteConfirmation($index, $label) { - if ($buttonIndex === 1 && $this->itemToDelete) { + if ($index === 1 && $this->itemToDelete) { // User confirmed deletion $this->performDelete($this->itemToDelete); Dialog::toast('Item deleted successfully'); $this->itemToDelete = null; } else { // User cancelled + Dialog::toast("You pressed '{$label}'"); $this->itemToDelete = null; } } @@ -163,31 +162,36 @@ class ItemManager extends Component } ``` -## Alert Button Styles - -### iOS Button Styles -- `'default'` - Standard blue button -- `'cancel'` - Bold cancel button (usually on the left) -- `'destructive'` - Red destructive action button - -### Android Button Styles -- `'positive'` - Primary action button -- `'negative'` - Cancel/dismiss button -- `'neutral'` - Additional option button +## Alert Button Examples +### Simple Confirmation ```php -// Cross-platform alert with proper styling Dialog::alert( 'Delete Account', 'This will permanently delete your account and all data.', - [ - ['text' => 'Cancel', 'style' => 'cancel'], - ['text' => 'Delete', 'style' => 'destructive'] - ], - null + ['Cancel', 'Delete'] ); ``` +### Three Button Options +```php +Dialog::alert( + 'Save Changes', + 'Do you want to save your changes before closing?', + ['Cancel', 'Don\'t Save', 'Save'] +); +``` + +### Single Button Alert +```php +Dialog::alert( + 'Welcome!', + 'Thanks for downloading our app!', + ['OK'] +); +``` + + ## Toast Guidelines ### Best Practices From 4ba3bdc5535ebc7cea505df7a450848b091048e2 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Wed, 9 Jul 2025 21:27:47 -0400 Subject: [PATCH 10/16] Add Browser API documentation for in-app browser functionality - Added new browser.md documentation for Browser::inApp() method - Documented OAuth authentication use cases with deep link integration - Included platform-specific behavior for iOS and Android - Added complete example showing Livewire component implementation - Covered security considerations and best practices - Explained integration with deep links for complete OAuth flows --- resources/views/docs/mobile/1/apis/browser.md | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 resources/views/docs/mobile/1/apis/browser.md diff --git a/resources/views/docs/mobile/1/apis/browser.md b/resources/views/docs/mobile/1/apis/browser.md new file mode 100644 index 00000000..d7846f28 --- /dev/null +++ b/resources/views/docs/mobile/1/apis/browser.md @@ -0,0 +1,116 @@ +--- +title: Browser +order: 250 +--- + +## Overview + +The Browser API allows you to open URLs in an in-app browser that is "owned" by your application. This is essential when working with OAuth redirects since you need to provide a redirect URL that would naturally open in the user's default browser. + +```php +use Native\Mobile\Facades\Browser; +``` + +## Methods + +### `inApp()` + +Opens a URL in an in-app browser window. + +**Parameters:** +- `string $url` - The URL to open in the in-app browser + +```php +Browser::inApp('https://nativephp.com/mobile'); +``` + +## Use Cases + +### OAuth Authentication + +The in-app browser is particularly useful for OAuth flows where you need to: +- Redirect users to a third-party authentication provider +- Capture the redirect URL after authentication +- Return control to your app seamlessly + +```php +use Native\Mobile\Facades\Browser; + +class AuthController extends Component +{ + public function authenticateWithProvider() + { + // Open OAuth provider in in-app browser + Browser::inApp('https://provider.com/oauth/authorize?client_id=your_client_id&redirect_uri=your_app_scheme://oauth/callback'); + } +} +``` + +### External Content + +Display external content while keeping users within your app: + +```php +// Open documentation +Browser::inApp('https://docs.example.com/help'); + +// Open terms of service +Browser::inApp('https://example.com/terms'); + +// Open privacy policy +Browser::inApp('https://example.com/privacy'); +``` + +## Integration with Deep Links + +Use the in-app browser in conjunction with App/Universal/Deep links for complete OAuth flows: + +1. **Open OAuth provider** - Use `Browser::inApp()` to start authentication +2. **User authenticates** - User completes authentication in the in-app browser +3. **Redirect to app** - OAuth provider redirects to your app's deep link +4. **Handle in app** - Your app receives the deep link and processes the authentication + +```php +use Native\Mobile\Facades\Browser; +use Livewire\Attributes\On; +use Native\Mobile\Events\DeepLink\Received; + +class OAuthHandler extends Component +{ + public function startOAuth() + { + Browser::inApp('https://github.com/login/oauth/authorize?client_id=your_client_id&redirect_uri=myapp://oauth/callback'); + } +} +``` + +## Platform Behavior + +### iOS +- Uses `SFSafariViewController` for the in-app browser +- Provides native Safari experience within your app +- Includes address bar, navigation controls, and share functionality +- Users can easily return to your app + +### Android +- Uses `Chrome Custom Tabs` when available +- Falls back to `WebView` if Custom Tabs unavailable +- Provides native browser experience +- Maintains your app's context + +## Best Practices + +1. **Use for OAuth flows** - Perfect for authentication redirects +2. **Keep users in your app** - Better than opening external browser +3. **Combine with deep links** - Essential for complete OAuth implementation +4. **Handle errors gracefully** - Network issues or invalid URLs should be handled +5. **Provide user feedback** - Show loading states when opening URLs + +## Security Considerations + +- **Validate URLs** - Ensure URLs are from trusted sources +- **Handle redirects** - Be prepared for OAuth redirects back to your app +- **Secure communication** - Always use HTTPS for sensitive operations +- **User consent** - Make it clear when opening external content + +The in-app browser provides a seamless way to handle external content and OAuth flows while maintaining your app's user experience. From 36c6d883a60d8f2ffeaddae252dcb03db8272262 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Thu, 10 Jul 2025 11:26:06 -0400 Subject: [PATCH 11/16] Update resources/views/docs/mobile/1/the-basics/icu-support.md Co-authored-by: Eser DENIZ --- .../docs/mobile/1/the-basics/icu-support.md | 128 ------------------ 1 file changed, 128 deletions(-) diff --git a/resources/views/docs/mobile/1/the-basics/icu-support.md b/resources/views/docs/mobile/1/the-basics/icu-support.md index a92fd01f..90750151 100644 --- a/resources/views/docs/mobile/1/the-basics/icu-support.md +++ b/resources/views/docs/mobile/1/the-basics/icu-support.md @@ -96,134 +96,6 @@ $collator = new Collator('en_US'); $result = $collator->compare('apple', 'äpple'); // Proper Unicode comparison ``` -## Code Examples - -### Number Formatting - -```php -use NumberFormatter; - -class LocalizedNumbers -{ - public function formatCurrency(float $amount, string $locale, string $currency): string - { - if (!extension_loaded('intl')) { - // Fallback for non-ICU builds - return $currency . ' ' . number_format($amount, 2); - } - - $formatter = new NumberFormatter($locale, NumberFormatter::CURRENCY); - return $formatter->formatCurrency($amount, $currency); - } - - public function formatPercent(float $value, string $locale): string - { - if (!extension_loaded('intl')) { - return number_format($value * 100, 1) . '%'; - } - - $formatter = new NumberFormatter($locale, NumberFormatter::PERCENT); - return $formatter->format($value); - } -} - -// Usage -$numbers = new LocalizedNumbers(); - -echo $numbers->formatCurrency(1234.56, 'en_US', 'USD'); // $1,234.56 -echo $numbers->formatCurrency(1234.56, 'de_DE', 'EUR'); // 1.234,56 € -echo $numbers->formatPercent(0.1234, 'en_US'); // 12.3% -``` - -### Date Formatting - -```php -use IntlDateFormatter; - -class LocalizedDates -{ - public function formatDate(\DateTime $date, string $locale): string - { - if (!extension_loaded('intl')) { - return $date->format('M j, Y'); - } - - $formatter = new IntlDateFormatter( - $locale, - IntlDateFormatter::LONG, - IntlDateFormatter::NONE - ); - - return $formatter->format($date); - } -} - -// Usage -$dates = new LocalizedDates(); -$date = new DateTime('2024-03-15'); - -echo $dates->formatDate($date, 'en_US'); // March 15, 2024 -echo $dates->formatDate($date, 'de_DE'); // 15. März 2024 -echo $dates->formatDate($date, 'ja_JP'); // 2024年3月15日 -``` - -### String Collation - -```php -use Collator; - -class LocalizedSorting -{ - public function sortNames(array $names, string $locale): array - { - if (!extension_loaded('intl')) { - // Simple ASCII sort fallback - sort($names); - return $names; - } - - $collator = new Collator($locale); - $collator->sort($names); - return $names; - } -} - -// Usage -$sorter = new LocalizedSorting(); -$names = ['Müller', 'Mueller', 'Miller', 'Möller']; - -$sorted = $sorter->sortNames($names, 'de_DE'); -// Proper German sorting with umlauts -``` - -## Framework Integration - -### Laravel Localization - -```php -// config/app.php -return [ - 'locale' => 'en', - 'available_locales' => ['en', 'es', 'fr', 'de', 'ja'], -]; - -// With ICU support, you can use advanced formatters -class LocalizedContent -{ - public function getLocalizedPrice(float $price): string - { - $locale = app()->getLocale(); - $currency = config('app.currency.' . $locale, 'USD'); - - if (extension_loaded('intl')) { - $formatter = new NumberFormatter($locale, NumberFormatter::CURRENCY); - return $formatter->formatCurrency($price, $currency); - } - - return $currency . ' ' . number_format($price, 2); - } -} -``` ## Performance Considerations From df3c84afd40d4c3c72fee83706af99575724881a Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Thu, 10 Jul 2025 11:26:20 -0400 Subject: [PATCH 12/16] Update resources/views/docs/mobile/1/apis/browser.md Co-authored-by: Eser DENIZ --- resources/views/docs/mobile/1/apis/browser.md | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/resources/views/docs/mobile/1/apis/browser.md b/resources/views/docs/mobile/1/apis/browser.md index d7846f28..64fc3969 100644 --- a/resources/views/docs/mobile/1/apis/browser.md +++ b/resources/views/docs/mobile/1/apis/browser.md @@ -84,33 +84,3 @@ class OAuthHandler extends Component } ``` -## Platform Behavior - -### iOS -- Uses `SFSafariViewController` for the in-app browser -- Provides native Safari experience within your app -- Includes address bar, navigation controls, and share functionality -- Users can easily return to your app - -### Android -- Uses `Chrome Custom Tabs` when available -- Falls back to `WebView` if Custom Tabs unavailable -- Provides native browser experience -- Maintains your app's context - -## Best Practices - -1. **Use for OAuth flows** - Perfect for authentication redirects -2. **Keep users in your app** - Better than opening external browser -3. **Combine with deep links** - Essential for complete OAuth implementation -4. **Handle errors gracefully** - Network issues or invalid URLs should be handled -5. **Provide user feedback** - Show loading states when opening URLs - -## Security Considerations - -- **Validate URLs** - Ensure URLs are from trusted sources -- **Handle redirects** - Be prepared for OAuth redirects back to your app -- **Secure communication** - Always use HTTPS for sensitive operations -- **User consent** - Make it clear when opening external content - -The in-app browser provides a seamless way to handle external content and OAuth flows while maintaining your app's user experience. From 90ae4c3ea4e14ebb5e5a7738663fcdff36faafbe Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Thu, 10 Jul 2025 12:54:41 -0400 Subject: [PATCH 13/16] Update mobile documentation with API improvements and platform notes - Add Android-only notes for ICU support across documentation - Update Dialog API documentation with new alert implementation - Enhance Geolocation API documentation - Improve API overview and navigation - Update Push Notifications API documentation - Enhance Secure Storage API documentation - Add database documentation improvements - Update CI/CD packaging commands with platform limitations - Ensure consistent platform availability information across docs --- resources/views/docs/mobile/1/apis/dialog.md | 121 --------- .../views/docs/mobile/1/apis/geolocation.md | 207 --------------- .../views/docs/mobile/1/apis/overview.md | 39 +-- .../docs/mobile/1/apis/push-notifications.md | 242 +----------------- .../docs/mobile/1/apis/secure-storage.md | 173 ------------- .../views/docs/mobile/1/concepts/ci-cd.md | 12 +- .../views/docs/mobile/1/concepts/databases.md | 176 ------------- .../docs/mobile/1/the-basics/icu-support.md | 75 +----- 8 files changed, 17 insertions(+), 1028 deletions(-) diff --git a/resources/views/docs/mobile/1/apis/dialog.md b/resources/views/docs/mobile/1/apis/dialog.md index 8a98c1b8..e66dc529 100644 --- a/resources/views/docs/mobile/1/apis/dialog.md +++ b/resources/views/docs/mobile/1/apis/dialog.md @@ -94,104 +94,6 @@ public function handleAlertButton($index, $label) } ``` -## Example Usage - -```php -use Livewire\Component; -use Livewire\Attributes\On; -use Native\Mobile\Facades\Dialog; -use Native\Mobile\Events\Alert\ButtonPressed; - -class ItemManager extends Component -{ - public array $items = []; - public ?int $itemToDelete = null; - - public function deleteItem(int $itemId) - { - $this->itemToDelete = $itemId; - - Dialog::alert( - 'Delete Item', - 'This action cannot be undone. Are you sure?', - ['Cancel', 'Delete'] - ); - } - - #[On('native:' . ButtonPressed::class)] - public function handleDeleteConfirmation($index, $label) - { - if ($index === 1 && $this->itemToDelete) { - // User confirmed deletion - $this->performDelete($this->itemToDelete); - Dialog::toast('Item deleted successfully'); - $this->itemToDelete = null; - } else { - // User cancelled - Dialog::toast("You pressed '{$label}'"); - $this->itemToDelete = null; - } - } - - public function shareItem(array $item) - { - Dialog::share( - 'Share Item', - "Check out this item: {$item['name']}", - "https://myapp.com/items/{$item['id']}" - ); - } - - public function showSuccess(string $message) - { - Dialog::toast($message); - } - - private function performDelete(int $itemId) - { - $this->items = array_filter( - $this->items, - fn($item) => $item['id'] !== $itemId - ); - } - - public function render() - { - return view('livewire.item-manager'); - } -} -``` - -## Alert Button Examples - -### Simple Confirmation -```php -Dialog::alert( - 'Delete Account', - 'This will permanently delete your account and all data.', - ['Cancel', 'Delete'] -); -``` - -### Three Button Options -```php -Dialog::alert( - 'Save Changes', - 'Do you want to save your changes before closing?', - ['Cancel', 'Don\'t Save', 'Save'] -); -``` - -### Single Button Alert -```php -Dialog::alert( - 'Welcome!', - 'Thanks for downloading our app!', - ['OK'] -); -``` - - ## Toast Guidelines ### Best Practices @@ -210,29 +112,6 @@ Dialog::toast('Settings updated'); Dialog::toast('Your photo has been successfully uploaded to the server and will be processed shortly'); ``` -## Sharing Content - -### Supported Content Types -- Plain text -- URLs -- Images (when sharing files) -- Mixed content - -```php -// Share just text -Dialog::share('', 'Check out this amazing app!', ''); - -// Share a URL -Dialog::share('', '', 'https://nativephp.com'); - -// Share text and URL together -Dialog::share( - 'NativePHP for Mobile', - 'Build mobile apps with PHP and Laravel!', - 'https://nativephp.com' -); -``` - ## Platform Differences ### iOS diff --git a/resources/views/docs/mobile/1/apis/geolocation.md b/resources/views/docs/mobile/1/apis/geolocation.md index 308ac08c..4ca4bad9 100644 --- a/resources/views/docs/mobile/1/apis/geolocation.md +++ b/resources/views/docs/mobile/1/apis/geolocation.md @@ -164,142 +164,6 @@ public function handlePermissionRequest($location, $coarseLocation, $fineLocatio } ``` -## Complete Example - -```php -use Livewire\Component; -use Livewire\Attributes\On; -use Native\Mobile\Facades\Geolocation; -use Native\Mobile\Events\Geolocation\LocationReceived; -use Native\Mobile\Events\Geolocation\PermissionStatusReceived; -use Native\Mobile\Events\Geolocation\PermissionRequestResult; - -class LocationTracker extends Component -{ - public ?float $latitude = null; - public ?float $longitude = null; - public ?float $accuracy = null; - public ?string $provider = null; - public bool $isLoading = false; - public string $error = ''; - public bool $showSettingsPrompt = false; - - // Permission states - public string $locationPermission = 'unknown'; - public string $coarsePermission = 'unknown'; - public string $finePermission = 'unknown'; - - public function mount() - { - // Check current permissions on load - $this->checkPermissions(); - } - - public function checkPermissions() - { - $this->error = ''; - Geolocation::checkPermissions(); - } - - public function requestPermissions() - { - $this->error = ''; - $this->isLoading = true; - Geolocation::requestPermissions(); - } - - public function getCurrentLocation() - { - $this->isLoading = true; - $this->error = ''; - - // Use high accuracy GPS - Geolocation::getCurrentPosition(true); - } - - #[On('native:' . PermissionStatusReceived::class)] - public function handlePermissionStatus($location, $coarseLocation, $fineLocation) - { - $this->locationPermission = $location; - $this->coarsePermission = $coarseLocation; - $this->finePermission = $fineLocation; - - if ($coarseLocation === 'granted' || $fineLocation === 'granted') { - // Has some level of location permission - $this->showLocationButton = true; - } elseif ($location === 'denied') { - // Permission denied - can request again - $this->showRequestButton = true; - } else { - // Permission not determined - can request - $this->showRequestButton = true; - } - } - - #[On('native:' . PermissionRequestResult::class)] - public function handlePermissionRequest($location, $coarseLocation, $fineLocation, $message = null, $needsSettings = null) - { - $this->isLoading = false; - - if ($location === 'permanently_denied') { - $this->error = $message ?? 'Location access permanently denied. Please enable location services in your device Settings app.'; - $this->showSettingsPrompt = true; - } elseif ($coarseLocation === 'granted' || $fineLocation === 'granted') { - // Permission granted - automatically get location - $this->getCurrentLocation(); - } else { - $this->error = 'Location permission is required to use this feature.'; - } - } - - #[On('native:' . LocationReceived::class)] - public function handleLocationReceived($success = null, $latitude = null, $longitude = null, $accuracy = null, $timestamp = null, $provider = null, $error = null) - { - $this->isLoading = false; - - if ($success) { - $this->latitude = $latitude; - $this->longitude = $longitude; - $this->accuracy = $accuracy; - $this->provider = $provider; - $this->error = ''; - - // Store for later use - session([ - 'last_location' => [ - 'lat' => $latitude, - 'lng' => $longitude, - 'accuracy' => $accuracy, - 'timestamp' => $timestamp, - 'provider' => $provider - ] - ]); - - Log::info('Location updated', [ - 'lat' => $latitude, - 'lng' => $longitude, - 'accuracy' => $accuracy - ]); - - } else { - $this->error = $error ?? 'Failed to get current location'; - Log::warning('Location request failed', ['error' => $error]); - } - } - - public function openSettings() - { - // You might want to show instructions or deep link to settings - $this->dispatch('show-settings-instructions'); - } - - public function render() - { - return view('livewire.location-tracker'); - } -} -``` - ## Understanding Permission States ### Permission Types @@ -316,45 +180,6 @@ class LocationTracker extends Component - More battery usage - Slower initial location fix -### Permission Flow - -```php -class PermissionFlowExample extends Component -{ - public function handleLocationFlow() - { - // 1. Check current permissions - Geolocation::checkPermissions(); - } - - #[On('native:' . PermissionStatusReceived::class)] - public function handleCheck($location, $coarseLocation, $fineLocation) - { - if ($coarseLocation === 'granted' || $fineLocation === 'granted') { - // 2a. Permission already granted - get location - Geolocation::getCurrentPosition(true); - } else { - // 2b. Need to request permission - Geolocation::requestPermissions(); - } - } - - #[On('native:' . PermissionRequestResult::class)] - public function handleRequest($location, $coarseLocation, $fineLocation, $message = null, $needsSettings = null) - { - if ($location === 'permanently_denied') { - // 3a. User must enable in Settings - $this->showSettingsInstructions($message); - } elseif ($coarseLocation === 'granted' || $fineLocation === 'granted') { - // 3b. Permission granted - get location - Geolocation::getCurrentPosition(true); - } else { - // 3c. Permission denied - show explanation - $this->showPermissionExplanation(); - } - } -} -``` ## Platform Support @@ -379,38 +204,6 @@ class PermissionFlowExample extends Component - **Use appropriate accuracy** - don't request fine location if coarse is sufficient - **Limit frequency** - don't request location updates constantly -### User Experience Tips - -```php -class LocationUX extends Component -{ - public function requestLocationWithExplanation() - { - // Show explanation first - $this->showExplanation = true; - } - - public function proceedWithLocationRequest() - { - $this->showExplanation = false; - - // Now request permission - Geolocation::requestPermissions(); - } - - public function handleDeniedGracefully($location, $coarseLocation, $fineLocation) - { - if ($location === 'permanently_denied') { - // Offer manual location entry - $this->showManualLocationEntry = true; - } else { - // Show benefit of enabling location - $this->showLocationBenefits = true; - } - } -} -``` - ## Accuracy and Performance ### Choosing Accuracy Level diff --git a/resources/views/docs/mobile/1/apis/overview.md b/resources/views/docs/mobile/1/apis/overview.md index 234fdbdb..87cce521 100644 --- a/resources/views/docs/mobile/1/apis/overview.md +++ b/resources/views/docs/mobile/1/apis/overview.md @@ -82,30 +82,7 @@ Execute immediately and return results: - `SecureStorage::set()` / `get()` ### Asynchronous APIs -Trigger operations and fire events when complete: -- `Camera::getPhoto()` → `PhotoTaken` event -- `Camera::pickImages()` → `MediaSelected` event -- `Biometrics::promptForBiometricID()` → `Completed` event -- `PushNotifications::enrollForPushNotifications()` → `TokenGenerated` event -- `Geolocation::getCurrentPosition()` → `LocationReceived` event -- `Geolocation::checkPermissions()` → `PermissionStatusReceived` event -- `Geolocation::requestPermissions()` → `PermissionRequestResult` event -- `Dialog::alert()` → `ButtonPressed` event - -### Event Handling -All async APIs use Laravel events with Livewire integration: - -```php -use Livewire\Attributes\On; -use Native\Mobile\Events\Camera\PhotoTaken; - -#[On('native:' . PhotoTaken::class)] -public function handlePhotoTaken(string $path) -{ - // Process the captured photo -} -``` - +[Read more about asynchronous API methods here.](/docs/mobile/1/the-basics/asynchronous-methods) ## Platform Support @@ -115,16 +92,4 @@ All APIs work on both iOS and Android with platform-appropriate implementations: - **Permissions**: Automatically handled with user prompts when required - **Fallbacks**: Graceful degradation when features aren't available -## Error Handling - -APIs provide both success and error events for proper error handling: - -```php -#[On('native:' . PhotoTaken::class)] -public function handleSuccess($data) { /* ... */ } - -#[On('native:' . PermissionDenied::class)] -public function handleError($error) { /* ... */ } -``` - -Each API documentation includes complete error handling examples and best practices. +- Each API documentation includes complete error handling examples and best practices. diff --git a/resources/views/docs/mobile/1/apis/push-notifications.md b/resources/views/docs/mobile/1/apis/push-notifications.md index 504e7124..d3b221f2 100644 --- a/resources/views/docs/mobile/1/apis/push-notifications.md +++ b/resources/views/docs/mobile/1/apis/push-notifications.md @@ -70,112 +70,20 @@ use Native\Mobile\Events\PushNotification\TokenGenerated; class NotificationManager extends Component { - public bool $isRegistered = false; - public bool $isRegistering = false; - public string $error = ''; - - public function mount() - { - // Check if already registered - $this->checkExistingRegistration(); - } - - public function enableNotifications() - { - $this->isRegistering = true; - $this->error = ''; - - // Request permission and get token - PushNotifications::enrollForPushNotifications(); - } - - #[On('native:' . TokenGenerated::class)] - public function handleTokenGenerated(string $token) + public function promptForPushNotifications() { - $this->isRegistering = false; - - try { - // Send token to your backend API - $response = Http::withToken(session('api_token')) - ->post('/api/push-tokens', [ - 'token' => $token, - 'device_id' => $this->getDeviceId(), - 'platform' => $this->getPlatform(), - 'user_id' => auth()->id() - ]); - - if ($response->successful()) { - $this->isRegistered = true; - session(['push_token' => $token]); - - Log::info('Push notification token registered', [ - 'user_id' => auth()->id(), - 'token_preview' => substr($token, 0, 10) . '...' - ]); - } else { - throw new Exception('Server rejected token registration'); - } - - } catch (Exception $e) { - $this->error = 'Failed to register for notifications: ' . $e->getMessage(); - - Log::error('Push token registration failed', [ - 'error' => $e->getMessage(), - 'user_id' => auth()->id() - ]); - } + PushNotifications::getPushNotificationsToken(); } - public function disableNotifications() + #[On('native:'. TokenGenerated::class)] + public function handlePushNotificationsToken(KitchenSinkService $service, $token) { - $token = session('push_token'); - - if ($token) { - try { - // Remove token from server - Http::withToken(session('api_token')) - ->delete("/api/push-tokens/{$token}"); - - session()->forget('push_token'); - $this->isRegistered = false; - - } catch (Exception $e) { - $this->error = 'Failed to disable notifications'; - } - } - } - - private function checkExistingRegistration() - { - $existingToken = session('push_token'); - - if ($existingToken) { - // Verify token is still valid - $currentToken = PushNotifications::getPushNotificationsToken(); - - if ($currentToken === $existingToken) { - $this->isRegistered = true; - } else { - // Token changed, need to re-register - session()->forget('push_token'); - $this->isRegistered = false; - } - } - } + $response = $service->sendForPushNotification($token); - private function getDeviceId(): string - { - if (!session()->has('device_id')) { - session(['device_id' => Str::uuid()]); + if ($response->successful()) { + nativephp_alert('Push Notification Sent!', + 'Push notifications will not display while the app is open, close the app and wait one minute to see the notification.'); } - - return session('device_id'); - } - - private function getPlatform(): string - { - // Detect platform from user agent or environment - return request()->header('X-Platform', 'unknown'); } public function render() @@ -185,140 +93,6 @@ class NotificationManager extends Component } ``` -## Backend Integration - -### Database Schema - -```php -// Migration for storing push tokens -Schema::create('push_tokens', function (Blueprint $table) { - $table->id(); - $table->foreignId('user_id')->constrained()->onDelete('cascade'); - $table->string('token')->unique(); - $table->string('device_id')->nullable(); - $table->enum('platform', ['ios', 'android', 'unknown']); - $table->timestamp('last_used_at')->nullable(); - $table->timestamps(); - - $table->index(['user_id', 'platform']); -}); -``` - -### API Controller - -```php -namespace App\Http\Controllers\Api; - -use App\Models\PushToken; -use Illuminate\Http\Request; - -class PushTokenController extends Controller -{ - public function store(Request $request) - { - $validated = $request->validate([ - 'token' => 'required|string|max:255', - 'device_id' => 'nullable|string|max:255', - 'platform' => 'required|in:ios,android,unknown' - ]); - - PushToken::updateOrCreate( - [ - 'user_id' => $request->user()->id, - 'device_id' => $validated['device_id'] - ], - [ - 'token' => $validated['token'], - 'platform' => $validated['platform'], - 'last_used_at' => now() - ] - ); - - return response()->json(['message' => 'Token registered successfully']); - } - - public function destroy(Request $request, string $token) - { - PushToken::where('user_id', $request->user()->id) - ->where('token', $token) - ->delete(); - - return response()->json(['message' => 'Token removed successfully']); - } -} -``` - -### Sending Notifications - -```php -namespace App\Services; - -use Google\Client as GoogleClient; -use Google\Service\FirebaseCloudMessaging; - -class PushNotificationService -{ - public function sendToUser(int $userId, array $notification, array $data = []) - { - $tokens = PushToken::where('user_id', $userId) - ->pluck('token') - ->toArray(); - - if (empty($tokens)) { - throw new Exception('No push tokens found for user'); - } - - return $this->sendToTokens($tokens, $notification, $data); - } - - private function sendToTokens(array $tokens, array $notification, array $data = []) - { - $client = new GoogleClient(); - $client->setAuthConfig(base_path('google-services.json')); - $client->addScope('https://www.googleapis.com/auth/firebase.messaging'); - - $fcm = new FirebaseCloudMessaging($client); - $projectId = config('services.firebase.project_id'); - - $results = []; - - foreach ($tokens as $token) { - try { - $message = [ - 'token' => $token, - 'notification' => $notification, - 'data' => array_map('strval', $data) - ]; - - $response = $fcm->projects_messages->send($projectId, [ - 'message' => $message - ]); - - $results[] = [ - 'token' => substr($token, 0, 10) . '...', - 'success' => true, - 'message_id' => $response->getName() - ]; - - } catch (Exception $e) { - $results[] = [ - 'token' => substr($token, 0, 10) . '...', - 'success' => false, - 'error' => $e->getMessage() - ]; - - // Remove invalid tokens - if (str_contains($e->getMessage(), 'registration-token-not-registered')) { - PushToken::where('token', $token)->delete(); - } - } - } - - return $results; - } -} -``` - ## Configuration Requirements ### Firebase Setup diff --git a/resources/views/docs/mobile/1/apis/secure-storage.md b/resources/views/docs/mobile/1/apis/secure-storage.md index 497fbdc5..ff95b05e 100644 --- a/resources/views/docs/mobile/1/apis/secure-storage.md +++ b/resources/views/docs/mobile/1/apis/secure-storage.md @@ -63,114 +63,6 @@ Deletes a secure value from the native keychain or keystore. **Returns:** `bool` - `true` if successfully deleted, `false` otherwise -```php -$deleted = SecureStorage::delete('api_token'); - -if ($deleted) { - // Token removed successfully -} else { - // Deletion failed or key didn't exist -} -``` - -## Example Usage - -```php -use Livewire\Component; -use Native\Mobile\Facades\SecureStorage; - -class AuthManager extends Component -{ - public bool $isLoggedIn = false; - public string $error = ''; - - public function mount() - { - // Check if user has stored credentials - $this->checkStoredAuth(); - } - - public function login(string $username, string $password) - { - try { - // Authenticate with your API - $response = Http::post('/api/login', [ - 'username' => $username, - 'password' => $password - ]); - - if ($response->successful()) { - $data = $response->json(); - - // Store tokens securely - SecureStorage::set('access_token', $data['access_token']); - SecureStorage::set('refresh_token', $data['refresh_token']); - SecureStorage::set('user_id', (string) $data['user']['id']); - - $this->isLoggedIn = true; - } else { - $this->error = 'Login failed'; - } - } catch (Exception $e) { - $this->error = 'Network error: ' . $e->getMessage(); - } - } - - public function logout() - { - // Clear stored credentials - SecureStorage::delete('access_token'); - SecureStorage::delete('refresh_token'); - SecureStorage::delete('user_id'); - - $this->isLoggedIn = false; - } - - private function checkStoredAuth() - { - $accessToken = SecureStorage::get('access_token'); - - if ($accessToken) { - // Verify token is still valid - $response = Http::withToken($accessToken) - ->get('/api/user'); - - if ($response->successful()) { - $this->isLoggedIn = true; - } else { - // Token expired, try refresh - $this->refreshToken(); - } - } - } - - private function refreshToken() - { - $refreshToken = SecureStorage::get('refresh_token'); - - if ($refreshToken) { - $response = Http::post('/api/refresh', [ - 'refresh_token' => $refreshToken - ]); - - if ($response->successful()) { - $data = $response->json(); - SecureStorage::set('access_token', $data['access_token']); - $this->isLoggedIn = true; - } else { - // Refresh failed, clear everything - $this->logout(); - } - } - } - - public function render() - { - return view('livewire.auth-manager'); - } -} -``` - ## Platform Implementation ### iOS - Keychain Services @@ -207,69 +99,4 @@ class AuthManager extends Component - Temporary data - Cached content -### Implementation Tips - -```php -class SecureSettings -{ - public function storeUserCredentials(string $userId, string $token) - { - // Use prefixed keys for organization - SecureStorage::set("user_{$userId}_token", $token); - SecureStorage::set("user_{$userId}_last_login", now()->toISOString()); - } - - public function getUserToken(string $userId): ?string - { - return SecureStorage::get("user_{$userId}_token"); - } - - public function clearUserData(string $userId) - { - // Clean up all user-related secure data - SecureStorage::delete("user_{$userId}_token"); - SecureStorage::delete("user_{$userId}_last_login"); - SecureStorage::delete("user_{$userId}_preferences"); - } - - public function rotateToken(string $userId, string $newToken) - { - // Atomic token rotation - $oldToken = $this->getUserToken($userId); - - if (SecureStorage::set("user_{$userId}_token", $newToken)) { - // New token stored successfully - Log::info("Token rotated for user {$userId}"); - } else { - // Rotation failed, keep old token - Log::error("Token rotation failed for user {$userId}"); - } - } -} -``` - -## Error Handling - -```php -public function storeSecurely(string $key, string $value) -{ - $attempts = 0; - $maxAttempts = 3; - - while ($attempts < $maxAttempts) { - if (SecureStorage::set($key, $value)) { - return true; - } - - $attempts++; - usleep(100000); // Wait 100ms before retry - } - - Log::error("Failed to store secure value after {$maxAttempts} attempts", [ - 'key' => $key - ]); - - return false; -} -``` diff --git a/resources/views/docs/mobile/1/concepts/ci-cd.md b/resources/views/docs/mobile/1/concepts/ci-cd.md index e2279813..9e13029a 100644 --- a/resources/views/docs/mobile/1/concepts/ci-cd.md +++ b/resources/views/docs/mobile/1/concepts/ci-cd.md @@ -41,6 +41,8 @@ php artisan native:run android --build=bundle --no-tty ### Packaging Command +> **Note**: ICU support is currently only available on Android. We are working to add iOS support as soon as possible and will remove this note when it becomes available. + Package signed releases for distribution: ```bash @@ -62,10 +64,6 @@ ANDROID_KEYSTORE_PASSWORD="your-keystore-password" ANDROID_KEY_ALIAS="your-key-alias" ANDROID_KEY_PASSWORD="your-key-password" -# Push Notifications (optional) -FCM_SERVER_KEY="your-fcm-server-key" -GOOGLE_SERVICE_ACCOUNT_KEY="/path/to/service-account.json" - # App Configuration NATIVEPHP_APP_ID="com.yourcompany.yourapp" NATIVEPHP_APP_VERSION="1.0.0" @@ -86,6 +84,7 @@ Overwrites existing files and directories: - Useful for clean builds in CI - Ensures fresh installation of NativePHP scaffolding - Prevents build failures from existing files +- Do this whenever you are updating the `nativephp/mobile` package. ### Build Types - `--build=debug`: Development builds with debugging enabled @@ -118,7 +117,7 @@ export ANDROID_KEY_PASSWORD="your-key-password" php artisan native:package android --build-type=release --output=./artifacts --no-tty ``` -## Common CI/CD Workflows +## Common CI Workflows ### Development Pipeline 1. Install dependencies: `composer install` @@ -135,7 +134,7 @@ php artisan native:package android --build-type=release --output=./artifacts --n ### Play Store Pipeline 1. Same as release pipeline through step 3 2. Package bundle: `native:package android --build-type=bundle --no-tty` -3. Upload to Play Console via API +3. Upload to Play Console ## Error Handling @@ -158,7 +157,6 @@ Cache these directories in CI for faster builds: - `nativephp/android/` (Android project) - Android SDK components - ### Optimization - Use `--no-dev` for production Composer installs - Enable Composer autoloader optimization diff --git a/resources/views/docs/mobile/1/concepts/databases.md b/resources/views/docs/mobile/1/concepts/databases.md index df70557b..b84e3d1e 100644 --- a/resources/views/docs/mobile/1/concepts/databases.md +++ b/resources/views/docs/mobile/1/concepts/databases.md @@ -128,142 +128,6 @@ $response = Http::withToken($this->getStoredToken()) [Laravel Sanctum](https://laravel.com/docs/sanctum) is the perfect solution for API authentication between your mobile app and Laravel backend. It provides secure, token-based authentication without the complexity of OAuth. -**Backend Setup:** -```php -// Install Sanctum in your API backend -composer require laravel/sanctum - -// Create login endpoint -Route::post('/login', function (Request $request) { - $credentials = $request->validate([ - 'email' => 'required|email', - 'password' => 'required' - ]); - - if (Auth::attempt($credentials)) { - $user = Auth::user(); - $token = $user->createToken('mobile-app')->plainTextToken; - - return response()->json([ - 'token' => $token, - 'user' => $user - ]); - } - - return response()->json(['error' => 'Invalid credentials'], 401); -}); - -// Protected API routes -Route::middleware('auth:sanctum')->group(function () { - Route::get('/user', function (Request $request) { - return $request->user(); - }); - - Route::get('/data', function (Request $request) { - // Your protected data endpoints - return Data::where('user_id', $request->user()->id)->get(); - }); -}); -``` - -### Secure Token Storage - -Use the [SecureStorage API](/docs/mobile/1/apis/secure-storage) to securely store authentication tokens on the device: - -```php -use Native\Mobile\Facades\SecureStorage; -use Illuminate\Support\Facades\Http; - -class ApiAuthManager extends Component -{ - public bool $isAuthenticated = false; - public string $error = ''; - - public function mount() - { - $this->checkStoredAuthentication(); - } - - public function login(string $email, string $password) - { - try { - $response = Http::post('https://your-api.com/api/login', [ - 'email' => $email, - 'password' => $password - ]); - - if ($response->successful()) { - $data = $response->json(); - - // Store token securely in device keychain/keystore - SecureStorage::set('api_token', $data['token']); - SecureStorage::set('user_data', json_encode($data['user'])); - - $this->isAuthenticated = true; - $this->error = ''; - } else { - $this->error = 'Invalid credentials'; - } - } catch (Exception $e) { - $this->error = 'Network error: ' . $e->getMessage(); - } - } - - public function logout() - { - // Revoke token on server - $token = SecureStorage::get('api_token'); - if ($token) { - Http::withToken($token)->post('https://your-api.com/api/logout'); - } - - // Clear local storage - SecureStorage::delete('api_token'); - SecureStorage::delete('user_data'); - - $this->isAuthenticated = false; - } - - private function checkStoredAuthentication() - { - $token = SecureStorage::get('api_token'); - - if ($token) { - // Verify token is still valid - $response = Http::withToken($token) - ->get('https://your-api.com/api/user'); - - if ($response->successful()) { - $this->isAuthenticated = true; - } else { - // Token expired or invalid - SecureStorage::delete('api_token'); - SecureStorage::delete('user_data'); - } - } - } - - public function makeAuthenticatedRequest(string $endpoint) - { - $token = SecureStorage::get('api_token'); - - if (!$token) { - throw new Exception('No authentication token available'); - } - - $response = Http::withToken($token) - ->get("https://your-api.com/api/{$endpoint}"); - - if ($response->status() === 401) { - // Token expired - $this->logout(); - throw new Exception('Authentication expired'); - } - - return $response->json(); - } -} -``` ### Best Practices @@ -281,44 +145,4 @@ class ApiAuthManager extends Component - Use HTTPS with proper SSL certificates - Log all authentication attempts and API access -**Token Management:** -```php -class TokenManager -{ - public function refreshTokenIfNeeded(): bool - { - $token = SecureStorage::get('api_token'); - $tokenExpiry = SecureStorage::get('token_expiry'); - - if (!$token || ($tokenExpiry && now()->timestamp > $tokenExpiry)) { - return $this->refreshToken(); - } - - return true; - } - - private function refreshToken(): bool - { - $refreshToken = SecureStorage::get('refresh_token'); - - if (!$refreshToken) { - return false; - } - - $response = Http::post('https://your-api.com/api/refresh', [ - 'refresh_token' => $refreshToken - ]); - - if ($response->successful()) { - $data = $response->json(); - SecureStorage::set('api_token', $data['access_token']); - SecureStorage::set('token_expiry', $data['expires_at']); - return true; - } - - return false; - } -} -``` - This approach ensures your mobile app remains secure while providing seamless access to your backend data through a well-designed API layer. diff --git a/resources/views/docs/mobile/1/the-basics/icu-support.md b/resources/views/docs/mobile/1/the-basics/icu-support.md index 90750151..3583a7f6 100644 --- a/resources/views/docs/mobile/1/the-basics/icu-support.md +++ b/resources/views/docs/mobile/1/the-basics/icu-support.md @@ -7,6 +7,8 @@ order: 300 ICU (International Components for Unicode) is a library that provides robust Unicode and locale support for applications. While NativePHP Mobile includes a lightweight PHP runtime by default, you can optionally enable ICU support for applications that require advanced internationalization features. +> **Note**: ICU support is currently only available on Android. We are working to add iOS support as soon as possible and will remove this note when it becomes available. + ## What is ICU? ICU provides: @@ -18,14 +20,6 @@ ICU provides: ## When Do You Need ICU? -### Required for: -- **Number formatting** with locale-specific rules -- **Date/time formatting** with localized patterns -- **Currency formatting** with proper symbols and rules -- **String collation** for sorting in different languages -- **Text normalization** and case conversion -- **Complex text rendering** for right-to-left languages - ### Specifically Required for: - **[Filament](https://filamentphp.com/)** - Uses `intl` extension extensively - **Laravel's localization helpers** that depend on `intl` @@ -58,74 +52,9 @@ If you already installed without ICU and need to add it: php artisan native:install --force ``` -## Size Considerations - -| PHP Runtime | Size | Use Case | -|-------------|-------|----------| -| Standard (no ICU) | ~7MB | Basic apps, simple localization | -| With ICU | ~34MB | Filament, complex i18n, number formatting | - -The ICU-enabled runtime is approximately **5x larger**, so only enable it if you specifically need these features. - -## Feature Comparison - -### Without ICU (Default) - -```php -// ✅ Works - Basic functionality -$date = now()->format('Y-m-d'); -$number = number_format(1234.56, 2); - -// ❌ Won't work - Requires intl extension -$formatter = new NumberFormatter('en_US', NumberFormatter::CURRENCY); -$collator = new Collator('en_US'); -``` - -### With ICU Enabled - -```php -// ✅ All basic functionality works -$date = now()->format('Y-m-d'); -$number = number_format(1234.56, 2); - -// ✅ Advanced internationalization works -$formatter = new NumberFormatter('en_US', NumberFormatter::CURRENCY); -$price = $formatter->formatCurrency(1234.56, 'USD'); // $1,234.56 - -$collator = new Collator('en_US'); -$result = $collator->compare('apple', 'äpple'); // Proper Unicode comparison -``` - - ## Performance Considerations ### Memory Usage - ICU adds ~28MB to your app size - Runtime memory usage increases slightly - Complex formatting operations are slower - -## Decision Matrix - -Use this matrix to decide if you need ICU support: - -| Feature Needed | ICU Required | Alternative | -|----------------|--------------|-------------| -| Basic number formatting | ❌ | `number_format()` | -| Locale-specific currency | ✅ | Manual formatting | -| Date localization | ✅ | Carbon with locales | -| String sorting (non-ASCII) | ✅ | Basic `sort()` | -| Filament admin panel | ✅ | Custom admin | -| Multi-language text processing | ✅ | Limited alternatives | -| Unicode normalization | ✅ | Basic string functions | - -## Best Practices - -1. **Evaluate early** - Decide on ICU support before building your app -2. **Design for fallbacks** - Always provide non-ICU alternatives -3. **Cache formatters** - Reuse NumberFormatter and Collator instances -4. **Test both scenarios** - Test your app with and without ICU -5. **Document requirements** - Clearly state if your app requires ICU -6. **Monitor app size** - Consider the trade-off between features and size -7. **Profile performance** - ICU operations can be slower than simple alternatives - -Choose ICU support if you're building a truly international app, using Filament, or need advanced text processing. For simpler apps, the default lightweight runtime is usually sufficient. From 14bbdf7396f6c7efe23174c4d25f1613e14e89a6 Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Thu, 10 Jul 2025 13:05:42 -0400 Subject: [PATCH 14/16] Simplify versioning documentation with Laravel-inspired approach - Reduce length by 70% while maintaining all essential information - Add explicit semantic versioning reference and link - Adopt Laravel's direct, confident tone and structure - Focus on practical information developers need - Remove verbose examples and repetitive explanations - Maintain mobile-specific versioning concepts (patch vs minor releases) - Keep OTA compatibility messaging and composer integration guidance --- .../mobile/1/getting-started/versioning.md | 167 +++--------------- 1 file changed, 22 insertions(+), 145 deletions(-) diff --git a/resources/views/docs/mobile/1/getting-started/versioning.md b/resources/views/docs/mobile/1/getting-started/versioning.md index 5ed474ef..c0d1b7f3 100644 --- a/resources/views/docs/mobile/1/getting-started/versioning.md +++ b/resources/views/docs/mobile/1/getting-started/versioning.md @@ -3,169 +3,46 @@ title: Release Notes order: 600 --- -## NativePHP Mobile Versioning Strategy +## Version Policy -This document outlines how NativePHP Mobile itself will be versioned moving forward. As we develop tools to assist with OTA updates and full app bundles, our versioning strategy ensures predictable release cycles and update compatibility. +NativePHP Mobile follows [semantic versioning](https://semver.org) with a mobile-specific approach that distinguishes between Laravel-only changes and native code changes. This ensures predictable updates and optimal over-the-air (OTA) compatibility. -**Note**: You can version your own applications however you prefer - including codenames, custom schemes, or any versioning pattern that works for your project. This document specifically covers how NativePHP Mobile releases will be versioned. - -## Our Release Types - -### Patch Releases (Laravel-Only Changes) - -When NativePHP Mobile updates contain only Laravel/PHP code changes, we will increment the patch version: - -- **Laravel code improvements** - Core functionality, API enhancements, bug fixes -- **Configuration updates** - Default settings, environment handling -- **Documentation updates** - README, guides, examples -- **Dependency updates** - Composer package updates that don't affect native code - -**Version Pattern**: `X.Y.Z` → `X.Y.Z+1` (patch increment) - -```bash -# Example: Laravel bug fix in NativePHP Mobile -1.2.3 → 1.2.4 -``` - -**Deployment**: These updates can be consumed immediately via Composer without requiring developers to rebuild their native applications. - -### Minor Releases (Native Code Changes) - -When NativePHP Mobile updates require changes to Kotlin/Swift code, we will increment the minor version: - -- **Kotlin/Swift code changes** - New native functionality, native bug fixes -- **New native features** - Camera integration, location services, push notifications -- **Native dependency updates** - Changes to native SDKs or libraries -- **Platform-specific implementations** - iOS or Android specific code changes - -**Version Pattern**: `X.Y.Z` → `X.Y+1.0` (minor increment, reset patch to 0) - -```bash -# Example: New camera feature requiring native code updates -1.2.3 → 1.3.0 -``` - -**Deployment**: These updates require developers to rebuild their applications and submit new versions to app stores. - -## Why This Versioning Strategy Matters - -By following this consistent versioning pattern, we guarantee: - -1. **Predictable Update Impact** - Patch versions are safe to update without rebuilding apps -2. **Clear Breaking Change Communication** - Minor versions signal when native rebuilds are required -3. **OTA Update Compatibility** - Our tools can safely determine which updates can be deployed over-the-air -4. **Developer Confidence** - You know exactly what each version bump means for your development workflow - -## Version Examples - -### Patch Version Scenarios - -```bash -# Bug fix in package command -1.2.3 → 1.2.4 - -# New Artisan command added -1.2.4 → 1.2.5 - -# Change the way we move files around on Windows -1.2.5 → 1.2.6 - -# Composer dependency security update -1.2.6 → 1.2.7 -``` - -### Minor Version Scenarios - -```bash -# New camera API integration -1.2.7 → 1.3.0 - -# iOS-specific push notification changes -1.3.0 → 1.4.0 - -# Android permissions handling update -1.4.0 → 1.5.0 - -# New native file system access -1.5.0 → 1.6.0 -``` - -## Impact on Your Development - -### For Patch Releases - -When we release a patch version: -- **Update immediately** - Run `composer update` to get the latest improvements -- **No rebuild required** - Your existing native apps continue working -- **OTA compatible** - Any improvements are immediately available to your users - -### For Minor Releases - -When we release a minor version: -- **Update package** - Run `composer update` to get the latest version -- **Reinstall native code** - Run `php artisan native:install --force` to update native components -- **Review changelog** - Check what new native features or changes are included -- **Test thoroughly** - Ensure compatibility with your existing application code -- **Submit to app stores** - New native code requires store approval - -## Release Communication +## Release Types ### Patch Releases -- **Immediate availability** - Available via Composer as soon as released -- **Minimal disruption** - No impact on your release schedule -- **Automatic compatibility** - Works with existing native builds +**Laravel/PHP code only** - Bug fixes, new Artisan commands, configuration updates, documentation, and dependency updates that don't affect native code. -### Minor Releases -- **Advance notice** - Announced ahead of time to allow planning -- **Migration guides** - Documentation for any required changes -- **Testing period** - Beta releases available for early testing +- Safe to update immediately via `composer update` +- No app rebuild required +- Compatible with existing native builds +- Perfect for OTA updates -## Implementation Details +### Minor Releases +**Native code changes** - New native APIs, Kotlin/Swift updates, platform-specific features, or native dependency changes. -### Version Code Management +- Require rebuilding your app with `php artisan native:install --force` +- Need app store submission for distribution +- Include advance notice and migration guides -Our version codes follow this pattern: +## Support Policy -```bash -# Version 1.2.3 = Version Code 123 -# Version 1.2.4 = Version Code 124 -# Version 1.3.0 = Version Code 130 -``` +### What's Safe to Update +- **Patch releases** - Update immediately without rebuilding your app +- **Minor releases** - Plan for rebuild and app store submission -### Composer Integration +### Version Constraints +Use semantic versioning constraints in your `composer.json`: ```json { "require": { - "nativephp/mobile": "^1.2.3" + "nativephp/mobile": "^1.1.0" } } ``` -Using semantic versioning constraints ensures you receive patch updates automatically while controlling when to adopt minor releases. +This automatically receives patch updates while giving you control over minor releases. ## Your Application Versioning -While NativePHP Mobile follows this structured approach, **you have complete freedom** in how you version your own applications: - -- **Semantic versioning** - `1.2.3`, `2.0.0`, etc. -- **Codenames** - `"Falcon"`, `"Eagle"`, `"Hawk"` -- **Date-based** - `2024.01.15`, `2024.02.01` -- **Build numbers** - `Build 1234`, `Version 5678` -- **Custom schemes** - Whatever works for your project - -The key is that regardless of how you version your app, NativePHP Mobile's consistent versioning ensures you always know the impact of framework updates on your development process. - -## Benefits of This Approach - -### For Developers -- **Predictable updates** - Always know what to expect from version bumps -- **Reduced friction** - Patch updates don't disrupt your release cycle -- **Clear upgrade paths** - Minor versions provide structured upgrade opportunities - -### For End Users -- **Faster improvements** - Laravel enhancements reach users immediately -- **Stable experience** - Native changes are properly tested before release -- **Reliable updates** - Consistent versioning prevents compatibility issues - -This versioning strategy enables us to deliver improvements quickly while maintaining the stability and predictability you need for production applications. +You have complete freedom in versioning your own applications - use semantic versioning, codenames, date-based versions, or any scheme that works for your project. NativePHP Mobile's consistent approach ensures you always understand the impact of framework updates regardless of your chosen versioning strategy. From 0c7714c603d9610143dd13259b6d92cdb4436c3b Mon Sep 17 00:00:00 2001 From: Shane Rosenthal Date: Thu, 10 Jul 2025 16:26:46 -0400 Subject: [PATCH 15/16] Update Browser API documentation with new methods and improved structure - Add system() method for opening URLs in device's default browser - Add auth() method for OAuth authentication flows with nativephp:// handling - Update inApp() method description with platform-specific details - Add comprehensive use cases for each browser method - Include platform behavior notes for iOS and Android implementations - Remove redundant sections and streamline documentation - Update examples to demonstrate all three browser methods --- resources/views/docs/mobile/1/apis/browser.md | 79 +++++++------------ 1 file changed, 29 insertions(+), 50 deletions(-) diff --git a/resources/views/docs/mobile/1/apis/browser.md b/resources/views/docs/mobile/1/apis/browser.md index 64fc3969..5910bf03 100644 --- a/resources/views/docs/mobile/1/apis/browser.md +++ b/resources/views/docs/mobile/1/apis/browser.md @@ -5,7 +5,7 @@ order: 250 ## Overview -The Browser API allows you to open URLs in an in-app browser that is "owned" by your application. This is essential when working with OAuth redirects since you need to provide a redirect URL that would naturally open in the user's default browser. +The Browser API provides three methods for opening URLs in mobile apps, each designed for specific use cases: in-app browsing, system browser navigation, and OAuth authentication flows. ```php use Native\Mobile\Facades\Browser; @@ -15,72 +15,51 @@ use Native\Mobile\Facades\Browser; ### `inApp()` -Opens a URL in an in-app browser window. - -**Parameters:** -- `string $url` - The URL to open in the in-app browser +Opens a URL in an embedded browser within your app using Custom Tabs (Android) or SFSafariViewController (iOS). ```php Browser::inApp('https://nativephp.com/mobile'); ``` -## Use Cases - -### OAuth Authentication +### `system()` -The in-app browser is particularly useful for OAuth flows where you need to: -- Redirect users to a third-party authentication provider -- Capture the redirect URL after authentication -- Return control to your app seamlessly +Opens a URL in the device's default browser app, leaving your application entirely. ```php -use Native\Mobile\Facades\Browser; - -class AuthController extends Component -{ - public function authenticateWithProvider() - { - // Open OAuth provider in in-app browser - Browser::inApp('https://provider.com/oauth/authorize?client_id=your_client_id&redirect_uri=your_app_scheme://oauth/callback'); - } -} +Browser::system('https://nativephp.com/mobile'); ``` -### External Content +### `auth()` -Display external content while keeping users within your app: +Opens a URL in a specialized authentication browser designed for OAuth flows with automatic `nativephp://` redirect handling. ```php -// Open documentation -Browser::inApp('https://docs.example.com/help'); +Browser::auth('https://provider.com/oauth/authorize?client_id=123&redirect_uri=nativephp://127.0.0.1/auth/callback'); +``` -// Open terms of service -Browser::inApp('https://example.com/terms'); +## Use Cases -// Open privacy policy -Browser::inApp('https://example.com/privacy'); -``` +### When to Use Each Method -## Integration with Deep Links +**`inApp()`** - Keep users within your app experience: +- Documentation, help pages, terms of service +- External content that relates to your app +- When you want users to easily return to your app -Use the in-app browser in conjunction with App/Universal/Deep links for complete OAuth flows: +**`system()`** - Full browser experience needed: +- Complex web applications +- Content requiring specific browser features +- When users need bookmarking or sharing capabilities -1. **Open OAuth provider** - Use `Browser::inApp()` to start authentication -2. **User authenticates** - User completes authentication in the in-app browser -3. **Redirect to app** - OAuth provider redirects to your app's deep link -4. **Handle in app** - Your app receives the deep link and processes the authentication +**`auth()`** - OAuth authentication flows: +- Login with WorkOS, Auth0, Google, Facebook, etc. +- Secure authentication with automatic redirects +- Isolated browser session for security -```php -use Native\Mobile\Facades\Browser; -use Livewire\Attributes\On; -use Native\Mobile\Events\DeepLink\Received; - -class OAuthHandler extends Component -{ - public function startOAuth() - { - Browser::inApp('https://github.com/login/oauth/authorize?client_id=your_client_id&redirect_uri=myapp://oauth/callback'); - } -} -``` +## Platform Behavior + +- **iOS**: Uses SFSafariViewController (inApp), Safari (system), ASWebAuthenticationSession (auth) +- **Android**: Uses Custom Tabs (inApp), default browser (system), Custom Tabs with auth handling (auth) + +The Browser API provides the right tool for each browsing scenario in your mobile application. From 639ec1dff7a4f04e7a1f4aac1fd00b6daad6daac Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Sun, 13 Jul 2025 03:24:23 +0100 Subject: [PATCH 16/16] polish (#182) --- resources/views/docs/mobile/1/_index.md | 95 ----- .../views/docs/mobile/1/apis/biometrics.md | 50 +-- resources/views/docs/mobile/1/apis/browser.md | 13 +- resources/views/docs/mobile/1/apis/camera.md | 75 +--- resources/views/docs/mobile/1/apis/dialog.md | 47 +-- .../views/docs/mobile/1/apis/geolocation.md | 104 +---- resources/views/docs/mobile/1/apis/haptics.md | 55 +-- .../views/docs/mobile/1/apis/overview.md | 95 ----- .../docs/mobile/1/apis/push-notifications.md | 111 +----- .../docs/mobile/1/apis/secure-storage.md | 27 +- resources/views/docs/mobile/1/apis/system.md | 33 +- .../views/docs/mobile/1/concepts/ci-cd.md | 14 +- .../views/docs/mobile/1/concepts/databases.md | 109 +++--- .../docs/mobile/1/concepts/deep-links.md | 259 +++---------- .../mobile/1/concepts/push-notifications.md | 282 +++----------- .../mobile/1/concepts/splash-screen-icons.md | 359 ------------------ .../mobile/1/getting-started/configuration.md | 83 +++- .../mobile/1/getting-started/development.md | 112 +++++- .../1/getting-started/environment-setup.md | 74 +++- .../mobile/1/getting-started/installation.md | 116 +++--- .../mobile/1/getting-started/introduction.md | 37 +- .../mobile/1/getting-started/quick-start.md | 35 ++ .../docs/mobile/1/getting-started/roadmap.md | 36 +- .../mobile/1/getting-started/versioning.md | 71 ++-- .../docs/mobile/1/the-basics/app-assets.md | 51 --- .../docs/mobile/1/the-basics/app-icon.md | 24 ++ .../views/docs/mobile/1/the-basics/assets.md | 28 ++ .../1/the-basics/asynchronous-methods.md | 308 --------------- .../views/docs/mobile/1/the-basics/events.md | 112 ++++++ .../docs/mobile/1/the-basics/icu-support.md | 60 --- .../mobile/1/the-basics/native-functions.md | 18 +- .../docs/mobile/1/the-basics/overview.md | 58 +++ .../mobile/1/the-basics/splash-screens.md | 17 + 33 files changed, 873 insertions(+), 2095 deletions(-) delete mode 100644 resources/views/docs/mobile/1/apis/overview.md delete mode 100644 resources/views/docs/mobile/1/concepts/splash-screen-icons.md create mode 100644 resources/views/docs/mobile/1/getting-started/quick-start.md delete mode 100644 resources/views/docs/mobile/1/the-basics/app-assets.md create mode 100644 resources/views/docs/mobile/1/the-basics/app-icon.md create mode 100644 resources/views/docs/mobile/1/the-basics/assets.md delete mode 100644 resources/views/docs/mobile/1/the-basics/asynchronous-methods.md create mode 100644 resources/views/docs/mobile/1/the-basics/events.md delete mode 100644 resources/views/docs/mobile/1/the-basics/icu-support.md create mode 100644 resources/views/docs/mobile/1/the-basics/overview.md create mode 100644 resources/views/docs/mobile/1/the-basics/splash-screens.md diff --git a/resources/views/docs/mobile/1/_index.md b/resources/views/docs/mobile/1/_index.md index 68ac888e..c719fbe2 100644 --- a/resources/views/docs/mobile/1/_index.md +++ b/resources/views/docs/mobile/1/_index.md @@ -2,98 +2,3 @@ title: Mobile order: 1 --- - -# NativePHP for Mobile - -**Build native iOS and Android apps with PHP and Laravel** - -NativePHP for Mobile revolutionizes mobile development by allowing PHP developers to create native mobile applications using the languages and frameworks they already know and love. No need to learn Swift, Kotlin, or React Native - just pure PHP and Laravel. - -## What Makes NativePHP Mobile Special? - -📱**Native Performance** - Your app runs natively on device with embedded PHP runtime -🔥**True Mobile APIs** - Access camera, biometrics, push notifications, and more -⚡ **Laravel Powered** - Use your existing Laravel skills and ecosystem -🚫**No Web Server** - Your app runs entirely on-device -🔄**Cross Platform** - Single codebase for iOS and Android - -## Quick Start - -Get your first mobile app running in minutes: - -```bash -# Install NativePHP Mobile -composer require nativephp/mobile - -# Configure your app -php artisan native:install - -# Run your app -php artisan native:run -``` - -## Current Features (v1.1) - -**Available now:** -- 📷 Camera & Gallery access -- 🔐 Biometric authentication (Face ID, Touch ID, Fingerprint) -- 🔔 Push notifications via Firebase -- 💬 Native dialogs & toasts -- 🔗 Deep links & universal links -- 📱 NFC support -- 📳 Haptic feedback & vibration -- 🔦 Flashlight control -- 📤 Native sharing -- 🔒 Secure storage (Keychain/Keystore) -- 📍 Location services - -[See the complete roadmap 🗺️](/docs/mobile/1/getting-started/roadmap) - -## Documentation Sections - -### [Getting Started](/docs/mobile/1/getting-started) -Everything you need to start building mobile apps with PHP: -- [Introduction](/docs/mobile/1/getting-started/introduction) - Learn how NativePHP Mobile works -- [Installation](/docs/mobile/1/getting-started/installation) - Set up your development environment -- [Environment Setup](/docs/mobile/1/getting-started/environment-setup) - Configure iOS and Android tools -- [Configuration](/docs/mobile/1/getting-started/configuration) - App settings and permissions -- [Development](/docs/mobile/1/getting-started/development) - Development workflow -- [Roadmap](/docs/mobile/1/getting-started/roadmap) - Current and planned features - -### [The Basics](/docs/mobile/1/the-basics) -Core concepts and fundamental knowledge: -- [Native Functions](/docs/mobile/1/the-basics/native-functions) - Understanding sync vs async APIs -- [Asynchronous Methods](/docs/mobile/1/the-basics/asynchronous-methods) - Event-driven mobile development -- [ICU Support](/docs/mobile/1/the-basics/icu-support) - International components for Unicode - -### [Concepts](/docs/mobile/1/concepts) -Important concepts for mobile app development: -- [CI/CD](/docs/mobile/1/concepts/ci-cd) - Continuous integration and deployment -- [Deep Links](/docs/mobile/1/concepts/deep-links) - Universal links, app links, and NFC -- [Push Notifications](/docs/mobile/1/concepts/push-notifications) - Firebase Cloud Messaging setup -- [Splash Screen/Icons](/docs/mobile/1/concepts/splash-screen-icons) - App branding and assets -- [Versioning](/docs/mobile/1/concepts/versioning) - App version management - -### [APIs](/docs/mobile/1/apis) -Complete API reference for all native features: -- [Biometrics](/docs/mobile/1/apis/biometrics) - Face ID, Touch ID, fingerprint authentication -- [Camera](/docs/mobile/1/apis/camera) - Photo capture and gallery access -- [Dialog](/docs/mobile/1/apis/dialog) - Alerts, toasts, and sharing -- [Geolocation](/docs/mobile/1/apis/geolocation) - GPS and location services -- [Haptics](/docs/mobile/1/apis/haptics) - Vibration and tactile feedback -- [PushNotifications](/docs/mobile/1/apis/push-notifications) - FCM token management -- [SecureStorage](/docs/mobile/1/apis/secure-storage) - Keychain and keystore operations -- [System](/docs/mobile/1/apis/system) - Flashlight and legacy methods - -### [Digging Deeper](/docs/mobile/1/digging-deeper) -Advanced topics for production apps: -- [Databases](/docs/mobile/1/digging-deeper/databases) - SQLite and data management -- [Security](/docs/mobile/1/digging-deeper/security) - Best practices and secure storage - -## Need Help? - -- **License Required** - [Purchase your license](https://nativephp.com/mobile) to get started -- **Community** - Join our Discord for support and discussions -- **Examples** - Check out the Kitchen Sink demo app (coming soon to app stores) - -Ready to build your first mobile app with PHP? [Let's get started! 🚀](/docs/mobile/1/getting-started/introduction) diff --git a/resources/views/docs/mobile/1/apis/biometrics.md b/resources/views/docs/mobile/1/apis/biometrics.md index e3f4ed2e..1a6bff46 100644 --- a/resources/views/docs/mobile/1/apis/biometrics.md +++ b/resources/views/docs/mobile/1/apis/biometrics.md @@ -5,7 +5,8 @@ order: 100 ## Overview -The Biometrics API allows you to authenticate users using their device's biometric sensors like Face ID, Touch ID, or fingerprint scanners. +The Biometrics API allows you to authenticate users using their device's biometric sensors like Face ID, Touch ID, or +fingerprint scanners. ```php use Native\Mobile\Facades\Biometrics; @@ -17,6 +18,12 @@ use Native\Mobile\Facades\Biometrics; Prompts the user for biometric authentication. +```php +use Native\Mobile\Facades\Biometrics; + +Biometrics::promptForBiometricID(); +``` + ## Events ### `Completed` @@ -27,7 +34,7 @@ Fired when biometric authentication completes (success or failure). use Livewire\Attributes\On; use Native\Mobile\Events\Biometric\Completed; -#[On('native:' . Completed::class)] +#[On('native:'.Completed::class)] public function handleBiometricAuth(bool $success) { if ($success) { @@ -40,45 +47,6 @@ public function handleBiometricAuth(bool $success) } ``` -## Example Usage - -```php -use Livewire\Component; -use Livewire\Attributes\On; -use Native\Mobile\Facades\Biometrics; -use Native\Mobile\Events\Biometric\Completed; - -class SecureArea extends Component -{ - public bool $isUnlocked = false; - public bool $isAuthenticating = false; - - public function authenticate() - { - $this->isAuthenticating = true; - Biometrics::promptForBiometricID(); - } - - #[On('native:' . Completed::class)] - public function handleBiometricAuth(bool $success) - { - $this->isAuthenticating = false; - - if ($success) { - $this->isUnlocked = true; - session(['biometric_authenticated' => true]); - } else { - $this->addError('auth', 'Biometric authentication failed'); - } - } - - public function render() - { - return view('livewire.secure-area'); - } -} -``` - ## Platform Support - **iOS:** Face ID, Touch ID diff --git a/resources/views/docs/mobile/1/apis/browser.md b/resources/views/docs/mobile/1/apis/browser.md index 5910bf03..5e51061a 100644 --- a/resources/views/docs/mobile/1/apis/browser.md +++ b/resources/views/docs/mobile/1/apis/browser.md @@ -1,11 +1,12 @@ --- title: Browser -order: 250 +order: 150 --- ## Overview -The Browser API provides three methods for opening URLs in mobile apps, each designed for specific use cases: in-app browsing, system browser navigation, and OAuth authentication flows. +The Browser API provides three methods for opening URLs, each designed for specific use cases: +in-app browsing, system browser navigation, and web authentication flows. ```php use Native\Mobile\Facades\Browser; @@ -55,11 +56,3 @@ Browser::auth('https://provider.com/oauth/authorize?client_id=123&redirect_uri=n - Login with WorkOS, Auth0, Google, Facebook, etc. - Secure authentication with automatic redirects - Isolated browser session for security - -## Platform Behavior - -- **iOS**: Uses SFSafariViewController (inApp), Safari (system), ASWebAuthenticationSession (auth) -- **Android**: Uses Custom Tabs (inApp), default browser (system), Custom Tabs with auth handling (auth) - -The Browser API provides the right tool for each browsing scenario in your mobile application. - diff --git a/resources/views/docs/mobile/1/apis/camera.md b/resources/views/docs/mobile/1/apis/camera.md index da2f750c..24841f0d 100644 --- a/resources/views/docs/mobile/1/apis/camera.md +++ b/resources/views/docs/mobile/1/apis/camera.md @@ -54,7 +54,7 @@ Fired when a photo is taken with the camera. use Livewire\Attributes\On; use Native\Mobile\Events\Camera\PhotoTaken; -#[On('native:' . PhotoTaken::class)] +#[On('native:'.PhotoTaken::class)] public function handlePhotoTaken(string $path) { // Process the captured photo @@ -72,7 +72,7 @@ Fired when media is selected from the gallery. use Livewire\Attributes\On; use Native\Mobile\Events\Gallery\MediaSelected; -#[On('native:' . MediaSelected::class)] +#[On('native:'.MediaSelected::class)] public function handleMediaSelected($success, $files, $count) { foreach ($files as $file) { @@ -82,80 +82,9 @@ public function handleMediaSelected($success, $files, $count) } ``` -## Example Usage - -```php -use Livewire\Component; -use Livewire\Attributes\On; -use Native\Mobile\Facades\Camera; -use Native\Mobile\Events\Camera\PhotoTaken; -use Native\Mobile\Events\Gallery\MediaSelected; - -class PhotoManager extends Component -{ - public array $photos = []; - public bool $isCapturing = false; - - public function takePhoto() - { - $this->isCapturing = true; - Camera::getPhoto(); - } - - public function pickFromGallery() - { - Camera::pickImages('images', true, 5); - } - - #[On('native:' . PhotoTaken::class)] - public function handlePhotoTaken(string $path) - { - $this->isCapturing = false; - $this->addPhoto($path); - } - - #[On('native:' . MediaSelected::class)] - public function handleMediaSelected($success, $files, $count) - { - foreach ($files as $file) { - $this->addPhoto($file); - } - } - - private function addPhoto(string $path) - { - $this->photos[] = [ - 'path' => $path, - 'data_url' => $this->createDataUrl($path), - 'timestamp' => now() - ]; - } - - private function createDataUrl(string $path): string - { - $data = base64_encode(file_get_contents($path)); - $mime = mime_content_type($path); - return "data:$mime;base64,$data"; - } - - public function render() - { - return view('livewire.photo-manager'); - } -} -``` - -## Platform Support - -- **iOS:** Uses UIImagePickerController and Photos framework -- **Android:** Uses `Intent.ACTION_IMAGE_CAPTURE` and gallery intents -- **Permissions:** Camera permission required for photo capture -- **File Location:** Photos saved to app's temporary directory - ## Notes - The first time your app requests camera access, users will be prompted for permission - If permission is denied, camera functions will fail silently - Captured photos are stored in the app's temporary directory -- Consider implementing cleanup for old temporary photos - File formats are platform-dependent (typically JPEG) diff --git a/resources/views/docs/mobile/1/apis/dialog.md b/resources/views/docs/mobile/1/apis/dialog.md index e66dc529..ad7bc558 100644 --- a/resources/views/docs/mobile/1/apis/dialog.md +++ b/resources/views/docs/mobile/1/apis/dialog.md @@ -39,6 +39,7 @@ Dialog::alert( Displays a brief toast notification message. + **Parameters:** - `string $message` - The message to display @@ -46,6 +47,13 @@ Displays a brief toast notification message. Dialog::toast('Item saved successfully!'); ``` +#### Good toast messages + +- Short and clear +- Great for confirmations and status updates +- Don't rely on them for critical information +- Avoid showing multiple toasts in quick succession + ### `share()` Opens the native sharing interface. @@ -77,7 +85,7 @@ Fired when a button is pressed in an alert dialog. use Livewire\Attributes\On; use Native\Mobile\Events\Alert\ButtonPressed; -#[On('native:' . ButtonPressed::class)] +#[On('native:'.ButtonPressed::class)] public function handleAlertButton($index, $label) { switch ($index) { @@ -93,40 +101,3 @@ public function handleAlertButton($index, $label) } } ``` - -## Toast Guidelines - -### Best Practices -- Keep messages short and clear -- Use for confirmations and status updates -- Don't rely on toasts for critical information -- Avoid showing multiple toasts in quick succession - -```php -// Good toast messages -Dialog::toast('Saved!'); -Dialog::toast('Photo uploaded'); -Dialog::toast('Settings updated'); - -// Avoid long messages -Dialog::toast('Your photo has been successfully uploaded to the server and will be processed shortly'); -``` - -## Platform Differences - -### iOS -- Alerts use UIAlertController -- Toasts use custom overlay views -- Sharing uses UIActivityViewController - -### Android -- Alerts use AlertDialog -- Toasts use native Toast system -- Sharing uses Intent.ACTION_SEND - -## Accessibility - -- All dialogs automatically support screen readers -- Button text should be descriptive -- Toast messages are announced by accessibility services -- Consider users with motor disabilities when designing button layouts diff --git a/resources/views/docs/mobile/1/apis/geolocation.md b/resources/views/docs/mobile/1/apis/geolocation.md index 4ca4bad9..0a2e4f84 100644 --- a/resources/views/docs/mobile/1/apis/geolocation.md +++ b/resources/views/docs/mobile/1/apis/geolocation.md @@ -69,27 +69,17 @@ Fired when location data is requested (success or failure). use Livewire\Attributes\On; use Native\Mobile\Events\Geolocation\LocationReceived; -#[On('native:' . LocationReceived::class)] -public function handleLocationReceived($success = null, $latitude = null, $longitude = null, $accuracy = null, $timestamp = null, $provider = null, $error = null) -{ - if ($success) { - // Location successfully retrieved - $this->latitude = $latitude; - $this->longitude = $longitude; - $this->accuracy = $accuracy; - $this->provider = $provider; - - Log::info('Location received', [ - 'lat' => $latitude, - 'lng' => $longitude, - 'accuracy' => $accuracy, - 'provider' => $provider - ]); - } else { - // Location request failed - $this->error = $error ?? 'Failed to get location'; - Log::warning('Location request failed', ['error' => $error]); - } +#[On('native:'.LocationReceived::class)] +public function handleLocationReceived( + $success = null, + $latitude = null, + $longitude = null, + $accuracy = null, + $timestamp = null, + $provider = null, + $error = null +) { + // ... } ``` @@ -111,20 +101,10 @@ Fired when permission status is checked. use Livewire\Attributes\On; use Native\Mobile\Events\Geolocation\PermissionStatusReceived; -#[On('native:' . PermissionStatusReceived::class)] +#[On('native:'.PermissionStatusReceived::class)] public function handlePermissionStatus($location, $coarseLocation, $fineLocation) { - $this->locationPermission = $location; - $this->coarsePermission = $coarseLocation; - $this->finePermission = $fineLocation; - - if ($coarseLocation === 'granted' || $fineLocation === 'granted') { - // At least some location permission is granted - $this->canRequestLocation = true; - } else { - // No location permissions granted - $this->showPermissionExplanation(); - } + // ... } ``` @@ -150,82 +130,26 @@ use Native\Mobile\Events\Geolocation\PermissionRequestResult; public function handlePermissionRequest($location, $coarseLocation, $fineLocation, $message = null, $needsSettings = null) { if ($location === 'permanently_denied') { - // Permission permanently denied - must go to Settings - $this->error = $message ?? 'Location permission permanently denied. Please enable in Settings.'; - $this->showSettingsPrompt = true; + $this->error = 'Location permission permanently denied. Please enable in Settings.'; } elseif ($coarseLocation === 'granted' || $fineLocation === 'granted') { - // Permission granted - can now request location - $this->permissionGranted = true; $this->getCurrentLocation(); } else { - // Permission denied but can ask again $this->error = 'Location permission is required for this feature.'; } } ``` -## Understanding Permission States - -### Permission Types - -**Coarse Location (`ACCESS_COARSE_LOCATION` on Android)** -- Network-based location (WiFi, cellular towers) -- Lower accuracy (~100-1000 meters) -- Less battery usage -- Faster location fixes - -**Fine Location (`ACCESS_FINE_LOCATION` on Android, Location Services on iOS)** -- GPS-based location -- Higher accuracy (~5-50 meters) -- More battery usage -- Slower initial location fix - - -## Platform Support - -### iOS -- Uses Core Location framework -- Requires location usage description in Info.plist -- Supports both "When in Use" and "Always" permissions -- Automatic permission prompts - -### Android -- Uses FusedLocationProviderClient (Google Play Services) -- Requires location permissions in AndroidManifest.xml -- Supports coarse and fine location permissions -- Runtime permission requests (Android 6+) - ## Privacy Considerations -### Best Practices - **Explain why** you need location access before requesting - **Request at the right time** - when the feature is actually needed - **Respect denials** - provide alternative functionality when possible - **Use appropriate accuracy** - don't request fine location if coarse is sufficient - **Limit frequency** - don't request location updates constantly -## Accuracy and Performance - -### Choosing Accuracy Level - -```php -// For general location (city-level) -Geolocation::getCurrentPosition(false); // ~100-1000m accuracy - -// For precise location (navigation, delivery) -Geolocation::getCurrentPosition(true); // ~5-50m accuracy -``` - ### Performance Considerations - **Battery Usage** - GPS uses more battery than network location - **Time to Fix** - GPS takes longer for initial position - **Indoor Accuracy** - GPS may not work well indoors - **Caching** - Consider caching recent locations for better UX -### Error Handling -- Always handle both success and failure cases -- Provide meaningful error messages to users -- Implement fallback strategies (manual entry, saved locations) -- Log errors for debugging but don't expose sensitive details - -The Geolocation API provides powerful location capabilities while respecting user privacy and platform requirements. Always handle permissions gracefully and provide clear value propositions for why location access is needed. diff --git a/resources/views/docs/mobile/1/apis/haptics.md b/resources/views/docs/mobile/1/apis/haptics.md index 6cc1e290..2ec5c28b 100644 --- a/resources/views/docs/mobile/1/apis/haptics.md +++ b/resources/views/docs/mobile/1/apis/haptics.md @@ -23,58 +23,7 @@ Triggers device vibration for tactile feedback. Haptics::vibrate(); ``` -## Example Usage +**Use haptics for:** Button presses, form validation, important notifications, game events. -### Basic Form Feedback -```php -use Livewire\Component; -use Native\Mobile\Facades\Haptics; - -class FormComponent extends Component -{ - public function save() - { - if ($this->hasErrors()) { - // Haptic feedback for errors - Haptics::vibrate(); - return; - } - - $this->saveData(); - - // Success haptic feedback - Haptics::vibrate(); - } - - public function deleteItem() - { - // Haptic feedback for important actions - Haptics::vibrate(); - $this->performDelete(); - } -} -``` - -### Best Practices -```php -class HapticsExample extends Component -{ - // ✅ Good: Button presses, form errors, important actions - public function onButtonPress() - { - Haptics::vibrate(); - $this->processAction(); - } - - // ❌ Avoid: Frequent events like scrolling - public function onScroll() - { - // Don't vibrate on every scroll - too annoying! - // Haptics::vibrate(); - } -} -``` - -**Use haptics for:** Button presses, form validation, important notifications, game events -**Avoid haptics for:** Frequent events, background processes, minor updates +**Avoid haptics for:** Frequent events, background processes, minor updates. diff --git a/resources/views/docs/mobile/1/apis/overview.md b/resources/views/docs/mobile/1/apis/overview.md deleted file mode 100644 index 87cce521..00000000 --- a/resources/views/docs/mobile/1/apis/overview.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Overview -order: 1 ---- -# API Reference - -Complete documentation for all NativePHP Mobile APIs. Each API provides access to native device capabilities through familiar PHP facades. - -## Available APIs - -### Biometrics -**Face ID, Touch ID, Fingerprint Authentication** -```php -Biometrics::promptForBiometricID(); -``` -Secure user authentication using device biometric sensors. Supports Face ID on iOS, Touch ID, and fingerprint readers on Android. - -### Camera -**Photo Capture & Gallery Access** -```php -Camera::getPhoto(); -Camera::pickImages('images', true, 5); -``` -Take photos with the device camera or select images from the photo gallery. Supports both single and multiple image selection. - -### Dialog -**Native UI Elements** -```php -Dialog::alert('Title', 'Message', $buttons, $callback); -Dialog::toast('Success message'); -Dialog::share('Title', 'Text', 'https://example.com'); -``` -Display native alerts, toast notifications, and sharing interfaces that match platform design guidelines. - -### Geolocation -**GPS & Location Services** -```php -Geolocation::getCurrentPosition(true); // High accuracy -Geolocation::checkPermissions(); -Geolocation::requestPermissions(); -``` -Access device location services with configurable accuracy levels and proper permission handling. - -### Haptics -**Vibration & Tactile Feedback** -```php -Haptics::vibrate(); -``` -Provide tactile feedback for user interactions, form validation, and important events. - -### PushNotifications -**Firebase Cloud Messaging** -```php -PushNotifications::enrollForPushNotifications(); -PushNotifications::getPushNotificationsToken(); -``` -Register devices for push notifications and manage FCM tokens for server-side notification delivery. - -### SecureStorage -**Keychain & Keystore Operations** -```php -SecureStorage::set('api_token', $token); -$token = SecureStorage::get('api_token'); -SecureStorage::delete('api_token'); -``` -Store sensitive data securely using iOS Keychain and Android Keystore with automatic encryption. - -### System -**System Functions & Legacy API** -```php -System::flashlight(); // Toggle flashlight -``` -Control system functions like the flashlight. Also provides deprecated methods that have moved to dedicated facades. - -## API Patterns - -### Synchronous APIs -Execute immediately and return results: -- `Haptics::vibrate()` -- `System::flashlight()` -- `Dialog::toast()` -- `SecureStorage::set()` / `get()` - -### Asynchronous APIs -[Read more about asynchronous API methods here.](/docs/mobile/1/the-basics/asynchronous-methods) - -## Platform Support - -All APIs work on both iOS and Android with platform-appropriate implementations: -- **iOS**: Uses native iOS frameworks and APIs -- **Android**: Uses Android SDK and native libraries -- **Permissions**: Automatically handled with user prompts when required -- **Fallbacks**: Graceful degradation when features aren't available - -- Each API documentation includes complete error handling examples and best practices. diff --git a/resources/views/docs/mobile/1/apis/push-notifications.md b/resources/views/docs/mobile/1/apis/push-notifications.md index d3b221f2..212bb675 100644 --- a/resources/views/docs/mobile/1/apis/push-notifications.md +++ b/resources/views/docs/mobile/1/apis/push-notifications.md @@ -19,27 +19,12 @@ Requests permission and enrolls the device for push notifications. **Returns:** `void` -```php -PushNotifications::enrollForPushNotifications(); -``` - ### `getPushNotificationsToken()` Retrieves the current FCM token for this device. **Returns:** `string|null` - The FCM token, or `null` if not available -```php -$token = PushNotifications::getPushNotificationsToken(); - -if ($token) { - // Send token to your server - $this->registerTokenWithServer($token); -} else { - // Token not available, enrollment may have failed -} -``` - ## Events ### `TokenGenerated` @@ -52,7 +37,7 @@ Fired when a push notification token is successfully generated. use Livewire\Attributes\On; use Native\Mobile\Events\PushNotification\TokenGenerated; -#[On('native:' . TokenGenerated::class)] +#[On('native:'.TokenGenerated::class)] public function handlePushToken(string $token) { // Send token to your backend @@ -60,65 +45,6 @@ public function handlePushToken(string $token) } ``` -## Example Usage - -```php -use Livewire\Component; -use Livewire\Attributes\On; -use Native\Mobile\Facades\PushNotifications; -use Native\Mobile\Events\PushNotification\TokenGenerated; - -class NotificationManager extends Component -{ - public function promptForPushNotifications() - { - PushNotifications::getPushNotificationsToken(); - } - - #[On('native:'. TokenGenerated::class)] - public function handlePushNotificationsToken(KitchenSinkService $service, $token) - { - $response = $service->sendForPushNotification($token); - - if ($response->successful()) { - nativephp_alert('Push Notification Sent!', - 'Push notifications will not display while the app is open, close the app and wait one minute to see the notification.'); - } - } - - public function render() - { - return view('livewire.notification-manager'); - } -} -``` - -## Configuration Requirements - -### Firebase Setup - -1. Create a Firebase project at [Firebase Console](https://console.firebase.google.com/) -2. Add your mobile app to the project -3. Download `google-services.json` (Android) and `GoogleService-Info.plist` (iOS) -4. Place these files in your Laravel project root -5. Enable push notifications in your NativePHP config: - -```php -// config/nativephp.php -return [ - 'permissions' => [ - 'push_notifications' => true, - ], -]; -``` - -### Environment Variables - -```bash -NATIVEPHP_APP_ID=com.yourcompany.yourapp -FIREBASE_PROJECT_ID=your-firebase-project-id -``` - ## Permission Flow 1. User taps "Enable Notifications" @@ -130,43 +56,8 @@ FIREBASE_PROJECT_ID=your-firebase-project-id 7. Backend stores token for user 8. Server can now send notifications to this device -## Error Handling - -```php -public function handleRegistrationFailure() -{ - // Common failure scenarios: - - // 1. User denied permission - if (!$this->hasNotificationPermission()) { - $this->showPermissionExplanation(); - return; - } - - // 2. Network error - if (!$this->hasNetworkConnection()) { - $this->showNetworkError(); - return; - } - - // 3. Firebase configuration missing - if (!$this->hasFirebaseConfig()) { - Log::error('Firebase configuration missing'); - return; - } - - // 4. Backend API error - $this->showGenericError(); -} -``` - - ## Best Practices - Request permission at the right time (not immediately on app launch) - Explain the value of notifications to users - Handle permission denial gracefully -- Clean up invalid tokens on your backend -- Implement retry logic for network failures -- Log registration events for debugging -- Respect user preferences and provide opt-out diff --git a/resources/views/docs/mobile/1/apis/secure-storage.md b/resources/views/docs/mobile/1/apis/secure-storage.md index ff95b05e..14256a6e 100644 --- a/resources/views/docs/mobile/1/apis/secure-storage.md +++ b/resources/views/docs/mobile/1/apis/secure-storage.md @@ -5,7 +5,8 @@ order: 700 ## Overview -The SecureStorage API provides secure storage using the device's native keychain (iOS) or keystore (Android) for sensitive data like tokens, passwords, and user credentials. +The SecureStorage API provides secure storage using the device's native keychain (iOS) or keystore (Android). It's +ideal for storing sensitive data like tokens, passwords, and user credentials. ```php use Native\Mobile\Facades\SecureStorage; @@ -24,13 +25,7 @@ Stores a secure value in the native keychain or keystore. **Returns:** `bool` - `true` if successfully stored, `false` otherwise ```php -$success = SecureStorage::set('api_token', 'abc123xyz'); - -if ($success) { - // Token stored securely -} else { - // Storage failed -} +SecureStorage::set('api_token', 'abc123xyz'); ``` ### `get()` @@ -44,14 +39,6 @@ Retrieves a secure value from the native keychain or keystore. ```php $token = SecureStorage::get('api_token'); - -if ($token) { - // Use the retrieved token - $this->authenticateWithToken($token); -} else { - // Token not found, user needs to login - $this->redirectToLogin(); -} ``` ### `delete()` @@ -84,18 +71,16 @@ Deletes a secure value from the native keychain or keystore. - **System Protection:** Protected by device authentication - **Tamper Resistance:** Hardware-backed security when available -## Best Practices - -### What to Store +## What to Store - API tokens and refresh tokens - User credentials (if necessary) - Encryption keys - Sensitive user preferences - Two-factor authentication secrets -### What NOT to Store +## What NOT to Store - Large amounts of data (use encrypted database instead) -- Non-sensitive configuration +- Non-sensitive data - Temporary data - Cached content diff --git a/resources/views/docs/mobile/1/apis/system.md b/resources/views/docs/mobile/1/apis/system.md index aec383fd..de949e96 100644 --- a/resources/views/docs/mobile/1/apis/system.md +++ b/resources/views/docs/mobile/1/apis/system.md @@ -23,33 +23,14 @@ Toggles the device flashlight (camera flash LED) on and off. System::flashlight(); // Toggle flashlight state ``` -## Example Usage +### `isIos()` -```php -use Livewire\Component; -use Native\Mobile\Facades\System; +Determines if the current device is running iOS. -class FlashlightController extends Component -{ - public bool $isFlashlightOn = false; - - public function toggleFlashlight() - { - System::flashlight(); - $this->isFlashlightOn = !$this->isFlashlightOn; - } - - public function render() - { - return view('livewire.flashlight-controller'); - } -} -``` +**Returns:** `true` if iOS, `false` otherwise + +### `isAndroid()` -## Platform Support +Determines if the current device is running Android. -### Flashlight -- **iOS:** Controls camera flash LED -- **Android:** Controls camera flash LED -- **Permissions:** None required -- **Limitations:** May not work if camera is currently in use \ No newline at end of file +**Returns:** `true` if Android, `false` otherwise diff --git a/resources/views/docs/mobile/1/concepts/ci-cd.md b/resources/views/docs/mobile/1/concepts/ci-cd.md index 9e13029a..0e05bd2c 100644 --- a/resources/views/docs/mobile/1/concepts/ci-cd.md +++ b/resources/views/docs/mobile/1/concepts/ci-cd.md @@ -5,7 +5,8 @@ order: 500 ## Overview -NativePHP Mobile provides robust CLI commands designed for automated CI/CD environments. With proper configuration, you can build, package, and deploy mobile apps without manual intervention. +NativePHP for Mobile provides robust CLI commands designed for automated CI/CD environments. With proper configuration, +you can build, package, and deploy mobile apps without manual intervention. ## Key Commands for CI/CD @@ -13,7 +14,7 @@ NativePHP Mobile provides robust CLI commands designed for automated CI/CD envir Install NativePHP dependencies in automated environments: -```bash +```shell # Install Android platform, overwriting existing files php artisan native:install android --force --no-tty @@ -28,7 +29,7 @@ php artisan native:install both --force Build your app for different environments: -```bash +```shell # Build debug version (development) php artisan native:run android --build=debug --no-tty @@ -41,8 +42,6 @@ php artisan native:run android --build=bundle --no-tty ### Packaging Command -> **Note**: ICU support is currently only available on Android. We are working to add iOS support as soon as possible and will remove this note when it becomes available. - Package signed releases for distribution: ```bash @@ -63,11 +62,6 @@ ANDROID_KEYSTORE_FILE="/path/to/keystore.jks" ANDROID_KEYSTORE_PASSWORD="your-keystore-password" ANDROID_KEY_ALIAS="your-key-alias" ANDROID_KEY_PASSWORD="your-key-password" - -# App Configuration -NATIVEPHP_APP_ID="com.yourcompany.yourapp" -NATIVEPHP_APP_VERSION="1.0.0" -NATIVEPHP_APP_VERSION_CODE="1" ``` ## Command Line Options diff --git a/resources/views/docs/mobile/1/concepts/databases.md b/resources/views/docs/mobile/1/concepts/databases.md index b84e3d1e..4e66ca96 100644 --- a/resources/views/docs/mobile/1/concepts/databases.md +++ b/resources/views/docs/mobile/1/concepts/databases.md @@ -27,27 +27,24 @@ upon foreign key constraints, [you need to enable SQLite support for them](https **It's important to test your migrations on prod builds before releasing updates!** You don't want to accidentally delete your user's data when they update your app. -## Seeding Data with Migrations +## Seeding data with migrations -Migrations are the perfect mechanism for seeding data in mobile applications. They provide the natural behavior you want for data seeding: +Migrations are the perfect mechanism for seeding data in mobile applications. They provide the natural behavior you +want for data seeding: -- **Run once**: Each migration runs exactly once per device -- **Tracked**: Laravel tracks which migrations have been executed -- **Versioned**: New app versions can include new data seeding migrations -- **Reversible**: You can create migrations to remove or update seed data +- **Run once**: Each migration runs exactly once per installation. +- **Tracked**: Laravel tracks which migrations have been executed. +- **Versioned**: New app versions can include new data seeding migrations. +- **Reversible**: You can create migrations to remove or update seed data. -### Creating Seed Migrations +### Creating seed migrations Create dedicated migrations for seeding data: -```bash -php artisan make:migration seed_default_categories +```shell php artisan make:migration seed_app_settings -php artisan make:migration seed_initial_user_data ``` -### Example: Seeding Default Categories - ```php use Illuminate\Database\Migrations\Migration; use Illuminate\Support\Facades\DB; @@ -61,44 +58,42 @@ return new class extends Migration ['name' => 'Personal', 'color' => '#10B981'], ]); } - - public function down() - { - // Rollback/fresh migrations never run - } }; ``` -### Best Practices for Seed Migrations +### Test thoroughly -1. **Use specific timestamps**: Name migrations with dates to ensure proper ordering -2. **Check before inserting**: Prevent duplicate data with updateOrCreate() or firstOrCreate() -3. **Handle conflicts gracefully**: Check if data already exists before seeding -4. **Use realistic data**: Seed with data that matches your production environment -5. **Test thoroughly**: Verify seed migrations work on fresh installs and updates +This is the most important step when releasing new versions of your app, especially with new migrations. -This approach ensures your mobile app has the initial data it needs while maintaining consistency across different app versions and user devices. +Your migrations should work both for users who are installing your app for the first time (or re-installing) _and_ +users who have updated your app to a new release. + +Make sure you test your migrations under the different scenarios that your users' databases are likely to be in. ## Things to note - As your app is installed on a separate device, you do not have remote access to the database. - If a user deletes your application from their device, any databases are also deleted. -## Database Security & Remote Data +## Can I get MySQL/Postgres/other support? + +No. -### Why No MySQL/PostgreSQL Support? +SQLite being the only supported database driver is a deliberate security decision to prevent developers from +accidentally embedding production database credentials directly in mobile applications. Why? -NativePHP for Mobile intentionally does not include MySQL, PostgreSQL, or other remote database drivers. This is a deliberate security decision to prevent developers from accidentally embedding production database credentials directly in mobile applications. +- Mobile apps are distributed to user devices and can be reverse-engineered. +- Database credentials embedded in apps may be accessible to anyone with the app binary. +- Direct database connections bypass important security layers like rate limiting and access controls. +- Network connectivity issues make direct database connections unreliable from mobile devices and can be troublesome + for your database to handle. -**Key security concerns:** -- Mobile apps are distributed to user devices and can be reverse-engineered -- Database credentials embedded in apps are accessible to anyone with the app binary -- Direct database connections bypass important security layers like rate limiting and access controls -- Network connectivity issues make direct database connections unreliable on mobile +## API-first -### The API-First Approach +If a key part of your application relies on syncing data between a central database and your client apps, we strongly +recommend that you do so via a secure API backend that your mobile app can communicate with. -Instead of direct database connections, we strongly recommend building a secure API backend that your mobile app communicates with. This provides multiple security and architectural benefits: +This provides multiple security and architectural benefits: **Security Benefits:** - Database credentials never leave your server @@ -113,36 +108,42 @@ Instead of direct database connections, we strongly recommend building a secure - Version your API for backward compatibility - Transform data specifically for mobile consumption -### Recommended Architecture +### Securing your API -```php -// ❌ NEVER DO THIS - Direct database in mobile app -DB::connection('production')->table('users')->get(); // Credentials exposed! +For the same reasons that you shouldn't share database credentials in your `.env` file or elsewhere in your app code, +you shouldn't store API keys or tokens either. -// ✅ DO THIS - Secure API communication -$response = Http::withToken($this->getStoredToken()) - ->get('https://your-api.com/api/users'); -``` +If anything, you should provide a client key that **only** allows client apps to request tokens. Once you have +authenticated your user, you can pass an access token back to your mobile app and use this for communicating with your +API. -### Laravel Sanctum Integration +Store these tokens on your users' devices securely with the [`SecureStorage`](/docs/mobile/1/apis/secure-storage) API. -[Laravel Sanctum](https://laravel.com/docs/sanctum) is the perfect solution for API authentication between your mobile app and Laravel backend. It provides secure, token-based authentication without the complexity of OAuth. +It's a good practice to ensure these tokens have high entropy so that they are very hard to guess and a short lifespan. +Generating tokens is cheap; leaking personal customer data can get _very_ expensive! +Use industry-standard tools like OAuth-2.0-based providers, Laravel Passport, or Laravel Sanctum. -### Best Practices + + +#### Considerations + +In your mobile apps: + +- Always store API tokens using `SecureStorage` - Use HTTPS for all API communications -- Implement retry logic with exponential backoff +- Cache data locally using SQLite for offline functionality +- Check for connectivity before making API calls -**For API Backends:** -- Use Laravel Sanctum or similar for token-based authentication +And on the API side: + +- Use token-based authentication - Implement rate limiting to prevent abuse - Validate and sanitize all input data - Use HTTPS with proper SSL certificates - Log all authentication attempts and API access - -This approach ensures your mobile app remains secure while providing seamless access to your backend data through a well-designed API layer. diff --git a/resources/views/docs/mobile/1/concepts/deep-links.md b/resources/views/docs/mobile/1/concepts/deep-links.md index 12912676..3bb8a305 100644 --- a/resources/views/docs/mobile/1/concepts/deep-links.md +++ b/resources/views/docs/mobile/1/concepts/deep-links.md @@ -1,249 +1,92 @@ --- -title: Deep, Universal, App Links and NFC +title: Deep Links order: 300 --- ## Overview -NativePHP for Mobile supports both **deep linking** and **web-based linking** into your mobile apps. +NativePHP for Mobile supports **deep linking** into your app via Custom URL Schemes and Associated Domains: -There are two types of link integrations you can configure: +- **Custom URL Scheme** + ``` + myapp://some/path + ``` +- **Associated Domains** (a.k.a. Universal Links on iOS, App Links on Android) + ``` + https://example.net/some/path + ``` -- **Deep Links** (myapp://some/path) -- **Universal Links (iOS)** and **App Links (Android)** (https://example.net/some/path) +In each case, your app can be opened directly at the route matching `/some/path`. -Each method has its use case, and NativePHP handles all the platform-specific configuration automatically when you provide the proper environment variables. +Each method has its use cases, and NativePHP handles all the platform-specific configuration automatically when you +provide the proper environment variables. ---- - -## Deep Links - -Deep links use a **custom URL scheme** to open your app. - -For example: - -``` -myapp://profile/123 -``` - -When a user taps a deep link, the mobile operating system detects the custom scheme and opens your app directly. +You can even use both approaches at the same time in a single app! -### Configuration +## Custom URL Scheme -To enable deep linking, you must define: +Custom URL schemes are a great way to allow apps to pass data between themselves. If your app is installed when a user +uses a deep link that incorporates your custom scheme, your app will open immediately to the desired route. -- **Scheme**: The protocol (e.g., myapp) -- **Host**: An optional domain-like segment (e.g., open) +But note that custom URL schemes can only work when your app has been installed and cannot aid in app discovery. If a +user interacts with URL with a custom scheme for an app they don't have installed, there will be no prompt to install +an app that can load that URL. -These are configured in your .env: +To enable your app's custom URL scheme, define it in your `.env`: ```dotenv NATIVEPHP_DEEPLINK_SCHEME=myapp -NATIVEPHP_DEEPLINK_HOST=open -``` - -### Platform Behavior - -- **iOS**: Deep links work immediately after installation -- **Android**: Deep links work immediately after installation -- **Both**: Apps will open directly when deep links are tapped - -## Universal Links (iOS) and App Links (Android) - -Universal Links and App Links allow real HTTPS URLs to open your app instead of a web browser, if the app is installed. - -For example: -```dotenv -https://example.net/property/456 ``` -### User Experience +You should choose a scheme that is unique to your app to avoid confusion with other apps. Note that some schemes are +reserved by the system and cannot be used (e.g. `https`). -When a user taps this link: +## Associated domains -- **If your app is installed**: It opens directly into the app -- **If not installed**: It opens normally in the browser -- **Seamless fallback**: No broken experience for users without the app +Universal Links/App Links allow real HTTPS URLs to open your app instead of in a web browser, if the app is installed. +If the app is not installed, the URL will load as normal in the browser. -This provides a seamless user experience without needing a custom scheme. +This flow increases the opportunity for app discovery dramatically and provides a much better overall user experience. -### How It Works +### How it works -1. You must prove to iOS and Android that you own the domain by hosting special files: +1. You must prove to the operating system on the user's device that your app is legitimately associated with the domain + you are trying to redirect by hosting special files on your server: - `.well-known/apple-app-site-association` (for iOS) - `.well-known/assetlinks.json` (for Android) 2. The mobile OS reads these files to verify the link association -3. Once verified, tapping a real URL will open your app instead of Safari or Chrome +3. Once verified, tapping a real URL will open your app instead of opening it in the user's browser -**NativePHP handles all the technical setup automatically** - you just need to host the verification files and configure your domain. +**NativePHP handles all the technical setup automatically** - you just need to host the verification files and +configure your domain correctly. -### Configuration - -To enable Universal Links and App Links, you must define: - -- **Host**: The domain name (e.g., example.net) - -These are configured in your .env: - -```dotenv -NATIVEPHP_DEEPLINK_HOST=example.net -``` - -#### Complete Configuration Example +To enable an app-associated domain, define it in your `.env`: ```dotenv -# For both deep links and universal/app links -NATIVEPHP_DEEPLINK_SCHEME=myapp NATIVEPHP_DEEPLINK_HOST=example.net - -# Your app will respond to: -# myapp://profile/123 (deep link) -# https://example.net/profile/123 (universal/app link) -``` - -## Domain Verification - -### Required Files - -The app stores generate the content for these files, but you must host them on your domain: - -#### iOS - `.well-known/apple-app-site-association` - -```json -{ - "applinks": { - "details": [ - { - "appIDs": ["TEAM_ID.com.yourcompany.yourapp"], - "components": [ - { - "*": "*" - } - ] - } - ] - } -} -``` - -#### Android - `.well-known/assetlinks.json` - -```json -[{ - "relation": ["delegate_permission/common.handle_all_urls"], - "target": { - "namespace": "android_app", - "package_name": "com.yourcompany.yourapp", - "sha256_cert_fingerprints": ["SHA256_FINGERPRINT"] - } -}] ``` -### Verification Process +## Testing & troubleshooting -1. **iOS**: Apple's servers periodically check the `apple-app-site-association` file -2. **Android**: Google Play Services verifies the `assetlinks.json` file -3. **Both**: Files must be accessible via HTTPS without redirects -4. **Content-Type**: Serve files as `application/json` - -### Automatic Configuration - -NativePHP automatically: -- Configures iOS `associatedDomains` in your app -- Sets up Android `intentFilters` for your domain -- Generates the correct fingerprints and app IDs -- Handles platform-specific URL routing - -## Platform-Specific Behavior - -### iOS Universal Links - -- **Immediate**: Work as soon as the app is installed -- **Smart Banner**: iOS can display an install banner if app isn't installed -- **Fallback**: Opens in Safari if app isn't installed -- **Cross-app**: Work from any app, not just Safari - -### Android App Links - -- **Verification**: Requires domain verification before working -- **Default**: Can be set as default handler for domain links -- **Fallback**: Opens in Chrome if app isn't installed -- **Intent**: Uses Android's Intent system for routing - -## Handling Universal/App Links - -Once you've configured your deep link settings, you can handle the link in your app. - -Simply set up a route in your web.php file and the deeplink will redirect to your route. - -```dotenv -https://example.net/profile/123 -``` - -```php -Route::get('/profile/{id}', function ($id) { - // Handle the deep link - // This works for both deep links and universal/app links -}); -``` - -## Testing and Development - -### Testing Limitations - -- **Development builds**: Universal/App Links may not work in development -- **Production required**: Full testing requires production builds and domain verification -- **Simulator**: iOS Simulator may not handle Universal Links correctly - -### Best Practices - -1. **Test both link types** - Ensure deep links and universal/app links work -2. **Verify domain files** - Check that .well-known files are accessible -3. **Production testing** - Test universal/app links with production builds -4. **Fallback handling** - Ensure your website handles users without the app -5. **Analytics tracking** - Monitor which link types are most effective - -## NFC - -NFC is a technology that allows you to read and write NFC tags. - -NativePHP handles NFC tag "bumping" just like a Universal/App Link. -You can use a tool like [NFC Tools](https://www.wakdev.com/en/) to write NFC tags. - -Set the url to a Universal/App Link and the tag will be written to the NFC tag. -"Bumping" the tag will open the app. - -### NFC Configuration - -NFC tags work best with Universal/App Links because: -- They provide fallback to website if app isn't installed -- They work across different devices and platforms -- They provide a better user experience than custom schemes - -```bash -# Write this URL to an NFC tag -https://example.net/product/456 - -# When "bumped": -# - Opens your app if installed -# - Opens website if not installed -``` +Associated Domains do not usually work in simulators. Testing on a real device that connects to a publicly-accessible +server for verification is often the best way to ensure these are operating correctly. -## Troubleshooting +If you are experiencing issues getting your associated domain to open your app, try: +- Completely deleting and reinstalling the app. Registration verifications (including failures) are often cached + against the app. +- Validating that your associated domain verification files are formatted correctly and contain the correct data. -### Common Issues +There is usually no such limitation for Custom URL Schemes. -1. **Universal Links not working**: Check domain verification files -2. **Deep links not opening**: Verify URL scheme configuration -3. **Wrong app opening**: Check for conflicting URL schemes -4. **iOS Smart Banner**: Ensure proper app store metadata +## Use cases -### Debug Steps +Deep linking is great for bringing users from another context directly to a key place in your app. Universal/App Links +are usually the more appropriate choice for this because of their flexibility in falling back to simple loading a URL +in the browser. -1. **Verify .env configuration** - Check scheme and host values -2. **Test deep links first** - Easier to debug than universal links -3. **Check domain files** - Ensure .well-known files are accessible -4. **Use production builds** - Development builds may not work correctly -5. **Monitor app logs** - Check for link handling errors +They're also more likely to behave the same across both platforms. -Remember that NativePHP handles all the complex platform-specific setup automatically - you just need to configure your domain and environment variables correctly. +Then you could use Universal/App Links in: +- NFC tags +- QR codes +- Email/SMS marketing diff --git a/resources/views/docs/mobile/1/concepts/push-notifications.md b/resources/views/docs/mobile/1/concepts/push-notifications.md index ef65a38e..07ee6d72 100644 --- a/resources/views/docs/mobile/1/concepts/push-notifications.md +++ b/resources/views/docs/mobile/1/concepts/push-notifications.md @@ -5,275 +5,89 @@ order: 400 ## Overview -NativePHP for Mobile uses Firebase Cloud Messaging (FCM) to send push notifications to your users on both iOS and Android devices. +NativePHP for Mobile uses Firebase Cloud Messaging (FCM) to send push notifications to your users on both iOS and +Android devices. -**Cross-Platform Support**: FCM is the unified push notification service for both platforms. For iOS devices, FCM automatically routes notifications through Apple Push Notification Service (APNS) behind the scenes - you don't need to configure APNS separately. +To send a push notification to a user, your app must request a token. That token must then be stored securely (ideally +on a server application via a secure API) and associated with that user/device. -**Supported Services**: NativePHP only supports FCM. Other push notification services are not supported. +Requesting push notification will trigger an alert for the user to either approve or deny your request. If they approve, +your app will receive the token. -## Setting up your app +When you want to send a notification to that user, you pass this token along with a request to the FCM service and +Firebase handles sending the message to the right device. -### Firebase Project Setup + -For server-side notifications, you'll also need a Firebase service account: +## Firebase -1. Go to your Firebase Console → Project Settings → Service Accounts -2. Click "Generate New Private Key" to download the service account JSON file -3. Save this file as `fcm-service-account.json` in your Laravel project's `public` directory +1. Create a [Firebase](https://firebase.google.com/) account +2. Create a project +3. Download the `google-services.json` file (for Android) and `GoogleService-Info.plist` file (for iOS) +4. These files contain the configuration for your app and is used by the Firebase SDK to retrieve tokens for each device + +Place these files in the root of your application and NativePHP will automatically handle setting them up appropriately +for each platform. -### NativePHP Configuration +You can ignore Firebase's further setup instructions as this is already taken care of by NativePHP. -Simply drag the `google-services.json` file into the root of your Laravel project, enable `push_notifications` in the config and it will be used automatically. +### Service account -You will see more instructions on how to configure your app in the Firebase documentation, you can ignore all of those, NativePHP handles all of that for you. +For sending push notifications from your server-side application, you'll also need a Firebase service account: -## Receiving Push Tokens +1. Go to your Firebase Console → Project Settings → Service Accounts +2. Click "Generate New Private Key" to download the service account JSON file +3. Save this file as `fcm-service-account.json` somewhere safe in your server application -### Token Management +## Getting push tokens -FCM tokens are unique identifiers for each app installation. These tokens can change when: +It's common practice to request push notification permissions during app bootup as tokens can change when: - The app is restored on a new device - The app data is restored from backup -- The app is updated on Android +- The app is updated - Other internal FCM operations -You should store both the FCM token and platform information for each user device. - -### Listening for Tokens - -To receive push notifications, you must register a listener for the event. For example, take a look at how easy it is to listen for a `TokenGenerated` event in Livewire: +To request a token, use the `PushNotifications::getPushNotificationsToken()` method: ```php -use Livewire\Attributes\On; -use Livewire\Component; use Native\Mobile\Facades\PushNotifications; -use Native\Mobile\Events\PushNotification\TokenGenerated; - -class PushNotifications extends Component -{ - public function render() - { - return view('livewire.system.push-notifications'); - } - - #[On('native:' . TokenGenerated::class)] - public function handlePushNotifications(string $token) - { - // Do something with the token... - } -} -``` - -Because of the nature of mobile applications you need an api server to handle these tokens. You can use Laravel's built-in `Http` facade to `POST` the token to your server, on the server side you need to associate the token with the "user" that owns the device. -We **strongly** recommend using [Sanctum](https://laravel.com/docs/sanctum) to handle this for you. - -## The flow - -Your app authenticates users against your own api server, when users create an account or login the server validates and authenticates the user and passes back a Sanctum token. - -The token is stored in your apps `session` and is used on subsequent requests to the api server. - -When a push notification is received, the token is sent to your api server and the server stores it for the user who sent it. - -> Optionally, you can have a `HasMany` relationship between your users and devices, this allows you to associate a device with a user and then use the device's token to send push notifications to that users devices. - -## Sending Push Notifications - -Once you have the token, you may use it from your server-based applications to trigger Push Notifications directly to your user's device. We use the `google/apiclient` package to send the notifications. - -```bash -composer require google/apiclient +PushNotifications::getPushNotificationsToken(); ``` -This is the exact code used by the NativePHP Kitchen Sink App: +If the user has approved your app to use push notifications and the request to FCM succeeded, a `TokenGenerated` event +will fire. -```php -Route::group(['middleware' => 'auth:sanctum'], function () { - Route::post('send-push-notification', PushNotificationController::class)->name('send-push-notification'); -}); -``` +Listen for this event to receive the token. Here's an example in a Livewire component: ```php -namespace App\Http\Controllers; - -use App\Jobs\SendPushNotification; -use Illuminate\Http\Request; +use App\Services\APIService; +use Livewire\Attributes\On; +use Native\Mobile\Facades\PushNotifications; +use Native\Mobile\Events\PushNotification\TokenGenerated; -class PushNotificationController extends Controller +class PushNotifications extends Component { - public function __invoke(Request $request) + #[On('native:'.TokenGenerated::class)] + public function storePushToken(APIService $api, string $token) { - $token = $request->get('token'); - - $request->user()->update([ - 'push_token' => $token - ]); - - SendPushNotification::dispatch($token)->delay(now()->addMinutes(1)); + $api->storePushToken($token); } } ``` -## SendPushNotification Job - -Create a job file at `app/Jobs/SendPushNotification.php` with the following structure: +## Sending push notifications -**Key Components:** -- Uses `Google_Client` to authenticate with Firebase -- Retrieves access token from service account JSON file -- Sends HTTP POST request to FCM API endpoint -- Includes notification title and body in the message payload +Once you have a token, you may use it from your server-side applications to trigger Push Notifications directly to your +user's device. -**Required Setup:** -1. Place your `fcm-service-account.json` file in the `public` directory -2. Configure `services.fcm.project_id` in your config -3. Install the `google/apiclient` package + diff --git a/resources/views/docs/mobile/1/concepts/splash-screen-icons.md b/resources/views/docs/mobile/1/concepts/splash-screen-icons.md deleted file mode 100644 index 1a46bed6..00000000 --- a/resources/views/docs/mobile/1/concepts/splash-screen-icons.md +++ /dev/null @@ -1,359 +0,0 @@ ---- -title: Splash Screen/Icons -order: 500 ---- - -## Overview - -App icons and splash screens are the first things users see when interacting with your mobile app. NativePHP Mobile makes it easy to customize both elements to create a professional, branded experience. - -## App Icons - -### Basic Icon Setup - -Place a single high-resolution icon file at: `public/icon.png` - -### Requirements -- **Format:** PNG -- **Size:** 1024 × 1024 pixels -- **Shape:** Square -- **Background:** Transparent or solid (your choice) -- **Content:** Should work well at small sizes - -``` -your-laravel-app/ -├── public/ -│ └── icon.png ← Place your 1024x1024 icon here -├── app/ -└── ... -``` - -### Automatic Icon Generation - -NativePHP automatically generates all required icon sizes for both platforms: - -#### iOS Icon Sizes (Generated Automatically) -- App Store: 1024x1024 -- iPhone: 60x60, 120x120, 180x180 -- iPad: 76x76, 152x152, 167x167 -- Settings: 29x29, 58x58, 87x87 -- Spotlight: 40x40, 80x80, 120x120 - -#### Android Icon Sizes (Generated Automatically) -- mdpi: 48x48 -- hdpi: 72x72 -- xhdpi: 96x96 -- xxhdpi: 144x144 -- xxxhdpi: 192x192 - -### Icon Design Best Practices - -```php -// Example of checking if custom icon exists -class IconValidator -{ - public function validateIcon(): array - { - $iconPath = public_path('icon.png'); - - if (!file_exists($iconPath)) { - return ['valid' => false, 'message' => 'Icon file not found']; - } - - $imageInfo = getimagesize($iconPath); - - if (!$imageInfo) { - return ['valid' => false, 'message' => 'Invalid image file']; - } - - [$width, $height] = $imageInfo; - - if ($width !== 1024 || $height !== 1024) { - return [ - 'valid' => false, - 'message' => "Icon must be 1024x1024px, got {$width}x{$height}px" - ]; - } - - if ($imageInfo['mime'] !== 'image/png') { - return ['valid' => false, 'message' => 'Icon must be PNG format']; - } - - return ['valid' => true, 'message' => 'Icon is valid']; - } -} -``` - -#### Design Guidelines -1. **Keep it simple** - Icons must be recognizable at 16x16 pixels -2. **Avoid text** - Text becomes unreadable at small sizes -3. **Use strong contrast** - Ensure visibility on various backgrounds -4. **Make it memorable** - Unique shape or color helps recognition -5. **Test at multiple sizes** - Check how it looks when scaled down -6. **Consider platform conventions** - iOS prefers rounded corners (applied automatically) - -## Splash Screens - -### Splash Screen Configuration - -Splash screens are shown while your app loads. NativePHP provides built-in splash screen support that you can customize. - -### Default Splash Screen - -By default, NativePHP creates a simple splash screen using your app icon and name. No additional configuration required. - -### Custom Splash Screen - -Create custom splash screen assets: - -``` -your-laravel-app/ -├── public/ -│ ├── icon.png -│ ├── splash-logo.png ← Optional custom splash logo -│ └── splash-background.png ← Optional background image -├── app/ -└── ... -``` - -### Splash Screen Specifications - -#### iOS Launch Images -- **iPhone:** Various sizes for different devices -- **iPad:** Portrait and landscape orientations -- **Safe Areas:** Account for notches and home indicators - -#### Android Splash Screens -- **Centered Logo:** Displayed on colored background -- **Responsive:** Adapts to different screen sizes and orientations -- **Theme-aware:** Can adapt to light/dark themes - -### Configuration Options - -```php -// config/nativephp.php -return [ - 'app' => [ - 'name' => 'Your App Name', - 'splash' => [ - 'background_color' => '#ffffff', - 'logo_path' => 'splash-logo.png', - 'show_loading' => true, - ], - ], -]; -``` - -### Dynamic Splash Screens - -```php -class SplashScreenManager -{ - public function configureSplashScreen(): void - { - $isDarkMode = $this->getUserPreference('dark_mode'); - - $splashConfig = [ - 'background_color' => $isDarkMode ? '#000000' : '#ffffff', - 'logo_path' => $isDarkMode ? 'logo-dark.png' : 'logo-light.png', - 'show_loading' => true, - ]; - - $this->updateSplashConfiguration($splashConfig); - } - - private function getUserPreference(string $key): bool - { - return session($key, false); - } - - private function updateSplashConfiguration(array $config): void - { - // Update runtime splash configuration - config(['nativephp.app.splash' => $config]); - } -} -``` - -## Asset Compilation - -### CSS and JavaScript Assets - -Your mobile app runs locally, so all assets must be compiled before deployment. - -```bash -# Always compile assets before building -npm run build - -# Then build your mobile app -php artisan native:run -``` - -### Build Process Integration - -```json -{ - "scripts": { - "dev": "vite", - "build": "vite build", - "build:mobile": "vite build --mode mobile", - "mobile:prepare": "npm run build:mobile && php artisan optimize" - } -} -``` - -### Asset Optimization for Mobile - -```javascript -// vite.config.js -import { defineConfig } from 'vite'; -import laravel from 'laravel-vite-plugin'; - -export default defineConfig({ - plugins: [ - laravel({ - input: ['resources/css/app.css', 'resources/js/app.js'], - refresh: true, - }), - ], - build: { - rollupOptions: { - output: { - manualChunks: { - vendor: ['lodash', 'axios'], - ui: ['@headlessui/vue', '@heroicons/vue'], - } - } - }, - chunkSizeWarningLimit: 1000, - }, -}); -``` - -## Development Workflow - -### Hot Reload (Experimental) - -For development, you can use the experimental watch flag: - -```bash -php artisan native:run --watch -``` - -### Watch Limitations - -Currently supports: -- ✅ Blade templates -- ✅ Livewire components -- ✅ PHP files -- ❌ Compiled CSS/JS assets -- ❌ Vite builds -- ❌ Inertia.js apps - -### Recommended Development Flow - -```bash -# For Blade/Livewire apps -php artisan native:run --watch - -# For apps with compiled assets (Vue, React, Inertia) -npm run dev & # Run in background -npm run build # Build assets -php artisan native:run # Launch app -``` - -## Testing Icons and Splash Screens - -### Icon Testing Checklist - -```php -class AssetTestSuite -{ - public function testIconExists(): void - { - $this->assertFileExists(public_path('icon.png')); - } - - public function testIconDimensions(): void - { - $iconPath = public_path('icon.png'); - [$width, $height] = getimagesize($iconPath); - - $this->assertEquals(1024, $width); - $this->assertEquals(1024, $height); - } - - public function testIconFormat(): void - { - $iconPath = public_path('icon.png'); - $imageInfo = getimagesize($iconPath); - - $this->assertEquals('image/png', $imageInfo['mime']); - } - - public function testSplashConfiguration(): void - { - $splashConfig = config('nativephp.app.splash'); - - $this->assertIsArray($splashConfig); - $this->assertArrayHasKey('background_color', $splashConfig); - } -} -``` - -### Visual Testing - -1. **Test on multiple devices** - Check how icons look on different screen sizes -2. **Test both orientations** - Portrait and landscape modes -3. **Test theme variations** - Light and dark modes -4. **Test loading states** - Ensure splash screens display properly -5. **Performance testing** - Monitor app launch times - -## Platform-Specific Considerations - -### iOS -- Icons automatically get rounded corners -- Supports Dynamic Type for accessibility -- Requires specific launch image sizes -- Splash screens adapt to safe areas - -### Android -- Icons can be adaptive (foreground + background) -- Supports vector drawables for splash screens -- Material Design guidelines apply -- Supports theme-aware splash screens - -### Cross-Platform Assets - -``` -public/ -├── icon.png # Universal app icon -├── icon-android.png # Android-specific (optional) -├── icon-ios.png # iOS-specific (optional) -├── splash-logo.png # Universal splash logo -├── splash-android.png # Android splash (optional) -└── splash-ios.png # iOS splash (optional) -``` - -## Best Practices - -### Icon Design -1. **Start with vector graphics** - Use SVG or AI files for source -2. **Export high quality** - Use 1024x1024 PNG with no compression -3. **Test readability** - Check visibility at 16x16 pixels -4. **Maintain brand consistency** - Match your web/desktop icons -5. **Consider accessibility** - Ensure sufficient contrast - -### Splash Screen Design -1. **Keep it simple** - Splash screens should load quickly -2. **Match your brand** - Use consistent colors and typography -3. **Don't include text** - Text may not scale properly -4. **Consider loading states** - Show progress if app takes time to load -5. **Test performance** - Long splash screens hurt user experience - -### Asset Management -1. **Optimize file sizes** - Compress images without quality loss -2. **Use appropriate formats** - PNG for icons, WebP for photos -3. **Version your assets** - Track changes to visual elements -4. **Automate generation** - Script the creation of multiple sizes -5. **Test regularly** - Verify assets display correctly after changes \ No newline at end of file diff --git a/resources/views/docs/mobile/1/getting-started/configuration.md b/resources/views/docs/mobile/1/getting-started/configuration.md index 2b385d19..78293577 100644 --- a/resources/views/docs/mobile/1/getting-started/configuration.md +++ b/resources/views/docs/mobile/1/getting-started/configuration.md @@ -5,19 +5,20 @@ order: 200 ## Overview -NativePHP for Mobile is designed so that most configuration happens **inside your Laravel application**, without requiring you to manually open Xcode or Android Studio. +NativePHP for Mobile is designed so that most configuration happens **inside your Laravel application**, without +requiring you to manually open Xcode or Android Studio. -After installation, NativePHP sets up the necessary native scaffolding, but your primary interaction remains inside Laravel itself. - -This page explains the key configuration points you can control directly through Laravel. +This page explains the key configuration points you can control through Laravel. ## The `nativephp.php` Config File -The nativephp.php config file is where you can configure the native project for your application. +The `config/nativephp.php` config file contains a number of useful options. -NativePHP uses sensible defaults and makes several assumptions based on default installations for tools required to build and run apps from your computer. +NativePHP uses sensible defaults and makes several assumptions based on default installations for tools required to +build and run apps from your computer. -You can override these defaults by editing the `nativephp.php` config file in your Laravel project or changing environment variables. +You can override these defaults by editing the `nativephp.php` config file in your Laravel project, and in many case +simply by changing environment variables. ```dotenv NATIVEPHP_APP_VERSION @@ -30,6 +31,42 @@ NATIVEPHP_GRADLE_PATH NATIVEPHP_ANDROID_SDK_LOCATION ``` +## `NATIVEPHP_APP_ID` + +You must set your app ID to something unique. A common practice is to use a reverse-DNS-style name, e.g. +`com.yourcompany.yourapp`. + +Your app ID (also known as a *Bundle Identifier*) is a critical piece of identification across both Android and iOS +platforms. Different app IDs are treated as separate apps. + +And it is often referenced across multiple services, such as Apple Developer Center and the Google Play Console. + +So it's not something you want to be changing very often. + +## `NATIVEPHP_APP_VERSION` + +The `NATIVEPHP_APP_VERSION` environment variable controls your app's versioning behavior. + +When your app is compiling, NativePHP first copies the relevant Laravel files into a temporary directory, zips them up, +and embeds the archive into the native application. + +When your app boots, it checks the embedded version against the previously installed version to see if it needs to +extract the bundled Laravel application. + +If the versions match, the app uses the existing files without re-extracting the archive. + +To force your application to always install the latest version of your code - especially useful during development - +set this to `DEBUG`: + +```dotenv +NATIVEPHP_APP_VERSION=DEBUG +``` + +Note that this will make your application's boot up slightly slower as it must unpack the zip every time it loads. + +But this ensures that you can iterate quickly during development, while providing a faster, more stable experience for +end users once an app is published. + ## Cleanup `env` keys The `cleanup_env_keys` array in the config file allows you to specify keys that should be removed from the `.env` file before bundling. @@ -41,12 +78,30 @@ The `cleanup_exclude_files` array in the config file allows you to specify files This is useful for removing files like logs or other temporary files. ## Permissions -In general, the app stores don't want apps to request permissions that they don't need. -To enable some permissions your app needs, you simply need to change their values in the permissions section. +In general, the app stores don't want your app to have permissions (a.k.a entitlements) it doesn't need. -```dotenv -biometric -camera -nfc -location +By default, all optional permissions are disabled. + +You may enable the features you intend to use simply by changing the value of the appropriate permission to `true`: + +```php + + 'permissions' => [ + + 'biometric' => true, + + ], ``` + +### Available permissions + +- `biometric` - Allows your application to use fingerprint or face-recognition hardware (with a fallback to PIN code) + to secure parts of your application. +- `camera` - Allows your application to request access to the device's camera, if present. Note that the user may deny + access and any camera functions will then result in a no-op. +- `nfc` - Allows your application to request access to the device's NFC reader, if present. +- `push_notifications` - Allows your application to request permissions to send push notifications. Note that the user + may deny this and any push notification functions will then result in a no-op. +- `location` - Allows your application to request access to the device's GPS receiver, if present. Note that the user + may deny this and any location functions will then result in a no-op. + diff --git a/resources/views/docs/mobile/1/getting-started/development.md b/resources/views/docs/mobile/1/getting-started/development.md index 0097512c..4ada4598 100644 --- a/resources/views/docs/mobile/1/getting-started/development.md +++ b/resources/views/docs/mobile/1/getting-started/development.md @@ -3,30 +3,114 @@ title: Development order: 300 --- +## Building -## The `nativephp` Directory +Building your NativePHP apps can be done completely in the browser with the workflows you're already familiar with. -After running: `php artisan native:install` you’ll see a new `nativephp` directory at the root of your Laravel project as well as a `nativephp.php` config file in your config folder in the Laravel root. +This allows you to iterate rapidly on parts like the UI and major functionality, even using your favorite tools for +testing etc. -This folder contains all the native project files that NativePHP generates for you. +But when you want to test _native_ features, then you must run it on a real/emulated device. -You should not need to manually open or edit any native project files under normal circumstances. -NativePHP handles the heavy lifting for you. +Whether you run your native app on an emulated or real device, it will always require compilation after changes have +been made. -## NATIVEPHP_APP_VERSION +We've worked incredibly hard to ensure that your apps will operate almost identically across both platforms - aside +from obvious platform differences. For the most part, you should be able to build without thinking about which platform +your app is running on, but if you do need to, you can check by using one of the following helper methods: -The NATIVEPHP_APP_VERSION environment variable controls your app's versioning behavior. +```php +use Native\Mobile\Facades\System; -When building for Android, NativePHP first copies the relevant Laravel files into a temporary directory, zips them, and embeds the archive into the Android project. When the app boots, it checks the embedded version against the previously installed version. +System::isIos() // -> `true` on iOS +System::isAndroid() // -> `true` on Android +``` -If the versions match, the app uses the existing files without re-extracting the archive. +To compile and run your app, simply run: -If the versions differ — or if the version is set to ***DEBUG*** — the app updates itself by re-extracting the new bundle. +```shell +php artisan native:run --build=debug +``` -This mechanism ensures developers can iterate quickly during development, while providing a faster, more stable experience for end users once an app is published. +This single command takes care of everything and allows you to run new builds of your application without having to +learn any new editors or platform-specific tools. -> Rule of Thumb: -> During development, keep NATIVEPHP_APP_VERSION set to DEBUG to always refresh the app. -> When preparing a new release, update it to a semantic version (e.g., 1.2.3) to enable versioned updates for your users. + + +## Working with Xcode or Android Studio + +On occasion, it is useful to compile your app from inside the target platform's dedicated development tools, Android +Studio and Xcode. + +If you're familiar with these tools, you can easily open the projects using the following Artisan command: + +```shell +php artisan native:open +``` + +## Hot Reloading (Experimental) + +You can enable hot reloading by adding the `--watch` flag when running the `native:run` command: + +```shell +php artisan native:run --watch +``` + +This is useful during development for quickly testing changes without rebuilding the entire app. + +### Caveats + +- This feature is currently best suited for **Blade** and **Livewire** applications. +- It does **not** currently detect or sync **compiled frontend assets**, such as those built with Vite or used by + **Inertia.js**. +- If you're working with a JavaScript-heavy stack (Vue, React, Inertia), you should continue + [building your frontend](/docs/mobile/1/the-basics/assets) before launching the app with `native:run`. + +## Releasing + +To prepare your app for release, you should set the version number to a new version number that you have not used +before and increment the build number: + +```dotenv +NATIVEPHP_APP_VERSION=1.2.3 +NATIVEPHP_APP_VERSION_CODE=48 +``` + +### Versioning + +You have complete freedom in how you version your applications. You may use semantic versioning, codenames, +date-based versions, or any scheme that works for your project, team or business. + +Remember that your app versions are usually public-facing (e.g. in store listings and on-device settings and update +screens) and can be useful for customers to reference if they need to contact you for help and support. + +The build number is managed via the `NATIVEPHP_APP_VERSION` key in your `.env`. + +### Build numbers + +Both the Google Play Store and Apple App Store require your app's build number to increase for each release you submit. + +The build number is managed via the `NATIVEPHP_APP_VERSION_CODE` key in your `.env`. + +### Run a `release` build + +Then run a release build: + +```shell +php artisan native:run --build=release +``` + +This builds your application with various optimizations that reduce its overall size and improve its performance, such +as removing debugging code and unnecessary features (i.e. Composer dev dependencies). + +**You should test this build on a real device.** Once you're happy that everything is working as intended you can then +submit it to the stores for approval and distribution. + +- [Google Play Store submission guidelines](https://support.google.com/googleplay/android-developer/answer/9859152?hl=en-GB#zippy=%2Cmaximum-size-limit) +- [Apple App Store submission guidelines](https://developer.apple.com/ios/submit/) diff --git a/resources/views/docs/mobile/1/getting-started/environment-setup.md b/resources/views/docs/mobile/1/getting-started/environment-setup.md index 2f955a04..561a5c95 100644 --- a/resources/views/docs/mobile/1/getting-started/environment-setup.md +++ b/resources/views/docs/mobile/1/getting-started/environment-setup.md @@ -3,17 +3,32 @@ title: Environment Setup order: 100 --- +## Requirements + +1. PHP 8.3+ +2. Laravel 11+ +3. [A NativePHP for Mobile license](https://nativephp.com/mobile) + +If you don't already have PHP installed on your machine, the most painless way to get PHP up and running on Mac and +Windows is with [Laravel Herd](https://herd.laravel.com). It's fast and free! + ## iOS Requirements -### For iOS -1. macOS (required - iOS development is only possible on Mac) + + +1. macOS (required - iOS development is only possible on a Mac) 2. [Xcode 16.0 or later](https://apps.apple.com/app/xcode/id497799835) 3. Xcode Command Line Tools -4. Homebrew (for dependency management) -5. CocoaPods -6. _Optional_ iOS device for testing +4. Homebrew & CocoaPods +5. _Optional_ iOS device for testing -#### Setting up iOS Development Environment +### Setting up iOS Development Environment 1. **Install Xcode** - Download from the [Mac App Store](https://apps.apple.com/app/xcode/id497799835) @@ -42,24 +57,28 @@ order: 100 pod --version ``` -#### Apple Developer Account +### Apple Developer Account You **do not** need to enroll in the [Apple Developer Program](https://developer.apple.com/programs/enroll/) ($99/year) to develop and test your apps on a Simulator. However, you will need to enroll when you want to: - Test your apps on real devices - Distribute your apps via the App Store -> **Note** You cannot build iOS apps on Windows or Linux - ## Android Requirements -### For Android 1. [Android Studio 2024.2.1 or later](https://developer.android.com/studio) 2. Android SDK with API 23 or higher 3. **Windows only**: You must have [7zip](https://www.7-zip.org/) installed. -> **Note** You do not need to separately install the Java Development Kit (JDK). Android Studio will automatically install the proper JDK for you. + + +### Setting up Android Studio and SDK 1. **Download and Install Android Studio** - Download from the [Android Studio download page](https://developer.android.com/studio) @@ -75,20 +94,39 @@ to develop and test your apps on a Simulator. However, you will need to enroll w That's it! Android Studio handles all the necessary configuration automatically. +### Preparing for NativePHP + +1. Check that you can run `java -v` and `adb devices` from the terminal. +2. The following environment variables set: + +#### On macOS +```shell +export JAVA_HOME=$(/usr/libexec/java_home -v 17) // This isn't required if JAVA_HOME is already set in your environment variables (check using `printenv | grep JAVA_HOME`) +export ANDROID_HOME=$HOME/Library/Android/sdk +export PATH=$PATH:$JAVA_HOME/bin:$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools +``` + +#### On Windows +```shell +set ANDROID_HOME=C:\Users\yourname\AppData\Local\Android\Sdk +set PATH=%PATH%;%JAVA_HOME%\bin;%ANDROID_HOME%\platform-tools + +# This isn't required if JAVA_HOME is already set in the Windows Env Variables +set JAVA_HOME=C:\Program Files\Microsoft\jdk-17.0.8.7-hotspot +``` + ## Testing on Real Devices You don't _need_ a physical iOS/Android device to compile and test your application, as NativePHP for Mobile supports -the iOS Simulator and Android emulators. However, we highly recommend that you test your application on a real device before submitting to the -Apple App Store and Google Play Store. - -### Running on a real device +the iOS Simulator and Android emulators. However, we highly recommend that you test your application on a real device +before submitting to the Apple App Store and Google Play Store. -#### On iOS +### On iOS If you want to run your app on a real iOS device, you need to make sure it is in [Developer Mode](https://developer.apple.com/documentation/xcode/enabling-developer-mode-on-a-device) and that it's been added to your Apple Developer account as [a registered device](https://developer.apple.com/account/resources/devices/list). -#### On Android +### On Android On Android you need to [enable developer options](https://developer.android.com/studio/debug/dev-options#enable) and have USB debugging (ADB) enabled. diff --git a/resources/views/docs/mobile/1/getting-started/installation.md b/resources/views/docs/mobile/1/getting-started/installation.md index cf99593e..a9f64b18 100644 --- a/resources/views/docs/mobile/1/getting-started/installation.md +++ b/resources/views/docs/mobile/1/getting-started/installation.md @@ -3,63 +3,33 @@ title: Installation order: 100 --- -## Requirements +## Get a license -1. PHP 8.3+ -2. Laravel 10 or higher -3. [A NativePHP for Mobile license](https://nativephp.com/mobile) +Before you begin, you will need to [purchase a license](/mobile). -### For iOS -1. An Apple Silicon Mac running macOS 12+ with [Xcode 16+](https://apps.apple.com/app/xcode/id497799835) -2. An active [Apple Developer account](https://developer.apple.com/) -3. _Optional_ iOS device - -You **do not** need to enroll in the [Apple Developer Program](https://developer.apple.com/programs/enroll/) ($99/year) -to develop and test your apps on a Simulator. But you will need to when you want to test your apps on real devices -and start distributing them to your users via the App Store. - -> **Note** You cannot build iOS apps on Windows or Linux - -### For Android -1. [Android Studio Giraffe (or later)](https://developer.android.com/studio) -2. The following environment variables set. -3. You should be able to successfully run `java -v` and `adb devices` from the terminal. -4. **Windows only**: You must have [7zip](https://www.7-zip.org/) installed. - -#### On macOS -```shell -export JAVA_HOME=$(/usr/libexec/java_home -v 17) // This isn't required if JAVA_HOME is already set in your environment variables (check using `printenv | grep JAVA_HOME`) -export ANDROID_HOME=$HOME/Library/Android/sdk -export PATH=$PATH:$JAVA_HOME/bin:$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools -``` - -#### On Windows -```shell -set ANDROID_HOME=C:\Users\yourname\AppData\Local\Android\Sdk -set PATH=%PATH%;%JAVA_HOME%\bin;%ANDROID_HOME%\platform-tools - -# This isn't required if JAVA_HOME is already set in the Windows Env Variables -set JAVA_HOME=C:\Program Files\Microsoft\jdk-17.0.8.7-hotspot -``` +To make NativePHP for Mobile a reality has taken a lot of work and will continue to require even more. For this reason, +it's not open source, and you are not free to distribute or modify its source code. -You don't _need_ a physical iOS/Android device to compile and test your application, as NativePHP for Mobile supports -the iOS Simulator and Android emulators. However, we highly recommend that you test your application on a real device before submitting to the -Apple App Store and Google Play Store. +Your license fee goes straight back into the NativePHP project and community, enabling us to: +- Develop premium features for everyone. +- Provide first-class support. +- Sponsor our dependencies. +- Donate to our contributors. +- Support community events. +- Ensure that the whole NativePHP project remains viable for a long time to come. -## Laravel +Thank you for supporting the project in this way! 🙏 -NativePHP for Mobile is built to work with Laravel. You can install it into an existing Laravel application, or -[start a new one](https://laravel.com/docs/installation). The most painless way to get PHP up and running on Mac and Windows is with -[Laravel Herd](https://herd.laravel.com). It's fast and free! +## Install the Composer package + Once you have your license, you will need to add the following to your `composer.json`: @@ -76,9 +46,21 @@ Then run: ```shell composer require nativephp/mobile ``` -*If you experience a cURL error when running this command make sure you are running PHP v8.3+ in your CLI.* + -**Windows Performance Tip:** Add `C:\temp` to your Windows Defender exclusions list to significantly speed up composer installs during app compilation. This prevents real-time scanning from slowing down the many temporary files created during the build process. + If this is the first time you're installing the package, you will be prompted to authenticate. Your username is the email address you used when purchasing your license. Your password is your license key. @@ -86,7 +68,7 @@ email address you used when purchasing your license. Your password is your licen This package contains all the libraries, classes, commands, and interfaces that your application will need to work with iOS and Android. -**Before** running the `install` command it is important to set the following variables in your `.env`: +**Before** running the `install` command, it is important to set the following variables in your `.env`: ```shell NATIVEPHP_APP_ID=com.yourcompany.yourapp @@ -102,6 +84,34 @@ php artisan native:install The NativePHP installer takes care of setting up and configuring your Laravel application to work with iOS and Android. +You may be prompted about whether you would like to install the ICU-enabled PHP binaries. You should install these if +your application relies on the `intl` PHP extension. + +If you don't need `intl` or are not sure, choose the default, non-ICU builds. + + + +### The `nativephp` Directory + +After running: `php artisan native:install` you’ll see a new `nativephp` directory at the root of your Laravel project +as well as a `config/nativephp.php` config file. + +The `nativephp` folder contains the native application project files needed to build your app for the desired platforms. + +You should not need to manually open or edit any native project files under normal circumstances. NativePHP handles +the heavy lifting for you. + +**You should treat this directory as ephemeral.** When upgrading the NativePHP package, it will be necessary to run +`php artisan native:install --force`, which completely rebuilds this directory, deleting all files within. + +For this reason, we also recommend you add the `nativephp` folder to your `.gitignore`. + ## Start your app **Heads up!** Before starting your app in a native context, try running it in the browser. You may bump into exceptions @@ -113,7 +123,7 @@ Once you're ready: php artisan native:run ``` -This will start compiling your application and boot it on whichever device you select. +Just follow the prompts! This will start compiling your application and boot it on whichever device you select. ### Running on a real device @@ -127,4 +137,4 @@ and that it's been added to your Apple Developer account as On Android you need to [enable developer options](https://developer.android.com/studio/debug/dev-options#enable) and have USB debugging (ADB) enabled. -And that's it! You should now see your Laravel application running as a native app! 🎉 \ No newline at end of file +And that's it! You should now see your Laravel application running as a native app! 🎉 diff --git a/resources/views/docs/mobile/1/getting-started/introduction.md b/resources/views/docs/mobile/1/getting-started/introduction.md index 7957c6a1..a4a91c4b 100644 --- a/resources/views/docs/mobile/1/getting-started/introduction.md +++ b/resources/views/docs/mobile/1/getting-started/introduction.md @@ -14,6 +14,19 @@ experiences using the tools you already know. **It's never been this easy to build beautiful, local-first apps for iOS and Android.** +## What makes NativePHP for Mobile special? + +- 📱 **Native performance** + Your app runs natively through an embedded PHP runtime optimized for each platform. +- 🔥 **True mobile APIs** + Access camera, biometrics, push notifications, and more. One cohesive library that does it all. +- ⚡ **Laravel powered** + Leverage the entire Laravel ecosystem and your existing skillset. +- 🚫 **No web server required** + Your app runs entirely on-device and can operate completely offline-first. +- 🔄 **Cross platform** + Build apps for both iOS and Android from a single codebase. + ## Old tools, new tricks With NativePHP for Mobile, you don’t need to learn Swift, Kotlin, or anything new. @@ -40,16 +53,22 @@ You simply interact with an easy-to-use set of functions from PHP and everything NativePHP for Mobile is way more than just a web view wrapper for your server-based application. Your application lives _on device_ and is shipped with each installation. -Thanks to our custom PHP extension, you can interact with many native APIs today, with more coming, including: +Thanks to our custom PHP extension, you can interact with many native APIs today, with more coming all the time, +including: -- Camera & Microphone -- Biometric ID -- Vibration -- Accelerometer, GPS and other sensors -- Push notifications, native alerts and other native UI elements +- 📷 Camera & Gallery access +- 🔐 Biometric authentication (Face ID, Touch ID, Fingerprint) +- 🔔 Push notifications via APNs (for iOS) or Firebase (both) +- 💬 Native dialogs & toasts +- 🔗 Deep links & universal links +- 📳 Haptic feedback & vibration +- 🔦 Flashlight control +- 📤 Native sharing +- 🔒 Secure storage (Keychain/Keystore) +- 📍 Location services You have the full power of PHP and Laravel at your fingertips... literally! And you're not sandboxed into the web view; -this goes way beyond what's possible with PWAs and WASM without any of the complexity... we've got full-cream PHP ready -to go! +this goes way beyond what's possible with PWAs and WASM without any of the complexity... we've got full-cream PHP at +the ready! -**What are you waiting for!? [Let's go!](installation)** +**What are you waiting for!? [Let's go!](quick-start)** diff --git a/resources/views/docs/mobile/1/getting-started/quick-start.md b/resources/views/docs/mobile/1/getting-started/quick-start.md new file mode 100644 index 00000000..0e209a59 --- /dev/null +++ b/resources/views/docs/mobile/1/getting-started/quick-start.md @@ -0,0 +1,35 @@ +--- +title: Quick Start +order: 099 +--- + +## Let's go! + + + +Get your first mobile app running in minutes: + +```bash +# Install NativePHP for Mobile into a new Laravel app +composer require nativephp/mobile + +# Ready your app to go native +php artisan native:install + +# Run your app on a mobile device +php artisan native:run +``` + +## Need help? + +- **Community** - Join our [Discord](/discord) for support and discussions. +- **Examples** - Check out the [Kitchen Sink demo app](https://play.google.com/store/apps/details?id=com.nativephp.kitchensinkapp) + on Android (coming soon to iOS!) + +Ready to build your first mobile app with PHP? [Let's get started! 🚀](/docs/mobile/1/getting-started/introduction) diff --git a/resources/views/docs/mobile/1/getting-started/roadmap.md b/resources/views/docs/mobile/1/getting-started/roadmap.md index cc8c7fc7..0991db47 100644 --- a/resources/views/docs/mobile/1/getting-started/roadmap.md +++ b/resources/views/docs/mobile/1/getting-started/roadmap.md @@ -1,30 +1,10 @@ --- title: Roadmap -order: 099 +order: 098 --- -## Current Status - -NativePHP for Mobile v1 is here. While we're still in the process of adding more and more -native API's and other features we believe it's robust enough to build useful applications that can be -distributed to users. - -Presently, NativePHP for Mobile offers the following "native" functionality: - -- Vibrate -- Show Toasts -- Show Alerts -- Share -- Camera -- Flashlight -- Biometric ID -- Push Notifications -- Deep Links -- NFC -- Secure Storage -- Location -- Native image picker -- Splash screen +NativePHP for Mobile is stable and already deployed in production apps released on the app stores. But it's still early +days. We haven't yet built interfaces to all of the available mobile APIs. We're working on adding more and more features, including (in no particular order): - Microphone access @@ -53,3 +33,13 @@ We're working on adding more and more features, including (in no particular orde - Ads - In-app billing + diff --git a/resources/views/docs/mobile/1/getting-started/versioning.md b/resources/views/docs/mobile/1/getting-started/versioning.md index c0d1b7f3..13369dc3 100644 --- a/resources/views/docs/mobile/1/getting-started/versioning.md +++ b/resources/views/docs/mobile/1/getting-started/versioning.md @@ -1,48 +1,69 @@ --- -title: Release Notes -order: 600 +title: Versioning Policy +order: 50 --- -## Version Policy +NativePHP for Mobile follows [semantic versioning](https://semver.org) with a mobile-specific approach that distinguishes between +Laravel-only changes and native code changes. This ensures predictable updates and optimal compatibility. -NativePHP Mobile follows [semantic versioning](https://semver.org) with a mobile-specific approach that distinguishes between Laravel-only changes and native code changes. This ensures predictable updates and optimal over-the-air (OTA) compatibility. +Our aim is to limit the amount of work you need to do to get the latest updates and ensure everything works. -## Release Types +We will aim to post update instructions with each release. -### Patch Releases -**Laravel/PHP code only** - Bug fixes, new Artisan commands, configuration updates, documentation, and dependency updates that don't affect native code. +## Release types -- Safe to update immediately via `composer update` -- No app rebuild required -- Compatible with existing native builds -- Perfect for OTA updates +### Patch releases -### Minor Releases -**Native code changes** - New native APIs, Kotlin/Swift updates, platform-specific features, or native dependency changes. +Patch releases of `nativephp/mobile` should have **no breaking changes** and **only change Laravel/PHP code**. +This will typically include bug fixes and dependency updates that don't affect native code. -- Require rebuilding your app with `php artisan native:install --force` -- Need app store submission for distribution -- Include advance notice and migration guides +These releases should be completely compatible with the existing version of your native applications. -## Support Policy +This means that you can: -### What's Safe to Update -- **Patch releases** - Update immediately without rebuilding your app -- **Minor releases** - Plan for rebuild and app store submission +- Safely update via `composer update`. +- Avoid a complete rebuild (no need to `native:install --force`). +- Allow for easier app updates avoiding the app stores. -### Version Constraints -Use semantic versioning constraints in your `composer.json`: +### Minor releases + +Minor releases may contain **native code changes**. Respecting semantic versioning, these still should not contain +breaking changes, but there may be new native APIs, Kotlin/Swift updates, platform-specific features, or native +dependency changes. + +Minor releases will: + +- Require a complete rebuild (`php artisan native:install --force`) to work with the latest APIs. +- Need app store submission for distribution. +- Include advance notice and migration guides where necessary. + +### Major releases + +Major releases are reserved for breaking changes. This will usually follow a period of deprecations so that you have +time to make the necessary changes to your application code. + +## Version constraints + +We recommend using the [tilde range operator](https://getcomposer.org/doc/articles/versions.md#tilde-version-range-) +with a full minimum patch release defined in your `composer.json`: ```json { "require": { - "nativephp/mobile": "^1.1.0" + "nativephp/mobile": "~1.1.0" } } ``` This automatically receives patch updates while giving you control over minor releases. -## Your Application Versioning +## Your application versioning + +Just because we're using semantic versioning for the `nativephp/mobile` package, doesn't mean your app must follow that +same scheme. + +You have complete freedom in versioning your own applications! You may use semantic versioning, codenames, +date-based versions, or any scheme that works for your project, team or business. -You have complete freedom in versioning your own applications - use semantic versioning, codenames, date-based versions, or any scheme that works for your project. NativePHP Mobile's consistent approach ensures you always understand the impact of framework updates regardless of your chosen versioning strategy. +Remember that your app versions are usually public-facing (e.g. in store listings and on-device settings and update +screens) and can be useful for customers to reference if they need to contact you for help and support. diff --git a/resources/views/docs/mobile/1/the-basics/app-assets.md b/resources/views/docs/mobile/1/the-basics/app-assets.md deleted file mode 100644 index 041012df..00000000 --- a/resources/views/docs/mobile/1/the-basics/app-assets.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: App Assets -order: 200 ---- - -## Customizing Your App Icon - -NativePHP makes it easy to apply a custom app icon to your iOS and Android builds. - -### Step 1: Provide Your Icon - -Place a single high-resolution icon file at: `public/icon.png` - - -### Requirements: -- Format: PNG -- Size: 1024 × 1024 pixels -- Shape: Square -- Background: Transparent or solid — your choice - -Note: This image will be automatically resized for all Android densities and used as the base iOS app icon. You must have the GD extension installed and active in your local PHP environment for this to work. - ---- - -## Compiling CSS and JavaScript - -Your device behaves like a server, so assets must be compiled before deployment. - -To ensure your latest styles and JavaScript are included, run: `npm run build` before running: `php artisan native:run`. - -If you’ve made changes to your frontend, this step is required to see them reflected in the app. - ---- - -## Using the --watch Flag (Experimental) - -NativePHP includes an experimental `--watch` flag that enables automatic file syncing while the app is running: - -php artisan native:run --watch - -This is useful during development for quickly testing changes without rebuilding the entire app. - -### Caveats - -- This feature is currently best suited for **Blade** and **Livewire** applications. -- It does **not** currently detect or sync **compiled frontend assets**, such as those built with Vite or used by **Inertia.js**. -- If you're working with a JavaScript-heavy stack (Vue, React, Inertia), you should continue using `npm run build` before launching the app with `native:run`. - -### Recommendation - -Use `--watch` when you're iterating on Blade views or Livewire components. For all other use cases, treat this flag as experimental and optional. diff --git a/resources/views/docs/mobile/1/the-basics/app-icon.md b/resources/views/docs/mobile/1/the-basics/app-icon.md new file mode 100644 index 00000000..f20f4dbe --- /dev/null +++ b/resources/views/docs/mobile/1/the-basics/app-icon.md @@ -0,0 +1,24 @@ +--- +title: App Icons +order: 300 +--- + +NativePHP makes it easy to apply a custom app icon to your iOS and Android apps. + +## Supply your icon + +Place a single high-resolution icon file at: `public/icon.png`. + +### Requirements +- Format: PNG +- Size: 1024 × 1024 pixels +- Background: Transparent or solid — your choice + +This image will be automatically resized for all Android densities and used as the base iOS app icon. +You must have the GD extension installed in your development machine's PHP environment for this to work. + + diff --git a/resources/views/docs/mobile/1/the-basics/assets.md b/resources/views/docs/mobile/1/the-basics/assets.md new file mode 100644 index 00000000..a38f1f76 --- /dev/null +++ b/resources/views/docs/mobile/1/the-basics/assets.md @@ -0,0 +1,28 @@ +--- +title: Assets +order: 500 +--- + +## Compiling CSS and JavaScript + +If you are using React, Vue or another JavaScript library, or Tailwind CSS, tools that requires your frontend to be +built by build tooling like Vite, you will need to run your build process _before_ compiling the native application. + +For example, if you're using Vite with NPM to build a React application that is using Tailwind, to ensure that your +latest styles and JavaScript are included, always run `npm run build` before running `php artisan native:run`. + +## Other files + +NativePHP will include all files from the root of your Laravel application. So you can store any files that you wish to +make available to your application wherever makes the most sense for you. + + diff --git a/resources/views/docs/mobile/1/the-basics/asynchronous-methods.md b/resources/views/docs/mobile/1/the-basics/asynchronous-methods.md deleted file mode 100644 index 4cc36a87..00000000 --- a/resources/views/docs/mobile/1/the-basics/asynchronous-methods.md +++ /dev/null @@ -1,308 +0,0 @@ ---- -title: Asynchronous Methods -order: 200 ---- - -## Overview - -Many native mobile operations take time to complete and require user interaction. NativePHP Mobile handles these through asynchronous methods that use Laravel's event system to notify your app when operations complete. - -## Understanding Async vs Sync - -### Synchronous Methods ⚡ -Execute immediately and return results directly. - -```php -// These complete instantly -Haptics::vibrate(); -System::flashlight(); -Dialog::toast('Hello!'); -``` - -### Asynchronous Methods 🔄 -Trigger operations that complete later and fire events when done. - -```php -// These trigger operations and fire events when complete -Camera::getPhoto(); // → PhotoTaken event -Biometrics::promptForBiometricID(); // → Completed event -PushNotifications::enrollForPushNotifications(); // → TokenGenerated event -``` - -## Event Handling Pattern - -All asynchronous methods follow the same pattern: - -1. **Call the method** to trigger the operation -2. **Listen for events** to handle the result -3. **Update your UI** based on the outcome - -### Basic Event Structure - -```php -use Livewire\Component; -use Livewire\Attributes\On; -use Native\Mobile\Facades\Camera; -use Native\Mobile\Events\Camera\PhotoTaken; - -class PhotoComponent extends Component -{ - public bool $isCapturing = false; - public ?string $photoPath = null; - - // Step 1: Trigger the async operation - public function takePhoto() - { - $this->isCapturing = true; - Camera::getPhoto(); - } - - // Step 2: Handle the result event - #[On('native:' . PhotoTaken::class)] - public function handlePhotoTaken(string $path) - { - $this->isCapturing = false; - $this->photoPath = $path; - } - - public function render() - { - return view('livewire.photo-component'); - } -} -``` - -## Event Naming Convention - -All frontend events use the `native:` prefix to prevent naming collisions: - -```php -// Backend event class -Native\Mobile\Events\Camera\PhotoTaken - -// Frontend Livewire event (with prefix) -native:Native\Mobile\Events\Camera\PhotoTaken -``` - -## Common Async Operations - -### Camera Operations - -```php -use Native\Mobile\Events\Camera\PhotoTaken; -use Native\Mobile\Events\Gallery\MediaSelected; - -class MediaManager extends Component -{ - public function capturePhoto() - { - Camera::getPhoto(); - } - - public function selectFromGallery() - { - Camera::pickImages('images', true); - } - - #[On('native:' . PhotoTaken::class)] - public function handlePhoto(string $path) - { - // Handle captured photo - } - - #[On('native:' . MediaSelected::class)] - public function handleGallerySelection($success, $files, $count) - { - // Handle selected media - } -} -``` - -### Biometric Authentication - -```php -use Native\Mobile\Events\Biometric\Completed; - -class SecureFeature extends Component -{ - public function authenticate() - { - Biometrics::promptForBiometricID(); - } - - #[On('native:' . Completed::class)] - public function handleBiometric(bool $success) - { - if ($success) { - $this->unlockFeature(); - } else { - $this->showAuthError(); - } - } -} -``` - -### Push Notification Registration - -```php -use Native\Mobile\Events\PushNotification\TokenGenerated; - -class NotificationSetup extends Component -{ - public function enableNotifications() - { - PushNotifications::enrollForPushNotifications(); - } - - #[On('native:' . TokenGenerated::class)] - public function handleToken(string $token) - { - // Send token to your backend - $this->registerToken($token); - } -} -``` - -### Location Services - -```php -use Native\Mobile\Events\Geolocation\LocationReceived; -use Native\Mobile\Events\Geolocation\PermissionStatusReceived; - -class LocationTracker extends Component -{ - public function getCurrentLocation() - { - Geolocation::getCurrentPosition(true); // High accuracy - } - - #[On('native:' . LocationReceived::class)] - public function handleLocation($success = null, $latitude = null, $longitude = null, $accuracy = null, $timestamp = null, $provider = null, $error = null) - { - $this->latitude = $latitude; - $this->longitude = $longitude; - } - - #[On('native:' . PermissionStatusReceived::class)] - public function handlePermissionStatus($location, $coarseLocation, $fineLocation) - { - if (!$coarseLocation == 'granted') { - $this->showLocationPermissionRequest(); - } - } -} -``` - -## Loading States - -Provide visual feedback during async operations: - -```php -class LoadingStatesExample extends Component -{ - public bool $isLoading = false; - public string $loadingMessage = ''; - - public function performAsyncOperation() - { - $this->isLoading = true; - $this->loadingMessage = 'Taking photo...'; - - Camera::getPhoto(); - } - - #[On('native:' . PhotoTaken::class)] - public function handleComplete($path) - { - $this->isLoading = false; - $this->loadingMessage = ''; - - // Process result - } - - public function render() - { - return view('livewire.loading-states-example'); - } -} -``` - -## Advanced Patterns - -### Chaining Async Operations - -```php -class ChainedOperations extends Component -{ - public function authenticateAndCapture() - { - // Step 1: Authenticate first - Biometrics::promptForBiometricID(); - } - - #[On('native:' . Completed::class)] - public function handleAuthComplete(bool $success) - { - if ($success) { - // Step 2: Then capture photo - Camera::getPhoto(); - } - } - - #[On('native:' . PhotoTaken::class)] - public function handlePhotoComplete(string $path) - { - // Step 3: Process the authenticated photo - $this->processSecurePhoto($path); - } -} -``` - -### Multiple Event Listeners - -```php -class MultiEventComponent extends Component -{ - public function performMultipleOperations() - { - Camera::getPhoto(); - PushNotifications::enrollForPushNotifications(); - Geolocation::getCurrentPosition(); - } - - #[On('native:' . PhotoTaken::class)] - public function handlePhoto(string $path) { /* ... */ } - - #[On('native:' . TokenGenerated::class)] - public function handlePushToken(string $token) { /* ... */ } - - #[On('native:' . LocationReceived::class)] - public function handleLocation($success = null, $latitude = null, $longitude = null, $accuracy = null) { /* ... */ } -} -``` - -## Troubleshooting - -### Debugging Async Operations - -```php -class DebuggingComponent extends Component -{ - public function startDebugOperation() - { - Log::info('Starting async operation'); - $this->isLoading = true; - - Camera::getPhoto(); - } - - #[On('native:' . PhotoTaken::class)] - public function handleResult($path) - { - Log::info('Async operation completed', ['result' => $result]); - $this->isLoading = false; - } -} -``` - -Understanding asynchronous methods is crucial for building responsive mobile apps with NativePHP. The event-driven pattern ensures your UI stays responsive while native operations complete in the background. diff --git a/resources/views/docs/mobile/1/the-basics/events.md b/resources/views/docs/mobile/1/the-basics/events.md new file mode 100644 index 00000000..07b7b7ae --- /dev/null +++ b/resources/views/docs/mobile/1/the-basics/events.md @@ -0,0 +1,112 @@ +--- +title: Events +order: 200 +--- + +## Overview + +Many native mobile operations take time to complete and await user interaction. PHP isn't really set up to handle this +sort of asynchronous behaviour; it is built to do its work, send a response and move on as quickly as possible. + +NativePHP for Mobile smooths over this disparity between the different paradigms using a simple event system that +handles completion of asynchronous methods using a webhook-/websocket-style approach to notify your Laravel app. + +## Understanding Async vs Sync + +Not all actions are async. Some methods run immediately, and in some cases return a result straight away. + +Here are a few of the **synchronous** APIs: + +```php +Haptics::vibrate(); +System::flashlight(); +Dialog::toast('Hello!'); +``` +Asynchronous actions trigger operations that may complete later. These return immediately, usually with a `bool` or +`void`, allowing PHP's execution to finish. In many of these cases, the user interacts directly with a native component. +When the user has completed their task and the native UI is dismissed, the native app + +```php +// These trigger operations and fire events when complete +Camera::getPhoto(); // → PhotoTaken event +Biometrics::promptForBiometricID(); // → Completed event +PushNotifications::enrollForPushNotifications(); // → TokenGenerated event +``` + +## Basic Event Structure + +All events are standard [Laravel Event classes](https://laravel.com/docs/12.x/events#defining-events). The public +properties of the events contain the pertinent data coming from the native app side. + +## Event Handling + +All asynchronous methods follow the same pattern: + +1. **Call the method** to trigger the operation. +2. **Listen for the appropriate events** to handle the result. +3. **Update your UI** based on the outcome. + +All events get sent directly to JavaScript in the web view _and_ to your PHP application via a special route. This +allows you to listen for these events in the context that best suits your application. + +### On the frontend + +Events are 'broadcast' to the frontend of your application via the web view through a custom `Native` helper. You can +easily listen for these events in JavaScript in two ways: + +- The `Native.on()` helper +- Livewire's `#[On()]` attribute + +#### The `Native.on()` helper + +Register the event listener directly in JavaScript: + +```blade +@@use(Native\Mobile\Events\Alert\ButtonPressed) + + +``` + +#### Livewire's `#[On()]` attribute + +Livewire makes listening to 'broadcast' events simple. Just add the event name, prefixed by `native:` to the `#[On()]` +attribute attached to the method you want to use as its handler: + +```php +use Native\Mobile\Events\Camera\PhotoTaken; + +#[On('native:'.PhotoTaken::class)] +public function handlePhoto(string $path) +{ + // Handle captured photo +} +``` + +### On the backend + +You can also listen for these events on the PHP side as they are simultaneously passed to your Laravel application. + +Simply [add a listener](https://laravel.com/docs/12.x/events#registering-events-and-listeners) as you normally would: + +```php +use App\Services\APIService; +use Native\Mobile\Events\Camera\PhotoTaken; + +class UpdateAvatar +{ + public function __construct(private APIService $api) {} + + public function handle(PhotoTaken $event): void + { + $imageData = base64_encode( + file_get_contents($event->path) + ); + + $this->api->updateAvatar($imageData); + } +} +``` diff --git a/resources/views/docs/mobile/1/the-basics/icu-support.md b/resources/views/docs/mobile/1/the-basics/icu-support.md deleted file mode 100644 index 3583a7f6..00000000 --- a/resources/views/docs/mobile/1/the-basics/icu-support.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: ICU Support -order: 300 ---- - -## Overview - -ICU (International Components for Unicode) is a library that provides robust Unicode and locale support for applications. While NativePHP Mobile includes a lightweight PHP runtime by default, you can optionally enable ICU support for applications that require advanced internationalization features. - -> **Note**: ICU support is currently only available on Android. We are working to add iOS support as soon as possible and will remove this note when it becomes available. - -## What is ICU? - -ICU provides: -- **Unicode support** - Full Unicode text processing -- **Localization** - Number, date, and currency formatting -- **Collation** - Language-sensitive string comparison -- **Text boundaries** - Word, sentence, and line breaking -- **Transliteration** - Script conversion between languages - -## When Do You Need ICU? - -### Specifically Required for: -- **[Filament](https://filamentphp.com/)** - Uses `intl` extension extensively -- **Laravel's localization helpers** that depend on `intl` -- **Third-party packages** that require `intl` extension -- **Multi-language applications** with complex formatting needs - -## Installation - -### During Initial Setup - -When running `php artisan native:install`, you'll be prompted: - -```bash -php artisan native:install - -# You'll see: -? ➕ Include ICU-enabled PHP binary for Filament/intl requirements? (~30MB extra) - > No (default - smaller app size) - Yes (required for Filament and advanced i18n) -``` - -Select **"Yes"** if you need ICU support. - -### After Installation - -If you already installed without ICU and need to add it: - -```bash -# Re-run the installer and select ICU support -php artisan native:install --force -``` - -## Performance Considerations - -### Memory Usage -- ICU adds ~28MB to your app size -- Runtime memory usage increases slightly -- Complex formatting operations are slower diff --git a/resources/views/docs/mobile/1/the-basics/native-functions.md b/resources/views/docs/mobile/1/the-basics/native-functions.md index c90fee2f..6ff841b1 100644 --- a/resources/views/docs/mobile/1/the-basics/native-functions.md +++ b/resources/views/docs/mobile/1/the-basics/native-functions.md @@ -3,11 +3,14 @@ title: Native Functions order: 100 --- -NativePHP for Mobile enables you to build powerful mobile applications using Laravel and PHP. What makes this approach unique is the ability to call native device functions directly from your PHP code, giving you access to platform-specific features while maintaining the productivity and familiarity of Laravel development. +Our custom PHP extension enables tight integration with each platform, providing a consistent and performant abstraction +that lets you focus on building your app. Build for both platforms while you develop on one. -These functions are called from your PHP code using one of an ever-growing list of facades. +Native device functions are called directly from your PHP code, giving you access to platform-specific features while +maintaining the productivity and familiarity of Laravel development. -All native functionality is namespaced into its own Facade, they are: +These functions are called from your PHP code using an ever-growing list of classes. These classes are also wrapped in +Laravel Facades for ease of access and testing: - `Native\Mobile\Facades\Biometrics` - `Native\Mobile\Facades\Camera` @@ -17,3 +20,12 @@ All native functionality is namespaced into its own Facade, they are: - `Native\Mobile\Facades\PushNotifications` - `Native\Mobile\Facades\SecureStorage` - `Native\Mobile\Facades\System` + + diff --git a/resources/views/docs/mobile/1/the-basics/overview.md b/resources/views/docs/mobile/1/the-basics/overview.md new file mode 100644 index 00000000..3ef606ce --- /dev/null +++ b/resources/views/docs/mobile/1/the-basics/overview.md @@ -0,0 +1,58 @@ +--- +title: Overview +order: 50 +--- + +NativePHP for Mobile is made up of multiple parts: + +- A Laravel application (PHP) +- The `nativephp/mobile` Composer package +- A custom build of PHP with custom NativePHP extension +- Native applications (Swift & Kotlin) + +## Your Laravel app + +You can build your Laravel application just as you normally would, for the most part, sprinkling native functionality +in where desired by using NativePHP's built-in APIs. + +## `nativephp/mobile` + +The package is a pretty normal Composer package. It contains the PHP code needed to interface with the NativePHP +extension, the tools to install and run your applications, and all the code for each native application - iOS and +Android. + +## The PHP builds + +When you run the `native:install` Artisan command, the package will fetch the appropriate versions of the custom-built +PHP binaries. + +NativePHP for Mobile currently bundles **PHP 8.4**. You should ensure that your application is built to work with this +version of PHP. + +These custom PHP builds have been compiled specifically to target the mobile platforms and cannot be used in other +contexts. + +They are compiled as embeddable C libraries and embedded _into_ the native application. In this way, PHP doesn't run as +a separate process/service under a typical web server environment; essentially, the native application itself is +extended with the capability to execute your PHP code. + +Your Laravel application is then executed directly by the native app, using the embedded PHP engine to run the code. +This runs PHP as close to natively as it can get. It is very fast and efficient on modern hardware. + +## The native apps + +NativePHP ships one app for iOS and one for Android. When you run the `native:run` Artisan command, your Laravel app is +packaged up and copied into one of these apps. + +To build for both platforms, you must run the `native:run` command twice, targeting each platform. + +Each native app "shell" runs a number of steps to prepare the environment each time your application is booted, +including: + +- Checking to see if the bundled version of your Laravel app is newer than the installed version + - Installing the newer version if necessary +- Running migrations +- Clearing caches + +Normally, this process takes just a couple of seconds in normal use. After your app has been updated, it will take a +few seconds longer. diff --git a/resources/views/docs/mobile/1/the-basics/splash-screens.md b/resources/views/docs/mobile/1/the-basics/splash-screens.md new file mode 100644 index 00000000..eedcd1d5 --- /dev/null +++ b/resources/views/docs/mobile/1/the-basics/splash-screens.md @@ -0,0 +1,17 @@ +--- +title: Splash Screens +order: 400 +--- + +NativePHP makes it easy to add custom splash screens to your iOS and Android apps. + +## Supply your Splash Screens + +Place the relevant files in the locations specified: + +- `public/splash.png` - for the Light Mode splash screen +- `public/splash-dark.png` - for the Dark Mode splash screen + +### Requirements +- Format: PNG +- Minimum Size/Ratio: 1080 × 1920 pixels