Skip to content

ReyML/decent_twitter

Repository files navigation

Decent Twitter

Unofficial Capacitor-based iOS wrapper for X/Twitter with optional self-hosted push notifications.

This started as a personal iPhone wrapper for x.com/home, then grew into a more capable experimental client shell with native notifications, a large tweak surface, and an optional self-hosted watcher backend.

It is not affiliated with, endorsed by, or maintained by X Corp. Treat it as an open-source experiment for sideloading and personal builds, not as an official or App Store-ready client.

Features

  • Wraps a remote X/Twitter URL in a native iPhone WKWebView
  • Adds a large tweak surface through the injected control panel
  • Persists app-owned settings on-device
  • Can register for APNs and open X links from native notifications
  • Includes an optional self-hosted watcher server for account notifications
  • Can watch public profiles without the official X API

Current status

  • Best suited for sideloading, local builds, and experimentation
  • Not an App Store-ready client in its current form
  • Native notifications require your own APNs setup and push server
  • The optional watcher can work without the official X API, but that approach is inherently brittle because it depends on X's current frontend behavior
  • If you fork it, use your own bundle ID, APNs credentials, tokens, and server configuration

Tweaks

The in-app Tweaks panel is one of the main reasons this project exists. It is not just a thin web wrapper.

Examples of what can be changed:

  • Home timeline behavior
    • default to Following
    • hide For you
    • sort the Following feed
    • add topic filters above For you
    • hide countries or regions from For you
  • Appearance
    • choose the dark mode theme, including Dim and Lights Out
    • replace X branding with Twitter-style branding
    • adjust layout density and full-width behavior
    • tweak follow/following button styling
  • Feed cleanup
    • hide suggested content, inline prompts, Grok, Jobs, Subscriptions, and upsells
    • hide blue-check reply noise and premium-heavy surfaces
    • hide view counts and other metrics
    • hide compose UI or disable the home timeline entirely
  • Notifications
    • connect the app to a self-hosted watcher backend
    • add account watches for Posts only or Posts + replies
    • receive native APNs that open the target X link in the app
  • Profile and conversation cleanup
    • hide retweets on profiles
    • hide likes/retweets in Notifications
    • show country flags and profile location cues
    • reduce clutter in quote-tweet and reply flows

Some tweaks depend on X's current markup and behavior. When X changes its frontend, parts of the tweak layer may need maintenance.

Project layout

  • capacitor.config.ts: Capacitor app config and remote URL handling
  • ios/App/App: native iOS shell, push handling, and injected tweaks bridge
  • server: optional notification backend and X account watcher
  • www: local fallback/offline pages

Requirements

  • macOS with Xcode
  • Node.js 20+
  • An Apple Developer account if you want real device installs and APNs
  • An always-on machine if you want self-hosted notifications
    • VPS, Mac mini, Raspberry Pi, or similar

Repo-safe local config

This repo is set up so secrets and signing stay out of git.

  • App runtime secrets live in .env
  • Notification server secrets live in server/.env
  • Local iOS signing values live in ios/signing.local.xcconfig

None of those files should be committed.

Quick start

  1. Copy .env.example to .env
  2. Copy ios/signing.local.example.xcconfig to ios/signing.local.xcconfig
  3. Set your local app values:
CAP_REMOTE_URL=https://x.com/home
CAP_APP_ID=com.example.decenttwitter
CAP_APP_NAME=Decent Twitter
  1. Set your local signing values:
APP_DISPLAY_NAME = Decent Twitter
APP_BUNDLE_ID = com.example.decenttwitter
APP_DEVELOPMENT_TEAM = YOUR_TEAM_ID
  1. Install dependencies and sync iOS:
npm install
npm run ios:sync
npm run ios:open
  1. In Xcode, select your Apple Development team if needed and run the app on a device or simulator.

iPhone install from Terminal

After the one-time Xcode signing setup is done, you can use the helper script:

npm run ios:devices
npm run ios:device -- YOUR_DEVICE_ID

If you prefer raw xcodebuild + devicectl, the helper script in scripts/run-ios-device.sh shows the exact flow.

Notifications overview

This app can receive native APNs notifications, but it does not inherit X's own web notifications automatically.

There are two layers:

  1. Native iOS notification plumbing in the app
  2. An optional self-hosted watcher that decides when to send pushes

What is supported

  • Native test pushes
  • Per-account watches
  • Posts only or Posts + replies
  • Self-hosted profile polling without the official X API

What is not supported

  • Official X push delivery inside WKWebView
  • DMs
  • Full home-timeline notifications
  • Reliable background watching without a backend

Notification flow

  1. The app asks for notification permission on launch.
  2. The app registers an APNs device token.
  3. The app registers that installation with your push server.
  4. The push server watches selected X accounts.
  5. When a matching post is detected, the server sends APNs.
  6. Tapping the push opens the target X URL in the wrapper.

Notification server setup

  1. Copy server/.env.example to server/.env
  2. Fill these required values:
PUSH_ADMIN_TOKEN=replace-with-a-long-random-admin-token
PUSH_DEVICE_REGISTRATION_TOKEN=replace-with-a-long-random-device-token
APPLE_TEAM_ID=YOUR_TEAM_ID
APPLE_KEY_ID=YOUR_KEY_ID
APPLE_BUNDLE_ID=com.example.decenttwitter
APPLE_PRIVATE_KEY_PATH=/absolute/path/to/AuthKey_ABC123XYZ.p8
APNS_DRY_RUN=false
  1. Install and start the server:
cd server
npm install
npm run install:browser
npm run dev
  1. Point the app at the server in your root .env:
CAP_PUSH_SERVER_URL=https://your-server.example.com/
CAP_PUSH_SERVER_REGISTRATION_TOKEN=the-same-device-registration-token-from-server-env
CAP_PUSH_DEVICE_NAME=My iPhone
  1. Sync and rebuild the iOS app:
cd ..
npm run ios:sync

X session cookies for harder-to-read accounts

Some accounts do not expose posts clearly to logged-out visitors. In that case, the server can use a logged-in X session:

X_AUTH_TOKEN=
X_CT0=

Those values belong only in server/.env.

Adding account watches

Once the app is connected to a working push server:

  1. Launch the app and allow notifications
  2. Open a profile inside the app
  3. Open the profile overflow menu
  4. Pick the custom watch option
  5. Choose Posts only, Posts + replies, or Off

The repository also includes a small admin dashboard for debugging devices, watches, and test pushes.

Publishing notes

  • This project is best treated as an experimental client, not an App Store-ready product
  • A public distribution strategy would require a more compliant architecture
  • If you fork it, use your own bundle ID, APNs keys, and server tokens

Support

If this project is useful, consider sponsoring development.

Sponsor

About

Unofficial Capacitor-based iOS wrapper for X/Twitter with optional self-hosted push notifications.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors