Nib load failure when presenting its view controller - ios

I have this function in a class that inherits from NSObject:
open func showCustomDialogInView(vc: UIViewController) {
let bundle = Bundle(for: CustomDialogViewController.self)
let customDialog = CustomDialogViewController(nibName: "CustomDialogViewController", bundle: bundle)
customDialog.delegate = vc
customDialog.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
vc.present(customDialog, animated: true, completion: nil)
}
I had this working in one of my iOS projects before updating to Xcode 8 and Swift 3 language, but now when I run the app I get a crash when vc.present(customDialog, animated: true, completion: nil) line is reached. I get this message in log console:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle /MyApp.app> (loaded)' with name 'CustomDialogViewController''
I don't understand what it is happening, since let customDialog = CustomDialogViewController(nibName: "CustomDialogViewController", bundle: bundle) line doesn't crash and it seems that I get a CustomDialogViewController object.
Does somebody could help me with this issue? Thanks

This error can occur when you rename files outside of XCode. To solve it you can just remove the files from your project (Right Click - Delete and "Remove Reference").
Then after you can re-import the files in your project and everything will be OK.

It looks like in this case this error was because for some reason the xib file was missing the appropriate target membership.
Thanks for your answers

Related

Could not find a storyboard named in bundle NSBundle

I am creating a pod with storyboard instance and I have problem when I try to launch my app, I need someone to fix this please, I have tried everything but still I have the same problem I got this error :
app due to uncaught exception 'NSInvalidArgumentException', reason:
'Could not find a storyboard named '' in bundle NSBundle
(loaded)'
*** First throw call stack: (0x1a0d1d27c 0x19fef79f8 0x1cd7e3350 0x102ea1700 0x102eac088 0x102eac018 0x103090c74 0x103092c84
0x1cf08ee40 0x102eac1b0 0x102b1e414 0x102b1e610 0x1cd30fd40
0x1ab3a1f28 0x1ab341304 0x1cd3142ac 0x1ab3a1f28 0x1ab3a216c
0x1ab341304 0x1cd311b94 0x1cd7e39a4 0x1cd657150 0x1cd6575d0
0x1cd655c34 0x1ccf192dc 0x1ccf21874 0x1ccf18f60 0x1ccf19850
0x1ccf17b9c 0x1ccf17864 0x1ccf1c3a4 0x1ccf1d188 0x1ccf1c25c
0x1ccf20f5c 0x1cd654328 0x1cd250ba8 0x1a36989fc 0x1a36a240c
0x1a36a1c14 0x103090c74 0x103094840 0x1a36d3040 0x1a36d2cdc
0x1a36d3294 0x1a0caf018 0x1a0caef98 0x1a0cae880 0x1a0ca97bc
0x1a0ca90b0 0x1a2ea979c 0x1cd657978 0x102b20f50 0x1a076e8e0)
libc++abi.dylib: terminating with uncaught exception of type
NSException
my storyboard share methods
My code :
public static var share: ChatKit = {
let storyboard = UIStoryboard(name: "ChatKit", bundle: nil)
return storyboard.instantiateViewController(withIdentifier: "ChatKit") as! ChatKit
}()
Since you are probably in a CocoaPod the storyboard will not be in the main bundle. This is due to the fact that the storyboard is added to the CocoaPod resource bundle (probably you can see this in your .podspec). Better pass this bundle instead of nil:
Bundle(for: SomeClass.self)
where SomeClass lives in your Pod.
This question might help you as well: How to load resource in cocoapods resource_bundle
let bundle = Bundle(for: YourBundleName.self)
bundle.loadNibNamed(NibFileName, owner: self, options: nil)

Why can I reach Storyboard_a.storyboard and fail to reach Storyboard_b.storyboard in my framework?

