1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

iOS: Fix building with JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK option

This also fixes an issue where endBackgroundTask wasn't guaranteed to be
called after suspended() completed. According to the docs,
endBackgroundTask must be called after the task completes.
This commit is contained in:
reuk 2025-09-25 19:52:03 +01:00 committed by Tom Poole
parent f863f16f5b
commit ce737946f0

View file

@ -63,6 +63,46 @@ namespace juce
}
};
/* Each successful call to beginBackgroundTask must be balanced
by a call to endBackgroundTask.
*/
class TaskHandle
{
public:
TaskHandle() = default;
explicit TaskHandle (UIBackgroundTaskIdentifier t)
: task (t) {}
~TaskHandle()
{
if (task != UIBackgroundTaskInvalid)
[[UIApplication sharedApplication] endBackgroundTask:task];
}
TaskHandle (TaskHandle&& other) noexcept
{
swap (other);
}
TaskHandle& operator= (TaskHandle&& other) noexcept
{
TaskHandle { std::move (other) }.swap (*this);
return *this;
}
TaskHandle (const TaskHandle&) = delete;
TaskHandle& operator= (const TaskHandle&) = delete;
private:
void swap (TaskHandle& other) noexcept
{
std::swap (other.task, task);
}
UIBackgroundTaskIdentifier task = UIBackgroundTaskInvalid;
};
struct SceneUtils
{
// This will need to become more sophisticated to enable support for multiple scenes
@ -80,20 +120,20 @@ namespace juce
appBecomingInactiveCallbacks.getReference (i)->appBecomingInactive();
}
static void sceneDidEnterBackground()
template <typename Self>
static void sceneDidEnterBackground ([[maybe_unused]] Self* s)
{
if (auto* app = JUCEApplicationBase::getInstance())
{
#if JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK
appSuspendTask = [application beginBackgroundTaskWithName:@"JUCE Suspend Task" expirationHandler:^{
if (appSuspendTask != UIBackgroundTaskInvalid)
{
[application endBackgroundTask:appSuspendTask];
appSuspendTask = UIBackgroundTaskInvalid;
}
}];
s->appSuspendTask = TaskHandle { [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"JUCE Suspend Task"
expirationHandler:^{ s->appSuspendTask = {}; }] };
MessageManager::callAsync ([app] { app->suspended(); });
MessageManager::callAsync ([app, s]
{
app->suspended();
s->appSuspendTask = {};
});
#else
app->suspended();
#endif
@ -112,10 +152,17 @@ namespace juce
API_AVAILABLE (ios (13.0))
@interface JuceAppSceneDelegate : NSObject<UIWindowSceneDelegate>
{
@public
TaskHandle appSuspendTask;
}
@end
@implementation JuceAppSceneDelegate
SharedResourcePointer<WindowSceneTracker> windowSceneTracker;
{
SharedResourcePointer<WindowSceneTracker> windowSceneTracker;
}
- (void) scene: (UIScene*) scene
willConnectToSession: (UISceneSession*) session
options: (UISceneConnectionOptions*) connectionOptions
@ -144,7 +191,7 @@ SharedResourcePointer<WindowSceneTracker> windowSceneTracker;
- (void) sceneDidEnterBackground: (UIScene*) scene
{
SceneUtils::sceneDidEnterBackground();
SceneUtils::sceneDidEnterBackground (self);
}
- (void) sceneWillEnterForeground: (UIScene*) scene
@ -167,7 +214,8 @@ SharedResourcePointer<WindowSceneTracker> windowSceneTracker;
@interface JuceAppStartupDelegate : NSObject <UIApplicationDelegate>
#endif
{
UIBackgroundTaskIdentifier appSuspendTask;
@public
TaskHandle appSuspendTask;
std::optional<ScopedJuceInitialiser_GUI> initialiser;
}
@ -216,13 +264,13 @@ SharedResourcePointer<WindowSceneTracker> windowSceneTracker;
@end
@implementation JuceAppStartupDelegate
{
NSObject* _pushNotificationsDelegate;
}
- (id) init
{
self = [super init];
appSuspendTask = UIBackgroundTaskInvalid;
#if JUCE_PUSH_NOTIFICATIONS
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
@ -260,7 +308,7 @@ SharedResourcePointer<WindowSceneTracker> windowSceneTracker;
- (void) applicationDidEnterBackground: (UIApplication*) application
{
SceneUtils::sceneDidEnterBackground();
SceneUtils::sceneDidEnterBackground (self);
}
- (void) applicationWillEnterForeground: (UIApplication*) application