-
Notifications
You must be signed in to change notification settings - Fork 194
Description
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