How do I fix Cocoa error 513? - iphone

I have an iPhone application in which a number of domain objects are populated with user-entered data. In order to restore state after being interrupted, these objects implement the NSCoding protocol and are written to disk (the Documents directory) in the applicationWillTerminate message. Then, when the application is launched again, the bytes of data are loaded up and the domain objects are repopulated. Code to get the documents directory is as follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
This worked great in the simulator but once I deployed the app to the iPhone it stopped working. The reason why is iPhone error code 513 - which apparently means "permission denied." There is a note in the iPhone dev docs that explain a little bit more -
On the device, the returned path
(documentsDirectory) is similar to the
following:
/var/mobile/Applications/30B51836-D2DD-43AA-BCB4-9D4DADFED6A2/Documents
However, on the Simulator, the
returned path takes the following
form:
/Volumes/Stuff/Users/johnDoe/Library/Application
Support/iPhone
Simulator/User/Applications/118086A0-FAAF-4CD4-9A0F-CD5E8D287270/Documents
This is the exact behavior that I'm seeing. I'm not really sure how this relates to getting a permission denied error and what I can do to fix it. It does say below -
To read and write user preferences,
use the NSUserDefaults class or the
CFPreferences API. These interfaces
eliminate the need for you to
construct a path to the
Library/Preferences/ directory and
read and write preference files
directly. For more information on
using these interfaces, see “Adding
the Settings Bundle.”
If your application contains sound,
image, or other resources in the
application bundle, you should use the
NSBundle class or CFBundle opaque type
to load those resources. Bundles have
an inherent knowledge of where
resources live inside the application.
In addition, bundles are aware of the
user’s language preferences and are
able to choose localized resources
over default resources automatically.
For more information on bundles, see
“The Application Bundle.”
I don't see how I can use the Application Bundle to load bytes of data though. Any help or examples?

Not sure how this works, but apparently using stringByAppendingPathComponent instead of stringByAppendingString for path creation fixed the problem.

The paragraph related to the application bundle refers to:
NSString *path = [[NSBundle mainBundle] pathForResource:#"somefile" ofType:#"png"];
This and other methods from NSBundle allow you to refer resources from inside the application bundle without actually knowing where the application bundle is located.
Disclaimer: I haven't worked with the iPhone.
With the disclaimer in mind, in plain OS X it's considered bad form to write stuff inside the application bundle. You save stuff under the user's Application Support directory -> see NSApplicationSupportDirectory.

I got this error (513) because the path was wrong. Double checking my path fixed the problem :)

Related

Can I save the downloaded files to NSCacheDictionary in iphone?

