How we use on-demand resources in our iOS apps

Oliver Ochs
ZEAL Tech Blog
Published in
5 min readFeb 13, 2023

Here at ZEAL, we offer customer-facing apps for both LOTTO24 and Tipp24 on the iOS and Android platforms. While our Android offerings are essentially glorified wrapper apps, our iOS apps are mostly native.

On Apple’s side, gaming, gambling, and lotteries can be tricky to manage and tend to be one of the most regulated offerings in the App Store. Apps that run web content (HTML5, CSS and JavaScript) simply in a remote web view are not allowed to “provide access to real money gaming”.

Our standard lotteries such as freiheit+, Deutsche Traumhauslotterie, Eurojackpot and LOTTO 6aus49 are all natively implemented in Swift. But our portfolio is more than just these standard lotteries. ZEAL also offers scratch cards and instant games. Going forward, there are potentially hundreds of these scratch cards and games that we would like to add to our iOS apps. Given that opening these in a remote web view is not allowed by Apple, we could instead simply bundle the web content in our apps and submit it to Apple for review. In fact, our experience up until now is that Apple is absolutely fine with this approach. So this begs the question — why don’t we just do that?

Well — these scratch cards and games are quite big, and we did not want our app size to increase massively. Therefore, we came up with another approach — using on-demand resources.

On-demand resources are a technique that helps to reduce the initial download size of an application. We submit all the code to Apple for review, but if the user downloads the app, they only download the core parts (the standard lotteries). If the user then wants to play a scratch card or an instant game, the binaries needed to do that are then fetched from Apple’s servers to be used in the app. This approach has worked really well for us up until this point.

On-demand resources are not a new thing. This feature was introduced in iOS 9 and tvOS when Apple tried to focus a little more on gaming. With on-demand resources, developer’s can build a large A+++ game with a lot of resources while keeping the initial download size small.

So, how can you do this in your own app? This tutorial will lead you through the following basic steps:

  • Enabling the feature
  • Tagging resources
  • Downloading these resources on demand

Step-by-step guide:

First, you have to enable this feature in your project:

Screenshot on how to enable ODRs in Xcode.

As soon as you’ve done this, this feature is available in your app. Before you can start using it, let’s explain how it works.

Imagine you have a main bundle and an additional bundle with some (default) scratch cards.

diagram showing bundles

Some games are in the main bundle, while other games are in a scratch cards folder. What we want to achieve in this tutorial, is to download all scratch cards from Apple’s servers instead of bundling them in the main app bundle.

diagram showing how bundles are moved into another bundle

In order to achieve this, we can use tags. You can tag resources to be:

  • installed on initial install
  • prefetched when the app has been installed on a device
  • or downloaded only on demand

To tag a resource (let’s say a folder) as an on-demand resource, you have to open the file inspector and find the “On Demand Resource Tags” section. There, you can tag the resource folder as an on demand resource (here with the tag “scratchcards”):

After we have tagged our folder, we can then request a resource, identified by its tag, to be downloaded when needed:

 func requestResourceWith(tag: String,
onSuccess: @escaping () -> Void,
onFailure: @escaping (NSError) -> Void) {

var request = NSBundleResourceRequest(tags: [tag])
request.beginAccessingResources { (error: Error?) in
if let error = error {
onFailure(error as NSError)
return
}
onSuccess()
}
}

Now, when the user chooses to play a scratch card in our app, we can use this function to download it and launch the game:

requestResourceWith(tag: "scratchcards", onSuccess: {
DispatchQueue.main.async {
self.launchGame { }
}, onFailure: {}
}

And if it’s really working, you can see in your debugger:

Of course, this tutorial is just about taking you through the basics — it does not detail everything you have to do. And as you can see from the above code snippets, we still have to handle errors. Typical errors are:

  • NSBundleOnDemandResourceOutOfSpaceError — your phone is filled, no space left on device
  • NSBundleOnDemandResourceExceededMaximumSizeError — your bundle is too big, try to create smaller bundles
  • NSBundleOnDemandResourceInvalidTagError — the tag you are requesting does not exist (any more)

Also, to be a good iOS citizen, you also have to think about cleaning up after using the resources. You can do this by telling your device when your app does not need this bundle any more:

request.endAccessingResources()

And as far as the basics go, that’s about it!

Today, we use on-demand resources a lot. When there is a new scratch card or game to be added, we even have a GitHub action to automatically create a new tag, and add it and the associated code to our iOS app repository. So we can add a lot of scratch cards and instant games to our app without ever-increasing the downloaded bundle size. Apple can review all of these because we submit them to Apple for review.

The only downsides we’ve seen to date are:

  • uploading to Apple takes a while (because everything is bundled)
  • fixing a bug in a bundled game requires a new app submission to Apple (because the bundles are on Apple’s servers, not our own web servers, any more).

If you want to learn more about on demand resources, check out this WWDC video, read more about it on kodeco.com or have a look at Apple’s documentation.

Until the next time, stay tuned to see a lot of new games in our apps coming soon! In the meantime, feel free to check out our LOTTO24 or Tipp24 Apps.

--

--