iOS : On Demand Resource

I’m experiencing an issue with On-Demand Resources (ODR) in my Unity iOS project. Despite setting up everything correctly and verifying that asset bundles are being downloaded, I’m facing a peculiar problem where the download completes, but the loading process fails, and the error message provided is empty.

Here’s a brief overview of my setup and the problem:

  • Unity Version: 2022.3.10f1
  • Xcode Version: 15.2
  • iOS Version on Device: 17.4.1
  • Asset Bundle Setup: Asset bundles are tagged and built specifically for iOS.

Issue Description:
When I initiate an ODR request to download asset bundles, the request completes, and the assets appear to be downloaded (as indicated by Xcode’s console logs). However, an error occurs right after, with the Unity log showing "ODR request failed: :: ", but no error message is displayed. Here is the snippet from my Unity code where I handle the download and error:

private void LoadAssetBundleFromOnDemandResources(string assetBundleName, string lobbyPrefabName)
{
    UnloadCurrentAssets();
    Debug.Log($"PAD/ODR :: Loading prefab {lobbyPrefabName} from AssetBundle :: {assetBundleName}");
    string[] tags = new string[] { assetBundleName };
    var request = OnDemandResources.PreloadAsync(tags);
    StartCoroutine(DisplayDownloadProgressIOS(request));
    request.completed += (asyncOperation) =>
    {
        if (asyncOperation.isDone)
        {
            if (request.error != null)
            {
                Debug.LogError($"ODR request failed: {request.error}  ::  {request.error.ToString()}");
            }
/////
rest of code
/////
        }
    };
}

Console Output:
Here is the console output captured when the issue occurs:
Xcode Log:

appstored : Calling the completion handler. Result [{
  "XXXX.asset-pack-20ba15b290e015492ae08bb6ed48af61" =   {
   "_NSODRURLKey" = "file:///var/mobile/Library/OnDemandResources/AssetPacks/540D121A-D2F9-4045-BEEF-99A1F6481095/12674363955244035182/XXXX.asset-pack-20ba15b290e015492ae08bb6ed48af61.assetpack/";
  };
}] Error [(null)]

[ODRA1AE3366/XXXX.asset-pack-934d1e3206917af72df7146445f79667/1F3556C0] Finished asset promise

ODR request failed:  ::
<>c__DisplayClass15_0:<LoadAssetBundleFromOnDemandResources>b__0(AsyncOperation)
UnityEngine.AsyncOperation:InvokeCompletionEvent()

XXXX : being the app id

Steps Already Taken:

  • Verified asset tags in both Unity and Xcode.
  • Checked the integrity and compatibility of the asset bundles.
  • Ensured paths used in Unity match those logged in Xcode.
  • Added detailed logging around the asset loading process.

Despite these steps, the issue persists without any clear error message. It appears that the completion handler is being called correctly, but there is a disconnect in loading the asset bundle from the provided path.

Has anyone faced a similar issue, or does anyone have insights or suggestions on what might be going wrong here? Any help or pointers would be greatly appreciated as I’ve been stuck on this for a while.

Thank you!

Try:
if (string.IsNullOrEmpty(request.error))

It would also mean a bug, since our documentation states it should return null if no error.

1 Like

Yeah , I tried that. “request.error” return a empty string for some reason. It should be null when asset is downloaded properly.

Main problem is that :
the path returned is also a empty string. Due to this nothing can be loaded

