'UIViewController' to 'WWCalendarTimeSelector.VNClockViewController' - ios

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.

Related

iOS: How to add Assets folder inside a Framework and how to access them in code?

I create an iOS app and added a framework to it. The generated framework doesn't have an assets folder like the generate Single View App. So I made an Assets folder inside the framework folder and drag and drop it to xcode, choose the target as my framework.
I tried using the asset but the asset doesn't show up. Can show one show me how to correctly do this? is it possible to create an assets folder inside a framework?
I am very new to iOS so any guidance would be much appreciated. Thanks in advance.
Add Asset catalog to framework target as usual via New File... > Resources, but to get resource from such asset it needs to specify bundle explicitly, as in below example...
Assuming that ImageProvider is in framework target, the code could be
public class ImageProvider {
// convenient for specific image
public static func picture() -> UIImage {
return UIImage(named: "picture", in: Bundle(for: self), with: nil) ?? UIImage()
}
// for any image located in bundle where this class has built
public static func image(named: String) -> UIImage? {
return UIImage(named: named, in: Bundle(for: self), with: nil)
}
}
of course you can name such class anyhow.
Here's how I do it.
First of all, here's how you can create a Bundle to hold your assets like images.
First, create a new target:
Navigate to main Xcode menu, File => New => Target. Choose the "macOS tab" then
from "Framework & Library" select "Bundle".
Give it your desired name and hit Finish. You should see the bundle in your project folder.
Second, Configuration changes in build settings of Bundle:
Go to Build Settings on your bundle target and change the Base SDK to be iOS.
Third, Add images:
Add your images to the Bundle directly, no need to add an assets folder. Just drag and drop.
Fourth, build the bundle:
Choose your bundle as a destination and choose the generic iOS device and hit Command + B
Fifth, the .bundle will appear in your products folder under your project folder. Right-click on it and view it in Finder and then drag and drop it inside of your main project folder.
Finally, here's how I'd access the assets inside of your bundle.
// Empty UIImage array to store the images in it.
var images = [UIImage]()
let fileManager = FileManager.default
let bundleURL = Bundle.main.bundleURL
let assetURL = bundleURL.appendingPathComponent("MyBundle.bundle") // Bundle URL
do {
let contents = try fileManager.contentsOfDirectory(at: assetURL,
includingPropertiesForKeys: [URLResourceKey.nameKey, URLResourceKey.isDirectoryKey],
options: .skipsHiddenFiles)
for item in contents { // item is the URL of everything in MyBundle imgs or otherwise.
let image = UIImage(contentsOfFile: item.path) // Initializing an image
images.append(image!) // Adding the image to the icons array
}
}
catch let error as NSError {
print(error)
}
You will have the .plist file inside of your bundle, therefore, I suggest you handle this by a simple condition to check if the file name is Info.plist don't create an image out of it.
Here's how I handled it in a very trivial way.
let fileManager = FileManager.default
let bundleURL = Bundle.main.bundleURL
let assetURL = bundleURL.appendingPathComponent("Glyphs.bundle")
do {
let contents = try fileManager.contentsOfDirectory(at: assetURL, includingPropertiesForKeys: [URLResourceKey.nameKey, URLResourceKey.isDirectoryKey], options: .skipsHiddenFiles)
for item in contents {
let imageName = item.lastPathComponent
if imageName != "Info.plist" {
let image = UIImage(contentsOfFile: item.path)
icons.append(image!)
}
}
}
catch {
//print(error)
showAlert(withTitle: "Error", message: "Can't get the icons.")
}
I had the same problem.
scenario:
our framework will be consumed iOS/OSX
a lot of PNGs inside Framework
we want pngs in iOSApp && MacOs
let's assume out framework project and target is "MyCustomFramework.framework" (usual yellow icon..) already in our app
-
func filePathInFrameworkBundle(name: String)->String{
let myBundlePath = Bundle.main.bundlePath
#if os(iOS)
let inMyFW = myBundlePath +
"/Frameworks" +
"/MyCustomFramework.framework"
#elseif os(OSX)
let inMyFW = myBundlePath +
"/Contents/Frameworks" +
"/MyCustomFramework.framework" + "/Versions/A/Resources"
#else
#endif
let pathInBundle = inMyFW+"/"+name
return pathInBundle
}
Now You can use THIS path in usual image loading calls.
For SwiftUI use:
Add an Asset catalog to your framework project (eg, "Media.xcassets").
Add a resource, like a Color Set. Let's say you name your color "Thunder".
Create a file in your framework called "Color+Extensions.swift" (or whatever you like).
Inside this file do:
import SwiftUI
private class LocalColor {
// only to provide a Bundle reference
}
public extension Color {
static var thunder: Color {
Color("Thunder", bundle: Bundle(for: LocalColor.self))
}
}
I imagine this works for other asset types as well.
In the project which is using your framework, once you have added the import for your framework, you should be able to just use the color normally:
Rectangle().foregroundColor(.thunder)

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

