Goal
White-label a MAM app from code instead of clicking through the admin UI: set
the app’s theme colors, app icon, icon background, and launch screen
programmatically so you can provision branding as part of a script, a migration,
or a sibling plugin.
Two distinct mechanisms are involved, and the distinction matters:
- Theme colors are settings. They live behind the settings cascade and are
read on every phone-data build, so you write them through the
mam_app_settings_set_settingfilter (or the underlyingtsl-setting-*
options) and they take effect on the next app launch. - App icon, icon background, and launch screen are publish-app assets. They
live in frozenlocal-app-*/ios_*option keys and only reach a device when
a new build is compiled and submitted through the WPMAM Fastlane pipeline.
Get those two timelines straight before you start: colors ship live; icons and
launch screens ship with the next store build.
Prerequisites
mam-mainactive. It owns both the settings cascade and the publish-app
subsystem.- Enrollment complete, with
local-app-account_codepopulated and verified — the
publish pipeline re-verifies it before accepting a build. - For the asset steps: a square app icon (1024×1024 PNG) and any launch-screen
artwork already available as media you can reference or upload. - Familiarity with Settings cascade overview (how
mam_app_settings_get_setting
/set_settingroute reads and writes) and Recipe: Publish your app to iOS and
Android (how assets become a build).
How branding splits across the two subsystems
The settings cascade resolves a value per-button, then per-role, then global,
then default. Theme colors are global by nature — you almost always want one
brand across every role — so you write them at the global scope and let the
cascade fall through to it.
The publish-app option keys are flat update_option storage. They are part of
the frozen public contract: customer apps and ~2K enrolled sites depend on
the exact key names, and several of them (ios_pn_app_bundle_id,
android_pn_app_bundle_id) are submitted to the App Store and Play Store. You
may read and write their values freely; never rename the keys.
| Branding concern | Mechanism | Reaches device when |
|---|---|---|
| Theme colors | Settings cascade | Next phone-data fetch (live) |
| App icon | Publish-app option | Next compiled build |
| App icon background | Publish-app option | Next compiled build |
| Launch screen image(s) | Publish-app option | Next compiled build |
| Launch screen background | Publish-app option | Next compiled build |
Steps
1. Identify the color setting keys
Theme colors are stored as tsl-setting-* options and surfaced in Mobile App
Mgr → App Settings as type => 'color' fields. The setting key (the
id) is what you write to. The most commonly white-labeled keys include:
| Setting (admin label) | Setting key |
|---|---|
| Theme Color Light | tsl-setting-mam_theme_color_light |
| Theme Color Dark | tsl-setting-mam_theme_color_dark |
| Bottom Tab bar Color | tsl-setting-tabbar_color |
| Bottom Tab bar Selected Icon Color | tsl-setting-tabbar_icon_color |
| Bottom Tab bar Un-selected Icon Color | tsl-setting-tabbar_unselected_icon_color |
| Preferred Accent Color | tsl-setting-colors_prefered_accent_color |
| Background color | tsl-setting-screen_background_color |
There are dozens of additional color keys (listing text/background, form submit
button colors, snackbar colors, featured outline, and more). The authoritative
list is thetype => 'color'field definitions in
mam-main/includes/app-settings/admin-settings-page.php. Treat the table above
as the high-traffic subset, not the whole inventory, and confirm any specific
key against that file before scripting it.
2. Write a theme color
Two of these keys behave differently from the rest, so match the write path to
how the value is read back.
Theme Color Light / Dark are stored as plain WordPress options and read back
with get_option() — the button color engine reads tsl-setting-mam_theme_color_light
and tsl-setting-mam_theme_color_dark directly. Set them with update_option:
update_option( 'tsl-setting-mam_theme_color_light', '#0A84FF' );
update_option( 'tsl-setting-mam_theme_color_dark', '#0A84FF' );
The other color keys (tab bar, accent, background) are read through the
settings cascade during the phone-data build, under the global button-id
namespace general-settings. Write those through the mam_app_settings_set_setting
filter so the value lands where the cascade reads it. The handler’s signature is
($status, $role, $button_id, $key, $value) — a pass-through status comes first
and the value comes last:
apply_filters(
'mam_app_settings_set_setting',
'', // status — passed through, returned unchanged
'mam-all', // role — 'mam-all' is the global scope
'general-settings', // button-id namespace global settings read under
'tsl-setting-tabbar_color', // setting key
'#0A84FF' // the value to store
);
Repeat for each cascade-backed color key you want to brand. Going through the
filter (rather than a bare update_option to the settings table) keeps the
per-role/per-button scoping consistent and is the contract the rest of the suite
uses.
3. (Optional) Read a color back to confirm
Cascade-backed keys go through the matching read filter, whose signature is
($value, $role, $button_id, $key). It resolves the value by role with a
fallback to the global mam-all scope, keyed on the same general-settings
button-id namespace you wrote under:
$tabbar = apply_filters(
'mam_app_settings_get_setting',
'#000000', // fallback default
'mam-all', // role — 'mam-all' is the global scope
'general-settings', // button-id namespace
'tsl-setting-tabbar_color' // key
);
The plain-option theme colors are the exception — read those with
get_option( 'tsl-setting-mam_theme_color_light' ) rather than the filter, since
that is where they are stored.
Because mam_app_settings_get_setting is on the hot path (it runs hundreds
of times per phone-data build), don’t add filter subscribers here just to set
branding — write the value in step 2 and let the existing read path pick it up.
4. Set the app icon and icon background
The app icon asset and its background are frozen publish-app options:
| Branding asset | Option key |
|---|---|
| App icon | local-app-images-app-icon |
| App icon background | local-app-app-icon-bg |
The icon background is stored as a hex color and is sanitized with
sanitize_hex_color() on the publish page, so write a valid hex value:
update_option( 'local-app-app-icon-bg', sanitize_hex_color( '#FFFFFF' ) );
The icon image itself is an uploaded asset. The admin flow uploads through the
local_app_update_image AJAX action and stores the resulting reference in
local-app-images-app-icon. The stored value is a media-library attachment
id (a numeric post id): the publish page reads it back with
get_option( 'local-app-images-app-icon' ) and resolves it via
wp_get_attachment_image_url( $id, 'full' ). To set it in code, upload the icon
into the media library first and store its attachment id:
update_option( 'local-app-images-app-icon', $attachment_id );
5. Set the launch screen image(s) and background
Launch-screen branding spans two image slots plus a background color:
| Branding asset | Option key |
|---|---|
| Launch screen image 1 | local-app-images-launch-screen-1 |
| Launch screen image 2 | local-app-images-launch-screen-2 |
| Launch screen background | ios_app_launch_screen_bg |
The background is a sanitized hex color, the same as the icon background:
update_option(
'ios_app_launch_screen_bg',
sanitize_hex_color( '#101418' )
);
The two launch-screen images are generated PNG bitmaps, not something you hand-author.
mam-main exposes the local_app_generate_launch_screen AJAX action (handled by
the image manager’s generate_launch_screen) to produce them: it composites the
app icon over the background color onto two fixed-size canvases (640×1136 and
1125×2436), writes launch_screen_1.png / launch_screen_2.png, and registers
them as media attachments. After changing the icon or background in code, trigger
that generation step so the compiled launch screen reflects your branding rather
than the previous one.
local_app_generate_launch_screen(and itsmam_generate_launch_screenalias)
is a logged-in admin AJAX action, not a plain function call. It expects two POST
parameters —bg_color(hex) andapp_icon(the icon image URL). To drive it
from a script, either call the image manager’sgenerate_launch_screen()method
directly or POST the action with those parameters.
6. Publish a build so assets reach devices
Color changes are live on the next phone-data fetch and need no build. Icon,
icon background, and launch-screen changes do not appear on any device until a
new app is compiled and submitted. Once your asset options are set, run the
publish flow described in Recipe: Publish your app to iOS and Android:
- The publish handler re-verifies
local-app-account_code. - It builds a Fastlane settings array from the persisted publish-app options
(icon, icon background, launch screen, version/build numbers, bundle ids). - It fires
apply_filters( 'mam_fastlane_settings', $settings )— the single
supported extension point for injecting build-time branding config (for
example a custom URL scheme or an extra Info.plist entry). - It POSTs the payload to the WPMAM publish endpoint, which runs Fastlane
asynchronously.
If you need to add branding-related build config that has no admin field, hook
mam_fastlane_settings rather than inventing a new option:
add_filter( 'mam_fastlane_settings', function ( array $settings ): array {
$settings['ios_extra_plist']['MyBrandKey'] = get_option( 'my_brand_key' );
return $settings;
} );
Coordinate any new Fastlane keys with the build pipeline team — what WPMAM does
with them lives on the WPMAM side.
7. Verify
- Colors: fetch the phone-data payload before and after and confirm the new
hex values flow through:
curl 'https://example.com/wp-admin/admin-ajax.php?action=local_app_get_phone_data'
> after.json
Or open the app (or the cloning-admin preview) and confirm the new theme.
- Icon / launch screen: these only show up in a freshly compiled build.
Watch the publish-status panel; a successful build surfaces in TestFlight (iOS)
and Google Play Internal Testing (Android) within 30–60 minutes. Install it and
confirm the home-screen icon, icon background, and launch screen.
Anti-patterns to avoid
- Renaming a frozen option key.
local-app-images-app-icon,
local-app-app-icon-bg,local-app-images-launch-screen-1/2,
ios_app_launch_screen_bgare frozen public contracts. Write their values;
never rename them. - Expecting icon/launch-screen changes to go live like colors. They don’t —
they require a new compiled build. Only theme colors are live on the next
fetch. - Adding subscribers to
mam_app_settings_get_settingto apply branding. That
filter is the hot path. Write the value once withset_setting; don’t pay a
per-read cost on every app launch. - Fabricating the stored shape of an image option. Upload through the image
manager and read the option back to learn the real shape, rather than guessing
an attachment id or URL format. - Inventing a new option for build-time branding. If there’s no admin field
for what you need at build time, hookmam_fastlane_settings— the one
supported publish-payload extension point.
Related
- Settings cascade overview (
mam-main) — howget_setting/set_setting
resolve reads and writes, the scopes, and the hot-path warning. - Frozen public contracts reference (
mam-main) — the authoritative inventory
of frozen option keys, including every branding asset key used here. - Recipe: Publish your app to iOS and Android (
mam-main) — how publish-app
options become a compiled, submitted build. - Hook:
mam_app_settings_set_setting— the write-filter reference. - Hook:
mam_fastlane_settings— the publish-payload extension point.