I have a framework with two storyboards in it: StoryBoard_A.storyboard and StoryBoard_B.storyboard.
I can reach StoryBoard_A but not StoryBoard_B
I use my framework as a pod in my main project.
in my framework podspec file I have:
s.source_files = "myFramework/**/*.{swift}"
s.resource_bundles = { 'myFramework' => ['myFramework/**/*.{storyboard,xib,xcassets}'] }
I know both storyboards are in myFramework bundle because:
In myFramework Build Phases, under Copy Bundle Resources I can see them both included.
In myFramework.framework I can see: StoryBoard_A.storyboardc and StoryBoard_B.storyboardc
When I 'pod install' myFramework as a development pod I can see both storyboards in the Project navigator of the main project
In myFramework, from ViewController_1 I initiate ViewController_a from StoryBoard_A.storyboard and ViewController_b from StoryBoard_B.storyboard.
I use the same technique:
let podBundle = Bundle(for: ViewController_1.self)
let bundleURL = podBundle.url(forResource: "myFramework", withExtension: "bundle")
let bundle = Bundle(url: bundleURL!)!
let storyBoard = UIStoryboard(name: "StoryBoard_A", bundle: bundle)
let viewController_a = storyBoard.instantiateViewController(withIdentifier: "ViewController_a_id") as? ViewController_a
but when I do:
let storyBoard = UIStoryboard(name: "StoryBoard_B", bundle: bundle)
let viewController_b = storyBoard.instantiateViewController(withIdentifier: "ViewController_b_id") as? ViewController_b
the app crashes in the second line with the following error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Could not find a storyboard named 'StoryBoard_B' in bundle...
What am I missing?
Thanks
Right click your app under Products in Xcode and explore it in Finder. Just check whether StoryBoard_B.storyboardc files are present.If files are not present, you have a different problem to solve.
If files are present, try accessing it like shown below.
let podBundle = Bundle(forClass: ViewController_1.self)
let bundleURL = podBundle.resourceURL?.URLByAppendingPathComponent("myFramework.bundle")
let resourceBundle = Bundle(URL: bundleURL!)
I found the problem:
Since this was a piece of code that I inherited and I tried to merge it into myFramework I wasn't aware of the fact that inside ViewController_b.swift (the one that I was trying to initiate from the storyboard) there was:
var storyboard = UIStoryboard(name: "Stroyboard_B", bundle: nil)
in the class scope.
I changed this to the bundle I created the same way as described above and that solved the problem.
I found this after I successfully tried to initiate other ViewControllers from this storyboard. That made me look inside ViewController_b to see how it defers than other view controllers

Using nibs from cocoapods in swift 4

