# Usage

Essential Kit's Task Services wraps platform-specific background task APIs into a unified async/await interface. When users background your app, Task Services requests additional execution time from the OS to complete critical operations like saving game state or uploading data.

## Table of Contents

* [Import Namespaces](#import-namespaces)
* [Understanding Background Task Protection](#understanding-background-task-protection)
* [Core API: AllowRunningApplicationInBackgroundUntilTaskCompletion](#core-api-allowrunningapplicationinbackgrounduntiltaskcompletion)
* [Extension Methods for Cleaner Syntax](#extension-methods-for-cleaner-syntax)
* [Pattern 1: Save Game Data on Pause](#pattern-1-save-game-data-on-pause)
* [Pattern 2: Extension Method Syntax](#pattern-2-extension-method-syntax)
* [Pattern 3: Task with Return Value](#pattern-3-task-with-return-value)
* [Pattern 4: Multiple Protected Tasks](#pattern-4-multiple-protected-tasks)
* [Handling Quota Expiration](#handling-quota-expiration)
* [Data Properties](#data-properties)
* [Core APIs Reference](#core-apis-reference)
* [Error Handling](#error-handling)
* [Advanced: Manual Initialization](#advanced-manual-initialization)

## Import Namespaces

```csharp
using VoxelBusters.EssentialKit;
using VoxelBusters.CoreLibrary;
```

## Understanding Background Task Protection

When a user presses the home button or switches apps, mobile operating systems transition your app through lifecycle states:

**Normal Flow (without Task Services)**:

```
User presses Home → App enters Background → OS suspends app after ~1 second → Task interrupted ❌
```

**Protected Flow (with Task Services)**:

```
User presses Home → Task Services requests background time → App continues for 30s (iOS) / minutes (Android) → Task completes ✅
```

{% hint style="warning" %}
**System Limits**: Task Services **extends** background time but cannot eliminate system limits. iOS allows \~30 seconds, Android allows a few minutes. Design tasks to complete within these limits or implement checkpoint/resume patterns.
{% endhint %}

## Core API: AllowRunningApplicationInBackgroundUntilTaskCompletion

The primary method protects any async Task from backgrounding interruption:

```csharp
using System.Threading.Tasks;
using UnityEngine;

// API Signature:
public static Task AllowRunningApplicationInBackgroundUntilTaskCompletion(
    Task task,
    Callback onBackgroundProcessingQuotaWillExpireCallback = null
);
```

| Parameter                                       | Type       | Purpose                                                   |
| ----------------------------------------------- | ---------- | --------------------------------------------------------- |
| `task`                                          | `Task`     | The async operation to protect from backgrounding         |
| `onBackgroundProcessingQuotaWillExpireCallback` | `Callback` | Optional callback when background time is about to expire |

**Returns**: Continuation task that completes when the original task finishes and handles cleanup.

### Generic Overload for Task\<TResult>

For tasks that return values:

```csharp
using System.Threading.Tasks;
using UnityEngine;

// Example: Using the generic overload with a task that returns a value
    // Assume you have a PlayerData class and SavePlayerDataAsync() method defined in your game code
    async Task<PlayerData> SavePlayerDataWithBackgroundSupport()
    {
        Task<PlayerData> saveTask = SavePlayerDataAsync();

        return await TaskServices.AllowRunningApplicationInBackgroundUntilTaskCompletion(
            saveTask,
            onBackgroundProcessingQuotaWillExpireCallback: () =>
            {
                Debug.Log("Background time expiring - save operation finishing");
            }
    );
}
```

Preserves the task result while providing background protection.

## Extension Methods for Cleaner Syntax

Task Services provides extension methods on `Task` and `Task<TResult>`:

**API Signatures:**

```csharp
using System.Threading.Tasks;
using UnityEngine;

// Extension method that allows running application in background until task completion
public static Task AllowRunningApplicationInBackgroundUntilCompletion(
            this Task task,
            Callback onBackgroundProcessingQuotaWillExpireCallback = null
);

public static Task<TResult> AllowRunningApplicationInBackgroundUntilCompletion<TResult>(
    this Task<TResult> task,
    Callback onBackgroundProcessingQuotaWillExpireCallback = null
);
```

Extension methods provide identical functionality with more convenient syntax.

## Pattern 1: Save Game Data on Pause

Protect save operations when the user backgrounds the app:

```csharp
using System.Threading.Tasks;
using UnityEngine;
using VoxelBusters.EssentialKit;
using VoxelBusters.CoreLibrary;

public class GameManager : MonoBehaviour
{
    async void OnApplicationPause(bool pauseStatus)
    {
        if (pauseStatus)
        {
            Debug.Log("App backgrounding - initiating protected save");

            // Create save task
            var saveTask = SaveGameDataAsync();

            // Protect with background execution
            await TaskServices.AllowRunningApplicationInBackgroundUntilTaskCompletion(
                saveTask,
                onBackgroundProcessingQuotaWillExpireCallback: () =>
                {
                    Debug.LogWarning("Background time expiring - save may be interrupted");
                    // Perform emergency cleanup if needed
                    MarkSaveAsPartial();
                }
            );

            Debug.Log("Save completed successfully");
        }
        else
        {
            Debug.Log("App resuming from background");
        }
    }

    async Task SaveGameDataAsync()
    {
        Debug.Log("Saving player progress...");
        await SavePlayerProgress();

        Debug.Log("Saving inventory...");
        await SaveInventory();

        Debug.Log("Uploading to cloud...");
        await UploadToCloud();

        Debug.Log("All save operations completed");
    }

    async Task SavePlayerProgress()
    {
        await Task.Delay(1000); // Simulate save operation
        PlayerPrefs.SetInt("PlayerLevel", 10);
        PlayerPrefs.Save();
    }

    async Task SaveInventory()
    {
        await Task.Delay(500); // Simulate inventory save
        // Save inventory data
    }

    async Task UploadToCloud()
    {
        await Task.Delay(2000); // Simulate cloud upload
        // Upload save to cloud service
    }

    void MarkSaveAsPartial()
    {
        PlayerPrefs.SetInt("SaveIncomplete", 1);
        PlayerPrefs.Save();
        Debug.Log("Marked save as incomplete for recovery on next launch");
    }
}
```

{% hint style="info" %}
**OnApplicationPause Timing**: `OnApplicationPause(true)` is called when the app is about to background. This is your opportunity to initiate protected save operations.
{% endhint %}

## Pattern 2: Extension Method Syntax

Use extension methods for cleaner, more readable code:

```csharp
using VoxelBusters.EssentialKit;
using VoxelBusters.CoreLibrary;
using System.Threading.Tasks;

public class AnalyticsManager : MonoBehaviour
{
    public async void UploadAnalytics()
    {
        try
        {
            Debug.Log("Starting analytics upload");

            var uploadTask = PerformAnalyticsUploadAsync();

            // Extension method syntax - cleaner and more natural
            await uploadTask.AllowRunningApplicationInBackgroundUntilCompletion(
                onBackgroundProcessingQuotaWillExpireCallback: () =>
                {
                    Debug.LogWarning("Upload may be interrupted - saving partial data");
                    SavePartialAnalyticsData();
                }
            );

            Debug.Log("Analytics upload completed successfully");
        }
        catch (Exception ex)
        {
            Debug.LogError($"Analytics upload failed: {ex.Message}");
        }
    }

    async Task PerformAnalyticsUploadAsync()
    {
        // Batch analytics events
        var events = GatherAnalyticsEvents();

        // Simulate network upload
        await Task.Delay(3000);

        // Upload to analytics service
        Debug.Log($"Uploaded {events.Count} analytics events");
    }

    List<string> GatherAnalyticsEvents()
    {
        return new List<string> { "level_complete", "item_purchased", "achievement_unlocked" };
    }

    void SavePartialAnalyticsData()
    {
        Debug.Log("Saving analytics events locally for next upload");
        // Save events to local storage for retry
    }
}
```

## Pattern 3: Task with Return Value

Protect operations that return results:

```csharp
using VoxelBusters.EssentialKit;
using VoxelBusters.CoreLibrary;
using System.Threading.Tasks;

public class CloudSyncManager : MonoBehaviour
{
    async void OnApplicationPause(bool pauseStatus)
    {
        if (pauseStatus)
        {
            var syncTask = SyncWithCloudAsync();

            // Generic overload preserves return value
            bool syncSuccess = await TaskServices.AllowRunningApplicationInBackgroundUntilTaskCompletion(
                syncTask,
                onBackgroundProcessingQuotaWillExpireCallback: () =>
                {
                    Debug.LogWarning("Cloud sync may timeout");
                }
            );

            if (syncSuccess)
            {
                Debug.Log("Cloud sync completed successfully");
            }
            else
            {
                Debug.LogError("Cloud sync failed");
            }
        }
    }

    async Task<bool> SyncWithCloudAsync()
    {
        try
        {
            Debug.Log("Starting cloud synchronization");

            // Upload local changes
            await UploadLocalChanges();

            // Download remote changes
            await DownloadRemoteChanges();

            // Merge data
            await MergeCloudData();

            Debug.Log("Cloud sync completed");
            return true;
        }
        catch (Exception ex)
        {
            Debug.LogError($"Cloud sync error: {ex.Message}");
            return false;
        }
    }

    async Task UploadLocalChanges()
    {
        await Task.Delay(1000);
        Debug.Log("Local changes uploaded");
    }

    async Task DownloadRemoteChanges()
    {
        await Task.Delay(1500);
        Debug.Log("Remote changes downloaded");
    }

    async Task MergeCloudData()
    {
        await Task.Delay(500);
        Debug.Log("Data merged successfully");
    }
}
```

## Pattern 4: Multiple Protected Tasks

You can protect multiple independent tasks:

```csharp
using VoxelBusters.EssentialKit;
using VoxelBusters.CoreLibrary;
using System.Threading.Tasks;

public class DataManager : MonoBehaviour
{
    async void OnApplicationPause(bool pauseStatus)
    {
        if (pauseStatus)
        {
            Debug.Log("App backgrounding - protecting multiple operations");

            // Create multiple tasks
            var saveTask = SaveGameDataAsync();
            var uploadTask = UploadAnalyticsAsync();
            var cleanupTask = CleanupResourcesAsync();

            // Protect all tasks with background execution
            // Note: All tasks share the same background time quota
            await Task.WhenAll(
                saveTask.AllowRunningApplicationInBackgroundUntilCompletion(),
                uploadTask.AllowRunningApplicationInBackgroundUntilCompletion(),
                cleanupTask.AllowRunningApplicationInBackgroundUntilCompletion()
            );

            Debug.Log("All background operations completed");
        }
    }

    async Task SaveGameDataAsync()
    {
        await Task.Delay(1000);
        Debug.Log("Game data saved");
    }

    async Task UploadAnalyticsAsync()
    {
        await Task.Delay(1500);
        Debug.Log("Analytics uploaded");
    }

    async Task CleanupResourcesAsync()
    {
        await Task.Delay(500);
        Debug.Log("Resources cleaned up");
    }
}
```

{% hint style="warning" %}
**Shared Background Quota**: All protected tasks share the same background time quota (30s on iOS, minutes on Android). Design tasks to complete within this limit or they may all be terminated together.
{% endhint %}

## Handling Quota Expiration

Always implement quota expiration callbacks for graceful degradation:

```csharp
using VoxelBusters.EssentialKit;
using VoxelBusters.CoreLibrary;
using System.Threading.Tasks;

public class RobustSaveManager : MonoBehaviour
{
    async void OnApplicationPause(bool pauseStatus)
    {
        if (pauseStatus)
        {
            var saveTask = SaveWithCheckpointingAsync();

            await saveTask.AllowRunningApplicationInBackgroundUntilCompletion(
                onBackgroundProcessingQuotaWillExpireCallback: () =>
                {
                    Debug.LogWarning("Background quota expiring - implementing emergency save");

                    // Quick save critical data only
                    SaveCriticalDataSynchronously();

                    // Set flag to resume on next launch
                    PlayerPrefs.SetInt("ResumeIncompleteSave", 1);
                    PlayerPrefs.Save();
                }
            );
        }
    }

    async Task SaveWithCheckpointingAsync()
    {
        // Save in stages with checkpoints
        await SaveStage1();
        SetCheckpoint(1);

        await SaveStage2();
        SetCheckpoint(2);

        await SaveStage3();
        SetCheckpoint(3);

        ClearCheckpoint();
        Debug.Log("Complete save finished");
    }

    async Task SaveStage1()
    {
        await Task.Delay(800);
        Debug.Log("Stage 1: Player stats saved");
    }

    async Task SaveStage2()
    {
        await Task.Delay(800);
        Debug.Log("Stage 2: Inventory saved");
    }

    async Task SaveStage3()
    {
        await Task.Delay(800);
        Debug.Log("Stage 3: World state saved");
    }

    void SetCheckpoint(int stage)
    {
        PlayerPrefs.SetInt("SaveCheckpoint", stage);
        PlayerPrefs.Save();
    }

    void ClearCheckpoint()
    {
        PlayerPrefs.DeleteKey("SaveCheckpoint");
        PlayerPrefs.Save();
    }

void SaveCriticalDataSynchronously()
{
    // Emergency save - only critical data, synchronous
    PlayerPrefs.SetInt("PlayerLevel", 10);
    PlayerPrefs.SetInt("LastKnownHealth", 75);
    PlayerPrefs.Save();
    Debug.Log("Emergency save completed");
}
}
```

## Data Properties

| Item                                                                           | Type             | Notes                                                                                                                      |
| ------------------------------------------------------------------------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `TaskServices.AllowRunningApplicationInBackgroundUntilTaskCompletion`          | Method           | Wraps a `Task` so Essential Kit requests platform background time until the work completes.                                |
| `TaskServices.AllowRunningApplicationInBackgroundUntilTaskCompletion<TResult>` | Method           | Same as above but preserves the original task’s return value.                                                              |
| `Task.AllowRunningApplicationInBackgroundUntilCompletion`                      | Extension Method | Fluent syntax for protecting tasks without additional wrappers.                                                            |
| `Callback onBackgroundProcessingQuotaWillExpireCallback`                       | `Callback`       | Invoked when the OS is about to reclaim background time; use it for emergency saves or cleanup.                            |
| `TaskServicesUnitySettings.IsEnabled`                                          | `bool`           | Confirms whether Task Services is active. If `false`, the helper methods will log an error instead of protecting the task. |

## Core APIs Reference

| API                                                                                      | Purpose                               | Returns                |
| ---------------------------------------------------------------------------------------- | ------------------------------------- | ---------------------- |
| `TaskServices.AllowRunningApplicationInBackgroundUntilTaskCompletion(task, callback)`    | Protect async Task from backgrounding | `Task` continuation    |
| `TaskServices.AllowRunningApplicationInBackgroundUntilTaskCompletion<T>(task, callback)` | Protect Task\<T> preserving result    | `Task<T>` continuation |
| `task.AllowRunningApplicationInBackgroundUntilCompletion(callback)`                      | Extension method for Task             | `Task` continuation    |
| `task.AllowRunningApplicationInBackgroundUntilCompletion<T>(callback)`                   | Extension method for Task\<T>         | `Task<T>` continuation |

## Error Handling

| Scenario                                | Trigger                                                     | Recommended Action                                                                                                               |
| --------------------------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Feature disabled or not initialized     | Helper logs “Feature is not active or not yet initialised.” | Enable Task Services in Essential Kit Settings or call `TaskServices.Initialize` before awaiting background-protected tasks.     |
| Background quota expires early          | Long-running operation exceeds OS limit                     | Use the quota callback to persist critical state synchronously and resume the workload on the next foreground session.           |
| Exceptions thrown inside protected task | `await` raises an exception after background completion     | Wrap protected calls in `try/catch` blocks so you can surface telemetry, retry on resume, or notify the player.                  |
| Nested protection                       | Same task wrapped more than once                            | Guard against double-wrapping by tracking outstanding tasks or creating helper methods that centralize protection.               |
| Android background restrictions         | Device kills background work while in Doze/battery saver    | Pair protected tasks with user-visible progress (e.g., foreground service) or postpone heavy uploads until the device is active. |

## Important Behaviors

**Background Time Quota**:

* **iOS**: Approximately 30 seconds of background execution time
* **Android**: Few minutes depending on device and Android version
* System may terminate earlier if resources are critically low

**Shared Quota**:

* All tasks protected in a single app share the same background time quota
* Multiple `AllowRunningApplicationInBackgroundUntilTaskCompletion` calls don't multiply available time

**Callback Timing**:

* `onBackgroundProcessingQuotaWillExpireCallback` is called shortly before system termination
* Use this callback for emergency cleanup, not to extend execution time

**Task Lifetime**:

* Protected tasks continue running even when the entire app is in background state
* All other Unity MonoBehaviour code also continues executing during background time

{% hint style="success" %}
**Best Practice**: Design tasks to complete in under 20 seconds to have safety margin before iOS quota expiration. For longer operations, implement checkpoint/resume patterns.
{% endhint %}

## Advanced: Manual Initialization

{% hint style="warning" %}
Manual initialization is only needed for specific runtime scenarios. For most games, Essential Kit's automatic initialization handles everything. **Skip this section unless** you need runtime configuration or custom task monitoring.
{% endhint %}

### Understanding Manual Initialization

**Default Behavior:** Essential Kit automatically initializes Task Services using global settings from the ScriptableObject configured in Unity Editor.

**Advanced Usage:** Override settings at runtime when you need:

* Custom background task timeout monitoring
* Different task priority levels for various operations
* Runtime task execution policies based on battery level or device capabilities

### Implementation

```csharp
void Awake()
{
    var settings = new TaskServicesUnitySettings(isEnabled: true);
    TaskServices.Initialize(settings);
}
```

{% hint style="info" %}
Call `Initialize()` once before using Task Services features. Most games should use the [standard setup](/features/task-services/setup.md) configured in Essential Kit Settings instead.
{% endhint %}

## Related Guides

* Demo scene: `Assets/Plugins/VoxelBusters/EssentialKit/Examples/Scenes/TaskServicesDemo.unity`
* Use with **Cloud Services** to sync game state on backgrounding
* Pair with **Network Services** to verify connectivity before uploads
* See [Testing Guide](https://github.com/voxelbusters/essential-kit-docs/blob/master/tutorials/v3/features/task-services/testing.md) for device testing checklist


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://assetstore.essentialkit.voxelbusters.com/features/task-services/usage.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
