Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2026 App ServicesApp Store, Distribution & Marketing

WWDC26 · 10 min · App Services / App Store, Distribution & Marketing

Unlock in-game content with StoreKit and Background Assets

Unlock native Apple In-App Purchases for your Unity game with the new StoreKit plug-in. Reduce download sizes with the new Background Assets plug-in, which delivers language-specific asset packs so each player gets just what they need. Plus, a new Steam Asset Converter helps you migrate existing builds.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

  • 0:01 — Introduction
  • 0:33 — Background Assets
  • 1:35 — Localized asset packs
  • 3:14 — Convert Steam depots to asset packs
  • 4:15 — Unity plug-ins
  • 5:52 — StoreKit and Background Assets sample code
  • 8:25 — Game presence
  • 9:10 — Next steps

Code shown on screen · 7 snippets

Asset pack manifest for a localized asset pack json · at 3:06 ↗
// Asset pack manifest

{
   "assetPackID": "voice-english",
   "downloadPolicy": { /* … */ },
   "language": "en-US",
   "sourceRoot": ".",
   "fileSelectors": [ /* … */ ],
   "platforms": [ /* … */ ]
    //… 
}
Convert a Steam depot to an asset pack manifest bash · at 3:27 ↗
# Convert a Steam depot to an asset pack manifest
xcrun ba-package convert --asset-pack-id voice-english -l en-US --on-demand voice-english.vdf -o voice-english.json
Convert an asset pack manifest to an asset pack archive bash · at 3:28 ↗
# Convert an asset pack manifest to an asset pack archive
xcrun ba-package voice-english.json -o voice-english.aar
Fetch and purchase products with the StoreKit plug-in csharp · at 5:52 ↗
// Fetch and purchase products with the StoreKit plug-in

using UnityEngine;
using Apple.StoreKit;

async void Start() {
    var products = await Product.FetchProducts(new[] {
            "com.thecoast.capecod"
    });
}
Fetch and purchase products with the StoreKit plug-in csharp · at 6:01 ↗
// Fetch and purchase products with the StoreKit plug-in

using UnityEngine;
using Apple.StoreKit;

async void Purchase(Product product) {
    var result = await product.Purchase();
    if (result.Result == PurchaseResult.ResultEnum.Success
        && result.TransactionVerification.IsVerified)
    {
        // Unlock access to purchased content

        result.TransactionVerification.SafePayload.Finish();
    }
}
Listen for Transaction updates with the StoreKit plug-in csharp · at 6:23 ↗
// Listen for Transaction updates with the StoreKit plug-in

using UnityEngine;
using Apple.StoreKit;

public static class TransactionListener {
    public static void Initialize() => Transaction.Updates += OnUpdate;


    async void OnUpdate(VerificationResult<Transaction> result) {
        if (!result.IsVerified) return;
        var verifiedTransaction = result.SafePayload;

        // Consumables are not in CurrentEntitlements, so handle them inline
        if (verifiedTransaction.ProductType == ProductType.ProductTypeEnum.Consumable) {
            if (verifiedTransaction.RevocationDate != null) {
                // Revoke the consumable identified by verifiedTransaction.ProductId
            } else {
                // Grant access to the consumable
            }
        }else {
            // Non-consumables and subscriptions: re-read CurrentEntitlements as source of truth
            await foreach (var verificationResult in Transaction.GetCurrentEntitlements()) {
                if (!verificationResult.IsVerified) continue;
                // Grant access to the product
            }
        }
        verifiedTransaction.Finish();
    }
}
Download asset packs with the Background Assets plug-in csharp · at 7:13 ↗
// Download asset packs with the Background Assets plug-in

using Apple.BackgroundAssets;
using UnityEngine;

async void LoadTutorial(string language) {
    try {
        string assetPackId = $"tutorial-{language}";
        AssetPackManifest manifest = await AssetPackManager.GetManifestAsync();
        AssetPack assetPack = manifest.GetAssetPack(assetPackId);
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        _ = Task.Run(async () => {
            await foreach (AssetPackManager.DownloadStatusUpdate statusUpdate in AssetPackManager.DownloadStatusUpdatesAsync(assetPackId)) { 
            		// Update download progress in UI
            }
        }, tokenSource.Token);
        await AssetPackManager.EnsureLocalAvailabilityOfAssetPackAsync(assetPack);
        tokenSource.Cancel();
        // Start tutorial with the locally available assets
    } catch (Exception exception) {
        // Handle the exception
    }
}

Resources