I'm trying to add support for SPM in one of your projects that has storyboards.
Currently we grab it UIStoryboard(name: String, bundle: String?)
but this doesn't seem to work with SPM, as there isn't really a bundle. Even printing all the bundles doesn't show the bundle of our package.
Any way we can support storyboards or are SPM's meant to be just files?
Attempts:
UIStoryboard(name: "GiftCards", bundle: Bundle(for: self))
UIStoryboard(name: "GiftCards", bundle: Bundle(for: type(of: self)))
UIStoryboard(name: "GiftCards", bundle: Bundle(identifier: "com.x.x"))
The Swift Package Manager is a tool for managing the distribution of Swift code. It's integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.
Swift Package Manager includes a build system that can build for macOS and Linux. Starting with Xcode 11, Xcode integrates with SwiftPM to provide support for including packages in iOS, macOS, watchOS, and tvOS applications.
Open your Xcode project, navigate the File tab within the macOS bar, and click on “Add Packages”. In the Add New Package window, you can select from recently used or Apple Swift Packages. Alternatively, you can search for a package via the name or the URL to the Github page.
As of Xcode 12.0 this sort of works, but needs a few extra steps to complete it.
BadgeKit
BadgeKit
with Package.swift
header // swift-tools-version:5.3
or higherBadgeKit.storyboard
Storyboard Reference property panel with Storyboard value BadgeKit
and Bundle identifier BadgeKit-BadgeKit-resources
.
Xcode automatically generates a bundle (and its identifier) for you to hold resources found in an SPM package using the following format: [package name]-[package target name]-resources
. In our case the package name and target name are the same (BadgeKit
).
While SPM resource bundles are always created and included in the app during the build process, they are not automatically available at runtime outside the package. If you aren't importing and using a package's target anywhere in your code, Xcode tries to optimize by not loading that package's resource bundle (it is probably an oversight on Apple's part that storyboard references alone aren't enough to trigger this). So a workaround is needed to trick Xcode into making an SPM package's bundle available if you are only using its resources in a storyboard.
AppDelegate.swift
file as a workaround:@UIApplicationMain final class AppDelegate: UIResponder {
[…]
override init() {
super.init()
// WORKAROUND: Storyboards do not trigger the loading of resource bundles in Swift Packages.
let bundleNames = ["BadgeKit_BadgeKit"]
bundleNames.forEach { (bundleName) in
guard
let bundleURL = Bundle.main.url(forResource: bundleName, withExtension: "bundle"),
let bundle = Bundle(url: bundleURL) else {
preconditionFailure()
}
bundle.load()
}
[…]
}
[…]
}
In our example, the array bundleNames
contains a single string that correspond to the expected filename of the bundle our package will create for its resources during the build process. Xcode automatically names these bundle files as follows: [package name]_[package target name].bundle
. Note that a bundle's filename is not the same as its identifier.
If you are curious about which bundles (and their corresponding identifiers) are loaded and available at runtime, you can use the following code to troubleshoot:
let bundles = Bundle.allBundles
bundles.forEach { (bundle) in
print("Bundle identifier loaded: \(bundle.bundleIdentifier)") }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With