Bundle.main.path forResource will return nil in a MacOS screen saver

May 3, 2020, 5:10 am

I thought it might be worth writing a quick post on something that had me stuck building a screen saver for MacOS in Swift.

If you have a file you include in your build target (like a SQLite database of map tiles) and need to open that file you might try something like this:

let stringPath: String? = Bundle.main.path(forResource: "satellite-lowres-v1.2-z0-z5", ofType: "mbtiles")

Because I am debugging my application in a regular MacOS app (that just shows my ScreenSaverView) this code worked fine when I was debugging. As soon as I built and deployed the actual screen saver bundle everything broke.

So at that point you might spend quite a bit of time trying to debug entirely unrelated tile loading code before realising that bundle.main.path forResource was returning nil, but only in the screen saver application.

The reasons for this is that MacOS screen savers are bundles loaded by the system screen saver application, so the path you get will be something like:

/Applications/System Preferences.app/Contents/Resources

So to get this to work (in both the test app and the screen saver app) you need to get the bundle via a class in that bundle:

let stringPath: String? = Bundle(for: type(of: self)).path(forResource: "satellite-lowres-v1.2-z0-z5", ofType: "mbtiles")

I am just using the ScreenSaverView class which works fine.

This all took a little longer than it should have to find because, this being my first MacOS app, I wasn't sure where debug logging was ending up. Turns out it all appears in the Mac Console application ("Console" in Spotlight). I also struggled a bit because the Console app, or something in the MacOS logging framework was making decisions about the sensitivity of variables I was logging. So logging something like this:

os_log("Location was ---> %s", stringPath ?? "We got nil for our stringPath")

Ends up in the logs like this "Location was ---> " which isn't very helpful. To tell the logging framework that you want to see those variables you can supply it to the format string:

os_log("Location was ---> %{public}s", stringPath ?? "We got nil for our stringPath")

Permalink - Tags: Development, Swift