Bundle.main.path pointing to odd path

I'm working on a pretty simple library and am struggling with something that's probably really simple. I'd be grateful for any insights.

I have a text file with bundled JavaScript code. I'm attempting to load that code into a String variable so I can evaluate it with the JavaScriptCore module. It's all working great when I load the file using an absolute file path. But I can't get the file to be found by the Bundle.main.path location. (Well, I can, but hang on for a second...).

If I look at Bundle.main.bundlePath in context it's pointing to "/Applications/XCode9/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Agents" (which I didn't expect). If I put the desired file in that location then Bundle.main.path(:forResource:ofType) finds it just fine. But neither an absolute path or a path outside the project directory is really optimal.

I set the original project up with 'swift package init -type=library'.

How do I either set the Bundle.main path to something relative to the project root, or specify a file path relative to the project root?

I'm sure this is a basic question so I'm grateful for any help.

Scott

Bundle.main always refers to the application that's running your code, not to the project where it's built. When you're running your code in some way other than an application, things can get weird:

  • A compiled command-line tool will give you the directory containing the executable.
  • An interpreted Swift script will give you the directory containing the Swift interpreter (!).
  • A playground will give you the directory containing the playground infrastructure (!!).

I'm guessing you're in the last case here. Fortunately, playgrounds have their own way of managing resources, which should be documented in the Xcode help.

Jordan, thanks for your pointers, very helpful. I suspect part of my problem may be I'm using the swift package command line to start my project (swift package init --type=library) and to build the Xcode project itself (swift package generate-xcodeproj). Given your explanation, it seems that the bundle location is established when you init the project.

I'm writing a Vapor backend for a JSON-heavy project and I built a JSON validator to help me debug JSON responses without going to the trouble of deploying on Heroku (kind of tough to wait 8 minutes to find out you made a trivial JSON error :)). I decided to make the project as a library so others might benefit from it.

If anyone can give me some insight on how to refer to a file without using absolute paths I'd be grateful. I have the solution working for my needs (the validator code's a part of my test target and I can live with absolute paths to find the file for testing purposes) but I am now pretty curious about how this should work.

Thanks again,

Scott

The bundle location has nothing to do with your project—it's a run-time property only. I guess in this case the main bundle is where the runner for XCTest modules lives.

But since you're structuring your code as a library, it doesn't really make sense to ask for the main bundle anyway—either in the library or in the tests. The main bundle is always the app or main executable. Unfortunately I don't know enough about the package manager myself to answer the question of "how do I find resources for tests?", and I don't think there's a good answer at all for "how do I ship resources with a library?" for the package manager today.

Are there any plans to have Bundle.Main behave more consistently? Why not always return the directory containing the Swift file in question?

If you're asking why it isn't "the bundle containing the current file", it's because there's no notion of which source file something came from at run time. The closest equivalent is Bundle.init(for: AnyClass), but Foundation could consider adding something more general.

If you're asking why it doesn't do something more useful for playgrounds and scripts, it's mostly an implementation restriction—the way it works is by looking for the main executable at the linker level. Apple has open bugs about that, but it's not something that can be changed just from Swift.