Skip to main content

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 nameMapped screen name
HomeViewControllerHome
ProfileViewControllerProfile
SettingsViewControllerSettings

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

  1. Screen tracking uses method swizzling on viewDidAppear, so initialize the SDK as early as possible (see Configuration).
  2. The first time a screen is captured, the mapper lazily loads paylisher_screens.json from your app bundle (Bundle.main), then caches it for the rest of the process.
  3. When a view controller appears, the SDK looks up its class name in the mapping.
  4. If a match is found, the mapped name is used. Otherwise the default name is used.
  5. The resulting name is sent with the $screen event 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:

  1. In Xcode, add the file to your project (for example under a Resources group).
  2. 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:

  1. JSON mapping by class name — a match for the view controller's class name in paylisher_screens.json (checked first).
  2. SwiftUI screens — for SwiftUI content hosted in UIHostingController (or PresentationHostingController, e.g. sheets and popovers), the SDK resolves a meaningful name from, in order: the controller's title, the navigation-item title, then the inferred SwiftUI view name.
  3. Default (UIKit) — the class name with the ViewController suffix removed (HomeViewControllerHome).

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 $screen event 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

SymptomLikely causeFix
Mapping has no effectFile not in the bundleConfirm paylisher_screens.json is in Copy Bundle Resources for the running target
Mapping has no effectJSON not flat string → stringA single non-string/nested value discards the whole file — ensure every value is a plain string
A screen isn't mappedClass-name mismatchThe 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 namesNo title to resolve fromSet .navigationTitle(...), or map the inferred view name
First screen missedSDK initialized too lateInitialize Paylisher as early as possible (swizzling-based tracking)
Returning to a screen fires nothingDe-duplicationConsecutive 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 the Loaded N screen mappings count 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.