How to make an iOS asset bundle? - ios

I saw a custom asset bundle in an iOS project I evaluated, so at least I know it's possible.
My issue is that I'm using a CATiledLayer with about 22,000 tiles for a given image and it takes a very long time to compile (half an hour clean build, 5-10 minutes for regular build). So, I want to take all of the images and make a custom bundle to make it portable and hopefully not recompile into the app bundle each time.
How do I go about this? I checked the docs, but didn't see an explanation on how to actually create the bundle.

Answer is stupidly simple
Make a folder in finder, add files to it, rename it to bundlename.bundle
drag into Xcode - success!
to access, use the form of PathToMainBundle+"/bundlename.bundle"

How to create a bundle
Create a folder in finder.
Add files to the folder
Rename the folder so that its extension is .bundle (e.g. "New folder" -> "BundleName.bundle")
PS: You can at any time right click the folder and hit "Show package content" in order to add, remove or modify any of the files.
How to add the bundle to Xcode
Drag it into Xcode
How to use the bundle
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"BundleName" ofType:#"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
NSString *resource = [bundle pathForResource:#"fileName" ofType:#"fileType"];
(Replace BundleName, fileName and fileType with appropriate names)

Two other helpful bits of advice:
First, in order to see the contents of the bundle in Xcode you need to set its type in the File Inspector Utility Pane, to "Application Bundle". You still won't be able to copy to and from via Xcode. You'll need to use Terminal but Xcode will update it immediately.
Second, in order to use resources in the bundle here's a helpful snippet...
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"AquarianHarp" ofType:#"bundle"];
NSString *imageName = [[NSBundle bundleWithPath:bundlePath] pathForResource:#"trebleclef2" ofType:#"png"];
UIImage *myImage = [[UIImage alloc] initWithContentsOfFile:imageName];
As mentioned in my comment above, you needn't actually load the bundle (you can't as it's not executable) and the ofType needs to match the case of your actual file for it to work on the device. It will work either way in simulator so don't be fooled by this red herring!
Finally, you don't need to put your resources in the "Resources" subfolder inside the bundle. It seems you can use an arbitrary layout but there may be unknown performance implications.

Here's how I got this to work: In Xcode create a new file | Resource | Settings Bundle. Then in the Finder select that bundle and choose Show Package Contents, and add whatever image files.
Then in the code reference an image this way:
NSString *imgName = #"bundlename.bundle/my-image.png";
UIImage *myImage = [UIImage imageNamed:imgName];

Here are the steps to create an asset or resource bundle (ex. FrameworkResources.bundle) - it's surprisingly non-obvious. This is especially useful if you are creating static frameworks.
Press File -> New -> Target in Xcode
Select "macOS" tab, search "Bundle"
Tap "Bundle" -> click "Next" -> type product name "MyResources" -> tap "Finish"
Go to "Build Settings" for the newly created Bundle. Change "Base SDK" (SDKROOT) to "iOS'
Go to "Build Phases" for the newly created Bundle. Delete "Compile Sources" and "Link Binary With Libraries" (this will remove the executable within the bundle which can cause all sorts of build and app submission errors)

Creating a loadable bundle project is just like creating an application—you just need to pick the appropriate project template. To create a loadable bundle project, perform the following steps:
Launch Xcode.
Choose New Project… from the File menu.
From the template list, select Cocoa Bundle.
Click Next.
Choose the location for the project and click Finish.
Build and run, in Xcode you will see the bundle file in your Products folder of Project Navigator. Right-click the bundle and select "Show in Finder".

My notes on bundling and reading files in an Xcode project
Steps:
Create a test.txt file and add the text "testing" to it then put it in a folder named test.bundle
Drag and drop it next to your .app file in Xcode (copy)
print(Bundle.main.resourcePath!+"/temp.bundle/test.txt") Output: /Users/James/Library/Developer/Xcode/DerivedData/GitSyncMac-heiwpdjbtaxzhiclikjotucjguqu/Build/Products/Debug/GitSyncMacApp.app/Contents/Resources/temp.bundle/test.txt
Example:
print(content(Bundle.main.resourcePath!+"/temp.bundle/test.txt")) // testing
static func content(_ path:String)->String?{
do {
let content = try String(contentsOfFile:path, encoding:String.Encoding.utf8) as String//encoding: NSUTF8StringEncoding
return content
} catch {
return nil
}
}

Related

UIImage load wrong image in main bundle

I had a main project contain more than 10 bundles. I had two image named map_bubble_right_normal, one in main bundle, the other one in sub bundle(SCommon.bundle). I write code as follow:
UIImage *image = [UIImage imageNamed:#"map_bubble_right_normal"];
The code was writen in didFinishLaunchingWithOptions of AppDelegate.
I want to load the image which I saved at main bundle. However, the result of the code that load image from sub bundle(SCommon.bundle).
I guess that image saved at main bundle maybe a error image(maybe copy problem). Therefore, I show the ipa content to see the "map_bubble_right_normal" file saved at root directory. The image is right!!!!!
I don't know why imageNamed: would load the image from sub bundle(SCommon.bundle). I tried to delete app and restart iPhone, the result is same. And, I tried to delete Derived Data of XCode, and clean the project. The image still load from sub bundles(SCommon.bundle).
Addition, I test the problem at iOS 8 and iOS 9 device.
Only a method which change the bundleId(main bundle) can solve the problem temporary so far.
I know the cache feature of UIImage, but I can't understand why did the strange scene will happen.
I sincerely request you a great god for help. Thank you~
Thank you for everyone's answer. I sorry about I had mislead all of us to bundle.
I found the cause of the problem. The Error Image isn't came from sub bundle, which came from Assets.car. The cause of the problem should be blame to Image.assert in physical directory actually. CocoaPods will copy all match "*.assets" to Assets.car.
I had made same question at Apple Developer forum, and found the key tip of the problem.
The issue of Cocoapods which relative to the problem Pods copy resource script overrides default xcasset bahaviour.
Pod_resources.sh script would copy every *.xcassets in Pod_Root if your Pod Repo had add any folder which name match *.xcassets.
Key Code of Pod_resources.sh:
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
...
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
while read line; do
if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
XCASSET_FILES+=("$line")
fi
...
fi
Condition XCASSET_FILES will be fulfill if your Pod had add any xcassets file.
To load image from main bundle try this:
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"map_bubble_right_normal" ofType:#"png"]];
If you want to load from mainBundle try this code:
NSBundle* myBundle = [NSBundle mainBundle];
NSString* myImage = [myBundle pathForResource:#"Seagull" ofType:#"jpg"];
If you want to load from sub bundle:
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"YourBundleName" ofType:#"bundle"];
NSBundle *subBundle = [NSBundle bundleWithPath:bundlePath];
NSString* myImage = [subBundle pathForResource:#"Seagull" ofType:#"jpg"];
Refer here
I've occured some same situation. Instead of two bundle you mentioned above, I failed to show the right image with the exact right name. And finally, I found the key point is that the image has a suffix of jpg instead of png. I changed to an image of png and all turned well. Maybe it's a possible situation you occured. Have a try and good luck. :)
PS: Here's a related link as a reference.

Remove external files from my project

I had 2 files .sql in my project wish was at the root then I deleted them with the button move to trash but they are still here when I print with this function :
NSArray *filePath = [[NSBundle mainBundle]
pathsForResourcesOfType:#"sql" inDirectory:#"/"];
NSLog(#"file path : %#",filePath);
The result is :
"/Users/Fabrik/Library/Developer/CoreSimulator/Devices/46A082C2-6BCD-4904-A882-798A8642FFE1/data/Containers/Bundle/Application/547F421A-1934-49FF-B166-8C6AFB9F8ED9/iOSFramework.app//DELL_001_01_CREATE_SCHEMA.sql",
"/Users/Fabrik/Library/Developer/CoreSimulator/Devices/46A082C2-6BCD-4904-A882-798A8642FFE1/data/Containers/Bundle/Application/547F421A-1934-49FF-B166-8C6AFB9F8ED9/iOSFramework.app//DELL_001_02_CREATE_DATA.sql"
I reseted all the setting but they are still here.
How can I remove them ?
Go to Product > Clean
Then go to Product while pressing option in your keyboard and do Product > Clean Build Folder.
If this doesn't work just delete the app in the simulator and build your target again.

read entire .m file (from bundle) as a string in iOS

I am trying to read entire .m file in a string using code:
NSString* fullFileName = #"file.m";
NSString* fileName = [[fullFileName lastPathComponent] stringByDeletingPathExtension];
NSString* extension = [fullFileName pathExtension];
NSString* filePath =[[NSBundle mainBundle] pathForResource:fileName ofType:extension];
NSString* fileTextContent=[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL];
But it returns null.
Is it possible to read .m file from bundle in a string in iOS?
It's possible, but you should make sure the .m file is in the bundle first (they aren't by default).
Right click on the app under the products folder in Xcode, select "Reveal in Finder", then right click on the app in finder and select "Show Package contents" to see what's in the bundle.
If you need to add it, you can do that under the project settings in xcode > build phases > copy files phase.
Including .m files in the bundle would be somewhat strange though. There's almost certainly a better way to do whatever it is you're trying to do. Maybe if you went into more detail on what that is, people could suggest them.

How to transfer resource files with Static Library (How to wrap resources in bundle)?

I'm creating a Static Library for iOS applications. I almost completed it, but there is an issue with the resources.
My Static Library uses a lot of images and sound files. How can I transfer it with my Static Library ?
I know we can wrap it on a bundle and give it along the .a file. But I don't know how to wrap Images and sound files in a Bundle file.
What I did:
I searched a lot, but couldn't find any useful links.
I got Conceptual CFBundles reference, but didn't find any solution for my problem.
I checked the file templates available for XCode, but didn't saw any bundle type other than Settings Bundle.
There are several good reasons to build an application with multiple bundles and several different ways to do it. To my experience the best way is open Xcode and create a new bundle project:
Select: File -> New Project… -> Group Mac OSX (!) -> Framework & Library -> Bundle. Add your resource files to the project.
Build the bundle as you build other iPhone apps.
You may add this project to your static library project and rebuild it all the time the library is changed. You must know that the bundle itself will not be linked to your library file.
In your app projects add the .bundle file to your project as an ordinary resource file (Add -> Existing Files… -> find and select the above built .bundle file. Do not copy it).
Example :
// Static library code:
#define MYBUNDLE_NAME #"MyResources.bundle"
#define MYBUNDLE_IDENTIFIER #"eu.oaktree-ce.MyResources"
#define MYBUNDLE_PATH [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: MYBUNDLE_NAME]
#define MYBUNDLE [NSBundle bundleWithPath: MYBUNDLE_PATH]
// Get an image file from your "static library" bundle:
- (NSString *) getMyBundlePathFor: (NSString *) filename
{
NSBundle *libBundle = MYBUNDLE;
if( libBundle && filename ){
return [[libBundle resourcePath] stringByAppendingPathComponent: filename];
}
return nil;
}
// .....
// Get an image file named info.png from the custom bundle
UIImage *imageFromMyBundle = [UIImage imageWithContentsOfFile: [self getMyBundlePathFor: #"info.png"] ];
For more help you can check these good articles
iOS Library With Resources
Resource Bundles
Hope it helps you.

NSBundle mainBundle pathForResource (maybe not the same problem)

Problem : [NSBundle mainBundle] pathForResource returns null (0x0)
I read a lot of posts on this topic, here is what I found:
Make sure the file you are trying to get a path to, is in the project. To check, look in the project file list for the file, if it is not there, drag and drop it in.
Check that the file your trying to load is being copied to the app. To do this, click on project under project files (blue bar with project name in it->Click on the target->Click on Build Phases->Click to expand the "copy Bundle Resources", and make sure your file is in it. If it is not, click the plus, and add it.
Make sure the case matches exactly. (aka - The simulator will work/the app will not problem) Make sure the case matches exactly, otherwise it will return nothing. To fix this, just rename it in the project, or use the right case in the source.
Project may be missing the media framework. To fix this click on your project menu -> click on the target -> click to expand "Link Binary With Libraries". Now select MediaPlayer.framework and build the code.
If all else fails, clean the project, and try again.
All this is checked/fixed, but not working. I had this working fine, and then I changed the video that I am showing in the app. Since i changed the file, it stopped working.
NSString *path = [[NSBundle mainBundle] pathForResource:#"multiplying" ofType:#"m4v"];
Q: why is it null? The file is case matched/typed correctly and is about 10 megs. (is the file too large?) Works fine in the simulator, and I can get the path to an image, a line above this one, with no error.
You say it's working in simulator and not working on device?
May be you should try to copy the file at first launch to one of the application sandbox folders and access it like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pathForTheFile= [documentsDirectory stringByAppendingPathComponent: #"multiplying.m4v"];

Resources