iCloud Drive Folder doesn't show my app's folder.
This is how I send a file to iCloud Drive:
- (IBAction)btnStoreTapped:(id)sender {
// Let's get the root directory for storing the file on iCloud Drive
[self rootDirectoryForICloud:^(NSURL *ubiquityURL) {
NSLog(#"1. ubiquityURL = %#", ubiquityURL);
if (ubiquityURL) {
// We also need the 'local' URL to the file we want to store
//NSURL *localURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Photo" ofType:#"pdf"]];
NSURL *localURL = [self localPathForResource:#"document" ofType:#"doc"];
NSLog(#"2. localURL = %#", localURL);
// Now, append the local filename to the ubiquityURL
ubiquityURL = [ubiquityURL URLByAppendingPathComponent:localURL.lastPathComponent];
NSLog(#"3. ubiquityURL = %#", ubiquityURL);
// And finish up the 'store' action
NSError *error;
if (![[NSFileManager defaultManager] setUbiquitous:YES itemAtURL:localURL destinationURL:ubiquityURL error:&error]) {
NSLog(#"Error occurred: %#", error);
}else{
NSLog(#"Succeed");
}
}
else {
NSLog(#"Could not retrieve a ubiquityURL");
}
}];
}
- (void)rootDirectoryForICloud:(void (^)(NSURL *))completionHandler {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *rootDirectory = [[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]URLByAppendingPathComponent:#"Documents"];
if (rootDirectory) {
if (![[NSFileManager defaultManager] fileExistsAtPath:rootDirectory.path isDirectory:nil]) {
NSLog(#"Create directory");
[[NSFileManager defaultManager] createDirectoryAtURL:rootDirectory withIntermediateDirectories:YES attributes:nil error:nil];
}
}
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(rootDirectory);
});
});
}
- (NSURL *)localPathForResource:(NSString *)resource ofType:(NSString *)type {
NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *resourcePath = [[documentsDirectory stringByAppendingPathComponent:resource] stringByAppendingPathExtension:type];
return [NSURL fileURLWithPath:resourcePath];
}
Actually I did everyting explained in this post
Everything looks like fine. I can upload files successfully and I can show them in my computer via terminal.
And I can see the files I just uploaded from my iphone.
But nothing shown in my iCloud Drive folder in my mac or icloud.com. Why they are not showing my app's folder even everything looks like fine and there is no error?
I have tried the same code in swift and got the same problem.
This is what worked for me :
1) Find the following lines in your info.plist source code
<key>CFBundleVersion</key>
<string>1</string>
2) Increase the version number. If it is 1, increase it to 2. Do the same whatever the version number is.
3) If needed, uninstall the app from your device and re-run it after following the above two steps
The only thing that worked for me (I had exactly the same issues) in an existing app (so I can't change the bundle ID) was to create a Documents subfolder under the URLForUbiquityContainerIdentifier folder and write all my files there.
Once I did this, they appeared in Finder on my Mac. However they did not appear in a Documents subfolder of my app's folder in iCloud Drive, they appeared directly in the app folder.
But nothing shown in my iCloud Drive folder in my mac or icloud.com.
Why they are not showing my app's folder even everything looks like
fine and there is no error?
Is this a new app or a newly created iCloud container?
Then the reason is: NSUbiquitousContainerIsDocumentScopePublic=true only gets in effect once the app (and the entitled iCloud container) has been gone through review and approval.
I can't remember where I read that "missing piece of information" (at least i didn't get it from the Apple documentation).
Another example from my experience, which seems to prove this: In an existing app (with the "old style - TeamIdentifierPrefix" iCloud container) i can make that container visible on iCloud drive. But a new container won't be visible in iCloud Drive "to the public" (even tough I can see it using Terminal.app on my Mac). Once the app (with the new container) has been approved and is available on the AppStore, you can play again with the Info.plist (and make the new container visible/hidden, ...)
Bump CFBundleVersion in Info.plist.
Create a new iCound container on https://developer.apple.com/account/ios/identifier/cloudContainer
Specify custom containers in Xcode. Untick the old containers and tick the new created container.
For me it was forgetting to add the above entries in the app's info.plist for the new container.
My case was that I have missed that your container at Info.plist should be wrapped to NSUbiquitousContainers dictionary.
I have fixed and increase build version, the folder did appear.
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com....</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>The name at iCloud folder</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
Related
So I have an app with a share extension each with their own target. The core of the apps' bundle identifier is com.company.app and the share extensions is com.company.app.share. Each have their own entitlements file, with the core apps being App Groups -> group.com.company.app and the share extension being the same. Both of the bundle identifiers are listed on the apple developer website but for some reason,
I don't seem to have the right permissions to write to the shared private/var/mobile/Containers/Shared/AppGroup/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Library/Caches/ directory.
The error message I get is:
You don’t have permission to save the file “Caches” in the folder “Library”.
The directory is being saved like this:
GetZipURLInItems(self.extensionContext.inputItems, ^(NSURL *url, NSError *error) {
if (error == nil) {
NSURL *containerURL = [[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:GroupIdentifier] URLByAppendingPathComponent:#"Library/Caches"];
NSLog(#"EXT: containerURL: %#", containerURL);
[WPZipArchive unzipFileAtPath:[url path] toDestination:[containerURL absoluteString]];
[self listDirectoryAtPath:[containerURL absoluteString]];
}});
What am I'm missing here?
The issue was actually using [url absoluteString] vs [url path], changing all the urls to path solved the permissions error.
Fairly self explanatory.
We can view the app's documents, library, tmp directories via Window > Devices (this has been the case forever).
But when app extensions came on the scene with iOS8, the App Group shared container came with them. How can I view its contents?
Edit: to clarify, I'm not asking how to interact with this directory in code. I'm asking about how to interact with this directory in the context of Finder.
Get App Group Path
#define APP_GROUP_PATH [[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"yourGrpupId"] path]
Following code to view Shared container data
NSLog(#"Shared container data:%#",[self listFileAtPath:APP_GROUP_PATH]);
-(NSArray *)listFileAtPath:(NSString *)path
{
int count;
NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
for (count = 0; count < (int)[directoryContent count]; count++)
{
NSLog(#"File %d: %#", (count + 1), [directoryContent objectAtIndex:count]);
}
return directoryContent;
}
You can't exactly interact with it in a finder way. Altough you can view the container and download it.
Go to Devices and select the device
You can download the container with the three dotted button under the app that has the container setup.
You can also show or download the container.
You can print the path to the directory in your code, then open the path in finder using Shift+CMD+G.
print(urlToAFileInTheDirectory.absoluteString)
where urlToAFileInTheDirectory is a URL that contains the path to a file in the shared documents directory.
This works in the simulator in Xcode 13. I'm not sure if it works on an actual device.
I am new to iOS development. My app got rejected from the review, stating the following reason,
2.23 Apps must follow the iOS Data Storage Guidelines or they will be rejected
We found that your app does not follow the iOS Data Storage Guidelines, which is required per the App Store Review Guidelines.
I am not storing my DB file in documents directory. Here's my code,
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [libraryPath stringByAppendingPathComponent:#"DatabaseFolder"];
NSURL *pathURL = [NSURL fileURLWithPath:path];
BOOL isDirectory = NO;
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]) {
if (isDirectory) {
return pathURL;
} else {
// Handle error. ".data" is a file which should not be there...
[NSException raise:#"'Private Documents' exists, and is a file" format:#"Path: %#", path];
}
}
NSError *error = nil;
if (![[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]) {
[NSException raise:#"Failed creating directory" format:#"[%#], %#", path, error];
}
return pathURL;
How to reproduce a crash or bug that only App Review or users are seeing?
The iOS Data Storage guideline document (login required to view) says,
Everything in your app’s home directory is backed up, with the exception of the application bundle itself, the caches directory, and temp directory.
This means even your NSLibraryDirectory directory contents gets backed up to iCloud. To resolve this you have following options,
Use the /tmp directory for storage
Use the /Caches directory for storage
Only use the /Documents directory for user-generated content that cannot be re-created.
Set the do not backup attribute on the file using setResourceValue:forKey:error: method of NSURL.
Here is how you can mark a resource for not backing up to iCloud.
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
NSError *error = nil;
BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success){
NSLog(#"Error excluding %# from backup %#", [URL lastPathComponent], error);
}
return success;
}
Hope that helps!
I assume the reviewer doesn't like that you are storing the database in the library folder and there within one created by you. If you read the mentioned guidelines you'll see that you shouldn't store there.
Data that can be downloaded again or regenerated should be stored in the /Library/Caches directory. Examples of files you should put in the Caches directory include database cache files and downloadable content, such as that used by magazine, newspaper, and map applications
I had this problem for a while also. So I made a class to handle this for me. There are different rules of where you can store stuff in different OS's. So my class checked the OS and returned a proper data director for each one and even handled the migration of data from one location to the other if the OS was updated.
But pretty much today you could just support the 5.1 and up location and be fine.
The key is that you need to set your do not backup attribute also.
I just put in a github here: https://github.com/badweasel/BWFileManager
I had read that I can mark folders with "do not backup" attribute.
As I understand, in such case all contents of directory will be excluded from backups.
In our app we need to exclude from backup all files in Documents directory (the files can be added or deleted from Documents during app execution).
We need to store our files in Documents because we use "Application supports iTunes file sharing" feature.
Can we mark Documents directory with "do not backup attribute"?
Does Apple permits this?
Could this become the reason to reject our app?
According to apple
In iOS 5.0 and earlier, put files in the
/Library/Caches directory to prevent them from being
backed up
In iOS 5.0.1 and later, put files in the
/Library/Application Support directory and apply the
com.apple.MobileBackup extended attribute to them. This attribute prevents the files from being backed up to iTunes or iCloud. If you
have a large number of support files, you may store them in a custom
subdirectory and apply the extended attribute to just the directory.
As far as I know
You can not mark documents directory with do not back up attribute
1)you may mark up the individual files inside the documents directory using below code snippet
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *)filePathString {
NSURL *fileURL =
[NSURL fileURLWithPath:filePathString];
assert([[NSFileManager defaultManager]
fileExistsAtPath: [fileURL path]]);
NSError *error = nil;
BOOL success = [fileURL setResourceValue:[NSNumber numberWithBool:YES]
forKey:NSURLIsExcludedFromBackupKey
error:&error];
return success;
}
2)You may create a subdirectory inside documents folder and apply extended attribute to that.
you may set extended attribute using the below syntax.
int result = setxattr(path, attrName, myDataBytes, [myData length], 0, 0);
you can find more information on reading and writing extended attributes in the following link
I hope this helps
My app is using a core data SQLite database. I would like to enable my users to use iCloud to sync it between devices - and I was thinking I could use UIManagedDocument.
I subclassed it, following Apple's documentation, and it is works when a new persistent store file needs to be created. However, when I try to use it to open my old persistent store file, I get the following exception thrown error:
"UIManagedDocument can only read documents that are file packages"
Does this mean that I need to migrate the old persistent store to a new store managed by UIManagedDocument? If so, do I need to do this manually (i.e. read each record one-at-a-time from the old store and write it into the new one)?
Thanks in advance!
UIManagedDocument creates packages(folders) rather than atomic stores. The store is still there but its buried in the package. If you right click on the file that is created in your Documents folder in the simulator you'll be able to see the structure. The default is
mydocument.foo
-> StoreContent
-> persistentStore
What you need to do is create a new extension for your app file type so for example if your database extension is .myappdb you need to create a new document type in your project settings which might be .myappdbw. You can copy all settings from the entry for .myappdb
Next at the point where you handle opening your legacy document at mydocumenturl instead of passing that to your persistent store co-ordinator you create the directory structure above.
NSURL *newurl = [[mydocumenturl URLByDeletingPathExtension] URLByAppendingPathExtension:#"myappdbw"];
NSURL *desturl = [newurl URLByAppendingPathComponent:#"StoreContent"];
[[NSFileManager defaultManager] createDirectoryAtURL:desturl withIntermediateDirectories:YES attributes:nil error:NULL];
NSURL *finalurl = [desturl URLByAppendingPathComponent:#"persistentStore"];
and then move the legacy database into the folder system you have created
[[NSFileManager defaultManager] moveItemAtURL:mydocumenturl toURL:finalurl error:NULL];
and then you can pass the bundle url to UIManagedDocument
UIManagedDocument *doc = [[UIManagedDocument alloc] initWithFileURL:newurl];
A link which will be useful for the iCloud integration is
http://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-iCloudCoreData/_index.html
Its all a bit mysterious as the most of the promised sample code has failed to appear so far but on the other hand its mostly fairly simple to deduce. Have a look at WWDC2011 sessions 107,116 and 315 for more hints.
But note that if you are going to use this method for migrating your legacy docs DONT set the NSPersistentStoreUbiquitousContentNameKey at point you migrate because the package changes when you do. The doc above describes it quite well.
Thanks for this tip. I think I found an even simpler solution.
I just create a new UIManagedDocument with a different filename than my old persistent store location.
In my subclassed UIManagedDocument, I override the configurePersistentStoreCoordinatorForURL method and do the migration once there:
- (BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)storeURL ofType:(NSString *)fileType modelConfiguration:(NSString *)configuration storeOptions:(NSDictionary *)storeOptions error:(NSError **)error
{
// If legacy store exists, copy it to the new location
NSFileManager* fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:legacyPersistentStoreURL.path])
{
NSError* thisError = nil;
[fileManager copyItemAtURL:legacyPersistentStoreURL toURL:storeURL error:&thisError];
[fileManager removeItemAtURL:legacyPersistentStoreURL error:&thisError];
}
return [super configurePersistentStoreCoordinatorForURL:storeURL ofType:fileType modelConfiguration:configuration storeOptions:storeOptions error:error];
}