Accessing Unity StreamingAssets in Universal Windows Apps

When developing applications that utilize data files, there are a few things to consider with file access – especially when building for a platform such as the Universal Windows platform (UWP). Before we dive into the specifics of how to do this with UWP apps (read: HoloLens), here are three important points to remember:

  • Universal Windows applications are not built using all of the same Win32 APIs. While many of them have been made available with Windows 10, there are several that aren’t usable to UWP apps, which means that you may need to switch up some strategies if you’re reliant on older APIs.
  • File paths on devices may or may not be the same, so it’s important to get it right when you’re programming it in (read: don’t just hardcode a reference!)
  • You (meaning your code) needs to have proper permissions to access files. I’m specifically going to focus on reading file data in this post, but this is also incredibly important if your application relies on writing data to a file, too.

For context, I’m in the process of building a voter turnout visualization application, an interactive holographic app for Microsoft HoloLens that shows historic data about voter turnout in the United States during the presidential elections over the past 36 years. As part of the application, I chose to make the app offline, and had a number of JSON-formatted text files that contained all of the information that I would be rendering.

   "Year": 1980,
   "ICPSR State Code": 40,
   "Alphanumeric State Code": 47,
   "Geography": "Virginia",
   "VEP Highest Office": "48.7%",
   "Total Ballots Counted": "",
   "Voting-Eligible Population (VEP)": "3,830,887"

Sample data from the state of Virginia for the year 1980

Each file is saved by year – and I have the data files from every election, a total of 18 files that break down the voter eligible population (VEP) and the turnout for the highest office. To simplify the first pass, I decided to initially test with presidential election years only, resulting in 9 files.

The first important part about being able to access these files from within a Universal Windows application was making sure that I was saving them in a location that would be accessible to the device, regardless of where I ran the app. I had two options: I could store the files or the data from them in Azure, and create an API to call them down at runtime, or I could store them in a folder that would be included in my project when I built it. Because I was working with a finite data set (and a relatively small one, at that) I went with the second option, and took advantage of the Unity StreamingAssets capability.

The Unity StreamingAssets allows developers to specify a strict file location for their application that is preserved at build time – while different platforms handle this differently, for a UWP app, what this means is that we can store files in our build in a way such that we know that they will be saved under a particular path. Setting up StreamingAssets is easy:

  1. Create a folder named “StreamingAssets” in your /Assets/ directory in Unity – this is case sensitive, so make sure it’s saved exactly as shown
  2. Add in your data files that you will be accessing from your code in your StreamingAssets folder

For UWP, this is now done! Pretty straightforward, right? If you build your application to the Universal Windows platform and open the solution in Visual Studio, you will see a StreamingAssets directory saved in the Data file.

The next thing to consider is how to access the file from within your C# code. In my application, I wanted to load this information in when I first ran the app, to set up our map object and get the data in a usable state. This means we need to do two things: form our full path, and read in the data as a byte stream.

Formatting the Path

While it would certainly be nice to just refer to a relative path for our data, this changes from file system to file system and from one user to the next, so we need to form our path for our application. We can do this with the following function:

string GetFilePath(string fileName)
        return Path.Combine(Application.streamingAssetsPath, fileName);

You’ll want to make sure that your file name string includes the file format – in my case, it was .JSON – but we can let Path.Combine handle the rest. This function will make sure that regardless of where our build sits on the file system, it creates the proper location for our data files given a file name and the StreamingAssets path.

Streaming in the Data

This part, I’ll admit, always trips me just just a little bit any time I sit down to read in a byte stream from a file. This project was no different – I had actually finished a good portion of the data formatting and Unity visualization before I was able to get the streaming working, but it turns out that I missed a pretty easy solution.

I mentioned in the introduction that not all Win32 APIs ship as part of the Universal Windows Platform – and turns out System.IO.FileStream, the class I was using within the Unity Editor – was not one of them. After playing around with the StorageFile API, which is the UWP solution for reading in file data, I made the exciting discovery that Unity’s built in UnityEngine.Windows class contained a file reader that didn’t involve async methods.

Win32 file streaming code:

        string fileName = Application.streamingAssetsPath +"\\" + YOURFILENAME;
        byte[] result;
        using (FileStream SourceStream = System.IO.File.Open(fileName, FileMode.Open))
            result = new byte[SourceStream.Length];
            SourceStream.Read(result, 0, (int)SourceStream.Length);
            return System.Text.Encoding.ASCII.GetString(result);

UnityEngine.Windows file streaming code:

        string fileData = "";
        string fileName = Path.Combine(Application.streamingAssetsPath, YOURFILENAME);
        byte[] bytes = UnityEngine.Windows.File.ReadAllBytes(fileName);
        fileData = System.Text.Encoding.ASCII.GetString(bytes);
        return fileData;

If you’re looking to read in information from a file on a UWP or HoloLens app, I highly recommend using the UnityEngine.Windows solution if possible.

When the solution was finally built, I was really excited to see that all of my state voting data was loaded in correctly, regardless of whether I was running it in the editor, on my local machine, or right on the HoloLens. I called each of the different functions above dependent on platform using #if WINDOWS_UWP, but I wouldn’t be surprised if the UnityEngine.Windows solution worked regardless of architecture. As someone who is still working on learning the ins and outs of multithreading and asynchronous programming, the UnityEngine.Windows solution saved a significant amount of back and forth with the Visual Studio output solution.

Hack on!

Related Posts

Leave a Reply

Your email address will not be published.