Streaming is available in most browsers,
and in the Developer app.
-
Meet Background Assets
Discover how you can use the Background Assets framework to download large files directly from your CDN and improve the initial launch experience of your apps and games. We'll show you how to schedule background downloads during initial app install, app updates, and periodically as someone uses the app. We'll also explore how you can manage scheduled downloads to make sure people have the content they want, when they want it.
Resources
Related Videos
Tech Talks
WWDC23
WWDC21
-
Download
♪ Mellow instrumental hip-hop music ♪ ♪ Hi! My name is Jared, and I'm a software engineer here at Apple.
Today I'd like to talk you about a new framework we are introducing this year to iOS, iPadOS, and macOS.
This new framework is called Background Assets, and we believe it will greatly enrich the user experience of the apps that we all know and love, and more importantly, that you develop.
To get started, I'll be introducing you to the new Background Assets framework.
Afterwards, I'll show you how to adopt the new framework into your app.
This will be followed by a quick overview of the extension and what new capabilities it provides.
Then we'll finish with some best practices and everything we've learned.
Before we begin, let's talk about what we're trying to solve here.
The truth is, waiting is not fun.
Anytime we find ourselves asking those that use our software to wait, we're increasing frustration and taking away from the experience we want our apps to provide.
For instance, how often have you found yourself browsing endlessly through the App Store looking for that perfect app? You finally find it, and oh does it look so perfect! You then tap that GET button.
With every moment, your level of excitement increases.
Then you shortly realize that depending on your network connection or the size of the app, you may find yourself having to wait as the app downloads.
Then after a few seconds of waiting, you find yourself putting your phone down, grabbing a cup of coffee, reading your favorite book about the benefits of practicing mindfulness and mastering patience.
Then a few hours pass, and you finally pick up your phone.
Your excitement levels rise as you're ready to dive into the perfect app that you've been waiting to use all day.
Just to launch the app and be immediately greeted with this: more downloading.
It's confusing.
You've been away from your phone all day.
Why is this app now making you wait even longer? Couldn't this app have downloaded this content automatically after the app was installed? For anyone on a slower internet connection, this might frustrate them to close and remove the app.
The truth is, this is not the experience any of us want to have.
We know this isn't your fault, and we believe we can make this experience so much better! This is why we're proud to introduce to you this year, Background Assets.
This framework was developed to help you enrich the user experience of your apps.
So that the moment your app is launched, it is providing a phenomenal first impression! Background Assets is designed to be flexible with your existing workflows.
A lot of you have already developed complex asset management systems, and we wanted this new framework to easily fit into the solutions that you have already developed.
We also know that you want to be able to push updated content to your apps without having to require an additional submission to the App Store.
It's not uncommon for games or other apps to need additional content after the app has already shipped.
Think updated art textures or a bug fix in game-level data.
Background Assets provides you with the ability to schedule and update your assets outside of your app's lifecycle.
We believe it's important that assets are present before first app launch, or whenever the app is updated overnight.
So we've worked to create a mechanism to help ensure that your content is present by the time your app is launched.
And finally, the easier a framework is to adopt, the more we can encourage you to use it in your apps.
We want Background Assets to be used in any place where large assets need to be predownloaded.
That way, we can minimize the time your app is waiting and showing a progress bar before its content is available.
So you may be wondering, how is this new framework helping you solve this problem? Well, in order to be as extensible as possible, we've created a new app extension for downloading content in the background.
This new extension is built on top of the powerful app extension technology that other extensions on our platforms use.
This provides an opportunity to run code outside of the app's lifecycle.
For instance, the extension will run whenever the user first installs your app but has yet to launch it.
The extension will also run whenever the app is updated automatically in the background.
This helps to ensure that your content can be scheduled and downloaded before the user opens the application after it has been updated.
Finally, the extension will run periodically in the background, allowing you to check for updated assets and schedule them periodically over time.
Though it is important to note, that extension runtime is short-lived, so all work will need to be scheduled by your extension with haste.
If downloads are not scheduled quickly, the system may terminate the extension.
It's also important to be aware that the frequency of the extension's ability to run periodically will back off based on app usage.
If your app isn't being used very much, then the extension will receive less frequent runtime.
So that's an overview of the new Background Assets framework.
It provides you with the tools you need to ensure that your assets are available by the time your app is launched.
This is done with an extension that runs whenever your app is installed or updated, but before the user has launched your app.
Now, let's take a look at adopting the Background Assets framework into your project and getting started! The download manager within the framework is the primary vehicle used to communicate with the Background Assets system service.
The manager is a singleton object that can be used throughout your app.
Using the manager, you can schedule the download of your assets in either the foreground or background.
You can also retrieve downloads that are currently in flight, which might have started before your application was ever launched.
Downloads can also be canceled.
This is useful if they were already scheduled or are in the middle of being downloaded and you no longer need that asset that you originally requested.
We've also introduced a synchronization mechanism for managing exclusive access between your app and the extension so that both the extension and application do not end up scheduling or modifying existing downloads at the same time.
I have an example to show you, but for now, more on this later! Let's take a look at just how easy it is to start using Background Assets.
I'm going to begin by walking you through some of the basics of the API.
I'll then follow that up later by showing you how to tie all of this together into an app extension.
To get started, you'll import the Background Assets framework module.
Then, it's as easy as defining a URL that points to the location of where your remote asset is located.
We then follow that up by defining an app group container that your extension and app are both members of.
Having your app and extension in the same group allows them to manage your assets during the download and after completion.
If you're not already familiar with app groups, you can easily add one from the Signing & Capability section of Xcode 14.
They're a powerful feature that allows two or more applications to access the same resources, or in this case, your app and its extension.
The next thing to do is create your download object.
The Background Assets framework is designed to support multiple different types of download objects.
However, in this example, we'll be focusing on the most common one: BAURLDownload.
Immediately, you'll notice that the initializer takes in the URL and the app group identifier.
This information tells the system both what we're downloading and where the resulting file will end up.
It also takes an identifier.
You'll use this identifier to track your download across multiple launches of your app, and within the extension.
The engine will not allow more than one download to be scheduled with the same identifier.
Therefore, you should make these identifiers unique.
Next, we'll grab a reference to the BADownloaderManager shared object.
The download manager is your single interface into Background Assets.
It's what allows you to observe, cancel, and schedule downloads.
We'll then pass it a weak reference to a delegate that conforms to the BADownloadManagerDelegate protocol.
I'll go more into this protocol shortly, but the most important part to know now is that it receives messages about downloads that have been scheduled.
The only thing left to do is to ask the download manager to schedule the download.
If for any reason the download cannot be scheduled, then an error is thrown.
In addition to scheduling downloads in the background, we also provide API for doing foreground downloads.
Running in the foreground not only gives you increased priority, but it also enables your download to begin immediately.
This is similar to using the default session configuration within URLSession.
We provide this API so that your app can promote any downloads scheduled by your extension in the background to the foreground.
One thing to keep in mind: performing a foreground download is not available from within the extension; it can only be initiated from the app.
Since extensions never present UI, and the user doesn't notice that they are running, extensions may only schedule downloads in the background.
If your app would like to promote existing background downloads to the foreground, this can easily be accomplished by fetching the list of currently active downloads from the manager.
The list that is returned contains all downloads the are currently scheduled, which may include downloads in flight or queued up in the scheduler.
Next, your app can begin the promotion process by calling startForegroundDownload.
If a download is already in the foreground, calling this method will effectively do nothing.
However, if the download was backgrounded, it will first be paused, then resumed in the foreground without requiring any content that was already downloaded up until this point to be redownloaded.
Together, this provides an effective and simple illustration for how easy it is to use Background Assets to promote downloads scheduled in the background to the foreground.
It really is as simple as that! The download manager is your primary interface that is used to schedule and monitor background downloads.
As these download objects are processed by the system, you'll receive messages in your delegate object.
Let's walk through the delegate now.
The delegate receives messages for all downloads that have been scheduled by either the extension or your app.
If there are numerous downloads that were scheduled, callbacks will be received for all of them.
This is where you use the download object's unique identifier to distinguish between them.
Your app will begin to receive callbacks the moment the delegate is established on BADownloadManager.
Callbacks are not enqueued by the system.
If your app does not handle one of the delegate methods or your delegate is not established, then your extension will wake to process the message.
This means that you should fully expect your extension to be sent messages if you have not established a delegate onto BADownloadManager within your app.
If your app is currently in the foreground being presented to the user and its delegate has been established, then callbacks will be sent to your app and the extension will not be woken.
The extension will only wake if your app does not handle its delegate callback.
If a download finishes, or fails, and the app does not process this message, then the extension will wake.
Keep in mind, the extension is not woken for all types of callbacks.
Only callbacks that share common interfaces between BADownloadManagerDelegate and the BADownloaderExtension protocol.
A download succeeding or failing is an example of a common interface between the delegate and the protocol.
Although your app extension has its own entry points that cause it to wake, if the extension is currently running, it can use BADownloadManager and establish a delegate.
This will allow both the app and extension to receive duplicate messages to their delegates.
Keep in mind that extensions do not wake to process delegate messages.
They only wake at extension entry points defined in the BADownloaderExtension protocol.
Let's take a look at the protocol for the download manager's delegate.
The first function is for receiving messages whenever a download starts.
This is useful for tracking when the device has finally chosen to schedule a specific download.
You may also be notified if a download pauses.
An example of a pause occurring would be if the extension starts a download in the background and then your app asks us to promote it to the foreground.
During this promotion there will be a small window where the download pauses before it is resumed.
The download manager also allows you to monitor active progress of your download as it is being downloaded in the foreground.
We also provide a mechanism to answer a challenge request, which is useful for validating the authenticity of a connection or for providing credentials to authorize a connection.
The most important functions are for dealing with a failed or finished download.
If a download fails, you may need to reschedule it or determine the cause.
For a successful download, the system has placed the file in a location that is managed by the operating system.
If the device ends up low on space, then the system will delete the file for you.
We strongly recommend that you leave the file at the location that the system has provided.
Only move the file if you absolutely must and please do not duplicate it unless you delete the originating file afterwards.
As a reminder, the protocol for the download manager's delegate is for receiving messages related to downloads that your app or extension has scheduled.
It is not the entry point for your extension, which brings us to our next topic.
Now we'll be taking a look at the most exciting part of Background Assets, the extension! The extension enables you to schedule the downloads of your assets before the user has launched your app.
This enables you to ensure that your assets are in place and ready to go in order to provide the best possible experience in your app with minimal wait time.
As discussed earlier, we're introducing a new app extension.
This extension can be created from within Xcode inside of your existing project.
As a quick reminder, the extension runs whenever your app is installed or updated.
Giving you the flexibility to make sure changes to your app always has its latest assets.
The extension also runs periodically based on how often a user uses your app.
If someone uses your app everyday, then the system learns this behavior and your extension will run more frequently.
However, if the app is never launched, then the frequency of this periodic check will subside.
The new extension also has a short lifecycle and a tight sandbox to ensure that its usage is limited to just downloading assets.
You are encouraged to make quick decisions in the extension and to limit the extension to the Background Assets framework.
Before we start navigating through the extension, there are a couple of configurations that you need to make before the extension can launch.
These changes are also a requirement for your app to be approved for distribution on the App Store.
In your app's information property list, you'll need to define a couple of additional keys.
These keys should not be placed in the extension's Info.plist, only the app's.
The first key is BAInitialDownloadRestrictions.
This is a dictionary where you will be specifying restrictions that will be placed upon your extension.
These restrictions are reviewed by App Review, so try to be as accurate as possible.
Now, let's dig into each individual key inside the dictionary.
The first restriction is the download allowance.
This is represented in bytes and is the maximum download size you're requesting to make within the extension during an initial app install.
This size pertains to the sum of all files combined that you are requesting to download, not the size of each individual file.
The next item is the domain AllowList, which takes an array of domains represented as strings.
The domain AllowList supports prefix wildcards and takes in a list of host names that your extension is permitted to download from.
It's important to note that the keys in the BAInitialDownloadRestrictions, such as the DownloadAllowance and AllowList are only enforced after first app install.
Whenever your app is launched, these restrictions are no longer enforced.
The last required key, which sits at the root of your Info.plist is the maximum size that your app will require in additional storage for these assets.
We expect that you might want to download compressed assets, so this value should be the final extracted uncompressed size.
The number that is placed here will be presented on the App Store before the app is downloaded.
Now that we've gone over some housekeeping, let's talk about the entry points into your extension in more detail.
The functions that you define from the protocol will be called by the system and not by your app.
Unlike other app extensions, where the application is responsible for talking to the extension, the background download extension is brokered by the system.
Since the system is maintaining the lifecycle of the extension, it should be viewed as an ephemeral service.
Whenever any of the functions inside of the protocol are invoked, it's important to keep the work that is done there to a minimum.
The extension will be terminated rather quickly after it is launched.
This is not the place to kick off decompression or other complex operations that may take a while.
One of the great parts of working in the extension is that all of the BackgroundAssets APIs available to your app are also available within the extension.
With the only exception being the ForegroundDownload API.
This means that you'll use BADownloadManager just like you would in your app.
As a matter of fact, it's entirely likely that you'll discover the ability to create something that uses the same code to schedule and manage your assets in both your app and its extension.
Also, when creating your extension, it's important to ensure that both are in a common app group.
You'll want to use the same group identifier so that content can be read and written by your app and its extension.
Now let's take a look at the downloader extension protocol that you will be conforming your extension to.
The first thing you'll notice is how similar it looks to the download manager delegate protocol.
As I stated earlier, you can use BADownloadManager and construct a delegate from within the extension.
However, only these entry points can actually wake the extension.
The first function is invoked whenever your application is first installed.
The app hasn't launched yet, but your extension has.
This is the perfect opportunity to start scheduling downloads that your app needs to provide the best experience once your app has been launched.
It's also important to recall that during initial app install, download restrictions are in effect.
You'll want to consult the BADownloadRestrictions key that you defined in your Info.plist to know what your maximum permitted download size and allowed domains are.
This next function is invoked whenever the App Store updates your app.
As long as the user hasn't quit your app in the app switcher, your newly updated extension will wake and you can begin scheduling work.
The checkForUpdates function provides support for your extension being periodically awoken by the system, so that you can check for any updates that need to be background downloaded.
This function is invoked by the system based on how often a user uses your app.
We also have support for responding to an authentication challenge request.
So that you can better restrict and ensure that the files you are downloading have come from a trusted source.
Finally, just like the delegate, you'll be informed if the download failed or succeeded.
You'll notice that in the backgroundDownloadDidFail function, there is no error returned.
The error can be retrieved along with its state in a variable inside of the returned BADownload object.
It's also important to note that the last three functions can be invoked even if your extension isn't what scheduled the download.
If your app scheduled a download, but hasn’t become backgrounded, then the extension will be expected to service the download.
Now that we understand how to use BADownloaderManager from our app and its extension, we have to start thinking about what it means if both the app and its extension are running simultaneously.
For instance, let's say the system has decided that it's time to wake the extension to have it periodically check for updates.
And of course, since the extension needs to access the network to do this check, it's going to use BADownloaderManager to schedule the download of a catalog or some other type of metadata that provides a list of updated assets that are available.
For instance, let's say the file is a small 100KB catalog that contains a list of large multi-gigabyte assets that we need to download.
Since the extension needs to know when the download it has scheduled finished, or failed, it's going to attach a delegate onto the download manager.
The download manager's delegate is used over its extension entry points since its downloading a small file to determine what larger assets it will be scheduling, and extension entry points are not guaranteed to be invoked immediately.
After the download has finished, the extension receives this message through its delegate.
Your extension now has access to the catalog file and has to make a choice with what it plans to do with the downloaded file.
You could imagine that the extension will read the file to determine which of the assets in the catalog need to be downloaded to the device.
Then the extension could schedule background downloads of those larger assets.
Now that the downloaded file is no longer necessary, the extension should delete the file.
While this seems appropriate, what happens if your app launches while your extension is running and creates its own BADownloadManager? Well, let's take a look! The app launches and immediately wants to know if it has updated content.
Perhaps a version number is stored in the app group that both consult to determine if their assets are up to date.
Since the app was launched before the newer catalog finished downloading, it's going to fetch the current downloads from the manager and realize that a download of the catalog is currently in flight and wait for it to be finished in its delegate.
But we have a problem.
Both the extension and the app will receive a download finished message in their respective delegates that were hooked into the download manager.
This means we have a data race on the file being downloaded.
Both the app and extension will try to read and delete the file at the same time; this is not good.
This means that either your app or extension could try to read the file and it might be missing.
This means that you will need think about your app and extension in a similar way that you would think about two threads within your app.
Luckily, Background Assets provides a way to synchronize between your app and its extension.
Let's talk about that now! Synchronizing between your app and its extension is extremely simple with Background Assets.
What we're currently looking at is the download manager's delegate function for when a download has completed.
A URL is provided that contains a local path to the file that your app or extension has access to.
In this example, we'll be ensuring mutual exclusion of this file.
Next, we grab a reference to the download manager and use the withExclusiveControl function, which takes a completion handler.
All code that is executed within the completion handler scope is guaranteed to be mutually exclusive with other calls that require exclusive control.
Meaning if your extension calls withExclusiveControl while you app has not returned from its completion handler, then the extension will wait.
This applies in the other direction as well.
If the extension acquires exclusive control first, then the app will wait until the extension is terminated or releases control by exiting scope.
An important thing to keep in mind is that acquiring exclusive control can fail.
It is extremely unlikely that this will occur, but in the event it does, your code should handle it.
You can detect if exclusive control could not be acquired by checking if the error provided by the function is not nil.
From this point on, you are guaranteed that your app or extension has exclusive access within its context.
So based on our earlier example, it is now perfectly valid to read the contents of the file and then clean it up, if you so choose.
Just make sure to be aware that when your other app or extension gets its opportunity to enter exclusive control that it knows that you have already processed the file.
One way this can be accomplished is by first checking if the file exists or writing to a database or plist.
As a reminder, the background downloader extension is for collecting and scheduling the downloads of large assets for your app.
Its runtime is short lived, so please keep the work that is done within the extension to a minimum.
You should also place your extension and app within a shared app group so that both can access files that are downloaded by one another.
And finally, the extension is brokered by the system and not your app.
Now that you know how to develop a basic background download extension, you have everything you need to start implementing Background Assets into your app.
Now, let's go over what we've learned.
The download manager is used to coordinate and schedule downloads between your app and its extension, therefore you should be using the download manager in both places.
Your extension runs even if your app is not in the foreground.
This can occur during app installation, update, or periodically at an interval determined by the system.
If your app is launched and content that was being downloaded in the background is now be waited on, please immediately promote those downloads to the foreground.
The extension can only schedule downloads in the background.
By having your app promote them to the foreground, ensures that your content will arrive as quickly as possible.
If you ever find yourself needing exclusive access to the download manager, please use the exclusive control APIs.
This will ensure that only your app or extension will have runtime within that window.
This is extremely useful so that you don't have to think about your extension racing your app when accessing its container or managing downloads.
If there is anything you should take from this presentation, it's that waiting results in a poor app experience.
Minimize waiting by making your app usable while the task you are waiting on is underway.
One of the ways you can minimize waiting in your app is to adopt the new Background Assets framework along with the underlying background download extension.
This helps to ensure that your app will have all of its content ready before the app is launched.
Make sure to also check out the documentation, which includes extra information that may not have been incorporated into this presentation; including how to test your extension and to simulate its entry points.
We're really excited to get to share Background Assets with you, and we value your feedback.
Please use Feedback Assistant to let us know what is working for you and what you'd like us improve.
This is a new framework and we have the opportunity to make adjustments during seeding.
We have some additional sessions that we think you might find interesting and we encourage you to check them out.
"Accelerating networking with HTTP3" is a fantastic session that pairs well with Background Assets.
Also, I encourage you to check out another session: the "Introducing on demand resources" presentation covers an alternative to Background Assets where your content is hosted by Apple and files are downloaded at your request.
Both of these sessions are really engaging and have a lot to offer.
Thank you for spending your time with me, and on behalf of everyone at Apple, we hope you have a fantastic WWDC! ♪
-
-
5:28 - Getting started with Background Assets
// Getting started with Background Assets import BackgroundAssets let url = URL(string: "https://cdn.example.com/large-asset.bin")! let appGroupIdentifier = "group.WWDC.AssetContainer" let download = BAURLDownload ( identifier: "Large-Asset", request: URLRequest(url:url), applicationGroupIdentifier: appGroupIdentifier ) let manager = BADownloadManager.shared manager.delegate = self // BADownloadManagerDelegate protocol // Schedule download at an opportunistic time determined by the system do { try manager.schedule(download) } catch { print("Failed to schedule download. \(error)") } // or Schedule download in foreground do { try manager.startForegroundDownload(download) } catch { print("Failed to start foreground download. \(error)") } // or Promote downloads to foreground. do { for download in try await manager.fetchCurrentDownloads) { try manager.startForegroundDownload(download) } } catch { print("Failed to promote downloads to foreground \(error)") }
-
10:28 - BADownloadManager delegate protocol
// BADownloadManager protocol definition public protocol BADownloadManagerDelegate : NSObjectProtocol { optional func downloadDidBegin(_ download: BADownload) optional func downloadDidPause(_ download: BADownload) optional func download(_ download: BADownload, bytesWritten: Int64, totalBytesWritten: Int64, totalExpectedBytes: Int64) optional func download(_ download: BADownload, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) optional func download(_ download: BADownload, failedWithError error: Error) optional func download(_ download: BADownload, finishedWithFileURL fileURL: URL) }
-
15:37 - BADownloaderExtension protocol
// BADownloaderExtension protocol definition public protocol BADownloaderExtension : NSObjectProtocol { optional func applicationDidInstall(metadata: BAApplicationExtensionInfo) optional func applicationDidUpdate(metadata: BAApplicationExtensionInfo) optional func checkForUpdates(metadata: BAApplicationExtensionInfo) optional func download(_ download: BADownload, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) optional func backgroundDownloadDidFail(failedDownload: BADownload) optional func backgroundDownloadDidFinish(finishedDownload: BADownload, fileURL: URL) optional func extensionWillTerminate() }
-
19:40 - Synchronizing between app and extension
// Synchronizing between app and extension func download(_ download: BADownload, finishedWithFileURL fileURL: URL) { let manager = BADownloadManager.shared manager.withExclusiveControl { error in guard error == nil else { print("Unable to acquire exclusive control \(String(describing: error))") return } // Exclusive control acquired // All code in this scope ensures mutual exclusion between extension and app do { let data = try Data(contentsOf: fileURL, options: .mappedIfSafe) // Do something with memory mapped data try FileManager.default.removeItem(at: fileURL) } catch { print("Unable to read/cleanup file data. \(error)") } } }
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.