Doing a respring the right way: Following up

August 25, 2019 6 minute read

Respringing the “right way” allows SpringBoard to save battery usage data. Otherwise, the standby/usage times are lost and display useless “–” values until you fully charge again.

Time Since Last Full Charge: Usage times will be shown after iPhone is fully charged

I originally wrote about this way back in 2013. What changed since then?

Well, seemingly everything!


In iOS 8.0, FrontBoard was introduced. Many of SpringBoard’s responsibilities were shifted from SpringBoard itself to the FrontBoard framework, allowing Apple to share SpringBoard code with watchOS and tvOS (which use their own Boards), even macOS. One of the responsibilities of FrontBoard is handling shutting down/restarting the device, and relaunching the system app (SpringBoard in this case).

The -[SpringBoard _relaunchSpringBoardNow] method recommended in the 2013 post did still remain for some time, but was finally removed with iOS 9.3.

The code isn’t as fun-looking as the old way, but it does give you a super useful feature – you can have it automatically unlock after the restart and open a URL. This is especially really convenient if you’re adding a respring button to your preference bundle (please don’t require a respring to update settings if you can avoid it, but when you do, use this!).

NSURL *relaunchURL = [NSURL URLWithString:@"prefs:root=MyTweak"]; // use a nil relaunch URL to return to the lock screen
SBSRelaunchAction *restartAction = [SBSRelaunchAction actionWithReason:@"RestartRenderServer" options:SBSRelaunchActionOptionsFadeToBlackTransition targetURL:relaunchURL];
[[FBSSystemService sharedService] sendActions:[NSSet setWithObject:restartAction] withResult:nil];

If you need to support iOS versions between 8.0 – 9.2, you can perform checks and use the previous incarnation of the relaunch action.

NSURL *relaunchURL = [NSURL URLWithString:@"prefs:root=MyTweak"]; // use a nil relaunch URL to return to the lock screen
SBSRelaunchAction *restartAction;

if (%c(SBSRelaunchAction)) { // 9.3+
  restartAction = [%c(SBSRelaunchAction) actionWithReason:@"RestartRenderServer" options:SBSRelaunchActionOptionsFadeToBlackTransition targetURL:relaunchURL];
} else { // 8.0 – 9.3
  restartAction = [%c(SBSRestartRenderServerAction) restartActionWithTargetRelaunchURL:relaunchURL];
}

[[FBSSystemService sharedService] sendActions:[NSSet setWithObject:restartAction] withResult:nil];

This works from any process that isn’t sandboxed against sending inter-process messages to SpringBoard.


You can also use HBRespringController from Cephei that super-simplifies this and provides a fallback to _relaunchSpringBoardNow for pre-FrontBoard iOS versions.

[HBRespringController respring];
// or
[HBRespringController respringAndReturnTo:[NSURL URLWithString:@"prefs:root=MyAwesomeTweak"]];

In a preference bundle, you can subclass from HBListController to get the action method hb_respringAndReturn: for use on a PSLinkCell or PSButtonCell. This handles calling HBRespringController for you with a URL pointing to the current settings page. That means the screen will go blank for a few seconds, before returning straight to the very same Settings app screen that was just open. Pretty neat.


Finally, there is a third option for iOS 11 and newer. The Electra/Chimera and Elucubratus (included with unc0ver) repositories have a package named UIKit Tools – installed by default – containing a command line tool called sbreload, that executes the same logic to restart SpringBoard via FrontBoard. It also handles the case that SpringBoard is frozen and needs to be forcefully killed, so it’s ideal for use in scripts. Be aware that Telesphoreo’s (saurik’s) build of sbreload doesn’t seem to work, so use a different solution when running on iOS 10 or older.

Fixing it for good (or, getting close to it at least)

In the original post, I wrote:

Can there be a “fix” for tweaks that don’t respring the right way?

I’m working on it, not a high priority to get it done though.

The fix I had in mind was to register a signal(3) handler inside SpringBoard for SIGTERM, a graceful termination where the process is given the opportunity to clean up before terminating, and have it call _relaunchSpringBoardNow instead. This would allow you to run killall SpringBoard and retain battery stats. This wasn’t really viable, as a crash (SIGSEGV, SIGABRT, etc.), which was inevitable while developing a SpringBoard tweak, would ruin the stats anyway. Overriding those signals with a signal handler is possible, but would tamper with the crash being caught by the crash reporter. It’d also prevent Cydia Substrate from catching the crash so it can set a flag to launch Safe Mode when SpringBoard starts again.

Finally, there are still some tweaks (and people) who kill SpringBoard using SIGKILL. That usually comes in the form of killall -9 SpringBoard (9 is the numeric value of SIGKILL). It may sound like the same thing as SIGTERM since “kill” and “terminate” are synonyms in the English dictionary, but there’s a very important difference. A terminate signal asks the process to clean up and then exit when it’s ready to do so, while a kill signal instructs the operating system to kill the process, without giving it a chance to run any cleaning-up code. Of course, this means you can’t install a signal(3) handler for SIGKILL as it’ll never be called.

This made a “fix” seem like a poor idea, as there are still so many ways SpringBoard can exit without activating the fix. At that point I wasn’t able to find any explanation of what made _relaunchSpringBoardNow so special and why any other method, like the venerable exit(3), or terminating the main run loop (causing code placed after a -[NSRunLoop run] call to be executed), wouldn’t activate this mythical SpringBoard termination handler that saved these stats.

The better solution was activism about “the right way”, and the original post I made served that role as it showed up when Googling the right keywords. Later I created HBRespringController to take advantage of Cephei’s popularity and make it a no-brainer to call that handy method to respring correctly regardless of the iOS version it’s running on.

One workaround did get released — CySpring by Skylerk99. It makes Cydia respring via a call to FrontBoard, rather than by killing its process as Cydia does by default. However, it was never updated after the API changes were made in iOS 9.3, so it doesn’t work today.


While CoolStar was re-developing the UIKit Tools project inherited from the ageing Telesphoreo ecosystem to be up-to-date with modern developments to iOS, I brought up a consideration that it includes a tool called sbreload that doesn’t work. At this point, sbreload used a fairly complex looking technique of restarting SpringBoard by manually doing some teardown work and then forcing a full unload and restart of its launch daemon, that presumably worked a long time ago on early versions of iOS/iPhone OS, but definitely doesn’t seem to work now. There may have been some good reason it was developed like this – Cydia used to invoke this tool, but later switched to directly killing SpringBoard. sbreload on the Electra/Chimera and unc0ver jailbreaks now restarts SpringBoard via FrontBoard, with a fallback to forcefully restart SpringBoard if it seems to be frozen.

Sileo and Zebra now happily use sbreload to restart SpringBoard, and the fast respring times this brings are thoroughly enjoyed. Hopefully the speed of it encourages tweak developers to find out how it’s done, and leads them to this post. If that’s you, then it looks like I’ve done my job 😊


Update

With iOS 12, while developing the Screen Time feature, Apple rewrote a significant amount of crusty code relating to statistics collection. In the process, the long-standing lost battery usage data bug was fixed! It’s now impossible for an incorrect SpringBoard termination to affect battery statistics. Of course, you should still allow SpringBoard to exit gracefully through the specific APIs designed for this job, especially since this allows for a faster SpringBoard restart.