Skip to content

MAUI iOS background service is not starting in physical device #573

@pshuklaD

Description

@pshuklaD

I Developers,

I have implemented a background service in my MAUI iOS app to perform sync operations and handle outbound data. I am registering the service in the AppDelegate.cs file inside the FinishedLaunching() method, and then scheduling it to run 3 minutes after the current time. For testing, I put the app in the background, but I am unable to see the process running.

public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
try
{
DataSyncBackgroundTask.Register();

     _ = NotificationManagerService.InitializeAsync();
     DataSyncBackgroundTask.Schedule();
     var result = base.FinishedLaunching(application, launchOptions);

     // Simple logging
     Console.WriteLine("iOS AppDelegate finished launching");

     return result;
 }
 catch (Exception ex)
 {
     // Fallback logging
     Console.WriteLine($"AppDelegate FinishedLaunching exception: {ex}");
     throw; // Re-throw to maintain crash behavior if critical
 }

}
public override void DidEnterBackground(UIApplication uiApplication)
{
Console.WriteLine("App entered background — scheduling BGProcessingTask");
DataSyncBackgroundTask.Schedule();
}

public static class DataSyncBackgroundTask
{
    public const string TaskId = "com.test.datasync";
    //  STEP 2.1 — Register ONCE (App launch)
    public static void Register()
    {
        BGTaskScheduler.Shared.Register(TaskId, null, task =>
        {
            if (task is BGProcessingTask processingTask)
            {
                Handle(processingTask);
            }
            if(task is BGAppRefreshTask refreshTask)
            {
                task.SetTaskCompleted(false);
            }
        });

    }

    //  STEP 2.2 — Handle execution
    static async void Handle(BGProcessingTask task)
    {
        var cts = new CancellationTokenSource();

        task.ExpirationHandler = () =>
        {
            cts.Cancel();
        };

        try
        {
            NotificationManagerService.Show("Background Sync", "Sync started");

            var services = IPlatformApplication.Current.Services;

            var outbound = services.GetService<IOutboundSynchronizer>();
            var master = services.GetService<IMasterSynchronizer>();
            var batch = services.GetService<IBatchSynchronizer>();

            if (outbound != null && !cts.IsCancellationRequested)
                await RunSyncAsync(outbound, "Outbound", cts.Token);

            if (master != null && !cts.IsCancellationRequested)
                await RunSyncAsync(master, "Master", cts.Token);

            if (batch != null && !cts.IsCancellationRequested)
                await RunSyncAsync(batch, "Batch", cts.Token);

            task.SetTaskCompleted(true);
        }
        catch (OperationCanceledException)
        {
            task.SetTaskCompleted(false);
        }
        catch (Exception)
        {
            task.SetTaskCompleted(false);
        }
    }

    //  STEP 2.3 — Sync runner
    private static async Task RunSyncAsync(ISynchronizer synchronizer,string name,CancellationToken token)
    {
        Func<SyncProgress, Task> handler = progress =>
        {
            NotificationManagerService.Show(
                $"{name} Sync",
                $"{progress.FilesProcessed} files processed");

            return Task.CompletedTask;
        };

        try
        {
            synchronizer.CancellationToken = token;
            synchronizer.ProgressChanged += handler;
            await Task.Delay(TimeSpan.FromSeconds(60), token);
            await synchronizer.InitiateSyncAsync(token);
        }
        finally
        {
            synchronizer.ProgressChanged -= handler;
        }
    }

    // 🔹 STEP 2.4 — Schedule (NO immediate execution)
    public static void Schedule()
    {
        try
        {
            var begin = DateTime.Now.AddMinutes(3).ToNSDate();
            Console.WriteLine($"BGTask begin: {begin}");
            //var begin = ToNSDate(DateTime.Now);
            var request = new BGProcessingTaskRequest(TaskId)
            {
                RequiresNetworkConnectivity = true,
                RequiresExternalPower = false, // set true if heavy sync
                EarliestBeginDate = begin // IMPORTANT
            };

            BGTaskScheduler.Shared.Submit(request, out var error);

            if (error != null)
            {
                Console.WriteLine($"BGTask submit error: {error}");
            }

        }
        catch (Exception ex)
        {
            Console.WriteLine($"BGTask schedule exception: {ex}");
        }
    }

}

info.plist

BGTaskSchedulerPermittedIdentifiers

com.test.datasync

UIBackgroundModes

processing
fetch

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions