> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.qonversion.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Displaying No-Codes

> First, you need to initialize the SDK. To do this, use the same project key that you use to initialize the Qonversion SDK.

<CodeGroup>
  ```swift Swift theme={null}
  // You can initialize using only project key
  let configuration = NoCodesConfiguration(projectKey: "projectKey")
  NoCodes.initialize(with: configuration)
  ```

  ```kotlin Kotlin theme={null}
  // You can initialize using only project key
  val noCodesConfig = NoCodesConfig.Builder(this, "projectKey").build()
  NoCodes.initialize(noCodesConfig)
  ```

  ```java Java theme={null}
  // You can initialize using only project key
  final NoCodesConfig noCodesConfig = new NoCodesConfig.Builder(this, "projectKey").build();
  NoCodes.initialize(noCodesConfig);
  ```

  ```typescript React Native theme={null}
  // You can initialize using only project key
  const noCodesConfig = new NoCodesConfigBuilder('projectKey')
    .build();
  NoCodes.initialize(noCodesConfig);
  ```

  ```dart Flutter theme={null}
  // You can initialize using only project key
  final noCodesConfig = new NoCodesConfigBuilder(projectKey).build();
  NoCodes.initialize(noCodesConfig);
  ```

  ```csharp Unity theme={null}
  // You can initialize using only project key
  var noCodesConfig = new NoCodesConfigBuilder("projectKey")
      .Build();
  NoCodes.Initialize(noCodesConfig);
  ```

  ```typescript Cordova theme={null}
  // You can initialize using only project key
  const noCodesConfig = new Qonversion.NoCodesConfigBuilder('projectKey')
    .build();
  Qonversion.NoCodes.initialize(noCodesConfig);
  ```

  ```typescript Capacitor theme={null}
  // You can initialize using only project key
  const noCodesConfig = new NoCodesConfigBuilder('projectKey')
    .build();
  NoCodes.initialize(noCodesConfig);
  ```
</CodeGroup>

Then you can set the delegates for handling events and customizing screens as follows - *(optional)*:

<CodeGroup>
  ```swift Swift theme={null}
  // The first delegate is used for main events (e.g., screen opened, button tapped).
  NoCodes.shared.set(delegate: self)
  // The second is for screen customization (if you want to override default styles).
  NoCodes.shared.set(screenCustomizationDelegate: self)

  // Delegates can be also passed via the initialization through the configuration:
  let noCodesConfig = NoCodesConfiguration(projectKey: "projectKey", delegate: self, screenCustomizationDelegate: self)
  ```

  ```kotlin Kotlin theme={null}
  // The first delegate is used for main events (e.g., screen opened, button tapped).
  NoCodes.shared.setDelegate(this)
  // The second is for screen customization (if you want to override default styles).
  NoCodes.shared.setScreenCustomizationDelegate(this)

  // Delegates can be also passed via the initialization through the configuration:
  val noCodesConfig = NoCodesConfig.Builder(this, "projectKey")
    .setDelegate(this)
    .setScreenCustomizationDelegate(this)
    .build()
  ```

  ```java Java theme={null}
  // The first delegate is used for main events (e.g., screen opened, button tapped).
  NoCodes.getSharedInstance().setDelegate(this);
  // The second is for screen customization (if you want to override default styles).
  NoCodes.getSharedInstance().setScreenCustomizationDelegate(this);

  // Delegates can be also passed via the initialization through the configuration:
  final NoCodesConfig noCodesConfig = new NoCodesConfig.Builder(this, "projectKey")
    .setDelegate(this)
    .setScreenCustomizationDelegate(this)
    .build();
  ```

  ```typescript React Native theme={null}
  // Set the listener via the initialization through the configuration
  // It is used for main events (e.g., screen opened, button tapped).
  const noCodesConfig = new NoCodesConfigBuilder('projectKey')
    .setNoCodesListener(this)
  .build();

  // Set presentation style config before showing screen if needed
  const screenConfig = new ScreenPresentationConfig(ScreenPresentationStyle.FULL_SCREEN, true);
  NoCodes.getSharedInstance().setScreenPresentationConfig(screenConfig, 'yourContextKey');
  ```

  ```dart Flutter theme={null}
  // Set presentation style config before showing screen if needed
  final config = NoCodesPresentationConfig(
      animated: true,
      presentationStyle: NoCodesPresentationStyle.fullScreen,
  );

  NoCodes.getSharedInstance().setScreenPresentationConfig(config, contextKey: 'yourContextKey');

  // Subscribe to separate NoCodes event streams
  _screenShownStream = NoCodes.getSharedInstance().screenShownStream.listen((event) {
      // add functionality here
  });

  _finishedStream = NoCodes.getSharedInstance().finishedStream.listen((event) {
      // add functionality here
  });

  _actionStartedStream = NoCodes.getSharedInstance().actionStartedStream.listen((event) {
      // add functionality here
  });

  _actionFailedStream = NoCodes.getSharedInstance().actionFailedStream.listen((event) {
      // add functionality here
  });

  _actionFinishedStream = NoCodes.getSharedInstance().actionFinishedStream.listen((event) {
      // add functionality here
  });

  _screenFailedToLoadStream = NoCodes.getSharedInstance().screenFailedToLoadStream.listen((event) {
      // add functionality here
  });
  ```

  ```csharp Unity theme={null}
  // Set the delegate via the initialization through the configuration
  // It is used for main events (e.g., screen opened, button tapped).
  var noCodesConfig = new NoCodesConfigBuilder("projectKey")
      .SetNoCodesDelegate(this)
      .Build();
  NoCodes.Initialize(noCodesConfig);

  // Set presentation style config before showing screen if needed
  var screenConfig = new ScreenPresentationConfig(ScreenPresentationStyle.FullScreen, true);
  NoCodes.GetSharedInstance().SetScreenPresentationConfig(screenConfig, "yourContextKey");
  ```

  ```typescript Cordova theme={null}
  // Set the listener via the initialization through the configuration
  // It is used for main events (e.g., screen opened, button tapped).
  const noCodesConfig = new Qonversion.NoCodesConfigBuilder('projectKey')
    .setNoCodesListener(this)
  .build();

  // Set presentation style config before showing screen if needed
  const screenConfig = new Qonversion.ScreenPresentationConfig(Qonversion.ScreenPresentationStyle.FULL_SCREEN, true);
  Qonversion.NoCodes.getSharedInstance().setScreenPresentationConfig(screenConfig, 'yourContextKey');
  ```

  ```typescript Capacitor theme={null}
  // Set the listener via the initialization through the configuration
  // It is used for main events (e.g., screen opened, button tapped).
  const noCodesConfig = new NoCodesConfigBuilder('projectKey')
    .setNoCodesListener(this)
  .build();

  // Set presentation style config before showing screen if needed
  const screenConfig = new ScreenPresentationConfig(ScreenPresentationStyle.FULL_SCREEN, true);
  NoCodes.getSharedInstance().setScreenPresentationConfig(screenConfig, 'yourContextKey');
  ```
