Screen Name Mapping
Screen name mapping lets you show friendly, human-readable screen names in your Paylisher dashboard instead of raw view controller class names. Reports become readable for non-technical teammates, and you can rename screens without touching your code — just edit a JSON file bundled with your app.
| Raw class name | Mapped screen name |
|---|---|
HomeViewController | Home |
ProfileViewController | Profile |
SettingsViewController | Settings |
Without mapping, the SDK still reports a clean default name (it strips the ViewController suffix, so HomeViewController becomes Home). Mapping is what lets you override that default with any label you like — including localized or product-specific names such as Ana Sayfa or Shopping Cart.
How it works
- Screen tracking uses method swizzling on
viewDidAppear, so initialize the SDK as early as possible (see Configuration). - The first time a screen is captured, the mapper lazily loads
paylisher_screens.jsonfrom your app bundle (Bundle.main), then caches it for the rest of the process. - When a view controller appears, the SDK looks up its class name in the mapping.
- If a match is found, the mapped name is used. Otherwise the default name is used.
- The resulting name is sent with the
$screenevent and shown in the dashboard.
The mapper is set up for you automatically — there is no extra setup call. If paylisher_screens.json is missing from the bundle, mapping is simply skipped and default names are used, so adding the file is completely optional and non-breaking.
1. Add the mapping file
Create a file named paylisher_screens.json and add it to your app target's bundle:
- In Xcode, add the file to your project (for example under a
Resourcesgroup). - Make sure it's included in your target's Build Phases → Copy Bundle Resources. (When you check your app target in the "Add to targets" dialog, Xcode does this for you.)
The file is a flat JSON object where each key is a class name and each value is the display name:
{
"HomeViewController": "Home",
"ProfileViewController": "Profile",
"SettingsViewController": "Settings"
}
The file must be a flat object of string → string. The SDK parses the whole file with a single
[String: String]cast. If any value is a number, boolean, or nested object, the cast fails and the entire mapping is discarded — not just the offending entry. Keep every value a plain string.
Module prefix is optional. iOS resolves the class name via
String(describing:), which usually yields the bare class name (HomeViewController). If your build reports names with a module prefix, add that variant as the key too.
2. Enable screen tracking
Screen tracking is enabled by default (captureScreenViews = true), so no code change is required for the common case:
let config = PaylisherConfig(apiKey: PAYLISHER_API_KEY, host: PAYLISHER_HOST)
// Automatically capture a screen event when a view controller appears.
// true by default.
config.captureScreenViews = true
PaylisherSDK.shared.setup(config)
Because tracking relies on method swizzling, initialize Paylisher as early as possible in your app's lifecycle so no early screens are missed.
Name resolution order
When a view controller appears, the SDK resolves its screen name in this order:
- JSON mapping by class name — a match for the view controller's class name in
paylisher_screens.json(checked first). - SwiftUI screens — for SwiftUI content hosted in
UIHostingController(orPresentationHostingController, e.g. sheets and popovers), the SDK resolves a meaningful name from, in order: the controller'stitle, the navigation-item title, then the inferred SwiftUI view name. - Default (UIKit) — the class name with the
ViewControllersuffix removed (HomeViewController→Home).
For SwiftUI screens and title-based names (steps 2 and 3), the resolved name is also run through the mapping. This means you can map either the class name or the resolved title:
{
"HomeViewController": "Home",
"Profile": "User Profile",
"ProductListView": "Products"
}
In the example above, HomeViewController maps by class name, while Profile (a resolved UIKit title) and ProductListView (an inferred SwiftUI view name) map by their resolved names.
Repeated screens are de-duplicated. The SDK suppresses a
$screenevent when the resolved name is identical to the previous screen (and drops the same name from the same controller within a 1-second window). Navigating back to a screen you just left may therefore not emit a new event.
SwiftUI notes
SwiftUI apps host their content in UIHostingController, whose class name is not meaningful on its own. To get useful names, either:
-
Set a navigation title on the screen — the SDK will use it:
ContentView()
.navigationTitle("Home") -
Or rely on the inferred view name (e.g.
ProductListView) that the SDK extracts from the hosting controller.
Then, if you want a different display name, add the resolved title or view name as a key in paylisher_screens.json and map it to your preferred label.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Mapping has no effect | File not in the bundle | Confirm paylisher_screens.json is in Copy Bundle Resources for the running target |
| Mapping has no effect | JSON not flat string → string | A single non-string/nested value discards the whole file — ensure every value is a plain string |
| A screen isn't mapped | Class-name mismatch | The key is the class name from String(describing:) (usually the bare name). Check capitalization; if your build namespaces types, try a module-prefixed key |
| SwiftUI screens have odd names | No title to resolve from | Set .navigationTitle(...), or map the inferred view name |
| First screen missed | SDK initialized too late | Initialize Paylisher as early as possible (swizzling-based tracking) |
| Returning to a screen fires nothing | De-duplication | Consecutive identical screen names are suppressed — this is expected |
Debug logging
The mapper prints [Paylisher] messages to the console when it loads — for example ✅ Loaded N screen mappings. or ℹ️ paylisher_screens.json not found in bundle. These print regardless of the debug flag, so they're a quick way to confirm the file was found and how many entries loaded.
config.debug = true
Turning on debug additionally enables [AutoScreen] diagnostics — for example when a SwiftUI screen name can't be resolved (it logs the raw class name) or when a duplicate screen event is skipped.
The SDK does not log the resolved name of a successfully captured screen. To confirm the exact key to use, rely on your class name (
String(describing: vc.classForCoder)) or theLoaded N screen mappingscount after editing the file.
Example paylisher_screens.json
{
"HomeViewController": "Home",
"ProfileViewController": "Profile",
"SettingsViewController": "Settings",
"CartViewController": "Shopping Cart",
"CheckoutViewController": "Checkout",
"ProductDetailViewController": "Product Detail",
"LoginViewController": "Login"
}
Using the same feature on Android? See the Android Screen Name Mapping guide — the concept is identical, but Android loads the file from
assets/and keys must be fully qualified class names.