See and skip the upcoming track right from the Lock Screen, Control Center and Dynamic Island.
NextUp 3 is a modern revival of the classic
[NextUp](https://github.com/Nosskirneh/NextUp) tweak (iOS 11 – 13). It adds
an **Up Next row** to the system now-playing UI showing the next track with its
artwork, so you always know what's coming and can act on it before it plays.
## Features
- **Works everywhere** — Lock Screen, Control Center, and the Dynamic
Island expanded player (iOS 16+).
- **Skip ahead** — remove the upcoming track from the queue with one tap, or tap
its artwork to play it right now.
- **Bring back the previous track** — re-queue what just played as the next track.
- **Swipe gestures** — swipe the row for an interactive carousel: ← skips the
next track, → puts the track you just heard back into the queue as up next,
with neighbour artwork sliding in as you drag.
- **Native look on every version** — the row adopts each iOS version's
now-playing style (padding, corner radii, and button feedback are matched to
Apple's own player), so it feels built-in everywhere.
- **Per-app and per-surface toggles** — a Settings pane lets you enable/disable
each app and each surface (Lock Screen / Control Center / Dynamic Island);
changes apply instantly, no respring. The pane lists only what your device
has: apps that are actually installed, and the Dynamic Island row only where
there is an island (including one enabled by a tweak such as VisibleIsland).
- **Accessible & localized** — VoiceOver, Reduce Motion, Increase Contrast and
Dynamic Type support; 27 languages.
## Supported apps
| App | Notes |
|---|---|
| Apple Music | full support |
| Apple Podcasts | full support |
| YouTube Music | full support, built against 9.28.4 |
| YouTube | full support, built against 21.32.4. Shows the next queue entry (playlist / mix / "Add to queue"); a standalone video has no queue, so the autoplay suggestion is shown instead — that one can only be played, not skipped or re-ordered. Artwork is 16:9 rather than square |
| Spotify | full support, built against 9.1.62 |
| SoundCloud | full support, built against 8.75.0. iOS 16.4+ only (the app's own minimum). Ads and not-yet-loaded placeholders are stepped over, so a free account sees the next real track rather than an ad |
## Compatibility
**iOS 14.2 – 26**, on all jailbreak types (rootful, rootless, roothide).
## Screenshots
Control Center
Lock Screen
Dynamic Island
## Install
- **[Havoc](https://havoc.app/package/nextup3)** (recommended) — search for
*NextUp 3* in your package manager.
- Or grab the `.deb` from [Releases](https://github.com/Yves000/NextUp3/releases)
and install it with Sileo / Filza / `dpkg -i`.
Dependencies (installed automatically): `libSandy`, `PreferenceLoader`, and
ElleKit or Substrate. The package restarts the media apps by itself; just
respring when your package manager asks. Uninstalling cleans up everything the
tweak wrote (settings and play history).
## Known issues
- Spotify, YouTube, YouTube Music and SoundCloud integrations are built against specific app versions
(see table above); an app update can silently break them until the tweak is
updated.
---
# For developers
NextUp 3 is a Theos tweak. One dylib is injected into eight processes and behaves
differently per process:
- **Providers** (one per media app, inside `com.apple.Music`,
`com.apple.podcasts`, `com.google.ios.youtubemusic`, `com.google.ios.youtube`,
`com.spotify.client`, `com.soundcloud.TouchApp`):
read that app's live queue, serve the current "next up" (title / artist /
artwork) over IPC, and perform skip / play-now / previous using the app's own
in-process queue API.
- **Display side** (`com.apple.MediaRemoteUI` and `com.apple.springboard`):
hooks the now-playing player per surface and iOS version, draws the row
(`NUNextUpRowView`), and talks to whichever provider owns the current
now-playing session (`NUNextUpManager`).
**IPC:** app sandboxes can't register bootstrap services, so each provider runs a
[LightMessaging](https://github.com/rpetrich/LightMessaging) (mach) server under
a [libSandy](https://github.com/opa334/libSandy) profile that grants the
mach-register/lookup extensions (`layout/Library/libSandy/com.yves.nextup3.plist`).
Darwin notifications signal "queue changed" (provider → display) and
skip / previous / jump (display → provider). Payload-free, sandbox-crossing,
instant.
## Repo structure
| Path | What it is |
|---|---|
| `hooks/*.x` | Logos hooks, split per process / surface / iOS version — each with its own `%ctor` gate. `NUHooksLockScreen{14,15,18}.x`, `NUHooksControlCenter{Legacy,18,26}.x`, `NUHooksDynamicIsland{16,17}.x`, `NUHooksNowPlaying.x` (shared player plumbing), `NUHooksSpringBoard.x` (swipe-vs-system-gesture arbitration), `NUHooksTCC.x` (iOS ≤ 16 usage-description shim), plus one `NUHooksProvider.x` per app |
| `NUProvider.{h,m}` | The six providers (`Music`, `Podcast`, `YouTubeMusic`, `YouTube`, `Spotify`, `SoundCloud`), all on `NUProviderBase` |
| `NUYouTubeShared.h` | The `YT*`/`YTI*` queue-item and renderer interfaces plus the metadata/artwork-URL helpers shared by the two Google clients |
| `NUNextUpManager.{h,m}` | Display side: source tracking, per-source LightMessaging client, snapshot state |
| `NUNextUpRowView.{h,m}` | The row UI: artwork, labels, skip button, swipe carousel |
| `NUHooksShared.{h,m}` | Cross-hook helpers (process gates, view lookup, CC row layout) |
| `NUShared.h` | IPC service names, Darwin notification names, `NUApplySandbox()` |
| `NUPrefs.{h,m}` | Prefs: live state on a notify-state token, CFPreferences as persisted fallback |
| `NUPrivate.h` | Private-API interface declarations (class-dump + Frida-verified) |
| `NULocalization.h` / `NULogFile.m` | String lookup; DEBUG-only file log sink |
| `prefs/` | PreferenceLoader settings pane + 27 localizations |
| `layout/` | libSandy sandbox profile |
| `vendor/` | Vendored LightMessaging / libSandy headers (see `vendor/README.md`) |
## Build & deploy
Requires [Theos](https://theos.dev) (roothide fork for the roothide variant)
and Xcode. The tweak builds against whatever recent iOS SDK your Xcode ships;
no patched SDK is needed because every private interface is declared in the
source itself (`NUPrivate.h` and the provider files). Only the Settings pane
needs one SDK from [theos/sdks](https://github.com/theos/sdks) in
`$THEOS/sdks` (any iOS 1x version works), since Xcode SDKs ship no linker stub
for the private Preferences framework. Set `NUPREFS_PRIVATE_SDK=` to pick a
specific one.
```sh
make package FINALPACKAGE=1 # roothide (default)
make package ROOTLESS=1 FINALPACKAGE=1 # rootless (palera1n / Dopamine)
make package ROOTFUL=1 FINALPACKAGE=1 # rootful — the iOS 14.2 build
make package DEBUG=1 # dev build with NULog logging
# install the .deb from packages/ — its postinst restarts the media apps and
# MediaRemoteUI. SpringBoard hosts hooks too, so finish with a respring:
killall SpringBoard
```
## Adding support for another app
The design is table-driven, so a new source stays localized:
1. **`NUShared.h`** — add `kNUServiceName` (+ `kNUSkipNotification` /
`kNUPrevNotification` as needed), following the
`com.yves.nextup3..` naming convention.
2. **`NUNextUpManager.m`** — add a `NUSource` enum case and a case in each
per-source table function: `NUSourceForBundleID`, `NUConnectionForSource`
(+ a `gConn`), `NUAppPrefKeyForSource`, `NUSkipNotificationForSource`,
`NUPrevNotificationForSource`. The callers need no edits.
3. **`NUPrefs.h` + `NUPrefs.m`** — add `kNUStateApp` (next free bit), its
`NUStateBitForKey` case, and its `NUPrefsPublishState()` line. *Easy to miss
and a silent failure*: without these the Settings switch looks right but
never takes effect live.
4. **Settings pane** — one `PSSwitchCell` in the "Apps" group of
`prefs/Resources/Root.plist` (`key` = the same `enabled` string from
`NUAppPrefKeyForSource`), plus the key's entries in
`prefs/NUPrefsRootListController.m` (`NUBundleIDForKey` and
`NUAppToggleKeys()`) — those drive the row icon and the "hide the toggle when
the app isn't installed" filtering.
5. **Provider** — new `NUProvider.{h,m}` on `NUProviderBase` plus
`hooks/NUHooksProvider.x` (its `%ctor` calls `NUApplySandbox()` before
gating on `NUIs()`), and add `NUIs()` to `NUHooksShared.h`.
**Don't forget the `NUIsDisplaySide()` exclusion**, or every display hook
initializes inside the new app too.
6. **Plumbing** — `NextUp3.plist` (bundle id), `Makefile` (source files +
`INSTALL_TARGET_PROCESSES`, which takes *executable* names, not bundle ids),
the libSandy profile (register/lookup extensions for the new service name and
the app's signing identifier in `AllowedProcesses`), the `killall` lists in
`layout/DEBIAN/postinst` and `postrm` (executable names again), and
`control`'s description.
## Credits
- The original **[NextUp / NextUp 2](https://github.com/Nosskirneh/NextUp)**
(iOS 11 – 13) by Andreas Henriksson, which this tweak revives.
- [LightMessaging](https://github.com/rpetrich/LightMessaging) by Ryan Petrich.
- [libSandy](https://github.com/opa334/libSandy) by opa334.
## License
[GPL-3.0](LICENSE). Vendored third-party headers remain under their original
terms (see `vendor/README.md`).
---
*AI agents / LLM tooling: see [AGENTS.md](AGENTS.md) (identical to
[CLAUDE.md](CLAUDE.md)) for an LLM-oriented map of this repo.*