</CodeGroup>

After initializing the SDK, you can move forward and display the No-Codes screen:

<CodeGroup>
  ```swift Swift theme={null}
  NoCodes.shared.showScreen(withContextKey: "yourContextKey")
  ```

  ```kotlin Kotlin theme={null}
  NoCodes.shared.showScreen("yourContextKey")
  ```

  ```java Java theme={null}
  NoCodes.getSharedInstance().showScreen("yourContextKey");
  ```

  ```typescript React Native theme={null}
  NoCodes.getSharedInstance().showScreen('yourContextKey');
  ```

  ```dart Flutter theme={null}
  NoCodes.getSharedInstance().showScreen('yourContextKey');
  ```

  ```csharp Unity theme={null}
  NoCodes.GetSharedInstance().ShowScreen("yourContextKey");
  ```

  ```typescript Cordova theme={null}
  Qonversion.NoCodes.getSharedInstance().showScreen('yourContextKey');
  ```

  ```typescript Capacitor theme={null}
  NoCodes.getSharedInstance().showScreen('yourContextKey');
  ```
</CodeGroup>

<Info>
  If you want to use Qonversion No-Codes with your own purchase infrastructure, [see](using-no-codes-with-custom-purchases-handling) how you can do it in two simple steps.
</Info>

## Loading screens before presentation

By default, `showScreen` is fire-and-present: it presents a loading view immediately and fetches the content afterwards, so your app learns the outcome through delegate callbacks only after the screen is already on screen.

If you want to decide up front — an "ask-first" flow — use `loadScreen`. It awaits the screen's availability and data (from cache or network) *before* anything is presented, so you can either present the screen or show your own fallback UI without the SDK loading view ever appearing. A successful load warms the shared screens cache, so the following `showScreen` call with the same context key renders from cache.

<CodeGroup>
  ```swift Swift theme={null}
  do {
    _ = try await NoCodes.shared.loadScreen(withContextKey: "yourContextKey")  // warms the cache
    NoCodes.shared.showScreen(withContextKey: "yourContextKey")                // renders from cache
  } catch {
    presentOwnFallbackUI()                                                     // SDK screen never shown
  }
  ```

  ```kotlin Kotlin theme={null}
  // Kotlin coroutines
  lifecycleScope.launch {
    try {
      NoCodes.shared.loadScreen("yourContextKey")   // warms the cache
      NoCodes.shared.showScreen("yourContextKey")   // renders from cache
    } catch (e: NoCodesException) {
      presentOwnFallbackUi()                        // SDK screen never shown
    }
  }

  // Or the callback-based variant — the callback is invoked on the main thread
  NoCodes.shared.loadScreen("yourContextKey", object : NoCodesScreenLoadCallback {
    override fun onSuccess(screen: QNoCodeScreen) {
      NoCodes.shared.showScreen("yourContextKey")
    }

    override fun onError(error: NoCodesError) {
      presentOwnFallbackUi()
    }
  })
  ```

  ```java Java theme={null}
  // The callback is invoked on the main thread
  NoCodes.getSharedInstance().loadScreen("yourContextKey", new NoCodesScreenLoadCallback() {
    @Override
    public void onSuccess(@NonNull QNoCodeScreen screen) {
      NoCodes.getSharedInstance().showScreen("yourContextKey");
    }

    @Override
    public void onError(@NonNull NoCodesError error) {
      presentOwnFallbackUi();
    }
  });
  ```

  ```typescript React Native theme={null}
  try {
    const screen = await NoCodes.getSharedInstance().loadScreen('yourContextKey'); // warms the cache
    NoCodes.getSharedInstance().showScreen('yourContextKey');                      // renders from cache
  } catch (e) {
    presentOwnFallbackUi();                                                        // SDK screen never shown
  }
  ```

  ```dart Flutter theme={null}
  try {
    final screen = await NoCodes.getSharedInstance().loadScreen('yourContextKey'); // warms the cache
    NoCodes.getSharedInstance().showScreen('yourContextKey');                      // renders from cache
  } on QonversionException {
    presentOwnFallbackUi();                                                        // SDK screen never shown
  }
  ```

  ```csharp Unity theme={null}
  NoCodes.GetSharedInstance().LoadScreen(
      "yourContextKey",
      onSuccess: screen => NoCodes.GetSharedInstance().ShowScreen("yourContextKey"),
      onError: error => PresentOwnFallbackUi()
  );
  ```

  ```typescript Cordova theme={null}
  try {
    const screen = await Qonversion.NoCodes.getSharedInstance().loadScreen('yourContextKey');
    Qonversion.NoCodes.getSharedInstance().showScreen('yourContextKey');
  } catch (e) {
    presentOwnFallbackUi();
  }
  ```

  ```typescript Capacitor theme={null}
  try {
    const screen = await NoCodes.getSharedInstance().loadScreen('yourContextKey');
    NoCodes.getSharedInstance().showScreen('yourContextKey');
  } catch (e) {
    presentOwnFallbackUi();
  }
  ```
