Does UnityWebRequest cache and how to test no network connection?

I’ve noticed a really strange behaviour when using UnityWebRequest.Get(url). I’m trying to simulate network loss, so I’m disabling my ethernet device via the OS and then exercise my web request code in Unity.

The following works as expected:

  • Must ensure I have internet to start Unity (pretty annoying, but well)
  • Disable ethernet adapter (verify that no browser or other applications are able to access the internet)
  • Enter play mode and run my script with UnityWebRequest to try download a json file from a server
  • The request returns an error and is unable to load data → that’s good!

But then it’s strange, when I re-enable the internet connection, everything loads without errors correctly, but after that, if I again disable the connection, it looks as if UnityWebRequest still returns correct data until I restart the editor.

Is this some sort of caching being used? How do I best test this situation during development?

So I think I found my own answer right after debugging my code some more:

It’s not the UnityWebRequest that was caching data, but my own ScriptableObject instance by accident. My code was something like this:

public class DataObject : UnityEngine.ScriptableObject
{
    public string Text { get; set; }

    public void Load()
    {
        // Load Text property from server here.
    }
}

One might assume that this works alright, since properties are not serialized in the editor by default, and so I can use them to store runtime-only data. I also like putting methods into ScriptableObjects, because then I can swap them out easily in the inspector. However, in this specific case, that’s an issue, because once I load the data at runtime and store the json string in my Text property, it will survive exit and re-enter play mode since the ScriptableObject is an asset in the project.

Event though I’ve been using ScriptableObject assets for years now as settings objects that can be tweaked at runtime, I run into this problem of data that accidentally survives play mode from time to time.

I think there are multiple solutions to my issue:

  • Remove the Text property from the ScriptableObject and put it on a MonoBehavior in the scene or a regular class that gets garbage collected.
  • Mark the property with [field: System.NonSerialized] and reset the values in OnEnable or during the call to Load.
  • Ensure that the property is always overwritten or network errors are handled in a way so that outdated text is never read.

I selected option #2 above, decorating it with NonSerialized and it works perfectly for your use case.

I do this exact thing in my Datasacks module, which uses ScriptableObjects to store runtime data, and it was persisting across runs in the editor.

HOWEVER: beware that OnEnable() and OnDisable() do not work the way you expect with ScriptableObjects!!!

They certainly implement those callbacks, but at my last testing, it appears that the lifecycle of a ScriptableObject is that it is created only once, and then never destroyed until you exit the editor.

And they are “created once” the first time they are “looked at.” This could be from loading a scene that contains a Component that references the SO, or simply from you selecting the SO in the editor. Neither of those times are when you want to get an OnEnable(), and after that you will NEVER get another OnEnable().

If you’re curious about the usage in Datasacks, the project is presently hosted at these locations:

https://bitbucket.org/kurtdekker/datasacks

2 Likes