Code :

 private void LoadAssetBundleFromOnDemandResources(string assetBundleName, string lobbyPrefabName)
    {
        UnloadCurrentAssets();
        Debug.Log($"PAD/ODR :: Loading prefab {lobbyPrefabName} from AssetBundle :: {assetBundleName}");
        string[] tags = new string[] { assetBundleName };
        var request = OnDemandResources.PreloadAsync(tags);
        StartCoroutine(DisplayDownloadProgressIOS(request));
        // _request = request;
        request.completed += (asyncOperation) =>
        {
            if (asyncOperation.isDone)
            {
                if (request.error != null)
                {
                    Debug.LogError($"ODR request failed: {request.error}  ::  {request.error.ToString()}");
                }

                string path = request.GetResourcePath(assetBundleName);
                Debug.Log($"Checking path before loading: {path}");

                if (File.Exists(path))
                {
                    _loadedAssetBundle = AssetBundle.LoadFromFile(path);
                    Debug.Log($"Loaded AssetBundle from path: {path}");

                    if (_loadedAssetBundle != null)
                    {
                        _lobbyPrefab = Instantiate(_loadedAssetBundle.LoadAsset<GameObject>(lobbyPrefabName), transform);
                        _lobbyPrefabName = lobbyPrefabName;
                    }
                    else
                    {
                        Debug.LogError($"PAD/ODR :: Failed to load AssetBundle {assetBundleName}. AssetBundle is null.");
                    }
                }
                else
                {
                    Debug.LogError($"AssetBundle file does not exist at path: {path}");
                }
            }
            else
            {
                Debug.LogError($"PAD/ODR :: Failed to preload On-Demand Resources for {assetBundleName}. Error: {request.error}  ::  {request.error.ToString()}");
            }
        };
    }

Console Log :

Checking path before loading:
<>c__DisplayClass15_0:<LoadAssetBundleFromOnDemandResources>b__0(AsyncOperation)
UnityEngine.AsyncOperation:InvokeCompletionEvent()

AssetBundle file does not exist at path:
UnityEngine.AsyncOperation:InvokeCompletionEvent()

Have a look at OnDemandResources.mm file in exported Xcode project. Maybe you can determine the issue there.

1 Like

On building and running the app directly on device through Xcode, with logs in OnDemandResources.mm file:

extern "C" OnDemandResourcesRequestData* UnityOnDemandResourcesCreateRequest(NSSet * tags, OnDemandResourcesRequestCompleteHandler handler, void* handlerData)
{
    OnDemandResourcesRequestData* data = new OnDemandResourcesRequestData();
    data->request = [[NSBundleResourceRequest alloc] initWithTags: tags];

    // Logging the tags and request initialization
    NSLog(@"Requested tags: %@", tags);
    NSLog(@"Resource request initialized: %@", data->request);

    [data->request beginAccessingResourcesWithCompletionHandler:^(NSError* error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            const char* errorMessage = error ? [[error localizedDescription] UTF8String] : NULL;
            if (error) {
                NSLog(@"Accessing resources failed with error: %@", error);
            } else {
                NSLog(@"Accessing resources completed successfully.");
            }
            handler(handlerData, errorMessage);
        });
    }];
    return data;

it shows the error:

Accessing resources failed with error: Error Domain=NSCocoaErrorDomain Code=4099 "Connection invalidated to streaming unzip service." UserInfo={NSLocalizedDescription=Connection invalidated to streaming unzip service.}

The file has problem in getting unziping or is it something else? Is there is restriction on what assets can be packed in asset bundle for on-demand resources?

I don’t think there i a limitation from Unity side.

ok , if we assume that iOS has some limitations, file should at least unzip , right?

Have you zipped the asset bundle?

no, just normal build pipe line , with iOS target.

this error comes on debug build through Xcode , and no such error on test flight. So I am not sure , what is the reason for this.

I cleared the Xcode derived data , and the issue of unzip-ing went away.

but while running debug on Xcode , i found these observation:

disk gauge shows that asset packs are present and shows their status.(image below)

when prompted to download, the files get downloaded. verified on device settings (app’s data&documents increases by asset pack size)

the path returned is still null, so asset bundle does not load on the game.

on stopping the game, the app’s data&documents decreases to original size(suggesting that the asset pack never got saved) . Maybe thats why there is no path of asset packs when prompted.

I don’t know how to fix this. Any help appreciated

Update :

 string path = request.GetResourcePath(assetBundleName);

This will always return empty string. Even its linker OnDemandResources.mm also return empty string.

  _loadedAssetBundle = AssetBundle.LoadFromFile("res://" + assetBundleName);

use this , after the request was completed ,and the asset loaded. You guys should mention to use this “res://” while loading asset bundle through on-demand resources in the tutorial. And if you can explain why is this required.

Also

 if (request.error != null)
                {
                    Debug.LogError($"ODR request failed: {request.error}  ::  {request.error.ToString()}");
                }

this return empty string even on successful download. Asset loads after this.

These are the bugs i have found on this implementation.
If You guys can update the documentation , it will save hours for someone.

Could you submit a bug for this?
Thanks.