What is the native bridge?
Some capabilities don’t live in JavaScript at all: reading files, fetching device and build info, reloading the bundle. They live in native modules, compiled into the app and exposed to JS through React Native’s bridge.native is how you reach them. It resolves native modules by name and ships a handful of pre-resolved handles for the ones Unbound needs most.
Separately, the Unbound loader installs its own JSI object on the global as UnboundNative. That one is not a Discord module. It’s Unbound’s own native surface, and it carries a versioned feature API you should query before you call into it.
Resolving native modules
A native module’s name varies across platforms and Discord versions. The iOS file manager might beDCDFileManager on one build and RTNFileManager on another. So getNativeModule takes a list of candidate names and returns the first one that resolves, checking both NativeModules and the TurboModule registry.
Built-in handles
The native modules Unbound depends on are already resolved for you:BundleInfo
BundleInfo
Build and release metadata: version, release channel, build number, identifier.
BundleManager
BundleManager
The bundle updater. Drives OTA update checks and reloads (see below).
DeviceInfo
DeviceInfo
Hardware and OS details: device model, manufacturer, system version, RAM.
File manager
File manager
The native file module the fs module is built on. Prefer
fs for file work, since it resolves paths and defaults encodings for you.Reloading
reload() is the safe way to restart the bundle: it persists pending settings first, then reloads, so nothing in flight is lost.
UnboundNative
UnboundNative is the JSI object the iOS loader installs directly on the JS global. It is re-exported from the native module so you can reach it through the API:
Top level: version & features
Top level: version & features
Eight feature-lifecycle functions, described below, let you ask what this build actually supports:
getNativeModuleVersion, supportsFeature, getFeatureInfo, isFeatureDeprecated, isFeatureRemoved, getSupportedFeatures, getDeprecatedFeatures, and getRemovedFeatures. evaluateBytecode also lives here.device
device
Hardware, OS and install-integrity details about the device Unbound is running on.
string
The device model identifier.
string
The iOS version as a string.
boolean
Whether the device is jailbroken.
boolean
Whether the app is installed as a system app.
boolean
Whether the running build passes the loader’s signature check.
object
The application’s entitlements as an object. Returns an empty object when they can’t be read.
string
The same entitlements formatted as a plist string, for display or export.
app
app
string
How this copy of the app was installed. Useful when a feature depends on the install method rather than on the device.
notifications
notifications
string
Schedules a local notification and returns its identifier. Every argument is optional:
title falls back to "Notification", body to an empty string, timeDelay to 1, soundEnabled to true, and identifier to a freshly generated UUID. Since the generated identifier is returned, capture it if you need to reference the notification later.pip
pip
string | null
Starts picture-in-picture playback for a video URL and returns a playback identifier. Returns
null when the URL is missing or empty, so check the result rather than assuming playback started.chat
chat
Native chat appearance controls. The getters read current state synchronously; the setters are fire-and-forget (see the warning below).
number
The current avatar corner radius, or
-1 when none is set.undefined
Sets the avatar corner radius. Defaults to
0 when the argument is missing.undefined
Clears the avatar corner radius override.
boolean
Whether message bubbles are on. Defaults to
false when unset.string
The light-theme bubble color.
string
The dark-theme bubble color.
number
The bubble corner radius, defaulting to
10 when unset.undefined
Toggles message bubbles. The two color arguments are optional; pass neither and only the enabled flag is applied.
undefined
Sets both bubble colors. Missing arguments are sent as empty strings.
undefined
Sets the bubble corner radius. Defaults to
10 when the argument is missing.undefined
Clears every message bubble override.
toolbox
toolbox
Opens the native toolbox menu. Fire-and-forget (see the warning below).
If
UnboundNative is missing, the environment isn’t a real Unbound install and most native features won’t work. That’s a bug worth reporting, not a state to handle in your addon. The feature API below is for handling older Unbound installs, which is a real and expected case.Feature lifecycle
The native surface changes between loader releases. Rather than making you sniff for the presence of a function,UnboundNative publishes a version and a table of named features, each tagged with the versions it was introduced, deprecated, and removed in. Ask before you call, and your addon keeps working on installs both older and newer than the one you developed against.
The features named in the current table are device.info, device.entitlements, app.source, notifications, pip.video, chat.avatar, chat.messageBubbles, toolbox.menu, and native.evaluateBytecode. All nine were introduced in 1.0.0, and none are currently deprecated or removed.
Asking about one feature
Asking about one feature
boolean
true when the feature is known, has an introduced version this build is at or past, and has not been removed. This is the check to gate a call on.boolean
true when the build is at or past the feature’s deprecated version and the feature has not yet been removed. A deprecated feature still works, so this is a signal to migrate, not to stop calling.boolean
true when the build is at or past the feature’s removed version. A removed feature never reports as supported.object
The full metadata record for a feature.
Asking about all features
Asking about all features
string
The native module’s own semver version. This is the version every feature check is evaluated against.
string[]
Every feature currently supported, sorted alphabetically.
string[]
Every feature currently deprecated, sorted alphabetically.
string[]
Every feature already removed, sorted alphabetically.
The five statuses
getFeatureInfo(...).status resolves to exactly one of these, checked in this order:
unknown and unavailable mean different things but call for the same handling: don’t call the feature. Only supported and deprecated are safe to call. Branch on supportsFeature for the decision, and read status when you want to tell the user why something is off.How versions are compared
The comparison is strict, and knowing the rule saves you from a surprising result:- A version must be exactly three dot-separated components.
1.0and1.0.0.1are both invalid. - Every component must be digits only and non-empty.
1.0.0-beta,v1.0.0, and1..0are all invalid. - Valid versions compare numerically, component by component, left to right. So
1.10.0is newer than1.9.0, not older. - A malformed version sorts below a valid one. If one side is malformed and the other isn’t, the malformed side is treated as older. Two malformed versions compare equal.
introduced value is malformed can never report as supported, because supportsFeature requires a parseable introduced string before it will compare anything. Prerelease tags aren’t a thing here.
Running precompiled bytecode
evaluateBytecode runs Hermes bytecode inside the app’s JS runtime and returns whatever the bytecode evaluates to. It’s how you ship an addon as precompiled bytecode instead of source, skipping parse and compile at load time.
ArrayBuffer
required
The Hermes bytecode to run. Must be an
ArrayBuffer.string
A label for the evaluation, used in stack traces and loader logs. Defaults to
UnboundNative.evaluateBytecode. Pass your addon’s id so failures are traceable to you.