# SimulatorStatusMagic: fixing the iOS Simulator status bar for screenshots

> A small Xcode project that replaces the simulator status bar with the 9:41 AM, full battery version, and keeps working as Apple moves the status bar server into Springboard.

**shinydevelopment/SimulatorStatusMagic** — Clean up your status bar for taking screenshots on the iOS simulator.

- Repository: https://github.com/shinydevelopment/SimulatorStatusMagic
- Stars: 2,349 · Forks: 132
- Language: Objective-C
- License: MIT
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/shinydevelopment-simulatorstatusmagic

## What it changes, exactly

The scope is deliberately narrow and stated that way in the README. The status bar shows 9:41 AM for the time, a full battery at 100 percent, five bars of cellular signal, and full WiFi bars. On iPad, the date reads Tue Jan 9.

Those are Apple's marketing values rather than arbitrary ones, and the README says the modifications are designed to match the images on Apple's own site. The project also states outright that it is not intended for customisation, that the team is not planning to add options for deep status bar tweaking, and that the goal is to do one job well.

The one thing it explicitly does not do is work on a physical device. The status bar server is blocked there. The README notes as a consolation prize that macOS can include a perfect status bar when recording a device screen with QuickTime, which is the manual workaround for people who want this on hardware.

Restoring the default is easy: run the demo app again and choose Restore Default Status Bar, or reset the iOS Simulator through the normal menu option.

## Why this still exists when simctl has a status_bar command

This is the question the README puts first, and the answer is specific enough to be checkable.

Xcode 11 added `simctl status_bar`, which lets you override the status bar appearance from the command line. The README says it will probably supersede this project eventually, but as written it still has a hole: there is no way to add localized date and time strings in the status bar. Since the date and time are exactly what store screenshots are judged on, that gap is the whole reason the project persists.

So the two tools are not really competing. `simctl status_bar` is a general override for clock, battery, and signal values. SimulatorStatusMagic is the thing that also gets a date string onto the screen, and on newer systems it reaches further into private API to do it.

A practical detail that matters if you script this: the demo app responds to an environment variable, so you can enable or disable overrides at launch instead of clicking the button.

```
SIMULATOR_STATUS_MAGIC_OVERRIDES = enable
```

Setting it to `disable` does the opposite, which is what you want for a screenshot run that should show a real status bar.

## The iOS 17 shift changed how you install it

This is the part of the project that changed most recently, and it changes the setup for everyone.

As of iOS 17, the API SimulatorStatusMagic uses is not accessible to processes other than Springboard. So on iOS 17 and later, the library has to be injected into the Springboard process itself: it is built as a dynamic library, and Springboard's launchd configuration is updated to load that library. Running the build and inject script does all of it:

```bash
build_and_inject.sh booted
```

Replacing `booted` with a simulator UDID targets a specific simulator instead of whichever one is running. On iOS 27 and later the README warns that it takes one to two minutes for the status bar to appear after injection completes, which is worth knowing so you do not conclude the injection failed.

Because the values are now set inside the injected library, changing them is a source edit rather than an API call, and the README points you at `DynamicLibrary/main.m` for that.

For iOS 16 and lower, the older approaches still apply. Adding the Swift package through the repository URL and selecting your UI test target is one route, with the overrides toggled from the test:

```swift
import SimulatorStatusMagic

final class YourUITests: XCTestCase {

    override func setUpWithError() throws {
      SDStatusBarManager.sharedInstance().enableOverrides()
    }
```

The CocoaPods and Carthage path exists too, where the calls are the Objective-C equivalents, `enableOverrides` and `disableOverrides` on the shared instance. The README recommends including `SDStatusBarManager` only in your debug configuration, so the code never reaches a release build.

## The maintenance recipe for each new iOS release

The most valuable content in the README is the update procedure, because it tells you this is fixable by hand rather than something you have to wait for.

The pattern is: copy the previous release's `SDStatusBarOverriderPostXX_Y` files and update them, then point `SDStatusBarManager.m` at the new overrider when it detects the new operating system version. Next you need the updated struct definitions, which come from generating private runtime headers. You download `dsdump` and run it against the UIKitCore framework binary inside the simulator runtime:

```bash
./dsdump --objc -a x86_64 --verbose=5 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore --defined > ~/Desktop/UIKitCore.txt
```