</CodeGroup>

The returned screen exposes its `id`, `contextKey`, and the typed default variables configured on it in the builder (see [Reading default variables](#reading-default-variables) below). The error lets you branch on the failure: a **screen not found** error (`NoCodesError.type == .screenNotFound` on iOS, error code `ScreenNotFound` on Android) means the screen is genuinely absent for that context key — show your fallback; any other code indicates a transient network or load failure — you may retry.

<Note>
  `loadScreen` is an optional, additional entry point — not a prerequisite for `showScreen`. Screens marked as preloadable in the No-Codes Builder are fetched automatically at SDK initialization (see [Screens Preloading](screens-preloading)), and `showScreen` works on its own.

  Two minor differences from a direct `showScreen` call: `loadScreen` does not flush pending user properties, so the targeting basis may differ slightly; and on-demand loads cache the screen without pre-embedded images (image embedding is applied during init-time preloading only).
</Note>

Available starting from the following SDK versions:

| Platform     | Minimum Version |
| ------------ | --------------- |
| iOS          | 6.13.0          |
| Android      | No-Codes 1.10.0 |
| React Native | 10.9.0          |
| Flutter      | 11.8.0          |
| Cordova      | 7.7.0           |
| Capacitor    | 1.6.0           |
| Unity        | 9.7.0           |

### Reading default variables

The screen returned by `loadScreen` carries the typed **default variables** configured on it in the No-Code Builder — `defaultVariables`. Each variable has a `kind`:

* **custom** — a Screen Variable authored in the builder's Variables section;
* **product** — a product slot: the variable key is the slot name and the value is the default Qonversion product ID assigned to it;
* **selectedProduct** — the screen's Default Product (Product Slots panel → Default Product): the Qonversion product ID selected by default when the screen opens. Read it via the typed shortcut `screen.defaultSelectedProductId` — no key needed.

Each variable preserves its configured native type (boolean / string / number) instead of being stringified:

<CodeGroup>
  ```swift Swift theme={null}
  public struct NoCodesScreenVariable {
    public let kind: NoCodesScreenVariableKind  // .custom, .product, .selectedProduct, or .unknown
    public let key: String    // variable name (`variable.<key>` in the builder) or slot name
    public let type: String   // "boolean", "string" or "number"
    public let value: NoCodesScreenVariableValue
  }

  public enum NoCodesScreenVariableValue {
    case bool(Bool)
    case string(String)
    case number(Double)
    case none                 // a null/absent configured default

    var stringValue: String { get }  // any value as a plain string ("" for none)
  }
  ```

  ```kotlin Kotlin theme={null}
  data class QScreenVariable(
    val kind: Kind,                  // Custom, Product, SelectedProduct, or Unknown
    val key: String,                 // variable name (`variable.<key>` in the builder) or slot name
    val type: String,                // "boolean", "string" or "number"
    val value: QScreenVariableValue, // Bool / Str / Num / None — preserves the native type
  )

  // QScreenVariableValue.asString(): any value as a plain string ("" for None)
  ```
</CodeGroup>

For example, to read a variable by key, the default product of a slot, and the screen's default selected product. Keys are only unique within a kind — a custom variable and a product slot may share a name — so the by-key lookup accepts an optional kind filter:

<CodeGroup>
  ```swift Swift theme={null}
  let screen = try await NoCodes.shared.loadScreen(withContextKey: "yourContextKey")

  if case .bool(let showTrial) = screen.defaultVariable(forKey: "show_trial", kind: .custom)?.value {
    print("show_trial = \(showTrial)")
  }

  let primarySlotProduct = screen.defaultVariable(forKey: "primary", kind: .product)?.value.stringValue
  let defaultSelected = screen.defaultSelectedProductId
  ```

  ```kotlin Kotlin theme={null}
  val screen = NoCodes.shared.loadScreen("yourContextKey")

  val showTrial = (screen.defaultVariable("show_trial", QScreenVariable.Kind.Custom)?.value
      as? QScreenVariableValue.Bool)?.value ?: false

  val primarySlotProduct = screen.defaultVariable("primary", QScreenVariable.Kind.Product)?.value?.asString()
  val defaultSelected = screen.defaultSelectedProductId
  ```
</CodeGroup>

<Info>
  `defaultVariables` is an empty list when the screen has none configured (or on older payloads), so it is always safe to iterate. The `kind` set may grow — values unknown to your SDK version arrive as `.unknown` / `Unknown` instead of failing the load.
</Info>

<Note>
  These values are the **defaults configured on the screen**, read back at load time. Conditional slot overrides and runtime mutations inside the running screen are not reflected, and they are not the same as the custom variables you pass *into* a screen from your app (see [Passing custom variables](#passing-custom-variables)).
</Note>

## Passing custom variables

You can pass dynamic values from your app into a No-Code screen and reference them in conditional logic, dynamic text, or component visibility rules. Variables are scoped to a specific `contextKey`, so different screens can receive different values, and each value is queried at the moment a screen is about to be shown — including sub-screens reached via in-screen navigation.

For native SDKs, implement a delegate that returns a `Map`/dictionary keyed by `contextKey`. For cross-platform SDKs, pass the values directly to `showScreen`.

<CodeGroup>
  ```swift Swift theme={null}
  // 1. Implement NoCodesCustomVariablesDelegate
  extension MyClass: NoCodesCustomVariablesDelegate {
    func customVariables(for contextKey: String) -> [String: String] {
      return ["button_color": "red", "username": "Alex"]
    }
  }

  // 2. Provide it to the SDK
  NoCodes.shared.set(customVariablesDelegate: self)
  // Or via configuration:
  let configuration = NoCodesConfiguration(projectKey: "projectKey", customVariablesDelegate: self)
  ```

  ```kotlin Kotlin theme={null}
  // 1. Implement CustomVariablesDelegate
  class MyActivity : AppCompatActivity(), CustomVariablesDelegate {
    override fun getCustomVariables(contextKey: String): Map<String, String> {
      return mapOf("button_color" to "red", "username" to "Alex")
    }
  }

  // 2. Provide it to the SDK
  NoCodes.shared.setCustomVariablesDelegate(this)
  // Or via configuration:
  val noCodesConfig = NoCodesConfig.Builder(this, "projectKey")
    .setCustomVariablesDelegate(this)
    .build()
  ```

  ```java Java theme={null}
  // 1. Implement CustomVariablesDelegate
  public class MyActivity extends AppCompatActivity implements CustomVariablesDelegate {
    @NonNull
    @Override
    public Map<String, String> getCustomVariables(@NonNull String contextKey) {
      Map<String, String> variables = new HashMap<>();
      variables.put("button_color", "red");
      variables.put("username", "Alex");
      return variables;
    }
  }

  // 2. Provide it to the SDK
  NoCodes.getSharedInstance().setCustomVariablesDelegate(this);
  // Or via configuration:
  final NoCodesConfig noCodesConfig = new NoCodesConfig.Builder(this, "projectKey")
    .setCustomVariablesDelegate(this)
    .build();
  ```

  ```typescript React Native theme={null}
  const customVariables = { button_color: 'red', username: 'Alex' };
  NoCodes.getSharedInstance().showScreen('yourContextKey', customVariables);
  ```

  ```dart Flutter theme={null}
  final customVariables = {'button_color': 'red', 'username': 'Alex'};
  NoCodes.getSharedInstance().showScreen('yourContextKey', customVariables: customVariables);
  ```

  ```csharp Unity theme={null}
  var customVariables = new Dictionary<string, string> {
    { "button_color", "red" },
    { "username", "Alex" }
  };
  NoCodes.GetSharedInstance().ShowScreen("yourContextKey", customVariables);
  ```

  ```typescript Cordova theme={null}
  const customVariables = { button_color: 'red', username: 'Alex' };
  Qonversion.NoCodes.getSharedInstance().showScreen('yourContextKey', customVariables);
  ```

  ```typescript Capacitor theme={null}
  const customVariables = { button_color: 'red', username: 'Alex' };
  NoCodes.getSharedInstance().showScreen('yourContextKey', customVariables);
  ```
</CodeGroup>

<Info>
  Values must be strings. Inside the screen, variables are injected into the JavaScript context via `window.noCodesSetVariable(name, value)` and then become available in conditional logic, dynamic text bindings, and visibility rules.
</Info>

## Delegates

Earlier, we discussed how to set delegates. Now, let's explore their purpose.

### Main delegate

The main delegate is needed so that you can receive messages about what is happening during the execution of No-Codes, as well as to provide us with the screen from which to start navigation and display the No-Codes screen. Let's go through this step by step.

<Warning>
  ### Unity Warning

  Unity NoCodes delegate events are delivered on iOS only. On Android, the No-Codes screen runs as a separate Activity on top of Unity's Activity and Unity's game loop is paused while the screen is shown, so delegate events do not fire.
</Warning>

##### controllerForNavigation *(iOS only)*

<CodeGroup>
  ```swift Swift theme={null}
  func controllerForNavigation() -> UIViewController?
  ```
</CodeGroup>

In this function, you must return a UIViewController from which we will start displaying the No-Codes flow. If you do not provide one, we will take the topmost screen in the current stack.

##### Screen shown event

<CodeGroup>
  ```swift Swift theme={null}
  func noCodesHasShownScreen(id: String)
  ```

  ```kotlin Kotlin theme={null}
  fun onScreenShown(screenId: String)
  ```

  ```java Java theme={null}
  void onScreenShown(String screenId)
  ```

  ```typescript React-Native theme={null}
  onScreenShown: (id: string) => {}
  ```

  ```dart Flutter theme={null}
  _screenShownStream = NoCodes.getSharedInstance().screenShownStream.listen((event) {
      // add functionality here
  });
  ```

  ```csharp Unity theme={null}
  void OnScreenShown(string contextKey)
  ```

  ```typescript Cordova theme={null}
  onScreenShown: (id: string) => {}
  ```

  ```typescript Capacitor theme={null}
  onScreenShown: (id: string) => {}
  ```
</CodeGroup>

This function notifies about the display of a screen with a specific identifier.

<Note>
  To read the screen's configured default variables and product slots, load the screen entity via `loadScreen` and use its `defaultVariables` — see [Reading default variables](#reading-default-variables).
</Note>

#### Action execution start event

<CodeGroup>
  ```swift Swift theme={null}
  func noCodesStartsExecuting(action: NoCodesAction)
  ```

  ```kotlin Kotlin theme={null}
  fun onActionStartedExecuting(action: QAction)
  ```

  ```java Java theme={null}
  void onActionStartedExecuting(QAction action)
  ```

  ```typescript React-Native theme={null}
  onActionStartedExecuting: (action: NoCodesAction) => {}
  ```

  ```dart Flutter theme={null}
  _actionStartedStream = NoCodes.getSharedInstance().actionStartedStream.listen((event) {
      // add functionality here
  });
  ```

  ```csharp Unity theme={null}
  void OnActionStarted(NoCodesAction action)
  ```

  ```typescript Cordova theme={null}
  onActionStartedExecuting: (action: Qonversion.NoCodesAction) => {}
  ```

  ```typescript Capacitor theme={null}
  onActionStartedExecuting: (action: NoCodesAction) => {}
  ```
</CodeGroup>

This function notifies you that executing the action you set up through our dashboard has begun.

#### Action execution fail event

<CodeGroup>
  ```swift Swift theme={null}
  func noCodesFailedToExecute(action: NoCodesAction, error: Error?)
  ```

  ```kotlin Kotlin theme={null}
  fun onActionFailedToExecute(action: QAction) {
    // For details look at the `error` property of the action
  }
  ```

  ```java Java theme={null}
  void onActionFailedToExecute(QAction action) {
    // For details look at the `error` property of the action
  }
  ```

  ```typescript React-Native theme={null}
  onActionFailedToExecute: (action: NoCodesAction) => {}
  ```

  ```dart Flutter theme={null}
  _actionFailedStream = NoCodes.getSharedInstance().actionFailedStream.listen((event) {
      // add functionality here
  });
  ```

  ```csharp Unity theme={null}
  void OnActionFailed(NoCodesAction action)
  ```

  ```typescript Cordova theme={null}
  onActionFailedToExecute: (action: Qonversion.NoCodesAction) => {}
  ```

  ```typescript Capacitor theme={null}
  onActionFailedToExecute: (action: NoCodesAction) => {}
  ```
</CodeGroup>

This function notifies you that executing the action you set up through our dashboard has failed.

#### Action execution finish event

<CodeGroup>
  ```swift Swift theme={null}
  func noCodesFinishedExecuting(action: NoCodesAction)
  ```

  ```kotlin Kotlin theme={null}
  fun onActionFinishedExecuting(action: QAction)
  ```

  ```java Java theme={null}
  void onActionFinishedExecuting(QAction action)
  ```

  ```typescript React-Native theme={null}
  onActionFinishedExecuting: (action: NoCodesAction) => {}
  ```

  ```dart Flutter theme={null}
  _actionFinishedStream = NoCodes.getSharedInstance().actionFinishedStream.listen((event) {
      // add functionality here
  });
  ```

  ```csharp Unity theme={null}
  void OnActionFinished(NoCodesAction action)
  ```

  ```typescript Cordova theme={null}
  onActionFinishedExecuting: (action: Qonversion.NoCodesAction) => {}
  ```

  ```typescript Capacitor theme={null}
  onActionFinishedExecuting: (action: NoCodesAction) => {}
  ```
</CodeGroup>

This function notifies you that the action you set up through our dashboard has been executed.

#### Custom action received event

<CodeGroup>
  ```swift Swift theme={null}
  func noCodesReceivedCustomAction(value: String)
  ```

  ```kotlin Kotlin theme={null}
  fun onCustomAction(value: String)
  ```

  ```java Java theme={null}
  void onCustomAction(String value)
  ```

  ```typescript React Native theme={null}
  onCustomAction: (value: string) => {}
  ```

  ```dart Flutter theme={null}
  _customActionStream = NoCodes.getSharedInstance().customActionStream.listen((event) {
      // event.value identifies the triggered action
  });
  ```

  ```csharp Unity theme={null}
  // Implement the optional NoCodesCustomActionDelegate interface alongside your NoCodesDelegate
  void OnCustomAction(string value)
  ```

  ```typescript Cordova theme={null}
  onCustomAction: (value: string) => {}
  ```

  ```typescript Capacitor theme={null}
  onCustomAction: (value: string) => {}
  ```
</CodeGroup>

This function is called when a <a href="/docs/custom-actions" target="_blank" rel="noopener">Custom action</a> is triggered on the screen. Custom actions are not executed by the SDK — implement your own handling here; the `value` string configured in the builder identifies which action was triggered. The screen stays open; close it using `NoCodes.shared.close()` if needed. If no value was configured, an empty string is delivered.

Available starting from the following SDK versions:

| Platform     | Minimum Version |
| ------------ | --------------- |
| iOS          | 6.13.0          |
| Android      | No-Codes 1.10.0 |
| React Native | 10.9.0          |
| Flutter      | 11.8.0          |
| Cordova      | 7.7.0           |
| Capacitor    | 1.6.0           |
| Unity        | 9.7.0           |

#### Screen finished event

<CodeGroup>
  ```swift Swift theme={null}
  func noCodesFinished()
  ```

  ```kotlin Kotlin theme={null}
  fun onFinished()
  ```

  ```java Java theme={null}
  void onFinished()
  ```

  ```typescript React-Native theme={null}
  onFinished: () => {}
  ```

  ```dart Flutter theme={null}
  _finishedStream = NoCodes.getSharedInstance().finishedStream.listen((event) {
      // add functionality here
  });
  ```

  ```csharp Unity theme={null}
  void OnFinished()
  ```

  ```typescript Cordova theme={null}
  onFinished: () => {}
  ```

  ```typescript Capacitor theme={null}
  onFinished: () => {}
  ```
</CodeGroup>

This function notifies that the No-Codes flow has been completed and No-Codes has been finished.

#### Screen loading failed

<CodeGroup>
  ```swift Swift theme={null}
  func noCodesFailedToLoadScreen(error: Error?)
  ```

  ```kotlin Kotlin theme={null}
  fun onScreenFailedToLoad(error: NoCodesError)
  ```

  ```java Java theme={null}
  void onScreenFailedToLoad(NoCodesError error)
  ```

  ```typescript React-Native theme={null}
  onScreenFailedToLoad: (error: NoCodesError) => {}
  ```

  ```dart Flutter theme={null}
  _screenFailedToLoadStream = NoCodes.getSharedInstance().screenFailedToLoadStream.listen((event) {
      // add functionality here
  });
  ```

  ```csharp Unity theme={null}
  void OnScreenFailedToLoad(NoCodesError error)
  ```

  ```typescript Cordova theme={null}
  onScreenFailedToLoad: (error: Qonversion.NoCodesError) => {}
  ```

  ```typescript Capacitor theme={null}
  onScreenFailedToLoad: (error: NoCodesError) => {}
  ```
</CodeGroup>

This function is called when No-Codes screen loading has failed.

### Localization

By default, No-Code screens automatically detect the device's system language and display the appropriate localization if available. You can override this behavior by setting a custom locale, which takes priority over the automatic system language detection.

The locale should be in standard format (e.g., `"en"`, `"en-US"`, `"de"`, `"de-DE"`). Pass `nil`/`null` to reset to the system default locale.

**Setting locale during initialization:**

<CodeGroup>
  ```swift Swift theme={null}
  var config = NoCodesConfiguration(
      projectKey: "your_project_key",
      locale: "de-DE"
  )
  NoCodes.initialize(with: config)
  ```

  ```kotlin Kotlin theme={null}
  val config = NoCodesConfig.Builder(context, "your_project_key")
      .setLocale("de-DE")
      .build()
  NoCodes.initialize(config)
  ```

  ```java Java theme={null}
  NoCodesConfig config = new NoCodesConfig.Builder(context, "your_project_key")
      .setLocale("de-DE")
      .build();
  NoCodes.initialize(config);
  ```

  ```typescript React Native theme={null}
  const noCodesConfig = new NoCodesConfigBuilder("projectKey")
      .setLocale("de-DE")
      .build();
  NoCodes.initialize(noCodesConfig);
  ```

  ```dart Flutter theme={null}
  final noCodesConfig = NoCodesConfigBuilder("projectKey")
      .setLocale("de-DE")
      .build();
  NoCodes.initialize(noCodesConfig);
  ```

  ```csharp Unity theme={null}
  var noCodesConfig = new NoCodesConfigBuilder("projectKey")
      .SetLocale("de-DE")
      .Build();
  NoCodes.Initialize(noCodesConfig);
  ```

  ```typescript Cordova theme={null}
  const noCodesConfig = new Qonversion.NoCodesConfigBuilder("projectKey")
      .setLocale("de-DE")
      .build();
  Qonversion.NoCodes.initialize(noCodesConfig);
  ```

  ```typescript Capacitor theme={null}
  const noCodesConfig = new NoCodesConfigBuilder("projectKey")
      .setLocale("de-DE")
      .build();
  NoCodes.initialize(noCodesConfig);
  ```
</CodeGroup>

**Setting locale after initialization:**

<CodeGroup>
  ```swift Swift theme={null}
  NoCodes.shared.setLocale("fr-FR")
  NoCodes.shared.showScreen(withContextKey: "your_context_key")

  // Reset to system default
  NoCodes.shared.setLocale(nil)
  ```

  ```kotlin Kotlin theme={null}
  NoCodes.shared.setLocale("fr-FR")
  NoCodes.shared.showScreen("your_context_key")

  // Reset to system default
  NoCodes.shared.setLocale(null)
  ```

  ```java Java theme={null}
  NoCodes.getSharedInstance().setLocale("fr-FR");
  NoCodes.getSharedInstance().showScreen("your_context_key");

  // Reset to system default
  NoCodes.getSharedInstance().setLocale(null);
  ```

  ```typescript React Native theme={null}
  NoCodes.getSharedInstance().setLocale("fr-FR");
  NoCodes.getSharedInstance().showScreen("yourContextKey");

  // Reset to system default
  NoCodes.getSharedInstance().setLocale(null);
  ```

  ```dart Flutter theme={null}
  NoCodes.getSharedInstance().setLocale("fr-FR");
  NoCodes.getSharedInstance().showScreen("yourContextKey");

  // Reset to system default
  NoCodes.getSharedInstance().setLocale(null);
  ```

  ```csharp Unity theme={null}
  NoCodes.GetSharedInstance().SetLocale("fr-FR");
  NoCodes.GetSharedInstance().ShowScreen("yourContextKey");

  // Reset to system default
  NoCodes.GetSharedInstance().SetLocale(null);
  ```

  ```typescript Cordova theme={null}
  Qonversion.NoCodes.getSharedInstance().setLocale("fr-FR");
  Qonversion.NoCodes.getSharedInstance().showScreen("yourContextKey");

  // Reset to system default
  Qonversion.NoCodes.getSharedInstance().setLocale(null);
  ```

  ```typescript Capacitor theme={null}
  NoCodes.getSharedInstance().setLocale("fr-FR");
  NoCodes.getSharedInstance().showScreen("yourContextKey");

  // Reset to system default
  NoCodes.getSharedInstance().setLocale(null);
  ```
</CodeGroup>

### Theme

No-Code screens support light and dark themes. By default, screens automatically adapt to the device's system appearance (Auto mode). You can override this behavior by setting a specific theme, which takes priority over the automatic system theme detection.

<Warning>
  Currently, the theme only affects skeleton loader colors, with full theming support for the No-Code Builder coming soon
</Warning>

Available theme modes:

* **Auto** (default) - Follows the device's system appearance
* **Light** - Forces light theme regardless of device settings
* **Dark** - Forces dark theme regardless of device settings

**Setting theme during initialization:**

<CodeGroup>
  ```swift Swift theme={null}
  var config = NoCodesConfiguration(
      projectKey: "your_project_key",
      theme: .dark
  )
  NoCodes.initialize(with: config)
  ```

  ```kotlin Kotlin theme={null}
  val config = NoCodesConfig.Builder(context, "your_project_key")
      .setTheme(NoCodesTheme.Dark)
      .build()
  NoCodes.initialize(config)
  ```

  ```java Java theme={null}
  NoCodesConfig config = new NoCodesConfig.Builder(context, "your_project_key")
      .setTheme(NoCodesTheme.Dark)
      .build();
  NoCodes.initialize(config);
  ```

  ```typescript React Native theme={null}
  const noCodesConfig = new NoCodesConfigBuilder("projectKey")
      .setTheme(NoCodesTheme.DARK)
      .build();
  NoCodes.initialize(noCodesConfig);
  ```

  ```dart Flutter theme={null}
  final noCodesConfig = NoCodesConfigBuilder("projectKey")
      .setTheme(NoCodesTheme.dark)
      .build();
  NoCodes.initialize(noCodesConfig);
  ```

  ```csharp Unity theme={null}
  var noCodesConfig = new NoCodesConfigBuilder("projectKey")
      .SetTheme(NoCodesTheme.Dark)
      .Build();
  NoCodes.Initialize(noCodesConfig);
  ```

  ```typescript Cordova theme={null}
  const noCodesConfig = new Qonversion.NoCodesConfigBuilder("projectKey")
      .setTheme(Qonversion.NoCodesTheme.DARK)
      .build();
  Qonversion.NoCodes.initialize(noCodesConfig);
  ```

  ```typescript Capacitor theme={null}
  const noCodesConfig = new NoCodesConfigBuilder("projectKey")
      .setTheme(NoCodesTheme.DARK)
      .build();
  NoCodes.initialize(noCodesConfig);
  ```
</CodeGroup>

**Setting theme after initialization:**

<CodeGroup>
  ```swift Swift theme={null}
  NoCodes.shared.setTheme(.light)
  NoCodes.shared.showScreen(withContextKey: "your_context_key")

  // Reset to auto (follow system appearance)
  NoCodes.shared.setTheme(.auto)
  ```

  ```kotlin Kotlin theme={null}
  NoCodes.shared.setTheme(NoCodesTheme.Light)
  NoCodes.shared.showScreen("your_context_key")

  // Reset to auto (follow system appearance)
  NoCodes.shared.setTheme(NoCodesTheme.Auto)
  ```

  ```java Java theme={null}
  NoCodes.getSharedInstance().setTheme(NoCodesTheme.Light);
  NoCodes.getSharedInstance().showScreen("your_context_key");

  // Reset to auto (follow system appearance)
  NoCodes.getSharedInstance().setTheme(NoCodesTheme.Auto);
  ```

  ```typescript React Native theme={null}
  NoCodes.getSharedInstance().setTheme(NoCodesTheme.LIGHT);
  NoCodes.getSharedInstance().showScreen("yourContextKey");

  // Reset to auto (follow system appearance)
  NoCodes.getSharedInstance().setTheme(NoCodesTheme.AUTO);
  ```

  ```dart Flutter theme={null}
  NoCodes.getSharedInstance().setTheme(NoCodesTheme.light);
  NoCodes.getSharedInstance().showScreen("yourContextKey");

  // Reset to auto (follow system appearance)
  NoCodes.getSharedInstance().setTheme(NoCodesTheme.auto);
  ```

  ```csharp Unity theme={null}
  NoCodes.GetSharedInstance().SetTheme(NoCodesTheme.Light);
  NoCodes.GetSharedInstance().ShowScreen("yourContextKey");

  // Reset to auto (follow system appearance)
  NoCodes.GetSharedInstance().SetTheme(NoCodesTheme.Auto);
  ```

  ```typescript Cordova theme={null}
  Qonversion.NoCodes.getSharedInstance().setTheme(Qonversion.NoCodesTheme.LIGHT);
  Qonversion.NoCodes.getSharedInstance().showScreen("yourContextKey");

  // Reset to auto (follow system appearance)
  Qonversion.NoCodes.getSharedInstance().setTheme(Qonversion.NoCodesTheme.AUTO);
  ```

  ```typescript Capacitor theme={null}
  NoCodes.getSharedInstance().setTheme(NoCodesTheme.LIGHT);
  NoCodes.getSharedInstance().showScreen("yourContextKey");

  // Reset to auto (follow system appearance)
  NoCodes.getSharedInstance().setTheme(NoCodesTheme.AUTO);
  ```
</CodeGroup>

### Screen customization

Use the screen customization delegate to customize the screen opening animation. For example, to enable or disable the animation (*iOS only*), specify the type of opening: push/popover (*iOS only*)/full screen. You can also pass a `statusBarHidden` flag that determines whether to show or hide the status bar when displaying the screen (*iOS only*).

For example:

<CodeGroup>
  ```swift Swift theme={null}
  func presentationConfigurationForScreen(contextKey: String) -> NoCodesPresentationConfiguration {
    return NoCodesPresentationConfiguration(animated: true, presentationStyle: .push)
  }
  ```

  ```kotlin Kotlin theme={null}
  override fun getPresentationConfigurationForScreen(contextKey: String): QScreenPresentationConfig {
    return QScreenPresentationConfig(QScreenPresentationStyle.Push)
  }
  ```

  ```java Java theme={null}
  @NonNull
  @Override
  public QScreenPresentationConfig getPresentationConfigurationForScreen(@NonNull String contextKey) {
    return new QScreenPresentationConfig(QScreenPresentationStyle.Push);
  }
  ```

  ```typescript React-Native theme={null}
  // Set presentation style config before showing screen if needed
  const screenConfig = new ScreenPresentationConfig(ScreenPresentationStyle.FULL_SCREEN, true);

  NoCodes.getSharedInstance().setScreenPresentationConfig(screenConfig, 'yourContextKey');
  ```

  ```dart Flutter theme={null}
  // Set presentation style config before showing screen if needed
  final config = NoCodesPresentationConfig(
      animated: true,
      presentationStyle: NoCodesPresentationStyle.fullScreen,
  );

  NoCodes.getSharedInstance().setScreenPresentationConfig(config, contextKey: 'yourContextKey');
  ```

  ```csharp Unity theme={null}
  // Set presentation style config before showing screen if needed
  var screenConfig = new ScreenPresentationConfig(ScreenPresentationStyle.FullScreen, true);

  NoCodes.GetSharedInstance().SetScreenPresentationConfig(screenConfig, "yourContextKey");
  ```

  ```typescript Cordova theme={null}
  // Set presentation style config before showing screen if needed
  const screenConfig = new Qonversion.ScreenPresentationConfig(Qonversion.ScreenPresentationStyle.FULL_SCREEN, true);

  Qonversion.NoCodes.getSharedInstance().setScreenPresentationConfig(screenConfig, 'yourContextKey');
  ```

  ```typescript Capacitor theme={null}
  // Set presentation style config before showing screen if needed
  const screenConfig = new ScreenPresentationConfig(ScreenPresentationStyle.FULL_SCREEN, true);

  NoCodes.getSharedInstance().setScreenPresentationConfig(screenConfig, 'yourContextKey');
  ```
</CodeGroup>

### Custom loading view

By default, the SDK displays a skeleton loading view while the No-Code screen is loading. You can replace it with your own custom loading view by implementing the `noCodesCustomLoadingView()` method in the screen customization delegate.

Your custom view must conform to the `NoCodesLoadingView` protocol (iOS) or implement the `NoCodesLoadingView` interface (Android). The SDK calls `startAnimating()` when the loading begins and `stopAnimating()` when the screen content is ready.

<CodeGroup>
  ```swift Swift theme={null}
  // Conform to NoCodesLoadingView
  class MyLoadingView: UIView, NoCodesLoadingView {
    func startAnimating() {
      // Start your loading animation
    }

    func stopAnimating() {
      // Stop your loading animation
    }
  }

  // Return it from the screen customization delegate
  func noCodesCustomLoadingView() -> NoCodesLoadingView? {
    return MyLoadingView(frame: .zero)
  }
  ```

  ```kotlin Kotlin theme={null}
  // Implement NoCodesLoadingView
  class MyLoadingView(context: Context) : FrameLayout(context), NoCodesLoadingView {
    override fun startAnimating() {
      // Start your loading animation
    }

    override fun stopAnimating() {
      // Stop your loading animation
    }
  }

  // Return it from the screen customization delegate
  override fun noCodesCustomLoadingView(): View? {
    return MyLoadingView(context)
  }
  ```

  ```java Java theme={null}
  // Implement NoCodesLoadingView
  public class MyLoadingView extends FrameLayout implements NoCodesLoadingView {
    @Override
    public void startAnimating() {
      // Start your loading animation
    }

    @Override
    public void stopAnimating() {
      // Stop your loading animation
    }
  }

  // Return it from the screen customization delegate
  @Nullable
  @Override
  public View noCodesCustomLoadingView() {
    return new MyLoadingView(context);
  }
  ```
</CodeGroup>

<Info>
  If `noCodesCustomLoadingView()` returns `nil` / `null`, the default skeleton loading view will be used.
</Info>

***

[Screens Preloading](screens-preloading)

[Success and Failure Actions](success-failure-actions)
