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 (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 $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.