Unit Testing Target not recognizing static csv file - ios

I am running Xcode 4.5 and I have added a new unit testing target to an existing project.
I added a csv file to the Copy Bundle Resources section of the target, but the target is not recognizing the file as part of the bundle. When I run the following in LLDB, I get nil result:
po [NSBundle mainBundle]
(id) $2 = 0x01937180 NSBundle </Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk/Developer/usr/bin> (loaded)
po [[NSBundle mainBundle] pathForResource:#"myFile" ofType:#"csv"]
(id) $3 = 0x00000000 <nil>
is there an additional step to get the target to recognize the file?

This one is a read the docs type of answer.
It turns out that calling [NSBundle mainBundle] does not load the bundle that holds that targets files. Instead I need to get the other bundles identifier and use that bundle. After which everything worked as expected.

Related

NSBundle bundleWithIdentifier returning null for Static framework and resource bundle

I am trying to create a static framework which will use some images to show in its view. I have created a MyFrameworkBundle.bundle from XCode by adding the target in my sdk project. Added all my images in the bundle and created the bundle and gave that target a bundle identifier my.frameworkbundle.
Now i am trying to fetch that bundle in my framework like following :
NSBundle * mybundleRef = [NSBundle bundleWithIdentifier:#"my.frameworkbundle"];
Then I put those framework and resource bundle in another project and tried to run function with above line. It always return null. Even if I try to fetch framework like above code i.e.:
NSBundle * mybundleRef = [NSBundle bundleWithIdentifier:#"my.framework"];
This also gave me null. But if I change this framework from static to dynamic it always gives me right bundle.
As I am going to use it with Unity so I don't want a dynamic framework to avoid post processing script.
I also tried the following code :
NSString *bundleString =[[NSBundle mainBundle] pathForResource:#"MyFrameworkBundle" ofType:#"bundle"];
NSBundle * PlayCacheBundle = [NSBundle bundleWithPath:bundleString];
but it gives error
no suitable image found
System Details:
Xcode : 8.2.1
MacOS : 10.12 (Sierra)
Because the framework is statically linked the classes seem to belong to the main bundle, thus +[NSBundle bundleForClass:] doesn't work.
You can create your own bundle pointing to the framework using the following:
NSString *fwPath = [[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:#"Shared.framework"];
NSBundle *bundle = [NSBundle bundleWithPath:fwPath];
I have tested on an empty project, it works, and allows loading NIB files. I haven't tested asset catalogs but I suspect since this is a valid NSBundle reference you should be fine.
Static libraries are just compilations of object files (multiple .o files in one .a file). Their identity disappears when they're linked into an executable target (app binary or dynamic library), and they bring no resources other than code and constant data.

release-debug configuration in React-Native

Currently in React-Native, according to the documentation, to build your iOS app for production, you need to :
change your scheme to Release
change your AppDelegate.m to load the correct bundle
change your Info.pList for ATS
This is a strong violation of 12 factor config recommandation, and it leads to mistakes being made in a continuous integration process.
RN does not provide either out-of-the box strategies to know the configuration environment in the JS code, leading to the existence of the package react-native-config, which does a great job already, but is not perfect (Xcode is not fully supported).
Why is it so? Is it because there are actually so few RN app in production today that nobody cares about this? Can we do better than react-native-config so that steps listed above are not required? I would like a command line that archives my app in the same way that I can run cd android && ./gradlew assembleRelease, without changing anything to my config.
EDIT:
Fastlane makes deployment a lot easier through its gym command (thank you Daniel Basedow). Apparently, the philosophy of Xcode is to call environments "schemes", only you cannot store variables in them, or know which scheme you're running in your code... Anyway, David K. Hess found a great way to export your scheme name in your Info.plist, and then in your Objective C code, which means I'm now able to chose my bundle according to the current scheme, and not touch my code.
Here is my code:
NSString *schemeName = [[[NSBundle mainBundle] infoDictionary] valueForKey:#"SchemeName"];
if ([schemeName isEqualToString:#"scheme1"]) {
jsCodeLocation = [NSURL URLWithString:#"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
} else if ([schemeName isEqualToString:#"scheme2"]) {
jsCodeLocation = [NSURL URLWithString:#"http://<my_local_ip_address>:8081/index.ios.bundle?platform=ios&dev=true"];
} else if ([schemeName isEqualToString:#"scheme3"]) {
jsCodeLocation = [[NSBundle mainBundle] URLForResource:#"main" withExtension:#"jsbundle"];
}
Now my problem is : I also want to know which scheme I'm running in my JS code. react-native-config's way is self-described as hacky, and overly complicated considering the fact the information is already in my Objective C code. Is there a way to bridge this information to my JS code?
Only knowing which scheme I'm running is not as good as being able to set environment variables, but at least I'll be able to switch between environments only by changing my scheme.
EDIT 2:
I managed to export my scheme to my JS code. I created a cocoa touch class with the following code:
// RNScheme.h
#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
#interface RNScheme : NSObject <RCTBridgeModule>
#end
// RNScheme.m
#import "RNScheme.h"
#interface RNScheme()
#end
#implementation RNScheme
{
}
RCT_EXPORT_MODULE()
- (NSDictionary *)constantsToExport
{
NSString *schemeName = [[[NSBundle mainBundle] infoDictionary] valueForKey:#"SchemeName"];
NSLog(#"%#", schemeName);
return #{
#"scheme_name": schemeName,
};
}
#end
and then in my JS code:
import {NativeModules} from 'react-native'
let scheme = NativeModules.RNScheme.scheme_name
EDIT 3:
There is actually another way than using schemes. You can create new "configurations" ("Release" and "Debug" are called configurations) with the following steps (thanks CodePush):
Open up your Xcode project and select your project in the Project
navigator window
Ensure the project node is selected, as opposed to one of your
targets
Select the Info tab
Click the + button within the Configurations section and select
which configuration you want to duplicate
Then you can define keys with different values according to your configuration.
Select your app target
Chose Build Settings
Go to User-Defined section (at the bottom of the scroll area)
You can define constants with a different value according to your configuration (for instance API_ENDPOINT)
You can then reference this value in your Info.plist file :
Open your Info.plist file
Create a new value and give it a name (ApiEndpoint)
Give it the value $(API_ENDPOINT) or whatever name you gave to your constant
Now you can reference this value in your code using the code I gave you in my second edit to this question.
You can create one scheme per configuration to switch quickly from one to the other, or change the build configuration each time (option click on the run button).
In your AppDelegate you can use the correct bundle like this
#ifdef DEBUG
jsCodeLocation = [NSURL URLWithString:#"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
#else
jsCodeLocation = [[NSBundle mainBundle] URLForResource:#"main" withExtension:#"jsbundle"];
#endif
When you do a release build, the DEBUG flag is not set. You can also use different files as your Info.plist depending on build type. There will probably be situations where you want an Xcode debug build with a production JS bundle or vice versa. In that case you need to touch code.
Building ios apps from command line can be a bit tricky. The problems you're describing are not specific to react-native but the Xcode build system. If you haven't already, check out fastlane especially the gym command. It is much simpler than using xcodebuild directly.
But you still have to define your schemes.

'Could not load NIB in bundle' from iOS Static Framework

I am creating a static iOS framework (default template in Xcode 6) that includes xib files.
However I am having trouble loading the nib files when adding my framework to another app, I'm getting the following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle </private/var/mobile/Containers/Bundle/Application/89B7C8F1-698A-4E0B-AD8F-4DB414A6001B/Sample.app> (loaded)' with name 'MyNibName''
I have tried several solutions, some outlined here. I still could not resolve the issue.
I see a lot of references to this popular project which is now unsupported because xcode has the built in feature. Should I be using this instead?
In my framework target I have the xib files included in the 'Copy Bundle Resources' build phase and I see them inside the built framework directory, however, I noticed that the structure of the framework produced does not follow the structure outlined in apple's documentation. Even when I changed the framework structure (using versions and linking the Header and Resources folders to the current version) I still couldn't resolve the issue.
I tried loading my nib in the following ways:
1 loading the bundle by framework name, the bundle is nil in this case
NSString *resourceBundlePath = [[NSBundle mainBundle] pathForResource:#"myframework" ofType:#"framework"];
resourcesBundle = [NSBundle bundleWithPath:resourceBundlePath];
self = [super initWithNibName:#"MyNibName" bundle:resourcesBundle];
2 Creating a resource bundle and adding it to the framework. Also is nil. Note that this solution works if I add the bundle to the project separately, but that defeats the purpose of the framework.
NSString *resourceBundlePath = [[NSBundle mainBundle] pathForResource:#"resources" ofType:#"bundle"];
resourcesBundle = [NSBundle bundleWithPath:resourceBundlePath];
self = [super initWithNibName:#"MyNibName" bundle:resourcesBundle];
3 Loading bundle by class, still did not work, however it returns the same app bundle as in [NSBundle mainBundle]. The FrameworkClass is any class embedded in the framework.
NSBundle* bundle = [NSBundle bundleForClass:[FrameWorkClass class]];
4 Loading the main bundle with [NSBundle mainBundle] which doesnt and shouldnt work.
I think what is happening is that the resources of the framework are not being included in the final app.
What am I missing? has anyone been able to use the default static framework template in xcode to make a framework with nib files?
This should load your bundle.
Swift
let frameworkBundleID = "com.framework.bundleId";
let bundle = Bundle(identifier: frameworkBundleID)
Objective C
NSString* const frameworkBundleID = #"com.framework.bundleId";
NSBundle* bundle = [NSBundle bundleWithIdentifier:frameworkBundleID];
this work for me:
import loginFramework/ClassA.h
NSBundle* resourcesBundle = [NSBundle bundleForClass:[ClassA class]];
ClassA * class = [[ClassA alloc]initWithNibName:#"ClassA" bundle:resourcesBundle];
UINavigationController * navigation = [[UINavigationController alloc] initWithRootViewController:class];
[self.window setRootViewController:navigation];
in app delegate in this case, and in the header of Framework add
#import "ClassA.h"
if you want, I can send you an example
SWIFT v5
I have created a variable in constant file
var frameworkBundle:Bundle? {
let bundleId = "com.framework.bundleId"
return Bundle(identifier: bundleId)
}
Then whenever I need bundle value I directly pass it like
collectionView.register(UINib(nibName: "MyCVCell", bundle: frameworkBundle), forCellWithReuseIdentifier: "MyCVCell")

Creating a custom cocoa pod with a storyboard in it

I am creating a pod, and in the resource bundle I have a storyboard (localised).
When I try to instantiate a storyboard, an error occurred: Could not find a storyboard named 'MyStoryboard' in bundle NSBundle. The code look like this:
NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:#"MyBundle" withExtension:#"bundle"];
NSBundle *bundle = [NSBundle bundleWithURL:bundleURL];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MyStoryboard" bundle:bundle];
MyBundle structure looks like this:
- MyBundle.bundle
- Base.lproj
- MyStoryboard.storyboard
- es.lproj
- MyStoryboard.strings
Can storyboard can be included in a bundle in the first place?
I have not seen examples of Pod that includes storyboards. If you know of any pods that share their storyboard, let me know too.
There are two things to keep in mind.
You add your pod resources via a predefined bundle like
s.resources = ["Resources/Pod.bundle"]
in this case the content of your bundle will be copied to your xcode project without any "further processing". This means that storyboards or xib files will not be compiled and won't be available in your project.
You can explicitly mention your storyboard/nib files like
s.resources = ["Resources/**/*.storyboard"]
In this case the storyboard will be compiled and will be available in your project. The downside of this (at the moment of this writing) is that you can not use localized storyboards, because all storyboards will be processed and copied at the root location of your bundle. So storyboards with the same name in different .lproj folders will be overwritten.
You want the resources option. Here are some specs that include theirs:
JCAutocompletingSearch/0.9.2/JCAutocompletingSearch.podspec
JCAutocompletingSearch/0.9.3/JCAutocompletingSearch.podspec
JCAutocompletingSearch/0.9.4/JCAutocompletingSearch.podspec
JCAutocompletingSearch/0.9.5/JCAutocompletingSearch.podspec
JCAutocompletingSearch/0.9.6/JCAutocompletingSearch.podspec
Keystone-Contacts-iOS/1.1.4/Keystone-Contacts-iOS.podspec
LumberjackConsole/2.0.0/LumberjackConsole.podspec
LumberjackConsole/2.0.1/LumberjackConsole.podspec
Mixpanel/2.1.0/Mixpanel.podspec
Mixpanel/2.2.0/Mixpanel.podspec
Mixpanel/2.2.1/Mixpanel.podspec
Mixpanel/2.2.2/Mixpanel.podspec
Mixpanel/2.2.3/Mixpanel.podspec
Mixpanel/2.3.0/Mixpanel.podspec
Mixpanel/2.3.1/Mixpanel.podspec
Mixpanel/2.3.2/Mixpanel.podspec
Mixpanel/2.3.4/Mixpanel.podspec
Mixpanel/2.3.5/Mixpanel.podspec
OpenBLE/1.0.0/OpenBLE.podspec
I solved my problem by using this line code to get bundle
let bundle = Bundle(for: type(of: self))
I currently have the same issue here. While not answering the question itself, I'd like to provide additional information that most likely is valid in your case, too:
Calling
NSString *path = [bundle pathForResource:#"MyStoryboard" ofType:#"storyboard"];
NSLog(#"path: %#",path);
will print
/var/mobile/Containers/Bundle/Application/_identifier_/MyApp.app/MyBundle.bundle/Base.lproj/MyStoryboard.storyboard
i.e. the storyboard itself is found, but not loadable "the normal way".

Unable to use iOS framework which is internally using .xib file

I am trying to make a universal framework, for iOS by following steps specified in this URL: Universal-framework-iOS
I have a viewController class within, that framework which internally loads a .xib file.
Below is a part of code which shows, how I am initializing that viewController and showing related view:
/*** Part of implementation of SomeViewController class, which is outside the framework ***/
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.viewControllerWithinFramework = [[ViewControllerWithinFramework alloc] initWithTitle:#"My custom view"];
}
- (IBAction)showSomeView:(id)sender {
[self.viewControllerWithinFramework showRelatedView];
}
/*** Part of implementation of ViewControllerWithinFramework class, which is inside the framework ***/
- (id)initWithTitle:(NSString *)aTitle
{
self = [super initWithNibName:#"ViewControllerWithinFramework" bundle:nil]; // ViewControllerWithinFramework.xib is within the framework
if (self)
{
_viewControllerTitle = aTitle;
}
return self;
}
While creating the framework, I included all .xib files, including ViewControllerWithinFramework.xib within its Copy Bundle Resources build phase.
Now my problem is when I try to integrate that framework within other project, it crashes with below stack trace:
Sample[3616:a0b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle </Users/miraaj/Library/Application Support/iPhone Simulator/7.0/Applications/78CB9BC5-0FCE-40FC-8BCB-721EBA031296/Sample.app> (loaded)' with name 'ViewControllerWithinFramework''
*** First throw call stack:
(
0 CoreFoundation 0x017365e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x014b98b6 objc_exception_throw + 44
2 CoreFoundation 0x017363bb +[NSException raise:format:] + 139
3 UIKit 0x004cc65c -[UINib instantiateWithOwner:options:] + 951
4 UIKit 0x0033ec95 -[UIViewController _loadViewFromNibNamed:bundle:] + 280
5 UIKit 0x0033f43d -[UIViewController loadView] + 302
6 UIKit 0x0033f73e -[UIViewController loadViewIfRequired] + 78
7 UIKit 0x0033fc44 -[UIViewController view] + 35
Any ideas, how could I resolve this problem?
Note: It works fine if there is no any xib within the framework.
If you're using Universal-framework-iOS all resources (including Nibs and images), will be copied inside a separate bundle (folder) such as MyApp.app/Myframework.bundle/MyNib.nib.
You need to specify this bundle by passing a NSBundle object instead of nil. Your can get your bundle object as follows:
NSString *path = [[NSBundle mainBundle] pathForResource:#"Myframework" ofType:#"bundle"];
NSBundle *resourcesBundle = [NSBundle bundleWithPath:path];
As for images you can just prepend Myframework.bundle/ to their names:
[UIImage imageNamed:#"Myframework.bundle/MyImage"
This also works in Interface Builder.
Finally your users to install/update a framework is a pain, specially with resources, so better try to use CocoaPods.
Unfortunately because iOS does not have an exposed concept of dynamic fragment loading some of NSBundle's most useful functionality is a little hard to get to.
What you want to do is register the framework bundle with NSBundle, and from then on you can find the bundle by it's identifier - and the system should be able to correctly find nibs, etc. within that bundle.
Remember, a framework is just a kind of bundle.
To make NSBundle "see" your bundle and it's meta information (from it's Info.plist), you have to get it to attempt to load the bundle. It will log an error because there will be no CFPlugin class assigned as a principal class, but it will work.
So for example:
NSArray *bundz = [[NSBundle bundleForClass:[self class]] URLsForResourcesWithExtension:#"framework" subdirectory:nil];
for (NSURL *bundleURL in bundz){
// This should force it to attempt to load. Don't worry if it says it can't find a class.
NSBundle *child = [NSBundle bundleWithURL:bundleURL];
[child load];
}
Once that is done, you can find your framework bundle using bundleWithIdentifier:, where the identifier is the CFBundleIdentifier in your framework's Info.plist.
If you need to use UINib directly to load your view controller nib directly at that point, it should be easy to locate the bundle using bundleWithIdentifier: and give that value to nibWithNibName:bundle: .
The simplest way is to use [NSBundle bundleForClass:[self class]] to get the NSBundle instance of your framework. This won't enable the ability to get the framework's NSBundle instance by its Bundle ID but that isn't usually necessary.
The issue with your code is the initWithNibName:#"Name" bundle:nil gets a file named Name.xib in the given bundle. Since bundle is nil, it looks in the host app's bundle, not your framework.
The corrected code for the OP's issue is this:
/*** Part of implementation of ViewControllerWithinFramework class, which is inside the framework ***/
- (id)initWithTitle:(NSString *)aTitle
{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
self = [super initWithNibName:#"ViewControllerWithinFramework" bundle:bundle];
// ...
return self;
}
The only thing changed is giving the correct bundle instance.
As an another option you can directly put your xib file into your framework project and can get your nib with calling
Swift 3 and Swift 4
let bundleIdentifier = "YOUR_FRAMEWORK_BUNDLE_ID"
let bundle = Bundle(identifier: bundleIdentifier)
let view = bundle?.loadNibNamed("YOUR_XIB_FILE_NAME", owner: nil, options: nil)?.first as! UIView
Objective-C
NSString *bundleIdentifier = #"YOUR_FRAMEWORK_BUNDLE_ID";
NSBundle *bundle = [NSBundle bundleWithIdentifier:bundleIdentifier];
UIView *view = [bundle loadNibNamed:#"YOUR_XIB_FILE_NAME" owner:nil options:nil];
Frameworks that come with XIBs usually come with bundles too - so you probably should not pass nil in the framework part.
Right click the framework -> Show in finder
Open it up and see what's the bundle name in the resources folder (For example - Facebook uses FBUserSettingsViewResources.bundle) and use it.
In general - static libraries do not include xib or resource files.
Frameworks is basically a wrapper to a static library, headers and some resources (usually inside a bundle)
You need to specify the bundle to search inside for the nib. Otherwise, it just (shallowly) searches your application's resources directory.
- (id)initWithTitle:(NSString *)aTitle
{
// replace 'framework' with 'bundle' for a static resources bundle
NSURL *frameworkURL = [[NSBundle mainBundle] URLForResource:#"myFrameworkName" withExtension:#"framework"];
NSBundle *framework = [NSBundle bundleWithURL:frameworkURL];
self = [super initWithNibName:#"ViewControllerWithinFramework" bundle:framework];
if (self)
{
_viewControllerTitle = aTitle;
}
return self;
}
I'm going to answer this the way I achieved the results you intended, but it may not be the best approach, improvements are more than welcome! I did it on a Cocoa Touch Framework subproject, but it should be easy to adapt to a Cocoa Touch Static Library, if it doesn't work already. This will be more like a tutorial than an answer, to be honest, but anyway...
First things first. Quick overview of my solution: you'll have two projects on the same workspace. One is the framework, the other one is the app itself. You'll create the xib/storyboard on the framework, and use it either on the framework or the app (although the latter doesn't make much sense to me).
The framework project will contain a build run script that will copy all it's resources (images, xibs, storyboards) to a bundle, and that bundle will be part of the app project. Also, the framework project will be a dependency of your app project. This way, whenever you compile your app, the build run script should run automatically and update the resources bundle before packaging your app.
It sure is NOT a quick & easy thing to set up, but that's why I'm answering your question. This way I'll be able to come back here myself and remember how I achieved the same goal. You never know when Alzheimer's gonna hit you :)
Anyway, let's get to work.
Create/open your app's project. I'll be referring to this as AppProject.
Create a framework/library project and add it as subproject to the AppProject, or just drag your current framework project to the AppProject. What matters here is that the framework project is a subproject of AppProject. I'll be referring to the framework project as MyFramework.
Configure whatever you need for your specific projects. I guess it's a standard thing to use linker flags -ObjC and -all_load, at least. This isn't really useful for the purpose of this mini-tutorial, but the project should at least be compiling. Your workspace should be looking something like this:
Open the AppProject folder and create a new directory called MyBundle.bundle.
Drag the MyBundle.bundle to the AppProject (this is important: you are NOT supposed to drag it to the library project!). You probably want to un-check the Copy items if needed and select/check the targets where this bundle will be copied to when compiling your app.
Leave MyBundle.bundle alone for now.
Go to MyFramework's Build Settings and add a new Run script phase.
This step might be optional, but it worked like this for me, so I'm including it. Drag the just-created Run script right above the Compile sources phase.
Edit the Run script phase, changing it's shell to /bin/sh if it's not that already. Set the script to the following (comments should explain the script):
Run script
#!/bin/bash
echo "Building assets bundle."
# Bundle name (without the ".bundle" part). I separated this because I have different app targets, each one with a different bundle.
BUNDLE_NAME='MyBundle'
CURRENT_PATH=`pwd`
# This should generate the full path to your bundle. Might need to be adapted if the bundle
# directory you created was in another directory.
BUNDLE_PATH="$CURRENT_PATH/$BUNDLE_NAME.bundle"
# Check if the bundle exists, and removes it completely if it does.
if [ -d "$BUNDLE_PATH" ]; then
rm -rf "$BUNDLE_PATH"
fi
# Re-creates the same bundle (I know this is weird. But at least I am SURE the bundle will
# be clean for the next steps)
mkdir "$BUNDLE_PATH"
# Copy all .jpg files to the bundle
find ./ -name *.jpg -type f -print0 | xargs -0 -J% cp % "$BUNDLE_PATH"
# Copy all .png files to the bundle
find ./ -name *.png -type f -print0 | xargs -0 -J% cp % "$BUNDLE_PATH"
# Copy all .xib files to the bundle.
find ./ -name *.xib -type f -print0 | xargs -0 -J% cp % "$BUNDLE_PATH"
# Copy all .storyboard files to the bundle.
find ./ -name *.storyboard -type f -print0 | xargs -0 -J% cp % "$BUNDLE_PATH"
# This is the golden thing. iOS code will not load .xib or storyboard files, you need to compile
# them for that. That's what these loop do: they get each .xib / .storyboard file and compiles them using Xcode's
# ibtool CLI.
for f in "$BUNDLE_PATH/"*.xib ;
do
# $f now holds the complete path and filename a .xib file
XIB_NAME="$f";
# Replace the ".xib" at the end of $f by ".nib"
NIB_NAME="${f%.xib}.nib";
# Execute the ibtool to compile the xib file
ibtool --compile "$NIB_NAME" "$XIB_NAME"
# Since the xib file is now useless for us, remove it.
rm -f "$f"
done
for f in "$BUNDLE_PATH/"*.storyboard ;
do
# $f now holds the complete path and filename a .storyboard file
STORYBOARD_NAME="$f";
# Replace the ".storyboard" at the end of $f by ".storyboardc" (could've just added the final "c", but I like
# to keep both loops equal)
COMPILED_NAME="${f%.storyboard}.storyboardc";
# Execute the ibtool to compile the storyboard file
ibtool --compile "$COMPILED_NAME" "$STORYBOARD_NAME"
# Since the .storyboard file is now useless for us, remove it.
rm -f "$f"
done
Your workspace/settings should be looking something like this now:
Go to AppProject Build phases, and add your framework to the Link Binary with Libraries section. Also add the framework as the Target dependencies section.
Everything seems to be set up. Create a xib or storyboard file on the framework project, and create the view (or view controller) just the way you usually do. Nothing special here. You can even set custom classes for your components and everything (as long as the custom class is inside the framework project, and not in the app project).
Before compiling the app project
After compiling the app project
On your code, wherever you need to load your NIB/Xib, use one of the following codes. If you managed to follow so far, I don't even need to tell you you'll need to adapt these codes to whatever you wanna do with the xib/Storyboard, so... Enjoy :)
Registering cell xib for a UITableView:
NSString* bundlePath = [[NSBundle mainBundle] pathForResource:#"MyBundle" ofType:#"bundle"];
NSBundle* bundle = [NSBundle bundleWithPath:bundlePath];
UINib* nib = [UINib nibWithNibName:#"TestView" bundle:bundle];
[tableView registerNib:nib forCellReuseIdentifier:#"my_cell"];
Pushing a UIViewController to the navigation controller:
NSString* bundlePath = [[NSBundle mainBundle] pathForResource:#"MyBundle" ofType:#"bundle"];
NSBundle* bundle = [NSBundle bundleWithPath:bundlePath];
UIViewController* vc = [[UIViewController alloc] initWithNibName:#"BundleViewController" bundle:bundle]; // You can use your own classes instead of the default UIViewController
[navigationController pushViewController:vc animated:YES];
Presenting modally a UIStoryboard from it's initial UIViewController:
NSString* bundlePath = [[NSBundle mainBundle] pathForResource:#"MyBundle" ofType:#"bundle"];
NSBundle* bundle = [NSBundle bundleWithPath:bundlePath];
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"BundleStoryboard" bundle:bundle];
UIViewController* vc = [storyboard instantiateInitialViewController];
vc.modalPresentationStyle = UIModalPresentationFormSheet;
vc.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:vc animated:YES completion:^{}];
If it doesn't work for you (or whoever is visiting this question/answer), please let me know and I'll try to help.
Try this in main.m
#import "ViewControllerWithinFramework.h" //import this
int main(int argc, char *argv[])
{
#autoreleasepool
{
[ViewControllerWithinFramework class]; //call class function
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
I hope this will work for you.

Resources