I have made a framework that contains a function that displays a login view according to a xib that is contained in that framework. Both the xib file and the swift file for the view are called AuthenticationViewController
However when I try to use this function in another project that uses this as a pod, it fails with "Could not load NIB in bundle...(not yet loaded)' with name 'AuthenticationViewController'"
The view is being shown by the following code that is located in my pod/framework:
func authenticate(viewController: UIViewController){
let bundle = Bundle(for:AuthenticationViewController.self)
let newViewController = AuthenticationViewController(nibName:"AuthenticationViewController" , bundle: bundle)
viewController.present(newViewController, animated: true, completion: nil)
}
What is the issue here? Is there supposed to be a separate bundle for my pod, because I only get one bundle when calling:
Bundle.allBundles
My .podspec file contains the following section:
s.resource_bundles = {
"MyPodName" => ["MyPodName/*.xib"]
}
but I have tried to load the bundle using:
Bundle(identifier:"MyPodName")
and that does not work either.
How are you supposed to use nibs from pods?
The problem was that I was using:
s.resource_bundles = {
"MyPodName" => ["MyPodName/*.xib"]
}
This does not seem to work with the approach i had done. So I had to change podspec file to:
s.resources = ["MyPodName/*.xib"]
That made everything work
I seem that the bundle is not the correct one. I use this function in my pod library in order to get the bundle
- (NSBundle *)getBundle {
NSBundle *podBundle = [NSBundle bundleForClass:self.classForCoder];
NSURL *podBundleURL = [podBundle URLForResource:#"MyPodName" withExtension:#"bundle"];
NSBundle *bundle = [[NSBundle alloc] initWithURL:podBundleURL];
return bundle;
}
Once you have the bundle you can load the view. Also, run a pod install after you edit the .podspec and add a new file to reorganize the files.

'UIViewController' to 'WWCalendarTimeSelector.VNClockViewController'

I get this error when trying to instantiate a viewcontroller from a storyboard.
I added a pod into my test project WWCalendarTimeSelector
and edited the pod. I added new files VNClockViewController.swift and VNClockViewController.storyboard.
In my VNClockViewController.swift, I have this:
open static func instantiate() -> VNClockViewController {
let podBundle = Bundle(for: VNClockViewController.self)
let bundleURL = podBundle.url(forResource: "WWCalendarTimeSelectorStoryboardBundle", withExtension: "bundle")
var bundle: Bundle?
if let bundleURL = bundleURL {
bundle = Bundle(url: bundleURL)
}
return UIStoryboard(name: "VNClockViewController", bundle: bundle).instantiateInitialViewController() as! VNClockViewController //This line causes the error
}
but when I try to instantiate this Viewcontroller in my Project, I get the error
Could not cast value of type 'UIViewController' (0x10326cca8) to 'WWCalendarTimeSelector.VNClockViewController' (0x1008122d0).`
Please tell me if you need to see more code.
EDIT:
I'm using my fork of the Pods from github.
Please use this pod in a sample project to see the error.
pod 'WWCalendarTimeSelector', :git => 'https://github.com/binsnoel/WWCalendarTimeSelector.git'
Try to instantiate VNClockViewController.instantiate() and see the error.
I had faced same issue with same error, It's happening because of bug in Library
Steps to solve the issue
1) First goto Pod -> WWCalendarTimeSelector -> Resource folder
2) Select WWCalendarTimeSelector.storyboard and check in View hierarchy and Select Clock View as in image.
3) Now, got to Identity Inspector and You see Custom class have missing Module. So set WWCalendarTimeSelector as like below image
4) Done, Check and run.

Loading a resource (e.g. storyboard) in a Swift framework

I'm trying to make a framework that will bundle a storyboard. I have checked, and the created .framework includes my .storyboard file (as a .storyboardc file), and have gotten the framework to load the storyboard at runtime. However, this being a framework, I'd like for the code to be as versatile as possible, and I feel like my current solution is a little hacky. Currently, I'm loading the storyboard using the following code:
let mainBundlePath: String = NSBundle.mainBundle().resourcePath
let frameworkBundlePath = mainBundlePath.stringByAppendingPathComponent("Frameworks/AuthenticationManager.framework")
let frameworkBundle = NSBundle(path: frameworkBundlePath)
let storyboard = UIStoryboard(name: "Storyboard", bundle: frameworkBundle)
A couple of things I've noticed that could be gotchas:
The path of the framework (Frameworks/) could, potentially, change in the future, and should not relied upon?
The name of the framework could change, but without first getting the NSBundle of the framework there's no way of getting the product name?
Frameworks aren't really bundles (?), so loading them as a bundle could have unforeseen consequences in the future?
There may be other issues, solutions to the above issues, or the above issues may not be issues after all, but I've not been able to think of them.
This question may be be more suited for Code Review, but I felt it'd fit in well here, too. If it needs to be moved over there, feel free to do that or tell me to do it.
A framework (created from the "Cocoa Touch Framework" template) has a bundle identifier,
which is stored in the Info.plist of the generated framework bundle.
The bundle identifier is configured in the general target settings.
By default, it is <organization prefix>.<framework name>.
You can locate the framework using this identifier, for example:
let frameworkBundle = NSBundle(identifier: "com.duffy.AuthenticationManager")
let storyboard = UIStoryboard(name: "Storyboard", bundle: frameworkBundle)
Here's a cleaner solution (no framework identifiers involved):
let storyboardName = "StoryboardName"
let storyboardBundle = NSBundle(forClass: self)
let storyboard = UIStoryboard(name: storyboardName, bundle: storyboardBundle)
If this code is not inside of a static method, use YourClass.self instead of self.
Example for Swift 3:
let storyboardBundle = Bundle(for: NumberPadViewController.self)
super.init(nibName: "NumberPadViewController", bundle: storyboardBundle)
For Swift 3/4, it's now simply Bundle :
let bundle = Bundle(identifier: "com.myframework.id")
let storyboard = UIStoryboard(name: "FrameworkMain", bundle: bundle)
This is a much better way to load the bundle in Swift 3 without the need of passing a string that is much more likely to fail
let myBundle = Bundle(for: MyViewController.self)
let myStoryboard = UIStoryboard(name: "StoryboardName", bundle: myBundle)

Resources