Then you search that output for `UIStatusBarServerListener`. Two structs have to line up with it, `StatusBarRawData` and `StatusBarOverrideData`, each corresponding to a line in the runtime header for that listener. Finally you update both structs to match the new headers, run the sample app, and verify the status bar looks right, adding further adjustments to the overrider if a new field has appeared.

The README is upfront that this pattern could change if Apple changes something in the future, which is the honest framing for anything built on private API.

## What the repository looks like and what it does not ship

The tree is small and reads clearly. `SDStatusBarManager/` holds the manager itself, with the per-version overrider files following the naming pattern above. `DynamicLibrary/` contains the injection library and its `main.m`, which is where the status bar values live for the iOS 17 path. `SimulatorStatusMagic/` is the demo app for the old route, `SimulatorStatusMagiciOS/` is the second target, and `SimulatorStatusMagic.xcodeproj/` holds the Xcode project. There is `Package.swift` for Swift Package Manager, `SimulatorStatusMagic.podspec` for CocoaPods, an `INSTALLATION.md`, and `build_and_inject.sh`.

Two details deserve a second look. GitHub reports the repository language as Objective-C, which fits the manager and overrider files, yet the project also ships a `Package.swift` and the README's UI test example is written in Swift. That is a Swift wrapper over Objective-C internals rather than a contradiction, but it does mean you should expect Objective-C when you go reading the source. Separately, the demo app instructions say to open the project with Xcode 6 or above. That advice is accurate as a minimum and noticeably dated given the iOS 27 mention elsewhere in the same page, so treat it as a floor rather than a recommendation.

There are no releases at all. No tagged version, no release notes, nothing to pin in a dependency manager. For a tool you add to a UI test target that is a reasonable trade, and it is also consistent with how the project is built, where you normally add it by repository URL and update on the default branch.

Scale is telling: 2,349 stars, 132 forks, and only 2 open issues. The last push was on 2026-08-28 and the repository is not archived, so it is being worked on despite the absence of releases.

## Conclusion

The reason this project still matters is narrow and specific: `simctl status_bar` exists and is the right tool, but it cannot put a localized date and time in the status bar, which is the one detail App Store screenshots keep getting wrong. SimulatorStatusMagic covers that gap, and its durability comes from the maintenance recipe in the README, which tells you exactly how to regenerate the private structs when a new iOS version lands. Two things to plan for: on iOS 17 and later the injection step is required and changes the values by editing a source file rather than calling an API, and the project has never published a tagged release, so there is no version to pin. Clone it, run the injection script against a booted simulator, and check your own screenshots against the app's guidelines before automating the rest of your capture pipeline.

## FAQ

### Why not just use xcrun simctl status_bar for App Store screenshots?

You can, and the README recommends it as the eventual replacement for this project. The gap is that `simctl status_bar` has no way to add a localized date and time string to the status bar, which is exactly the detail store screenshots are checked on. SimulatorStatusMagic covers that case and, on iOS 17 and later, reaches into Springboard to do it.

### How do I install SimulatorStatusMagic on iOS 17 or later?

Injection is required, because the API is no longer reachable from processes other than Springboard. Run `build_and_inject.sh booted` from the repository, substituting a simulator UDID for `booted` if you want to target one device. The status bar values then come from `DynamicLibrary/main.m` rather than from a runtime call.

### Does SimulatorStatusMagic work on a real iPhone?

No. The status bar server is blocked on devices, so this only affects the simulator. The README points at QuickTime on macOS as a way to get an equivalent perfect status bar when recording a physical device screen.

### How do I make SimulatorStatusMagic work after a new iOS version is released?

Follow the update procedure in the README: copy the previous `SDStatusBarOverriderPostXX_Y` files, regenerate the private runtime headers with `dsdump` against UIKitCore, find `UIStatusBarServerListener` in that output, and update the `StatusBarRawData` and `StatusBarOverrideData` structs to match. Then point the manager at the new overrider.

## Sources

- [Issues](https://github.com/shinydevelopment/SimulatorStatusMagic/issues)
- [License: MIT](https://github.com/shinydevelopment/SimulatorStatusMagic/blob/master/LICENSE)
- [README](https://github.com/shinydevelopment/SimulatorStatusMagic/blob/master/README.md)
- [shinydevelopment/SimulatorStatusMagic on GitHub](https://github.com/shinydevelopment/SimulatorStatusMagic)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/shinydevelopment-simulatorstatusmagic
