Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Caching On-Demand Resources on disk

Discussion in 'iOS and tvOS' started by Swah, Aug 31, 2021.

  1. Swah

    Swah

    Joined:
    May 13, 2015
    Posts:
    80
    Hello,

    We've got an app on iOS that uses ODRs to load asset bundles. Unfortunately, 50%+ of our users have to download all assets every time they open the app (see this thread), which is critical and needs to be fixed ASAP.

    The solution would be to cache the bundles on the disk. We have been trying multiple ways to copy the bundles on disk, but are always facing the same problem: "res://" isn't a path that most methods can read. The only way to access the files is with AssetBundle.LoadFromFileAsync. Methods like System.IO.File.Copy don't see the files or folders in that path (we've tried countless variations). UnityWebRequest methods also do not work, as the URL is "malformed" ("res://", "file:///res://", nothing seems to work). Using either DownloadHandlerFile or DownloadHandlerAssetBundle in order to use Unity's Caching would be nice, but it still relies on UnityWebRequest.

    What's the recommended way to access asset bundles downloaded with ODR? Ideally a solution that limits memory usage.
     
  2. Swah

    Swah

    Joined:
    May 13, 2015
    Posts:
    80
    The suspicion is that AssetBundle.LoadFromFileAsync has some code that understands "res://" to be a special path and loads the file through a method that's just not available anywhere else. These two threads seem to support this:
    https://forum.unity.com/threads/sec...and-resources-and-app-slicing-support.353491/
    https://forum.unity.com/threads/odr-issues-with-getresourcepath.425050/
    Even code related to asset bundles, like AssetBundle.RecompressAssetBundleAsync, won't work.

    So... our 2 options seem to be:
    - add the bundles as regular files in Xcode. It's really unclear if we'll need to do additional native coding besides that, and if methods like UnityEngine.iOS.OnDemandResources.PreloadAsync will work with ODR tagged as files (not as part of an Asset Catalog)
    - just not use ODR all together

    It's pretty clear altogether that Unity's support for ODR is incomplete - we had to create native code just to check if files were already downloaded and the employee that states future work is needed for ODR hasn't posted in years. If anything the documentation for ODR should clearly state these limitations. Not knowing which bundles were already downloaded will prevent you from going through Apple's review (you need to state remaining download size), and not being able to cache downloaded files heavily limits the usefulness of this feature.
     
    Last edited: Sep 1, 2021
  3. Swah

    Swah

    Joined:
    May 13, 2015
    Posts:
    80
    Turns out the first option (to add the bundles as regular files in XCode) seems to be working for us. We can now access asset bundles as files, and were able to automate tagging bundles as part of our build process (for others, the PBXProject class has useful methods such as AddAssetTagForFile()). Methods like
    OnDemandResources.PreloadAsync can fetch these ODRs, and you can now use GetResourcePath to copy the files wherever you want.

    Navigating Unity's implementation of ODRs is really counter-intuitive and could at least be mitigated by updating the documentation. Using iOS.BuildPipeline.collectResources (the recommended usage in this documentation) has serious downsides, the most severe being that you can't access the ODRs as files and therefore can't cache them.
     
    khaled24 and ScottJustScott like this.
  4. nwtesting

    nwtesting

    Joined:
    Jan 31, 2019
    Posts:
    4
    @Swah Hi, Can you explain how you added bundles as regular files in Xcode?
     
  5. Swah

    Swah

    Joined:
    May 13, 2015
    Posts:
    80
    We do this through a post build script. The addressablesInfoSO are scriptable objects containing info about the bundles.
    Code (CSharp):
    1. /// <summary>
    2. /// Asset bundles need to be added to the xcode project and tagged as a resource in order for On-Demand Resources
    3. /// to be work on the App Store (hosting by Apple). The way that is recommended by Unity to setup projects with ODRs
    4. /// has a lot of problems, as documented here: https://forum.unity.com/threads/caching-on-demand-resources-on-disk.1164302/
    5. /// So we're adding the asset bundles ourselves through these scripts in order to bypass the problems above.
    6. /// </summary>
    7. /// <param name="xcodePath"></param>
    8. public static void AddAssetBundlesToProject(string xcodePath)
    9. {
    10.     var settings = AddressableAssetSettingsDefaultObject.Settings;
    11.     var bundlesPath = BuildUtility.FormatPath(settings.RemoteCatalogBuildPath.GetValue(settings));
    12.     var addressablesInfoSO = AssetDatabase.LoadAssetAtPath<AddressablesInfoFromBuildSO>(AddressablesInfoFromBuildSO.UNIQUE_SCRIPTABLE_OBJECT_PATH);
    13.     var bundles = new HashSet<string>(addressablesInfoSO.BuiltAssetBundles.Select(bundleInfo => bundleInfo.name));
    14.     Option<DirectoryInfo> sourceFolder = new DirectoryInfo(bundlesPath);
    15.     if (sourceFolder.TryValue(out var folderInfo) && folderInfo.Exists)
    16.     {
    17.         string projPath = Path.Combine(xcodePath, "Unity-iPhone.xcodeproj/project.pbxproj");
    18.         PBXProject proj = new PBXProject();
    19.         proj.ReadFromFile(projPath);
    20.         var targetProject = proj.GetUnityMainTargetGuid();
    21.         foreach (var file in folderInfo.GetFiles("*.bundle"))
    22.         {
    23.             var newFilePath = Path.Combine(xcodePath, file.Name);
    24.             File.Copy(file.FullName, newFilePath);
    25.             var tagName = Path.GetFileNameWithoutExtension(file.Name);
    26.             if (bundles.Remove(tagName))
    27.             {
    28.                 var addedFile = proj.AddFile(newFilePath, file.Name, PBXSourceTree.Source);
    29.                 proj.AddFileToBuild(targetProject, addedFile);
    30.                 proj.AddAssetTagForFile(targetProject, addedFile, tagName);
    31.             }
    32.             else
    33.                 Debug.LogError($"Found unregistered bundle '{tagName}'. This likely means an addressables group was not automatically added to " + AddressablesInfoFromBuildSO.UNIQUE_SCRIPTABLE_OBJECT_PATH);
    34.         }
    35.         proj.WriteToFile(projPath);
    36.     }
    37. }
     
    khaled24 likes this.
  6. nwtesting

    nwtesting

    Joined:
    Jan 31, 2019
    Posts:
    4
    @Swah Can u pls share me the unity sample project for adding a unity bundle in to xcode as files
     
  7. nwtesting

    nwtesting

    Joined:
    Jan 31, 2019
    Posts:
    4
    @Swah On adding bundles as files in xcode the bundle size adds to the app size too. I am facing this problem