I am downloading some mp3 files through my application using NSURLConnection. Actually where can I save the downloaded file. Someone says that saving in to NSDocumentDirectory will lead to app rejection.
Can I save the file to NSCacheDictionary and retrieve this from itunes?
I used this bit of code to save files to NSCacheDictionary
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(
NSCachesDirectory, NSUserDomainMask, YES)
objectAtIndex: 0];
NSString *documentsDirectoryPath = [cachesPath stringByAppendingPathComponent:#"music.mp3"];
[receivedData writeToFile:documentsDirectoryPath atomically:YES];
Can I use like this?
If you save the files to NSCacheDictionary you will not be able to retrieve them from itunes.
Edit:
You can store the mp3 files to NSDocumentDirectory and set "do not backup" flag
for setting the flag you can check the Technical Q&A QA1719.
For additional information you can check the docs.
specifically:
Use this attribute with data that can be recreated but needs to
persist even in low storage situations for proper functioning of your
app or because customers expect it to be available during offline use.
This attribute works on marked files regardless of what directory they
are in, including the Documents directory.

NSHomeDirectory in iPhone unit test

When using NSHomeDirectory() inside a unit test I get:
/Users/hgpc/Library/Application Support/iPhone Simulator/5.0
I expected the actual home directory of the current application. Something like:
/Users/hgpc/Library/Application Support/iPhone Simulator/5.0/Applications/6D1091C6-02AE-4803-A7DF-D623D0F89579
What am I doing wrong?
To solve this, set the value of Bundle Loader within your Unit Test target build settings to:
$(BUILT_PRODUCTS_DIR)/MyAppName.app/MyAppName
and also your Test Host to:
$(BUNDLE_LOADER)
you should then find NSHomeDirectory() returns the right value.
Unfortunately, while in a unit-test (or logic test) - you're not really "in your app" (i.e. its sandbox). That's why stuff like NSDocumentDirectory or [NSBundle mainBundle] will not work.
If it works for you, I'd just go ahead and create a "Documents" folder in
/Users/hgpc/Library/Application Support/iPhone Simulator/5.0
You might want to do this in your test's setUp, that way you can delete it in tearDown.
If that doesn't work because your tests depend on stuff already being in your app's NSDocumentDirectory, you might want to re-think your test a little, as they should all be self-contained (i.e. install all resources from your bundle in setUp)
You could also use NSLibraryDirectory instead of NSDocumentDirectory, depending on what it is that you want to test.
If you'd like to customised your test target to Application test you should add params mentioned by Andrew Ebling:
$(BUILT_PRODUCTS_DIR)/MyAppName.app/MyAppName
and also your Test Host to:
$(BUNDLE_LOADER)
If you don't have this params that means your tests is logic. But if you add this params you change tests to Application tests and notice that AppDelegate will start did you run tests.
see link for more information.
So you can use NSHomeDirectory( for Application tests only.
On an iOS device you should use
NSArray *path = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirPath = [paths objectAtIndex:0];

Can't find momd file: Core Data problems

Aw geez! I screwed something up!
I'm a Core Data noob, working on my first iOS app. After much Stack Overflowing I'm using this code:
NSString *path = [[NSBundle mainBundle] pathForResource:#"CoreData" ofType:#"momd"];
if (!path) {
path = [[NSBundle mainBundle] pathForResource:#"CoreData" ofType:#"mom"];
}
NSAssert(path != nil, #"Unable to find Resource in main bundle");
CoreData is the name of my app.
I've tried to put in initial data into the app by finding the path to the sqlite file in my iPhone simulator, and then going and inserting into that sqlite file. But at some point, I moved the sqlite (thinking it would create a fresh copy), deleted the app from the simulator, and the sqlite file is gone. I'm not sure if I'm leaving out some part of the process (this was a few hours ago) but the end result is that everything is screwed up.
How do I resubstantiate this sqlite / momd file? "Clean" and "Clean all targets" are grayed out.
I'm happy to post the relevant code from my app that would help shed some light on this problem but there's tons of code relating to Core Data which I don't understand, so I'm not sure what part to post! Any help is greatly appreciated.
Here are a few recommendations:
The code you posted to get the .mom(d) file is not exactly the recommended way. Use mergedModelFromBundles instead, as in
self.managedObjectContent= [NSManagedObjectModel mergedModelFromBundles:nil];
It takes care of getting the path, choosing/merging the correct mom or momd, and initializing of the MOC all by one step. You should use this. But note that you need to clean the build process once in a while, as discussed in this SO question/answer.
This shows that, although crawling through StackOverflow is often good, that's not the best approach when you deal with a big framework like CoreData.
Honestly, take a day and read the documentation from the start to the end. You might want to google bits of code and to start coding immediately, but reading through the documentation definitely saves the development time considerably in the long run.
Also, Marcus Zarra's CoreData book (see here) helped me a lot. It might look expensive, but it was totally worth while.
On a different topic, I don't think it a good strategy to put the pre-cooked sqlite file into the simulator's directory (inside ~/Library/Application Support/iPhone Simulator/) even for development purpose... because it doesn't work on the real device. If you need to do so, put the sqlite file as a resource of the app, and copy it at the launch time to either Documents or Caches, and use that afterwards.
If the clean targets menu items are grey, it could be that you have the app running.
The way you're getting the path to the mom/momd looks like you've tried to integrate CoreData after the fact (rather than opting to have it included when you created the project). I had the same issues as you probably do. The default code that looks for momd assumes it exists, and you should be able to, as well, if you add a version of your data model to the project.
To do that, select the xcdatamodel file, and in the Design menu select Data Model > Add Model Version. Then, you'll have CoreData.xcdatamodeld with a subtree containing Coredata.xcdatamodel and a new version. You can just delete the extra version and you'll have the hierarchy you need. If you build and look in the App bundle, you'll see the CoreData.momd directory with the mom inside.
Those steps aren't included in any of the CoreData tutorials I've found so far. I hope it's helpful!
Practical answer:
You might try the following code if you're having path issues. You'll also want to check that your xcdatamodel file is included as a resource for your target.
[[NSBundle bundleForClass:[self class]] pathForResource:#"CoreData" ofType:#"momd"]
Better answer: There's a distinction between a momd file, which represents your NSManagedObjectModel, and a sqlite file, which is used by your NSPersistentStore object. The best approach to working with CoreData is to let the SDK handle all the interaction with sqlite. The only thing you should need to set up regarding sqlite is telling your NSPersistantStore where to stash your sqlite file. The managed objects that you define in your managed object model will handle all the data insertion and updates for you.
If you're not familiar with the objects I've referenced, check out the apple docs for a very helpful overview. You'll definitely want to take the time to grasp the full CoreData stack, as the time investment will pay off in far less headaches if you work with Core Data the way Apple expects. It is a more abstracted design than many web developers are accustomed to, but trying to circumvent that abstraction by working directly with SQLite creates its own set of complexity.
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdBasics.html#//apple_ref/doc/uid/TP40001650-TP1
I found it very valuable to go look at my executable in the simulator "disk image".
If you head into ~/Library/Application Support/iPhone Simulator//Applications, you can see the apps on the phone. Figure out which one is yours (use find, or look at the date), and then browse into your target.app and look for the .momd file.
In my case, my momd name was different from my .xcdatamodeld file name, which was why core data couldn't load it. Once I corrected the name in the AppDelegate things started working correctly.
Note that the momd "file" seems to actually be a directory (like the .app setup).
--
In terms of adding the code to initialise core data; I did this too. I created a new blank project with core data enabled, and copied over all the initialisation steps for core data in the AppDelegate file. There's a whole bunch of stuff related to initialising the whole stack, so if that is not present, you need to put it there.
If you added a version to your model, change .mom to .momd
NSString *path = [[NSBundle mainBundle] pathForResource:#"CoreData" ofType:#"momd"];
if (!path) {
path = [[NSBundle mainBundle] pathForResource:#"CoreData" ofType:#"momd"];

calling audio in tableview from core data

i'd like to have the user tap a cell in the tableview and hear audio. the information for the cell is loaded from core data, so i'm assuming i put the path to the audio file in core data as an attribute with type 'string'.
i am completely lost as to where to go from here. how do i call the path? after a lengthy search, i haven't found much on the topic so any help is greatly appreciated.
You should store just the file name in the managed object and then reconstruct the file path each time.
In iOS the name of the app's directory is just a UUID and it is different on every install. Further, the system may change the directories UUID without warning. This is part of the security system.
Suppose you wanted to put the audio files in a directory called AudioFiles in the Library directory. You would do something like this:
NSString *fileName=//... file name from the managed object
NSArray *libraryPaths=NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libPath=[libraryPaths objectAtIndex:0];
NSArray *components=[NSArray arrayWithObjects:libPath,#"AudioFiles",fileName,nil];
NSString *theAudioFilePath=[NSString pathWithComponents:components];
See Low-Level File Management Programming Topics:Standard System Directories
Depending on how you play the audio files, you may need to convert the file path to a file URL.
Your question is quite wide. The best way for you to get started is to work through Apple's avTouch example. It will show you how to load and play audio files.

Downloading image into bundle?

I display text and images in a UIWebView. Content isn't always the same. I access images within the content using the bundle path. For an inbetween versions update of content, I'd like to allows users the ability to download new content (text & images). This new content will also display in a UIWebView. The problem is I will have to use a disk path rather than my common pattern of using the bundle path. Unless there is a way to repackage the image at runtime into the bundle.
Once the next app store update for the app is availble, all of the previously downloaded images will be in the app bundle. On this update, I'll write overwrite the previous content and use the bundle path for images. Content will be exactly the same minus the image path.
Can anyone give some insight into how this might work or a better approach?
So far as I know you cannot repackage the bundles on the iPhone once your app has been released to the App Store. So go the other way, and put the data from the bundle on the filesystem so you can change it at runtime.
My usual technique for this stuff is:
bundle up the initial data
have a routine that checks for the presence of a versioned file on the iPhone's filesystem at startup
if that routine doesn't find the current version of the file, copy all the data into the iPhone's filesystem
reference the data from the filesystem in my app, rather than using the bundle path
So, essentially your bundle is just a delivery mechanism, a way to preload the filesystem with the stuff you are going to need. Once it's on the filesystem you can change anything you wish.
Agree with Benjamin - you cannot change your bundle contents.
Instead you can (and should) save your downloaded contents to Documents folder of your application sandbox. You can get the path to it this way:
// Look in Documents for an existing plist file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
Moreover contents of this folder persists during updates so it may be not necessary to put downloadable optional contents to the application bundle.