Loading from mainBundle - ios

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.

Related

Can we access the plist inside a framework target from the application?

I have embedded a framework inside a project. I would like to keep certain congifuration specific to the framework target inside a plist withing the framework target.
Is there any way I can access the plist when running the application ? Bundle.main always return the current executable.
I would like to access the specific target and get the plist contents.
You have to access for resources via Bundle. In your Framework define something like this:
private class BundleClass {}
extension Bundle {
public static var myFramework: Bundle {
Bundle(for: BundleClass.self)
}
}
and access your resources like this:
let filePlistURL = Bundle.myFramework.url(forResource: "file", withExtension: "plist")

Image from bundle loads ok in SwiftUI but not in UIKit

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)")
}()

UIImage(named: ) returns nil in cocoapod library on ios 12.4-5

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..

Not able to access pod swift file, after installing pod

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.

Access Bundle Identifier of App in Custom Framework

I am designing a sdk where I am making some url call at some point of time. For this I want the context of the app which is making a call. As a context I want to pass the bundle identifier of the app using that sdk.
SDK is in form of library which some third party app will import and use. I have the reference of UIViewController which is invoking the url call in third party app. I have no idea if having only UIViewController reference is enough or not.
So how can I get access to bundle identifier of their app?
Thanks
It's quite easy.
Each type (class or struct) knows which bundle it is associated with.
Step 1# Get the type of any object inside the framework.
Step 2# Get the bundle of that type. And access its id.
// VC is ref to the class object of the main app
// # 1 -> This will give the class type of the object
let objectType = type(of: vc)
// # 2 -> This will get you the bundle of the main app
let bundle = Bundle(for: objectType.self)
// This will finally give you the bundle id
let bundleId = bundle.bundleIdentifier

Resources