Tilly The Coder
Tilly The Coder
How to Track Errors in NativePHP with NativeWatch
Tilly The Coder

How to Track Errors in NativePHP with NativeWatch

Tilly The Coder
6 mins
This is the estimated time it takes to read the article.

You ship a NativePHP app, someone opens it on their own machine, and a button stops working. The useful question is what happened inside that particular build. A screenshot might show the symptom. An error report can give you the exception, the source location and the steps immediately before it.

Disclosure: NativeWatch is my own product. This guide walks through connecting it to a NativePHP desktop or mobile app, making diagnostics optional, and checking that an error actually reaches your project.

Start with a development project

Create a workspace and app at NativeWatch, choosing Desktop, iOS or Android. Open the project's Setup guide for its ingest key. Use separate projects where you want separate streams, such as independent iOS and Android apps. You do not need to connect GitHub to receive errors.

Both SDKs require PHP 8.4 or later within PHP 8 and support Laravel 11, 12 and 13. This assumes you already have a working NativePHP app. The error-reporting package does not install the desktop runtime for you. The getting-started guide is the reference for the shared configuration.

Install the package for your platform

For desktop, install the PHP SDK in the Laravel project you package:

composer require zinxan/native-desktop
php artisan vendor:publish --tag=nativewatch-config

Laravel package discovery registers its provider. On mobile, there is an extra piece: the Swift and Kotlin collectors must be compiled into your application. Follow the mobile installation guide:

composer require zinxan/native-mobile
php artisan vendor:publish --tag=nativephp-plugins-provider
php artisan native:plugin:register zinxan/native-mobile
php artisan native:plugin:validate vendor/zinxan/native-mobile

Preserve your existing provider and plugins when publishing, then check that NativeWatch appears in the plugin list. The current manifest requires iOS 17 or later and Android API 26 or later. Some Android crash evidence needs newer versions.

Configure the receiving project

Add the following values to the configuration used by your app, replacing the placeholder with its project ingest key. Set the URL explicitly because older SDK configuration defaults to a different domain.

NATIVEWATCH_KEY=nw_your_project_key
NATIVEWATCH_URL=https://native.watch
NATIVEWATCH_ENABLED=true
NATIVEWATCH_REQUIRE_CONSENT=true
NATIVEWATCH_ENVIRONMENT=development

Keep these settings available before the SDK boots. During development, clear cached configuration:

php artisan config:clear

For mobile, rebuild through your NativePHP workflow:

php artisan native:run ios

Use php artisan native:run android for Android. Rebuild after plugin upgrades too. Refreshing Blade cannot put new native code into an installed binary. On desktop, rebuild and reinstall the package when checking distributed settings.

Let the person using the app choose

With consent required, diagnostics start off. Add an initially unselected setting that explains what gets sent, why you collect it and how to turn it off. The app should remain usable when someone declines. Display the confirmed state using NativeWatch::hasConsent().

For a Laravel form, validate granted with ['required', 'boolean'] in a dedicated form request. Keep your normal authorization and CSRF protection. The action handling that validated request should check whether the SDK saved the choice:

use Illuminate\Validation\ValidationException;
use ZXN\NativeWatch\Core\Laravel\NativeWatch;

if (! NativeWatch::setConsent($request->boolean('granted'))) {
    throw ValidationException::withMessages([
        'granted' => 'Your diagnostic choice could not be saved. Please retry.',
    ]);
}

Only show success after that call returns true. Passing false withdraws consent; do not silently enable it at boot or after login. The privacy guide explains pending-report cleanup and the separate handling needed for previously uploaded data.

Verify two different PHP paths

Opt in within the environment you are checking, then run the supplied diagnostic command:

php artisan nativewatch:test

Save its printed event UUID and find the matching stored occurrence in the intended project. API acceptance happens before server processing, so the command's success message is only part of the check.

This command constructs a PHP exception. To exercise Laravel's automatic reporting integration, add a temporary development-only control inside the packaged app that calls:

report(new RuntimeException('NativeWatch packaged-app verification'));

Open that occurrence and check the message, source frame, environment and runtime. Remove the control before distribution. A terminal command uses that terminal's configuration and storage; consent saved in the installed app may not apply there. I would check both paths before treating the integration as ready.

Include the web view

JavaScript reporting is a separate integration. In your root Blade layout, place the directive once in the head, before application scripts. Keep the rest of your existing layout:

@nativewatchScripts
@vite('resources/js/app.ts')

The JavaScript guide explains its local Laravel endpoint. Keep that endpoint relative, especially on mobile, where changing the origin can block reporting. In a consented development web view, trigger an uncaught error:

setTimeout(() => {
    throw new Error('NativeWatch web-view verification');
}, 0);

Check its message and timestamp in NativeWatch, then test an unhandled rejection separately:

Promise.reject(new Error('NativeWatch promise verification'));

These listeners cover uncaught errors and unhandled rejections. A framework can catch an error before it reaches them. They also do not report every console message or apply source maps to production bundles.

Give the error some context

A breadcrumb should describe an operation without copying customer content into the report. For example, add one immediately before an inventory sync:

use ZXN\NativeWatch\Core\Laravel\NativeWatch;

NativeWatch::addBreadcrumb('sync', 'Started inventory sync');

NativeWatch groups related occurrences into issues. Use the occurrence to inspect this specific failure, then the issue to investigate repeats. Review the stack, breadcrumbs and build together. The release defaults to nativephp.version unless configured otherwise, so keep that value meaningful.

Inspect representative payloads before distributing the app. Key-based redaction helps with fields such as passwords and tokens, but a secret inside an exception message or URL still needs your attention.

Check the installed app before shipping

The desktop SDK captures PHP reporting; installing it does not establish Electron main-process crash collection. Mobile native crashes need their own checks using the compiled plugin. Follow the mobile guide's disposable Release-build procedure, including relaunch, because native evidence is delivered afterwards.

Finally, check a fresh installation with consent denied, enabled reporting, withdrawal and offline delivery on your target devices. If an event is missing, start with the project key, URL, consent and installed configuration, then inspect the outbox. The useful finish line is a stored occurrence from the build your users will actually run.