Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Bug iOS : On Demand Resource

Discussion in 'iOS and tvOS' started by Xarpie, May 15, 2024.

  1. Xarpie

    Xarpie

    Joined:
    Jul 25, 2017
    Posts:
    9
    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:


    Code (CSharp):
    1. private void LoadAssetBundleFromOnDemandResources(string assetBundleName, string lobbyPrefabName)
    2. {
    3.     UnloadCurrentAssets();
    4.     Debug.Log($"PAD/ODR :: Loading prefab {lobbyPrefabName} from AssetBundle :: {assetBundleName}");
    5.     string[] tags = new string[] { assetBundleName };
    6.     var request = OnDemandResources.PreloadAsync(tags);
    7.     StartCoroutine(DisplayDownloadProgressIOS(request));
    8.     request.completed += (asyncOperation) =>
    9.     {
    10.         if (asyncOperation.isDone)
    11.         {
    12.             if (request.error != null)
    13.             {
    14.                 Debug.LogError($"ODR request failed: {request.error}  ::  {request.error.ToString()}");
    15.             }
    16. /////
    17. rest of code
    18. /////
    19.         }
    20.     };
    21. }
    22.  
    Console Output:
    Here is the console output captured when the issue occurs:
    Xcode Log:
    Code (JavaScript):
    1. appstored : Calling the completion handler. Result [{
    2.   "XXXX.asset-pack-20ba15b290e015492ae08bb6ed48af61" =   {
    3.    "_NSODRURLKey" = "file:///var/mobile/Library/OnDemandResources/AssetPacks/540D121A-D2F9-4045-BEEF-99A1F6481095/12674363955244035182/XXXX.asset-pack-20ba15b290e015492ae08bb6ed48af61.assetpack/";
    4.   };
    5. }] Error [(null)]
    6.  
    7. [ODRA1AE3366/XXXX.asset-pack-934d1e3206917af72df7146445f79667/1F3556C0] Finished asset promise
    8.  
    9. ODR request failed:  ::
    10. <>c__DisplayClass15_0:<LoadAssetBundleFromOnDemandResources>b__0(AsyncOperation)
    11. UnityEngine.AsyncOperation:InvokeCompletionEvent()
    12.  
    13.  
    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!
     
  2. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,767
    Try:
    if (string.IsNullOrEmpty(request.error))

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

    Xarpie

    Joined:
    Jul 25, 2017
    Posts:
    9

    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 :

    Code (CSharp):
    1.  private void LoadAssetBundleFromOnDemandResources(string assetBundleName, string lobbyPrefabName)
    2.     {
    3.         UnloadCurrentAssets();
    4.         Debug.Log($"PAD/ODR :: Loading prefab {lobbyPrefabName} from AssetBundle :: {assetBundleName}");
    5.         string[] tags = new string[] { assetBundleName };
    6.         var request = OnDemandResources.PreloadAsync(tags);
    7.         StartCoroutine(DisplayDownloadProgressIOS(request));
    8.         // _request = request;
    9.         request.completed += (asyncOperation) =>
    10.         {
    11.             if (asyncOperation.isDone)
    12.             {
    13.                 if (request.error != null)
    14.                 {
    15.                     Debug.LogError($"ODR request failed: {request.error}  ::  {request.error.ToString()}");
    16.                 }
    17.  
    18.                 string path = request.GetResourcePath(assetBundleName);
    19.                 Debug.Log($"Checking path before loading: {path}");
    20.  
    21.                 if (File.Exists(path))
    22.                 {
    23.                     _loadedAssetBundle = AssetBundle.LoadFromFile(path);
    24.                     Debug.Log($"Loaded AssetBundle from path: {path}");
    25.  
    26.                     if (_loadedAssetBundle != null)
    27.                     {
    28.                         _lobbyPrefab = Instantiate(_loadedAssetBundle.LoadAsset<GameObject>(lobbyPrefabName), transform);
    29.                         _lobbyPrefabName = lobbyPrefabName;
    30.                     }
    31.                     else
    32.                     {
    33.                         Debug.LogError($"PAD/ODR :: Failed to load AssetBundle {assetBundleName}. AssetBundle is null.");
    34.                     }
    35.                 }
    36.                 else
    37.                 {
    38.                     Debug.LogError($"AssetBundle file does not exist at path: {path}");
    39.                 }
    40.             }
    41.             else
    42.             {
    43.                 Debug.LogError($"PAD/ODR :: Failed to preload On-Demand Resources for {assetBundleName}. Error: {request.error}  ::  {request.error.ToString()}");
    44.             }
    45.         };
    46.     }
    47.  
    48.  

    Console Log :

    Code (JavaScript):
    1. Checking path before loading:
    2. <>c__DisplayClass15_0:<LoadAssetBundleFromOnDemandResources>b__0(AsyncOperation)
    3. UnityEngine.AsyncOperation:InvokeCompletionEvent()
    4.  
    5. AssetBundle file does not exist at path:
    6. UnityEngine.AsyncOperation:InvokeCompletionEvent()
    7.  
     
  4. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,767
    Have a look at OnDemandResources.mm file in exported Xcode project. Maybe you can determine the issue there.
     
    Xarpie likes this.
  5. Xarpie

    Xarpie

    Joined:
    Jul 25, 2017
    Posts:
    9
    On building and running the app directly on device through Xcode, with logs in OnDemandResources.mm file:

    Code (JavaScript):
    1. extern "C" OnDemandResourcesRequestData* UnityOnDemandResourcesCreateRequest(NSSet * tags, OnDemandResourcesRequestCompleteHandler handler, void* handlerData)
    2. {
    3.     OnDemandResourcesRequestData* data = new OnDemandResourcesRequestData();
    4.     data->request = [[NSBundleResourceRequest alloc] initWithTags: tags];
    5.  
    6.     // Logging the tags and request initialization
    7.     NSLog(@"Requested tags: %@", tags);
    8.     NSLog(@"Resource request initialized: %@", data->request);
    9.  
    10.     [data->request beginAccessingResourcesWithCompletionHandler:^(NSError* error) {
    11.         dispatch_async(dispatch_get_main_queue(), ^{
    12.             const char* errorMessage = error ? [[error localizedDescription] UTF8String] : NULL;
    13.             if (error) {
    14.                 NSLog(@"Accessing resources failed with error: %@", error);
    15.             } else {
    16.                 NSLog(@"Accessing resources completed successfully.");
    17.             }
    18.             handler(handlerData, errorMessage);
    19.         });
    20.     }];
    21.     return data;

    it shows the error:
    Code (JavaScript):
    1. 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?
     
  6. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,767
    I don't think there i a limitation from Unity side.
     
  7. Xarpie

    Xarpie

    Joined:
    Jul 25, 2017
    Posts:
    9
    ok , if we assume that iOS has some limitations, file should at least unzip , right?
     
  8. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,767
    Have you zipped the asset bundle?
     
  9. Xarpie

    Xarpie

    Joined:
    Jul 25, 2017
    Posts:
    9
    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.
     
  10. Xarpie

    Xarpie

    Joined:
    Jul 25, 2017
    Posts:
    9
    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
     

    Attached Files:

  11. Xarpie

    Xarpie

    Joined:
    Jul 25, 2017
    Posts:
    9

    Update :

    Code (CSharp):
    1.  string path = request.GetResourcePath(assetBundleName);
    This will always return empty string. Even its linker OnDemandResources.mm also return empty string.


    Code (CSharp):
    1.   _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


    Code (CSharp):
    1.  if (request.error != null)
    2.                 {
    3.                     Debug.LogError($"ODR request failed: {request.error}  ::  {request.error.ToString()}");
    4.                 }
    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.
     
  12. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,767
    Could you submit a bug for this?
    Thanks.