App Icons Plugin for NativePHP Mobile#
Let people choose your app's icon, such as a dark version, seasonal icons or a reward for members, on iOS and Android.
See it in action#
- iPad: tap Gold, Apple confirms, the home-screen icon is gold
- Android: tap Forest, the app stays open, the launcher shows the new icon
Overview#
- No Xcode, no Android Studio. List your icons in a config file; the build adds them to the iOS asset catalog and Xcode settings, and to the Android launcher, on every build.
- Android done properly: each icon also becomes an adaptive icon on a background of its own colour, so it fills the launcher's shape instead of sitting shrunk inside a white circle.
- Mistakes are caught when you build, with a plain message: wrong size, not a PNG, a bad name.
- Clean: rebuilding never piles anything up, and removing every icon restores NativePHP's files exactly as they were.
Requirements: NativePHP Mobile 4.x, Laravel 11–13, PHP 8.3+ with GD (for the build step), iOS 18+, Android 10+.
Installation#
composer require firsthand/nativephp-app-icons
Don't forget to register the plugin, and publish the config:
php artisan native:plugin:register firsthand/nativephp-app-iconsphp artisan vendor:publish --tag=app-icons-config
List your icons in config/app-icons.php. Each one is a 1024×1024 PNG, square and full-bleed (the system rounds the corners):
'icons' => [ 'dark' => resource_path('icons/dark.png'), 'holiday' => resource_path('icons/holiday.png'),],
Then rebuild your app. Your normal app icon stays as it is, under the name default.
Usage#
PHP (Super Native / Livewire / Blade)#
use Firsthand\AppIcons\Facades\AppIcon; AppIcon::set('holiday');AppIcon::reset(); // back to the normal iconAppIcon::current(); // 'holiday', or 'default'AppIcon::available(); // ['supported' => true, 'icons' => ['dark', 'holiday']]
JavaScript (Vue / React / Inertia)#
import { set, reset, current, available, onAppIconChanged } from '@firsthand/nativephp-app-icons'; await set('dark');
Events#
AppIconChanged#
Firsthand\AppIcons\Events\AppIconChanged, fired when a switch has finished.
| Property | Type | Description |
|---|---|---|
name |
string |
The icon now in use, or default |
ok |
bool |
false when the system refused |
message |
?string |
Why it refused |
use Firsthand\AppIcons\Events\AppIconChanged;use Native\Mobile\Attributes\On; #[On(AppIconChanged::class)] // #[OnNative(...)] in a Livewire web-view pagepublic function iconChanged(string $name, bool $ok, ?string $message = null): void { … }
onAppIconChanged(({ name, ok }) => { /* … */ });
Methods#
set(string $name): bool#
Switches to a configured icon, or default. Returns straight away (false outside a NativePHP app); the outcome arrives as AppIconChanged. A name that isn't in config/app-icons.php throws InvalidArgumentException before it reaches the phone.
reset(): bool#
Same as set('default').
current(): ?string#
The icon in use, or default. null outside the app.
available(): ?array#
['supported' => bool, 'icons' => [...]]: the icons this build contains, and whether the device lets the app switch. null outside the app.
Platform behavior#
- iPad needs a real iPad app. NativePHP builds iPhone-only apps unless
'ipad' => trueis set inconfig/nativephp.php, and iOS never lets an iPhone app running on an iPad change its icon. The build prints a warning when this applies. - iOS always shows a short "You have changed the icon" notice after a switch. It's Apple's own, and no app can hide it. Apple rejects app icons with transparent pixels, so the build flattens them onto white for iOS.
- Android switches as the person leaves the app. Android changes an icon by moving the app's launcher entry, and on most phones that sends the app to the background. So the plugin keeps the choice and makes the switch when they go to the home screen; they stay where they are, and
current()already reports the new icon. To switch on the spot instead (the app goes to the background), setAPP_ICONS_ANDROID_SWITCH=immediate. Either way the launcher can take a moment to redraw. - A change to the list of icons takes effect on the next build.
Testing#
Script the bridge with NativePHP's testing suite:
use Native\Mobile\Testing\Native; it('switches to the holiday icon', function () { Native::fakeBridge() ->respondTo('AppIcon.Current', ['name' => 'default']) ->respondTo('AppIcon.Available', ['supported' => true, 'icons' => ['holiday']]) ->respondTo('AppIcon.Set', ['started' => true]); Native::test(SettingsScreen::class)->tap('Holiday icon'); Native::fakeBridge()->assertCalled('AppIcon.Set', fn (array $p) => $p['name'] === 'holiday');});
Troubleshooting#
| What you see | Why |
|---|---|
| "No app icon called 'x'" | x isn't in config/app-icons.php, or you haven't rebuilt since adding it |
| The build prints "App icons: … must be 1024×1024" | Resize the PNG; icons are exactly 1024×1024 |
| iPad: "This is an iPhone app running on an iPad" | Set 'ipad' => true in config/nativephp.php and rebuild |
| Android: the icon hasn't changed yet | It switches when the person leaves the app (see Platform behavior) |
| Android shows two copies of the app in the launcher for a moment | The launcher is catching up after a switch; it settles by itself |
| A native screen shows no text | Native screens need NativePHP's UI package, nativephp/mobile-ui |
License#
One purchase covers unlimited apps. See the LICENSE file in the package.