I'm having some trouble working with libraries and included xib files. Let me explain my situation first. I've got a project that's running pretty good. Now I need a part of my project as a library for another customer who want's to use some of the functionality in his app. I've created a library with help of that highly recommended article.
My customer should be able to initialize a main view controller like this:
LibraryMainViewController *lmvc = [[LibraryMainViewController alloc] initWithNibName:#"LibraryMainViewController.xib" bundle:foo];
That for sure leads to an error when I try to present that view controller modally. I'm not sure what bundle I have to use here. I tried something like
[NSBundle bundleForClass:[SomeClassInTheLibrary class]];
but that didn't solve the problem.
I would be glad if someone could tell me how to actually use a xib file in that kind of situation.
Thanks
–f
Update
Ok, I see I got it wrong somehow. Just for clarification: What I need is the library, the headers and the additional resources, right? Is there some kind of best practice for creating and shipping "a feature" with all it's parts above mentioned?
Static libraries can't include graphics, sounds, xibs or even headers. Only object code. Even if you added the xibs to Copy Bundle Resources, they won't become a part of the object file. Unfortunately, you can't use dynamic libraries or frameworks on the iPhone. See my answer here for a suggestion of how to create a separate assets bundle. You could also just send your customer the xib files separately, but then they have to replace them by hand if they change in the future.
Try without adding the extension to the XIB file. That's the way i usually do it. I'm also not sure if the XIB must be compiled to a NIB..
NSBundle *bundle = [NSBundle bundleForClass:[SomeClassInTheLibrary class]];
LibraryMainViewController *lmvc = [[LibraryMainViewController alloc] initWithNibName:#"LibraryMainViewController" bundle:bundle];
Related
My question is about the application bundle in a project. I was reading about that and can understand some basic things (I'm not a native english speaker). I can understand that the resources folder is used to hold the files that will be used in the project, e.g. media files (images, audio, video, etc.) and should be in the application bundle to be identified.
So, what is the point if I want to use images and another resources in my project? In my other related question, I can't use them by referencing with NSImage imageNamed:.
I have used the following with no success loading my files:
NSBundle methods
imageNamed:#"string" with/without file extension
the images are in resources folder
I'm learning Cocoa and Objective-C, and of course this is different to C++ or Java when I want to create an ImageIcon or a QImage.
I may not have completely understood the issue, so correct me if I am wrong. I believe the problem has to do with your image's target membership or how you're retrieving the image in your code.
Adding an image to your project target will appropriately copy the resource at compile time. To add the image to your target, select it in the file navigator and then reveal the Utilities Panel. On the Utilities Panel, select the File Inspector Tab. Look for the Target Membership section and ensure that the image is selected for the desired targets:
Do you mean that you can't use the NSImage imageNamed: method to retrieve resources? If so, you can retrieve the resource like this (from the main resource bundle):
NSString *imageName = [[NSBundle mainBundle] pathForResource:#"image1" ofType:#"png"];
NSImage *imageObj = [[NSImage alloc] initWithContentsOfFile:imageName];
It also looks like you already have a good answer to your other related question.
Is it possible to force the Xcode complier to verify that files referenced in code are valid?
There are multiple points in Cocoa development when you naturally reference a file programmatically via an NSString:
[UINib nibWithNibName:#"MyNib" bundle:nil];
[UIImage imageNamed:#"MyImage"];
[[UIViewController alloc] initWithNibName:#"MyNib" bundle:nil];
Is there any way at compile time to check is these file references are valid?
Often times after using above methods, I end up changing the name of the referenced file but forget to change the name in code. Everything complies without a problem and it is only when you happen to go to the portion of the app that accesses this file that the bug will reveal itself.
Is there another approach or technique that people use to avoid this sort of error?
Referencing a file name via a string feels very fragile.
Warning: This answer is mostly outdated. The general idea is fine but better solutions exist now (e.g. Image assets with a SwiftGen script to generate an enum).
Nibs usually have a class with the same name as the file, e.g.
[[MyViewController alloc] initWithNibName:NSStringFromClassName([MyViewController class]) bundle:nil];
I usually hide it into the controller's init method as [self class].
For image loading, compile-time checks are difficult. Help yourself with macros, first replace the loading method by a simple macro, e.g.
#define LOAD_IMAGE(__IMAGE_NAME__) [UIImage imageNamed:__IMAGE_NAME__]
First thing you should do is to put an assert into this macro and always check that the image was successfully loaded. It's not a compile-time check but it helps to find missing resources.
The second thing is to write a ruby/python/shell/(any scripting language) script that will search your source files for LOAD_IMAGE and check if the file (between parenthesis) exists. As a shell script, it will be very simple (e.g. using grep). You can add this script into your xcode project to be run when compiling.
Don't forget to check images referenced by xibs.
However, often you have to create the image name dynamically, e.g. NSString* imageName = [NSString stringWithFormat:#"image_%i", index]. There's no way how you can check this at compile time.
Also don't forget to do the reverse check - don't include image files which are not used anywhere.
AutoComplete for [UIImage imageNamed:] by Kent Sutherland.
This provides code completion support within Xcode - a brilliant piece of code. This is working for me in Xcode 4.6:
Currently this project does not have support for strings other than imageNamed:. To support those, I will try to write a compile time script. Or maybe I will become bold and try to extend Mr. Sutherland's spectacular work.
Xcode doesn't support this, but if this problem is really biting you then you could use the following hack:
Give every in-bundle file a unique prefix (e.g. app__)
When you add a file to your project, make sure you first rename it to add this prefix.
Your compile time (pre-distribution) check then has two parts: 1) Search through all .m files and enumerate strings that begin with the prefix. You shouldn't have to check if the string is quoted since your prefix is unique. 2) grep project.pbxproj for each string to check if it is included in the bundle.
With some effort, this process can be mostly automated and also optimized, but the above recipe ought to work.
here is a bash script that we use that lists all images on disk but NOT referenced in code.
https://gist.github.com/3750087
it would likely be easy to reverse this to check for non-exting images and xibs.
Anyways, the script should be a good starting point
I'm looking for executing a .xib (with its own controllers and libraries) precompiled on a server, downloading it on runtime.
Is it possible?
Thanks!
EDIT:
So could somebody give me an example of a program that uses NSBundle that executes other app?
And how do I create the bundled application?
I don't think you can import a xib into the application's bundle at run-time (which you would have to in order for this to happen). Others may know more and correct me!
I can think of a couple of ways you could try to do this, but are you aiming to get it in to the store?
This is expressly prohibited by Apple Developer Guidelines.
A .xib file is just a data file, so there shouldn't be any problem loading one that's outside your app's bundle. I can't say I've ever tried it, but as long as it's in a bundle, you should be able to:
Create an instance of NSBundle using the path to the bundle containing the .xib you want to load. See +[NSBundle bundleWithPath:] for that.
Load the .xib using the bundle you created in the previous step with any of the normal .xib-loading methods, such as -[UIViewController initWithNibNamed:bundle:] or +[UINib nibWithNibName:bundle:].
with it's own controllers and libraries
That part won't work. iOS doesn't allow dynamic linking to frameworks other than the ones provided by the system, so there's no way to load your code. If you can build all the code you need into your app, though, you should still be able to use downloaded .xib's as described above. That would let you do things like update the way your views are laid out or what targets and actions your controls are connected to.
Let's say I have an existing iOS or Mac Framework that we'll call Test.framework. This framework has compiled and linked successfully (a trick was done to lipo both i386 and armv6/7 archs for the iOS part) and is working perfectly fine.
Now I want to add an image to it let's call it 'test.png'. I have copied it into the Resources folder of my framework so that I have the following structure:
ls Test.framework
- Headers -> Versions/Current/Headers
- Resources -> Versions/Current/Resources
- Test -> Versions/Current/Test
- Versions
ls Test.framework/Resources
- Info.plist
- test.png
Yet when I create a project using my framework I cannot access that image.
I've tried:
[UIImage imageNamed:#"test.png"]
[UIImage imageNamed:#"Test.framework/test.png"]
[UIImage imageNamed:#"Test.framework/Resources/test.png"]
But nothing worked out that always gave me back a nil object.
Any ideas ?
EDIT
After much further investigation it seems what I am trying to accomplish can't be done on iOS. The reason is that the final application bundle (the .app) doesn't copy the private frameworks where applications compiled for Mac OS will.
This is further detailed in the iOS Bundle Structures documentation.
Thanks to Rob Keniger and xuzhe for their appreciated help. I will credit xuzhe for the answer as it is actually the most appropriate answer to my original problem (even though Rob comment made me dig quite deeper into the issue)
The "imageNamed:" method is only for images in your App bundle. You should use
[[UIImage alloc] initWithContentsOfFile:path];
instead.
About the path, since I am not sure if the "Test.Framework" is in you App bundle, I am not able to give you a sample code. But if it dose, the code below should work:
NSString* path = [Nsstring stringWithFormat:#"%#/Test.framework/Resources/test.png", [[NSBundle mainBundle] bundlePath]];
You can get the bundle for a particular class using [NSBundle bundleForClass:[SomeClass class]]. All you need to do is pass in a class that's defined in the framework and you'll have a reference to the framework's bundle.
You can then ask the bundle for the path to the image using the pathForResource:ofType: method of NSBundle, and then use the initWithContentsOfFile: method of NSImage to create your image.
Note that you should never hard-code paths. Ever. There are many different functions and methods for obtaining paths to resources, you never need to hard-code them.
I want to add a simple one-page HTML page help to my Cocoa app. Can you tell me how to do it? I assume I just have to throw in one lousy .html (and maybe one .css?) file somewhere into my Cocoa project in Xcode?
Creating Apple Help documents that are opened in the Help viewer is straightforward but you must follow the directions in the documentation exactly.
Help files are HTML but you need to place a couple of special tags in the page and name the files in a particular way.
It's all explained in the documentation.
Today I've been facing the same problem. I found no up-to-date howto so here is one of my own. Help is nicely working with this Step by Step to create Apple Help in your Cocoa Xcode Application.
If you only want a single HTML page and not a proper help file, you could add an HTML document and CSS file to your project. These will be copied to your application's Resources directory inside the app bundle when you compile the project. To load the document, you'll need to get its address. This is actually quite easy:
NSString *helpFilePath = [[NSBundle mainBundle] pathForResource:#"YourHelpDocumentHere" ofType:#"html"];
NSURL *helpFileURL = [NSURL fileURLWithPath:helpFilePath];
The resulting URL will be a file URL that a WebView can display inside your application, or you can pass it off to the operating system using NSWorkspace and it will be opened in the user's default web browser.