I haven't had time to support this in a while, you should use orionblur's fork instead, it's more up to date. Unfortunately seems like login workarounds are dead for good unless someone can come up with an entirely new method.
![]() |
![]() |
![]() |
- Install Theos.
Sideloaded and TrollStore builds inject and re-sign with patina, which is downloaded automatically if it isn't in
PATH(or setPATINAto a binary or a local checkout). deb (rootless/rootful) builds don't need it.
- Clone the NeoFreeBird-BHTwitter repository:
git clone --recursive https://github.com/NeoFreeBird/tweak
cd tweak- Run the script with your preferred option:
./build.py [OPTIONS]Available options:
--sideloaded: for sideloading.
--trollstore: for TrollStore users.
--rootless: for rootless jailbreaks.
--rootful: for rootful jailbreaks.
(combine them to build several at once, e.g. --sideloaded --trollstore)
--release: build optimised and stripped, as the workflows do; otherwise builds unoptimised with symbols.
--clean: wipe previous build output instead of building incrementally.
--package-only: skip compiling and package the dylibs from a previous build (IPA builds only).
--rebrand [DIR|ZIP]: rebrand the app from a patina config bundle (IPA builds only).
--help: for help
- Fork this repository.
- Open the "Actions" tab and enable workflows.
- Run either workflow:
- Build Tweak builds one format and uploads it as an artifact. Pick a deployment format, and give a decrypted IPA URL for sideloaded and TrollStore builds.
- Release builds every format, branded and unbranded, and collects them into a draft release. It always needs a decrypted IPA URL.
- Take the packages from the run's artifacts, or from the "Releases" tab for a release run.
- Get a decrypted IPA for Twitter/X.
- Rename it to
base.ipaand move it to thepackagesfolder.
./build.py --sideloadedResult: packages/<tweak>-sideloaded-X_<tweak version>_<app version>.ipa, named after the tweak in tweak.json.
Follow the same steps as sideloading, then run:
./build.py --trollstoreResult: packages/<tweak>-trollstore-X_<tweak version>_<app version>.tipa.
Just run:
./build.py --rootlessResult: packages/<tweak>-rootless_<tweak version>.deb.
Just run:
./build.py --rootfulResult: packages/<tweak>-rootful_<tweak version>.deb.
Name and icon branding is applied while building an IPA, and shows up in the output filename:
./build.py --sideloaded --rebrandResult: the same IPA with the bundle's name in place of the app's own, so ...-sideloaded-Twitter_... rather than ...-sideloaded-X_..., carrying the name and icons it sets.
The branding lives in a patina config bundle — a folder (or a zip of it, so it can be hosted) laid out as patina expects:
config.json settings, e.g. the app name
icon.png the app icon
alt-icons/ alternate icons, named after the file
car/*.png images swapped inside the app's asset catalogue
overlay/** files merged into the app, laid out as they are in the app
--rebrand uses patina/ by default; pass a path to use another bundle.
tweak.json is the only build file to edit. The deb control and the Substrate filter plist are generated from it, so neither is in the repo, and the Makefile takes every setting from it through the environment.
Top-level keys become deb control fields. filter.bundles is the app the tweak loads into, and build holds the compile settings:
archs, sdk_version, target_version what to build for
subprojects, sideload_subprojects Theos projects under deps/, the latter
built for sideloading but never packaged
frameworks, private_frameworks, extra_frameworks
obj_files, cflags
Resources go in bundle/, installed as <name>/<name>.bundle under Application Support and reachable from the app as TWEAK_NAME_STRING.
A dependency is a name plus where to get it, so sideloaded and TrollStore builds can carry what a jailbreak would install:
{
"name": "ws.hbang.common",
"url": "https://github.com/hbang/libcephei/releases/download/2.0/ws.hbang.common_2.0_iphoneos-arm64.deb"
}Use path instead of url for a local .deb (relative to the repo). Setting both takes the local one, which is how a package you built yourself is tested in place of the published one. A plain string declares a dependency without bundling it.
Every language lives in bundle/strings.json, one entry per string key with a value per language:
{
"Localizable": {
"MODERN_SETTINGS_LAYOUT_TITLE": {
"en": "General",
"de": "Allgemein",
"fr": "Général"
}
}
}build.py writes each table into <name>.bundle/Localizations.bundle/<language>.lproj/<table>.strings as it stages the bundle, so there are no .lproj folders to edit. Localizable is Cocoa's default table and holds the interface strings, setting the languages the tweak ships: a key missing one falls back to English, and the build says which. Every other table is optional and strictly per-language, never falling back — RenameWords and RenameOverrides are two, driving the "Restore Twitter names" feature.
strings.schema.json completes language codes and flags typos and empty values as you type. It lists the ISO 639-1 designators plus the script and region variants iOS resolves; iOS takes any BCP 47 designator, so add one there if you need it. Neither file is shipped.