Swift 4.2 - Cocoapods: Adding and accessing scnassets in resource_bundles

Attempting to create a cocoapod that utilizes an .scnassets folder. The folder is imported into Resources without any problems with the children objects, but I can't find a way to load the .dae files into an SCNScene. From my understanding there may be a problem with how XCode converts .dae into .scn assets.
.podspec includes:
s.resource_bundles = {
'ARUtilsScenes' => ['ARUtils/Assets/ARScenes.scnassets']
}
which properly loads the ARScenes.scnassets into Resources folder. I'm attempting to load a scene inside of my Pod (inside the pod and not a project that uses the pod, and have tried a variety of methods):
let arrowScene = SCNScene(named: "arrow.dae")
let arrowScene = SCNScene(named: "Resources/ARScenes.scnassets/arrow.dae")
let arrowScene = SCNScene(named: "arrow.dae", inDirectory: "Resources/ARScenes.scnassets", options: nil)
I've tried a variety of file/folder names, and am able to load images but I'm not able to get a .dae loaded as an SCNScene. Do I need to add .xib files to my resource_bundles, or is there some other way to ensure .scnassets folder properly compiles these .dae to .scn as well as make them available for loading into an SCNScene?
Suppose ZARKit is your framework distributed in CocoaPods.
.podspec includes:
s.resource_bundles = {
'ARUtilsScenes' => ['ARUtils/Assets/ARScenes.scnassets']
}
then you should do this
let bundle = Bundle(for: #Your Class#.self).
let resourcePath = bundle.path(forResource: <#T##String?#>, ofType: <#T##String?#>)
let resourceBundle = Bundle(path: resourcePath)
let url = resourceBundle.url(forResource: "ARScenes.scnassets/<#YourResourceName-arrow#>", withExtension: "<#scn#>")
let arrowScene = SCNScene(url: url)
edit: by Z. Bagley
Accepted answer, but this was the actual code I used:
// find bundle based on existing class in pod
let frameworkBundle = Bundle(for: ARUtilsClass.self) //ARUtilsClass is an empty class, Pod is only extensions for existing classes
// append the scenes bundle, scnassets, and scn/dae
let arrowURL = frameworkBundle.resourceURL?.appendingPathComponent("ARUtilsScenes.bundle/ARScenes.scnassets/newArrow.scn")
// create a node in parent
var arrow = SCNNode()
do {
// get arrow scene from bundle
let bundleArrowScene = try SCNScene(url: (arrowURL)!, options: nil)
// add arrow node from scene
arrow = bundleArrowScene.rootNode.childNode(withName: "arrow", recursively: true)!
} catch {
print("failed to find arrow resource")
}
When accessing the .dae asset from the .scnassets folder, there should be no need to include the "Resources/" in the path
For example: let arrowScene = SCNScene(named: "ARScenes.scnassets/arrow.dae")
Assuming that your .scnassets folder is called that.
There is a known bug introduced with updated build system by Apple which causes asynchronous compilation of asset catalogs making them unaccessible in the final build. For reference see this thread on Cocoapods GitHub. There are several workarounds for this listed, but none of them guarantee the results, so for now we seem to have to live with it

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.

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