# iOS parity audit (2026-08-03) Revenge dropped official iOS support; this repo was archived upstream in Nov 2024 (`revenge-mod/revenge-tweak`, last commit `35ad28b`) while the Android loader (`revenge-mod/revenge-xposed`) kept shipping. This audit compares the two and fixes what could be fixed without a jailbroken device to test against. ## Fixed in this fork - **`Sources/Sideloading.x`**: `BunnyLog(...)` referenced a macro that no longer exists (the project was renamed Bunny → Revenge; only `RevengeLog` is defined in `Headers/Logger.h`). This made the file **fail to compile**. Fixed by using `RevengeLog`. - **`Sources/LoaderConfig.m`**: `-loadConfig` and `+defaultConfig` were each defined twice in the same `@implementation` (leftover merge conflict). Removed the duplicates. - **`.github/workflows/deploy.yml`**: the CI patcher step pointed at `Cristiandis/ipa-patcher` release `release-pyon`, which 404s — that repo no longer exists. Repointed at the still-live fork `bunny-mod/ipa-patcher` (same tag, release build) and pinned it with a verified SHA-256 checksum. (These three match the fixes proposed in the never-merged upstream PR #8, which the sole maintainer confirmed worked but never landed before the repo was archived.) ## The real blocker: this tweak's hook point is very likely dead `Sources/Tweak.x` hooks `RCTCxxBridge -executeApplicationScript:url:async:` — the script-injection point for React Native's **legacy bridge**. Evidence points to Discord having moved off that architecture entirely: - `revenge-bundle`'s `package.json` pins `react-native: 0.81.4`, which defaults to the New Architecture ("Bridgeless mode"). - `revenge-xposed` (the actively-maintained Android loader) hooks `com.facebook.react.runtime.ReactInstance$loadJSBundle$1` — the Bridgeless-mode class — not the legacy `CatalystInstanceImpl`, and its `DiscordDevSupport.kt` explicitly targets `com.facebook.react.devsupport.BridgelessDevSupportManager`. Cross-platform RN apps built from one codebase almost always enable/disable the New Architecture together, so if Android Discord is Bridgeless, iOS Discord almost certainly is too — meaning `RCTCxxBridge` is likely never instantiated on current Discord iOS builds, and the tweak's only injection point silently never fires. **This is now confirmed, not just theorized.** [RainTweak](https://codeberg.org/raincord/RainTweak) is the iOS loader for `raincord/rain`, a sibling Revenge/Bunny-derived mod that is *actively shipping* (last commit 2026-07-17, vs. this repo's Nov 2024). Its `%ctor` explicitly checks `RCTNewArchEnabled` from Info.plist and refuses to run with "Please update Discord to v332 or above" if it's off — i.e. current Discord iOS is Bridgeless-only, exactly as the Android-side evidence predicted, and RainTweak has real users validating this on-device. **Fix applied**, ported from RainTweak's verified mechanism (better than the `RCTInstance -_loadScriptFromSource:` + fake-`RCTSource` approach this fork originally tried — see below for why that was replaced): - `%hook RCTHost` / `-instance:didInitializeRuntime:` (`Headers/RCTHost.h`, `Sources/Tweak.xm`) — fires once the Bridgeless JS runtime is ready. Discord's own bundle is left completely untouched via `%orig` first; Revenge's own scripts (loader payload, downloaded bundle, theme, preloads) are then run directly against the live `facebook::jsi::Runtime` via `[JSI evaluate:tag:runtime:]` (`Headers/JSI.h` / `Sources/JSI.mm`, ported near-verbatim from RainTweak's `JSI.mm`) instead of being smuggled in as fake bundle loads. - This sidesteps the problem the first attempt ran into: `RCTSource` (what `-_loadScriptFromSource:` takes) has no public initializer, so faking one meant poking private ivars — fragile and, per RainTweak's actual production code, not even the path they use. Evaluating directly against the runtime via JSI is both simpler and how the proven-working reference implementation does it. - The legacy `RCTCxxBridge -executeApplicationScript:...` hook is kept as a no-cost fallback for any host still on the old bridge (Logos skips hooking classes that aren't present at runtime, and a `didInject` guard stops both hooks from double-firing if they somehow both resolve). - `Headers/jsi/{jsi.h,jsi-inl.h,instrumentation.h}` are vendored copies of Meta's JSI headers (MIT-licensed, same as RN itself) — Theos doesn't ship them, and RainTweak vendors the identical files for the same reason. **Still unverified against a real device or the current Discord binary** — I don't have a jailbreak or Xcode/Theos here. But unlike the first attempt, this is no longer a guess extrapolated from RN's public source: the mechanism (`RCTHost` hook + JSI evaluation) is the exact one running in a comparable, currently-shipping mod. Confidence is meaningfully higher; it should still be tested before relying on it, since Discord's specific build could in principle differ. ## Remaining feature gap vs. Android (not attempted here) Beyond the injection hook, `revenge-xposed` has grown a much larger feature set that iOS has no equivalent of. Porting these is real native engineering work, not something to guess blind without a device to test each step against: | Feature | Android | iOS | |---|---|---| | Script injection | `RevengeScriptLoader` + Bridgeless hook | Fixed above (unverified) | | Theme/font/sideloading | `themes`/`fonts`/`sysColors`, resource-ID fix | `Theme.m`/`Fonts.x`/`Sideloading.x` (present, untouched) | | **Native↔JS RPC bridge** | `RevengeBridge` — bidirectional calls smuggled through existing exposed RN native modules (`RNSVGRenderableManager.getBBox`, `FileReaderModule.readAsDataURL`) | **none** | | **Plugin system** | External plugin loading + repos (`PluginLoader`, `RepoStore`, `RepoInstall`, `Resolver`, ...) | **none** | | Internal plugins | Recovery mode, no-track, OTA-update prevention, example plugin | **none** | | Dev support | LogBox + shake-to-recovery wired into RN's dev menu | **none** | | Caches/lifecycle management | `Caches.kt`, `LifecycleSupport.kt` | **none** | The bridge + plugin system is the biggest gap and the most architecturally novel part of the Android side — it's what everything else (internal plugins, dev support) is built on. Recommend treating it as its own follow-up project once the injection hook above is confirmed working on-device, rather than attempting it speculatively.