38

In Swift, you can call

let bundle = NSBundle(forClass: self.dynamicType)

in any class and get the current bundle. If you NSBundle.mainBundle() this will fail getting correct bundle when for example running unit tests.

So how can you get the current bundle for a Swift struct?

bogen
  • 9,954
  • 9
  • 50
  • 89
  • Also searching for an answer to this.. I currently have a placeholder class definition and an `NSBundle` extension helping out so I can continue working until a proper solution is found – Max Chuquimia Aug 12 '15 at 06:55

6 Answers6

29

The best solution here depends on what you need a bundle for.

Is it to look up resources that exist only in a specific app, framework, or extension bundle that's known to be loaded when the code you're writing runs? In that case you might want to use init(identifier:) instead of dynamically looking up the bundle that defines a certain type.

Beware of "follows the type" bundle lookups. For example, if a framework class Foo uses NSBundle(forClass: self.dynamicType) to load a resource, a subclass of Foo defined by the app loading that framework will end up looking in the app bundle instead of the framework bundle.

If you do need a "follows the type" bundle lookup for a struct (or enum), one workaround that might prove helpful is to define a class as a subtype:

struct Foo {
     class Bar {}
     static var fooBundle: NSBundle { return NSBundle(forClass: Foo.Bar.self) }
}

Note there's nothing dynamic here, because nothing needs to be — every Foo comes from the same type definition (because structs can't inherit), so its static type matches its dynamic type.

(Admittedly, an NSBundle(forType:) that could handle structs, enums, and protocols might make a nice feature request. Though I imagine it could be tricky to make it handle extensions and everything...)

rickster
  • 124,678
  • 26
  • 272
  • 326
  • would "static let fooBundle = Bundle(for: Foo.Bar.self)" work too? @rickster – Kirill Apr 28 '19 at 01:38
  • 1
    Yeah, that looks workable. (My original answer predates `NSBundle` getting renamed to `Bundle` for Swift.) Feel free to test it, though. – rickster Apr 28 '19 at 01:46
20
extension Bundle {
    static var current: Bundle {
        class __ { }
        return Bundle(for: __.self)
    }
}
Valentyn Zakharenko
  • 2,710
  • 1
  • 21
  • 47
11

Updated for Swift 3.0+:

struct Foo {
     class Bar {}
     static var fooBundle: Bundle { return Bundle(for: Foo.Bar.self) }
}
NatashaTheRobot
  • 6,879
  • 4
  • 32
  • 27
4

Swift 5

struct Foo {
    class Bar {}
    static var fooBundle: Bundle { return Bundle(for: Foo.Bar.self) }
}
Oleh Kudinov
  • 2,533
  • 28
  • 30
3

For Swift Packages, we get the Bundle where the Swift struct is declared anywhere in the same module (target) via:

Bundle.module

However, we need to import a resource in the Package to get this autogenerated.

Bundle.main also works for other projects if you don't have extension targets.

Pranav Kasetti
  • 8,770
  • 2
  • 50
  • 71
  • What do you mean by import a resource in the Package to get this autogenerated? How do you get this working? – Bogdan Razvan Jun 13 '23 at 09:17
  • Resources include storyboard files, asset catalogs, Core Data .xcdatamodeld files and localised resources. For more info, see: https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package. In order to access `Bundle.module` we need to add one of these resource files to the package. – Pranav Kasetti Jun 13 '23 at 12:27
2

Swift 4+

You can do let bundle = InternalConstants.bundle if you add this struct to your project. A very elegant solution in my opinion.

internal struct InternalConstants {
    private class EmptyClass {}
    static let bundle = Bundle(for: InternalConstants.EmptyClass.self)
}

Another potential solution (less elegant):

internal struct InternalConstants {
    internal static let bundle = Bundle(identifier: "com.hello.world")!
}
Kirill
  • 738
  • 10
  • 26