iOS 27 extends Managed Background Assets with localized asset pack support, allowing the system to deliver only the language-specific assets a player needs, dramatically reducing download size and on-device storage. The new `ba-package convert` tool also enables Steam depot migration to Apple asset packs.
• iOS 27 adds localized asset pack support: the system inspects the user's Settings language and only downloads matching language asset packs, with automatic fallback logic.
• New `xcrun ba-package convert` command-line tool converts Steam depot manifests into Apple asset pack manifests.
• New Apple Unity plug-ins for Background Assets and StoreKit are now publicly available on GitHub, bridging native APIs to C# for Unity 2022 LTS+.
• The Background Assets mock server now auto-attaches to Xcode 27 debug sessions for local asset serving during development.
• Localized asset packs mean players only download assets in their preferred language, cutting storage footprint significantly for games with multi-language audio, video, or textures.
• The system handles language fallback automatically (e.g. en-GB → en-US → primary app language), so developers declare locales once and Apple's CDN does the rest.
• New Unity plug-ins for Background Assets and StoreKit bring these capabilities to Unity-based games without writing native Objective-C or Swift glue code.
Demonstrates how a SwiftUI game view checks whether a localized asset pack is available and monitors its download progress using the BackgroundAssets framework APIs introduced/updated in iOS 27.
import SwiftUIimport BackgroundAssets−// Pre-iOS 27: no localized asset pack support in the manifest;−// all language variants were downloaded regardless of the user's language setting.−// Developers had to manually partition assets and manage downloads themselves.+// Model representing a single asset pack download state+struct AssetPackState: Identifiable {+ let id: String+ var fractionCompleted: Double+ var isFinished: Bool+}@MainActor−class LegacyAssetViewModel: ObservableObject {− @Published var downloadedBytes: Int64 = 0− @Published var totalBytes: Int64 = 1+class AssetPackViewModel: ObservableObject {+ @Published var packs: [AssetPackState] = []+ @Published var errorMessage: String?− // BADownloadManager existed pre-iOS 27 but lacked language-tag awareness− func beginDownload() {− // All language asset packs would be enqueued — no system-level locale filtering− let url = URL(string: "https://example.com/assets-all-languages.pack")!− var request = URLRequest(url: url)− let download = BAURLDownload(− identifier: "com.example.thecoast.assets.all",− request: request,− fileSize: 500_000_000, // 500 MB — all languages bundled− applicationGroupIdentifier: "group.com.example.thecoast"− )− BADownloadManager.shared.startForegroundDownload(download)+ // BAAppExtensionInfo is the entry point for querying managed asset packs+ private let manager = BAAppExtensionInfo()++ func loadPackStatus() async {+ // Fetch all asset packs the system knows about for this app+ do {+ let assetPacks = try await BAAppExtensionInfo.assetPacks()+ for pack in assetPacks {+ let state = AssetPackState(+ id: pack.identifier,+ fractionCompleted: pack.fractionCompleted,+ isFinished: pack.state == .installed+ )+ packs.append(state)+ }+ } catch {+ errorMessage = error.localizedDescription+ }}++ func downloadPack(identifier: String) async {+ do {+ // Request a specific asset pack by identifier (language-tagged in manifest)+ let request = BAURLDownload(identifier: identifier,+ request: URLRequest(url: URL(string: "https://example.com")!),+ fileSize: 0,+ applicationGroupIdentifier: "group.com.example.thecoast")+ BADownloadManager.shared.startForegroundDownload(request)+ }+ }}−struct LegacyAssetView: View {− @StateObject private var vm = LegacyAssetViewModel()+struct AssetPackDashboardView: View {+ @StateObject private var viewModel = AssetPackViewModel()var body: some View {− VStack(spacing: 16) {− Text("Downloading all language assets…")− ProgressView(value: Double(vm.downloadedBytes),− total: Double(vm.totalBytes))− Button("Start Download") { vm.beginDownload() }+ NavigationStack {+ List(viewModel.packs) { pack in+ VStack(alignment: .leading, spacing: 6) {+ Text(pack.id)+ .font(.headline)+ if pack.isFinished {+ Label("Installed", systemImage: "checkmark.circle.fill")+ .foregroundStyle(.green)+ } else {+ ProgressView(value: pack.fractionCompleted)+ Text("\(Int(pack.fractionCompleted * 100))% downloaded")+ .font(.caption)+ .foregroundStyle(.secondary)+ }+ }+ .padding(.vertical, 4)+ }+ .navigationTitle("Asset Packs")+ .task {+ await viewModel.loadPackStatus()+ }+ .overlay {+ if let error = viewModel.errorMessage {+ ContentUnavailableView(error,+ systemImage: "exclamationmark.triangle")+ }+ }}− .padding()}}#Preview {− LegacyAssetView()+ AssetPackDashboardView()}
Localized asset packs require updated asset pack manifest JSON files that include a language tag — existing manifests without the tag will not benefit from language filtering. The ba-package convert tool for Steam depot migration currently requires macOS with Xcode 27; Linux and Windows support is listed as 'coming soon'. The Background Assets mock server for local testing is only available in Xcode 27 debug sessions.
Apple-hosted Background Assets requires App Store distribution; up to 200 GB of hosted assets per app included in the Apple Developer Program membership.
More iOS 27 APIs land every week.
Get notified when new capabilities are published — no noise, just signal.