Cydia App Documents Folder Not Created - objective-c

I have been working on a Core Data iOS app that works perfectly through Apple's "channels" – iOS simulator & Xcode installing, yet when I try to manually install it onto a device, the application crashes. My main goal is to put the app on Cydia.
A guide to preparing an app for Cydia
I read this article, and I at the bottom it said
Appstore app has a Documents folder that is created by the installation process. Jailbroken app does not. It is up to the app to create its own folder. Should you need this type of folder, you must create this with a simple mkdir command in your applicationDidFinishLaunching function. Just add a simple function: mkdir(“/var/mobile/Library/YOURAPPNAME”, 0755); If the folder already exists, no harm done. You want to do this because the install process runs as user root and the app runs as user mobile. If Cydia does this for you then the folder will have the incorrect permissions.
I do not know much about how exactly Core Data works, but I know the Core Data "database" is stored in the Documents folder, and I now believe this is the cause of the crash of my app.
The mkdir function did not work in creating a Documents folder.
How would I go about creating a Documents folder, and how would I get it to work with Core Data, ensuring the database is loaded from this folder I created?
Thanks in advance

Most likely you are still using the AppStore friendly Documents path in the methods which create and load the CoreData database file (I think Xcode will put these in your AppDelegate by default).
Check the method which loads the persistentStorageCoordinator and look for a line like this:
NSURL *storeUrl = [NSURL fileURLWithPath: [docPath stringByAppendingPathComponent:#"my_cool_app.sqlite"]];
And make sure that docPath is "/var/mobile/Library/My_Cool_App" and not originating from the standard AppStore friendly:
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [documentPaths objectAtIndex:0];
You might want to create a method which returns the proper Documents directory depending on what target you compile the app for:
+(NSString *)documentsDirectoryPath
{
#ifdef JAILBREAK
NSString *documentPath = #"/var/mobile/Library/My_Cool_App";
if (![[NSFileManager defaultManager] fileExistsAtPath:documentPath])
{
[[NSFileManager defaultManager] createDirectoryAtPath:documentPath
withIntermediateDirectories:NO
attributes:nil
error:NULL];
}
return documentPath;
#else
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [documentPaths objectAtIndex:0];
#endif
}

Related

iOS : I can't write my file

I want to write a file on my iPhone app. My file is in my project's folder, but when I try to reach it :
NSString *myFile = [[NSBundle mainBundle] pathForResource:#"myFile" ofType:#"txt"];
NSLog(#"%#",myFile);
I get the following path :
2012-06-13 17:36:56.398 MyFileApp[610:15203] /Users/Damian/Library/Application Support/iPhone Simulator/5.1/Applications/1FFD4436-DCCA-4280-9E47-F6474BEE0183/MyFileApp.app/myFile.txt
Why ?
Thanks for your advices
You ask:
Why?
It's that path because that's where the file is stored.
On the device it will be different.
Note that you can't write a file to that folder anyway. You should perhaps instead write to your app's documents folder:
//Get the documents folder
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
//Get the final path of your file.
NSString *finalPath = #"myfile.txt"];
//And actually write that file.
[[NSFileManager defaultManager] createFileAtPath:finalPath contents:/*fileData as NSData*/ attributes:nil];
As already said you can't write to the main bundle on an actual device.
To your other question:
When you run your app in the simulator xcode will copy your project to a folder in your library directory .. in your case to:
/Users/Damian/Library/Application Support/iPhone Simulator/5.1/Applications/
Every app you run in your simulator has a folder there. Apps are not run in the folders where you actually edit your code.

Preloading Documents into iOS App

The Situation:
I have an iOS app that deals with files and lets the user save, edit, open and perform various operations with these files. I'd like to be able to have some pre-made documents for the user to look at when they open the app (ex. a template) alongside their own custom documents.
The Problem:
How can I create a document (or template file) and have it appear in the Documents folder after the user installs my app and launches it (and all preceding times)?
Background:
This document (the one that'd be installed into the app's documents directory) is created by me, not the user.
I know that to do this you need to save it in your bundle, and then when your app runs for the first time silently copy it into the Documents Directory. Should I copy it in the appDidFinishLaunchingWithOptions method or in my viewDidLoad method and write logic to detect if it's the first time the app has run?
My Code:
At this webpage: http://textsnip.com/d35fbc    
But when it runs, it always says: "File does not exist [in documents folder]", then it tells me that it's being copied. The problem is that when I examine the app's documents folder it is never there, it's still in the bundle.
Why won't it copy with this code
How does this work?
the files must in fact be added to the app bundle, then copied silently when the app launches.
The following code copies a text file from the top level of my bundle to the documents directory if it doesn't already exist.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *txtPath = [documentsDirectory stringByAppendingPathComponent:#"txtFile.txt"];
if ([fileManager fileExistsAtPath:txtPath] == NO) {
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"txtFile" ofType:#"txt"];
[fileManager copyItemAtPath:resourcePath toPath:txtPath error:&error];
}
The only flaw in this code is that if the user deletes the file, it immediately reappears when they open the app again.
As you said, you include it in your app bundle (add it to your project and make sure it's part of your target). Then you can access it's path by calling something like this:
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"MyTemplateDoc"
ofType:#"extension"];
Then you copy it to your app's documents folder.
NSString *docPath = <a path in your documents folder>
NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtPath:bundlePath
toPath:docPath
error:&error];
if (error) {
// handle copy error.
}

NSFileManager works when built in Xcode as release, but not when ran as standalone OS X Cocoa app

I have the following function written to randomly pick a file from a directory. It works totally fine when I build the project in Xcode for release with the application that automatically opens. However, if I open the application from finder, pressing the button that triggers this function will cause my program to freeze then crash. The only thing I could think of was changing the argument to contentsOfDirectoryAtPath: to not have the ./, but either version has the same exact issue.
Looking at Console tells me that my program exited abnormally with a Floating Point Exception, but I have no idea what's causing it. Is there something jumping out to you guys that I'm not seeing? I only started learning/using objective-C and cocoa about a week ago, so this is all fairly new to me.
Thanks for taking a look at this...
- (NSMutableString*)setFilePathRandom{
NSArray* files;
NSFileManager* fileManager;
fileManager = [[NSFileManager alloc] init];
files = [fileManager contentsOfDirectoryAtPath:#"./Random Trippy Pics" error:NULL];
NSString* directoryPath = (NSMutableString*)[fileManager currentDirectoryPath];
NSString* fileName;
do{
fileName = [files objectAtIndex:(arc4random()%[files count])];
}while([fileName isEqualToString:#".DS_Store"]);
filePath = [NSString stringWithFormat:#"%#/Random Trippy Pics/%#",directoryPath,fileName];
[fileManager release];
return filePath;
}
When an OS X application is run from Xcode, its current directory is the path to the build folder. When run "normally", the current directory is /. So your program is looking for a directory at /Random Trippy Pics, which almost certainly doesn't exist. Where is that directory normally?
Edit:
You could get the directory in which the application is currently stored with this bit of code:
NSString *currentStoragePath = [[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent];
However, if the Random Trippy Pics directory is required by the application, you should store it in a known location -- preferably the application's Resource directory. Then you can get the contents with:
NSArray *files = [[NSBundle mainBundle] pathsForResourceOfType:nil inDirectory:#"Random Trippy Pics"];

File write with [NSBundle mainBundle] fails

I am trying to take content from one file and write it into another. I am reading fine, but I am not able to write it into another file.
I have a database of words. I want to separate the words into different files based on the number of letters. All four letter words go into one file, and so on. I added a txt file called "4letter" into my resources and the following is my code:
NSError *error;
//READ
NSString *dbFile = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"txt"];
NSString *test = [NSString stringWithContentsOfFile:dbFile encoding:NSUTF8StringEncoding error:&error];
//convert from string to array
NSArray *lines = [test componentsSeparatedByString:#"\n"];
NSFileHandle *logFile = nil;
logFile = [NSFileHandle fileHandleForWritingAtPath:[[NSBundle mainBundle] pathForResource:#"4letter" ofType:#"txt"]];
//Test if write works
for (int i=0; i<5; i++)
{
NSString *randomAnagram = [[lines objectAtIndex:i] lowercaseString];
[logFile writeData: [randomAnagram dataUsingEncoding: NSNEXTSTEPStringEncoding]];
}
In iOS, you can't write into a file in your app's bundle -- the entire bundle is read-only. Use a path into the Documents folder instead.
See special File System Programming Guide for better understnading.
In iOS, you can't write into a file in your app's bundle -- the entire bundle is read-only.
Consider reading iOS Data Storage Guidelines to better understand the purpose of directories below, in context of iCloud backup.
<Application_Home>/AppName.app
This is the bundle directory containing the app itself. Do not write
anything to this directory. To prevent tampering, the bundle directory
is signed at installation time. Writing to this directory changes the
signature and prevents your app from launching again.
<Application_Home>/Documents/
Use this directory to store critical user documents and app data
files. Critical data is any data that cannot be recreated by your app,
such as user-generated content. The contents of this directory can be
made available to the user through file sharing. The contents of this
directory are backed up by iTunes.
<Application_Home>/Library/
This directory is the top-level directory for files that are not user
data files. You typically put files in one of several standard
subdirectories but you can also create custom subdirectories for files
you want backed up but not exposed to the user. You should not use
this directory for user data files. The contents of this directory
(with the exception of the Caches subdirectory) are backed up by
iTunes. For additional information about the Library directory, see
“The Library Directory Stores App-Specific Files.”
See full list (tmp/, Documents/Inbox) in iOS Standard Directories: Where Files Reside
UPDATE
I use NSFileManager method URLForDirectory:inDomain:appropriateForURL:create:error:
Like Caleb said, you can't write to your app's directory, but you can write to your app's Documents folder. You can get it like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
Your app's bundle is read-only. There is two ways I could see:
1) Write in documents folder:
NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path =  [myPathList  objectAtIndex:0];
2) Use sqlite database. This is the same as 1 (you must save db in documents anyway), but you're using sqlite database. I think this is better than a lot of txt and plist files: here's a tutorial on the topic.
I use the following code :
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"set.txt"];
NSString *data=#"Kostas";
[data writeToFile:appFile atomically:YES];
NSString *myData = [NSString stringWithContentsOfFile:appFile];
NSLog(#"Data : %# ",myData);

Document Directory in iPod Touch and iPhone

I'm designing an application that reads data to the iPod touch/iPhone that is sent to it via multicast sockets with UDP and I need to store it as a file in a document directory. Does that exist on on the iPhone or iPod Touch? I know there is NSFileHandle and NSFileManager, which is what I plan on using, to take care of reading and writing to the file, but I'm not sure where the "My Documents" section of the iPod touch is if you know what I'm saying. I am not familiar with the iPod/iPhone file directory that well yet, so any help is appreciated! Is there some kind of "general" directory that all developers use to store their files in if they have any involved in their application?
You should use your application's Documents directory to store persistent files. You can get the path to the directory using this function, which Apple includes in their template for an application using Core Data:
/**
Returns the path to the application's documents directory.
*/
- (NSString *)applicationDocumentsDirectory {
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
More recently, the template for a Core Data application provides code like this:
- (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
If the returned NSArray from NSSearchPathForDirectoriesInDomains is empty, lastObject returns nil, so as a result, the code is shorter and cleaner.
One thing you should be aware of -- as of iOS 5, you shouldn't put non-user-generated-data to Documents directory. Your app may be rejected. Instead you should think of putting such logs in Caches directory. To get path of this one, you need to replace NSDocumentDirectory with NSCachesDirectory in the above example code.