here i created local root for document storage but i want to implement iCloud.so i need to create iCloud Root and also i will check if iCloud available or not. if it is possible to create like Method. here i added my local root method code
- (NSURL *)localRoot {
if (_localRoot != nil) {
return _localRoot;
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *artdirectoryPath = [documentsDirectory stringByAppendingPathComponent:#"/Me"];
_localRoot=[[NSURL alloc]initFileURLWithPath:artdirectoryPath];
return _localRoot;
}
You find the iCloud "root" directory using:
NSURL *url = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
If you have only one ubiquity container for your app, you can pass nil for the argument, otherwise you must pass the container identifier.
You can also use this to check if iCloud is available to your app-- if it returns nil, you can't use iCloud. It's not the best way to check for availability though, because it can block for a while. For a quick, non-blocking check, use this:
id token = [[NSFileManager defaultManager] ubiquityIdentityToken];
And then be sure to observe NSUbiquityIdentityDidChangeNotification in case the availability changes.
However: this is nowhere near enough to start using iCloud for documents. You can't just read/write files in that directory and have iCloud do the right thing. At an absolute minimum you'll need:
NSMetadataQuery to find documents that exist on the cloud server but are not downloaded locally.
-[NSFileManager startDownloadingUbiquitousItemAtURL:error:] to tell iCloud to begin downloading documents you've found with the metadata query.
Coordinated access via NSFileCoordinator, and notifications of changes via NSFilePresenter.
Apple has a lot of documentation, and videos from WWDC that will help with this.
Related
I want to add a sqlite database to XCode 4 (applies to XCode 3 too). Tutorials state adding the .db file to the resources folder, and I suppose this gets copied to ~/Library/Application Support/iPhone Simulator/4.2/Applications/{some-id}/Documents/ during build where you can find the file with NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)
etc.
However, XCode 4 doesn't have a visible resources folder.
I've tried adding a file with the Add File... command, and then it appears in Targets > AppName > Copy Bundle Resources, but always an empty .db file appears in the above documents folder (which I then manually replace - obviously not the correct approach!)
(due to the nature of the data I'm sticking with sqlite over CoreData)
you have to start adding your db in your project-xcode, so it will be added in your bundle folder, where you can find it via code:
[NSBundle mainBundle]
It's the only folder where you can add files via xcode when you "build" your app (eventually with subfolders, but not "system" folders as "documents") now you just need to keep in mind that the main bundle folder is just "read only", so you cant use your db there with write privileges.
So the normal way is:
1) when you wanna use your db, check via code if it exists in the app:documents folder.
Of course the first time it doesn't, so
2) copy it from the main bundle
- (BOOL)transferDb {
NSError **error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"yourData.db"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
NSString *bundle = [[ NSBundle mainBundle] pathForResource:#"preferenze" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath:path error:error];
return YES;
}
return NO;
}
3) use it (r/w) in the documents folder
ps:
and (of course) keep in mind that when you use/edit/write the db in iPhone/simulator, maybe adding records, the one in the main bundle and of course the one in your mac/project won't be updated, no records will be added to it, so if for any reason you delete your app on iPhone/simulator (or "clean all targets" by the xCode "build" menu) the check/copy method will copy the "virgin" db in the documents folder again, so you will loose all your changes...
Be careful with the amount of data you are putting into the Documents folder, this is meant for user data and since this data will be backed up using iCloud Apple have limited the amount of data an app can store and use in the Documents folder.
My app was rejected for using a 6MB SQLite database in this way. Instead copy it to the caches directory: NSCachesDirectory.
Or prevent the file from being backed up: https://developer.apple.com/library/ios/#qa/qa1719/_index.html
I want to use pathForResource, but it doesn't look like it will create the path if one doesn't exist. Therefore I'm trying to create one manually by doing the following:
NSString *path = [NSString stringWithFormat:#"%#/%#.plist",[[NSBundle mainBundle] bundlePath],#"myFileName"];
I'm creating files dynamically, so I need to access them after I have Build and Run the application. But it puts the project in a unique id folder so the path comes out to something like:
/Users/RyanJM/Library/Application Support/iPhone Simulator/3.0/Applications/80986747-37FD-49F3-9BA8-41A42AF7A4CB/MyApp.app/myFileName.plist
But that unique id changes every time I do a build. What is the proper way to create a path that I can get to every time (even in the Simulator)?
Thanks.
Update: edited the question, hopefully to help anyone who comes across it in the future.
Update: IWasRobbed answered the proper way to get create a path URL. But the the best answer I've been able to find is from Brad Parks. Though, I do wish there was a cleaner way.
With the way you phrased your question, this is how you read a plist that has been included in the bundle before build:
NSString *propertyListPath = [[NSBundle mainBundle] pathForResource:SomeString ofType:#"plist"];
If you want to access the directories that each app has as a unique storage area for a file that you create AFTER build, you use this:
#define kFilename #”data.plist”
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
Then you can check for it and do some data handling here:
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
// do something with data here
}
You would save yourself a lot of trouble if you bought/read Beginning iPhone 3 Development specifically chapter 11 where he goes over data persistence (which is where this example came from). It's a great book.
i deployed my App to my iPhone and get
Unknown error calling sqlite3_step (8: attempt to write a readonly database) eu
on Insert / Update Statements.
On the Simulator it all works like it should.
My sqlite Database is placed in the Resource Folder (Xcode).
Thanks for help!
Your application bundle is not writable on the iPhone. You MUST copy the file somewhere else, like your documents folder. It works in the simulator because the Mac does not enforce all the sandboxing restrictions the iPhone does.
You can copy your database from the application bundle directory to the Documents directory in viewDidLoad. You can read/write from/to your database in the Documents directory after this. Of course, you need to check if the database in the Documents directory exist before you do the copy in order not to overwrite it the next time you bring up the app.
Assuming you have defined your database name '#define kFilename #"yourdatabase.db"' in the .m file.
In viewDidLoad add:
// Get the path to the main bundle resource directory.
NSString *pathsToReources = [[NSBundle mainBundle] resourcePath];
NSString *yourOriginalDatabasePath = [pathsToResources stringByAppendingPathComponent:kFilename];
// Create the path to the database in the Documents directory.
NSArray *pathsToDocuments = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [pathsToDocuments objectAtIndex:0];
NSString *yourNewDatabasePath = [documentsDirectory stringByAppendingPathComponent:kFilename];
if (![[NSFileManager defaultManager] isReadableFileAtPath:yourNewDatabasePath]) {
if ([[NSFileManager defaultManager] copyItemAtPath:yourOriginalDatabasePath toPath:yourNewDatabasePath error:NULL] != YES)
NSAssert2(0, #"Fail to copy database from %# to %#", yourOriginalDatabasePath, yourNewDatabasePath);
}
Good luck!
aobs
I am posting this question because I had a complete answer for this written out for another post, when I found it did not apply to the original but I thought was too useful to waste. Thus I have also made this a community wiki, so that others may flesh out question and answer(s). If you find the answer useful, please vote up the question - being a community wiki I should not get points for this voting but it will help others find it
How can I get a path into which file writes are allowed on the iPhone? You can (misleadingly) write anywhere you like on the Simulator, but on the iPhone you are only allowed to write into specific locations.
There are three kinds of writable paths to consider - the first is Documents, where you store things you want to keep and make available to the user through iTunes (as of 3.2):
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
Secondly, and very similar to the Documents directory, there is the Library folder, where you store configuration files and writable databases that you also want to keep around, but you don't want the user to be able to mess with through iTunes:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryDirectory = [paths objectAtIndex:0];
Note that even though the user cannot see files in iTunes using a device older than 3.2 (the iPad), the NSLibraryDirectory constant has been available since iPhoneOS 2.0, and so can be used for builds targeting 3.0 (or even earlier if you are still doing that). Also the user will not be able to see anything unless you flag an app as allowing users to modify documents, so if you are using Documents today you are fine as long as you change location when updating for support of user documents.
Last there is a cache directory, where you can put images that you don't care exist for the long term or not (the phone may delete them at some point):
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
BOOL isDir = NO;
NSError *error;
if (! [[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&isDir] && isDir == NO) {
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:&error];
}
Note that you have to actually create the Caches directory there, so when writing you have to check and create every time! Kind of a pain, but that's how it is.
Then when you have a writable path, you just append a file name onto it like so:
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"SomeDirectory/SomeFile.txt"];
or
NSString *filePath = [cachePath stringByAppendingPathComponent:#"SomeTmpFile.png"];
Use that path for reading or writing.
Note that you can make subdirectories in either of those writable paths, which one of the example string above is using (assuming one has been created).
If you are trying to write an image into the photo library, you cannot use file system calls to do this - instead, you have to have a UIImage in memory, and use the UIImageWriteToSavedPhotosAlbum() function call defined by UIKit. You have no control over the destination format or compression levels, and cannot attach any EXIF in this way.
Thanks to Kendall & Dave, above, and I thought this amendment was useful to bring up. When using for one-off debug code, I used this trick from Mike Ash's NSBlog to eliminate the temporary variables isDir & error, minimizing the number of lines and making the verbosity almost bearable:
NSFileHandle *dumpFileHandle = nil;
#ifdef DEBUG
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&(BOOL){0}])
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:YES attributes:nil error:&(NSError*){nil}];
NSString *dumpPath = [cachePath stringByAppendingPathComponent:#"dump.txt"];
[[NSFileManager defaultManager] createFileAtPath:dumpPath contents:nil attributes:nil];
[(dumpFileHandle = [NSFileHandle fileHandleForWritingAtPath:dumpPath]) truncateFileAtOffset:0];
#endif
if (dumpFileHandle) [dumpFileHandle writeData:blah];
Is the NSSearchPathForDirectoriesInDomains function still the best way to get the path of the iPhone Documents directory? I ask because most topics I see on this are dated last year, and it still seems like a pretty cumbersome way of getting to a directory that is used commonly on iPhones. You'd think that there'd be a convenience method for this by now, similar to NSBundle's bundlePath, executablePath, etc.
Just to be clear, this means calling "NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)" and getting back an array with the Documents path at index 0.
The Core Data-based application template in Xcode provides this method:
- (NSString *)applicationDocumentsDirectory {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
So it would seem that Apple continues to endorse getting the documents directory that way. You could put it into a category, I suppose, but I have found it is enough to include that method in the small handful of classes in a given app that need to do work in the documents directory. If you're doing a lot of file stuff all over the place, you might consider refactoring your code a bit to confine those tasks to one or two manager classes.
For me, at least, the third or fourth time I said "Hey, getting the docs directory is a pain in the neck" was the point where I realized some opportunities to shift the file juggling into a dedicated class.
The current Core Data iOS app template in Xcode provides this method:
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
This works for me, pretty short and sweet
#define kDOCSFOLDER [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"]
Cheers!