I have developed a framework that consists of some colors in the asset catalogue. I packaged it in the cocoapod and integrated in another project where I try to access the assets from the framework. Everything works correctly as long as I'm in the SwiftUI domain, if I try to do exactly the same thing in UIKit, suddenly the images are no longer available (or at least this is what the UIImage constructor claims).
func testAssetSDKBundleAccess() {
let bundle = Assets.bundle
XCTAssertNotNil(bundle, "The Assets.bundle should not be nil")
XCTAssertNotNil(Image("Switzerland", bundle: bundle))
XCTAssertNotNil(UIImage(named: "Switzerland", in: bundle, compatibleWith: nil)) <- XCTAssertNotNil failed
}
When I run the same test case in my asset framework - it passes fine. Looks like something UIKit specific, but it got me puzzled for a couple of days now and I can't figure it out.
Xcode 13.2.1, Swift version 5
This turned out to be a problem connected with cocoapods. My assets framework exposes it's bundle with Assets.bundle and inside there you have to check what is available to you - when building from source you access the bundle in a different way than when using xcframework. So, our bundle access code tried one way, when it failed it tried the other. We changed the order and suddenly it started working - I don't understand why it works now but I take it. The correct order below:
public static let bundle: Bundle = {
let bundleName = "MyFramework"
/* Bundle should be present here when linked as pod dependency. */
let podBundle = Bundle(for: MyFramework.self)
if let path = podBundle.path(forResource: bundleName, ofType: "bundle"), let bundle = Bundle(path: path) {
return bundle
}
/* Bundle should be present here when running local tests or linked as xcframework. */
if let bundle = Bundle(identifier: "com.app.example.\(bundleName)") {
return bundle
}
fatalError("Unable to load bundle named \(bundleName)")
}()
Related
I try to instatiat a ViewController vom a local SPM Package but I always get the error "Could not find a storyboard".
let myViewController = UIStoryboard(
name: "MyStoryboard",
bundle: Bundle(
url: Bundle.main.url(
forResource: "myModule_myModule",
withExtension: "bundle"
)!
)
).instantiateViewController(withIdentifier: "MyViewController")
This code works when using it as an remote package, but not when dragging it to Xcode and use it local.
Thanks Bernhard
Unlike Remote Packages, When adding a Local one Xcode doesn't prompt you to add it to a target. Hence you need to add it manually in your Target's General settings, under Frameworks, Libraries, and Embedded Content
I have my Cocoapod library, In this library I access my images this way:
let bundle = Bundle(identifier: "com.mylib.mylib")
let image = UIImage(named: "image_name", in: bundle, compatibleWith: nil)
This works perfectly on devices 14.0 and above, but in IOS 12.4 this always returns the nil.
I tried other way to get bundle like this: let bundle = Bundle(for: type(of: self))
result is same.
Interesting is that when I import library in project, from library project (as source code), it works without problem (on 12.4).
but when I try to import library from pod, it returns nil (only on 12.4). Also images from main bundle is not problem, it can display main bundle icons in my library.
I'm out of ideas of what can be the case..
I have created a pod using the raywenderlich tutroial link, everything works fine
creating
installing and
importing the pod too
but when I am trying to access the view controller its says
use of unresolved identifier 'file name'
I tried cmd+click to jump to defination of the file and it navigating me to the file also
code implemented
let bundle = Bundle(for: SDKSplashVC.self) // error here - Use of unresolved identifier 'SDKSplashVC'
let storyboard = UIStoryboard(name: "SDKSplash", bundle: myBundle)
let controller = storyboard.instantiateViewController(withIdentifier: "SDKSplashVC")
self.present(controller, animated: true, completion: nil)
I have imported the pod, crossed check in managed scheme and build phases, my custom pod is listed their but still error persist
Any help would be really helpful.
Thank you
In the tutorial author is using class PickFlavorViewController which is defined in RWPickFlavor cocoa pod.
let bundle = Bundle(for: PickFlavorViewController.self)
You can take a look and see all the files in this pod here.
Docs say that you can use the init(for:) initializer of Bundle to locate the framework bundle based on a class defined in that framework.
You are getting the error because SDKSplashVC is not defined. You need to write something like this:
import UIKit
public class SDKSplashVC: UIViewController {}
Make sure the class is defined as public.
I have a working iOS project with Storyboards. Then I added a playground file to it with following content:
The
import UIKit
import PlaygroundSupport
import SpriteKit
var str = "Hello, playground"
let gameViewController = GameViewController.loadFromStoryboard() // <<<<- Fails
PlaygroundPage.current.liveView = gameViewController
I am getting following error at line, where I loadFromStoryboard():
NSInvalidArgumentException', reason: 'Could not find a storyboard named 'Main' in bundle NSBundle
What should I fix?
There are tonnes of questions on stackoverflow with the above title/contents however, each has a very specific scenario. I have gone through many of them over past few days and I think that my scenario is distinct due to use of Playground hence I am posting again.
If this is in an existing project, you can instantiate the storyboard by using your full bundle identifier, as demonstrated here:
let myBundle = Bundle(identifier: "com.asad.MyProgram")
let storyboard = UIStoryboard(name: "MyStoryboard", bundle: myBundle)
Please note that this does require that the playground is in the same workspace as the project with that particular bundle identifier.
In a some popular open source swift project. I noticed following approach used to load a file from main bundle.
#objc class TestClass: NSObject { }
let bundle = NSBundle(forClass: TestClass.self)
let path = bundle.pathForResource(filename, ofType: "json")
We can also use this approach.
let path = NSBundle.mainBundle().pathForResource(filename, ofType: "json")
Why would someone choose first approach over second one?
This returns the bundle that contains the TestClass class:
NSBundle(forClass: TestClass.self)
While this returns the main bundle of the application:
NSBundle.mainBundle()
If you execute this code from your application code, it will always return your main bundle. But if that class is contained in a different library or framework, it will return the bundle that contains it.
For example, all Swift libraries in CocoaPods are integrated using dynamic frameworks, and they are deployed in a different bundle inside the main bundle. So all the frameworks must use the embedded bundle to access their resources.
I'd recommend using the first approach (NSBundle(forClass:) method) to improve code portability. And it's required when creating dynamic frameworks.