I see it's apparently possible to submit apps without Lion installed, but my app downloads some large files, which need to be saved according to Apple storage guidelines.
Including the code below, I can't even compile due to Use of undeclared identifier 'NSURLIsExcludedFromBackupKey':
-(BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL{
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"5.1")) {
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;
}
}
How can I keep using this code while I've only got iOS 5.0 SDK in xcode?
(or is my desire moot because I literally cannot tell my app it supports iOS 5.1?)
(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO comes from https://stackoverflow.com/a/5337804/194309)
I added a definition of that string constant if the ios version is not 5.1.
Here is how my implementation looks like:
+ (BOOL)addSkipBackupAttributeToItemAtURL:(NSString*) path
{
if (SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(#"5.01"))
{
const char* folderPath = [_privateDirectoryPath fileSystemRepresentation];
u_int8_t attrValue = 1;
int result = setxattr(folderPath, kMobileBackupAttrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
}
else
{
#ifndef __IPHONE_5_1
#define NSURLIsExcludedFromBackupKey #"NSURLIsExcludedFromBackupKey"
#endif
NSError *error = nil;
NSURL* url = [NSURL fileURLWithPath:path];
ASSERT_CLASS(url, NSURL);
BOOL success = [url setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey
error: &error];
//Assert success
return success;
}
}
Related
In my application , i want to set default system message tones for upcoming message settings. How can i open default device alertTones list.
I have tried following code, but its not returning any sound.
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSURL *directoryURL = [NSURL URLWithString:#"/System/Library/Audio/UISounds"];
NSArray *keys = [NSArray arrayWithObject:NSURLIsDirectoryKey];
NSDirectoryEnumerator *enumerator = [fileManager
enumeratorAtURL:directoryURL
includingPropertiesForKeys:keys
options:0
errorHandler:^(NSURL *url, NSError *error) {
// Handle the error.
// Return YES if the enumeration should continue after the error.
return YES;
}];
for (NSURL *url in enumerator) {
NSError *error;
NSNumber *isDirectory = nil;
if (! [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&error]) {
// handle error
}
else if (! [isDirectory boolValue]) {
[audioFileList addObject:url];
}
}
Please Help.
Checked this link https://github.com/TUNER88/iOSSystemSoundsLibrary. I think are you using this reference code and its working I have tested in the iPhone. I think you were testing in the iPhone Simulator. its not working in the simulator. So, Test in the Device its working fine
My app had been rejected the 2nd times and I lost 3 weeks :(
The first submit, I excluded ONLY DIRECTORIES from being backed-up in iCloud. Apple rejected...
The second submit, I excluded DIRECTORIES & PICTURES downloaded from being backed-up in iCloud. Apple again rejected... Apple also complaint that I have no "Restore" feature for my In-App purchase, while in fact, I do have a "Restore" button and it worked when I tested it.
I've done as Apple had suggested by excluding the file from being backedup using NSURLIsExcludedFromBackupKey. There was an interesting comment made by Macmade's on stackoverflow here:
sometimes Apple reviewers think your data can be re-generated, when
it's not. Then you'll have to explain why the data has to be backed-up
How often do the reviewer misunderstood and we have to explain to them that the content is required offline & not re-generateable?
Here is the code I used to exclude my files & directories from iCloud. Do you spot any problems?
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
// There's a chance the download failed, but don't assert here
//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;
}
//Download picture from Google and exclude it from being backed-up in iCloud
- (void)downloadFetcher:(GTMHTTPFetcher *)fetcher
finishedWithData:(NSData *)data
error:(NSError *)error
{
if (error == nil) {
// successfully retrieved this photo's data; save it to disk
GDataEntryPhoto *photoEntry = [fetcher propertyForKey:#"photo entry"];
// Create album directory if it doesn't already exist
NSString *path = [self findOrCreateApplicationSupportSubPath:[photoEntry albumTitle]];
path = [path stringByAppendingPathComponent:[[photoEntry title] stringValue]];
if (path != nil) {
// Write to disk
BOOL didSave = [data writeToFile:path
options:NSDataWritingAtomic
error:&error];
if (didSave) {
// Exclude file from being backed up in iCloud
NSURL *url = [NSURL fileURLWithPath:path];
BOOL excludeBackupResult = [self addSkipBackupAttributeToItemAtURL:url];
if (excludeBackupResult == NO) {
NSLog(#"Error excluding FILE from iCloud: %#", path);
}
// Update the download progress bar
_downloadedFileCounter = _downloadedFileCounter + 1;
float progress = _downloadedFileCounter / kMaleWireframeImagesTotal;
[self updateProgress:progress];
// The download completed. -2 just incase a package is lost, but let the user move on...
if (_downloadedFileCounter >= _downloadableFilesTotal -2) {
[_panel6 downloadCompleted];
}
} else {
// error saving file. Perhaps out of space? Write permissions error?
NSLog(#"Save anatomy picture failed: %#", error.localizedDescription);
}
} else {
NSLog(#"downloadFetcher: Cannot create directory");
}
} else {
NSLog(#"downloadFetcher failed: %#", error);
}
}
//Create directory and exclude it from being backed-up in iCloud
-(NSString*)findOrCreateApplicationSupportSubPath:(NSString*)subPath
{
NSString *resolvedPath;
NSArray *appSupportDir = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
if ([appSupportDir count] != 0) {
resolvedPath = [appSupportDir objectAtIndex:0];
// Append the name of this application
NSString *executableName = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleExecutable"];
resolvedPath = [resolvedPath stringByAppendingPathComponent:executableName];
resolvedPath = [resolvedPath stringByAppendingPathComponent:subPath];
NSFileManager *manager = [NSFileManager defaultManager];
if (![manager fileExistsAtPath:resolvedPath]) {
// Path doesn't exist, creates it
NSError *error;
BOOL successful = [manager createDirectoryAtPath:resolvedPath withIntermediateDirectories:YES attributes:nil error:&error];
if(!successful) {
NSLog(#"ERROR creating APP Support Sub-Directory: %#", error.localizedDescription);
return nil;
} else {
// Exclude path from backing-up in iCloud
NSURL *url = [NSURL fileURLWithPath:resolvedPath];
BOOL excludeBackupResult = [self addSkipBackupAttributeToItemAtURL:url];
if(!excludeBackupResult){
NSLog(#"Error excluding DIRECTORY from iCloud backup. This is a violation to their guideline.");
return nil;
}
}
}
} else {
NSLog(#"No Application Support Path available");
return nil;
}
return resolvedPath;
}
I think the trick is to add the NSURLIsExcludedFromBackupKey OR make sure the directory is outside the documents directory. I did this by moving my documents to the Library/Application Support folder (since it didn't make sense in the /tmp or /Caches folders):
- (void)createNewRefFolder
{
NSError *error;
// store in /Library/Application Support/BUNDLE_IDENTIFIER/Reference
// make sure Application Support folder exists
NSURL *applicationSupportDirectory = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:&error];
if (error) {
NSLog(#"KCDM: Could not create application support directory. %#", error);
}
NSURL *referenceFolder = [applicationSupportDirectory URLByAppendingPathComponent:#"Reference" isDirectory:YES];
if (![[NSFileManager defaultManager] createDirectoryAtPath:[referenceFolder path]
withIntermediateDirectories:YES
attributes:nil
error:&error]) {
NSLog(#"KCDM: Error creating Reference folder: %# ...", error);
}
BOOL success = [referenceFolder setResourceValue:#YES forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success){
NSLog(#"KCDM: Error excluding %# from backup %#", referenceFolder, error);
}
}
Little UPDATE from previous answers.
You have to check the existence of the file. Otherwise, you will get this error,
Error excluding [FileName] from backup:
Error Domain=NSCocoaErrorDomain Code=4 "The file “[FileName]” doesn’t exist."
...
I am not sure if we should check for the value is already updated or not.
e.g. if the API reset already set value or not. If it tries to update the file system again for a set value that is more time consuming, I guess.
Updated method...
+ (BOOL)addSkipBackupAttributeToURLAtPath:(NSURL *)url
{
if (!url) return NO;
if (![[NSFileManager defaultManager] fileExistsAtPath:url.path]) return NO;
NSError *error = nil;
NSNumber *value = nil;
BOOL success = [url getResourceValue:&value forKey:NSURLIsExcludedFromBackupKey error:&error];
if (value.boolValue == YES) return YES;
success = [url setResourceValue:[NSNumber numberWithBool:YES]
forKey:NSURLIsExcludedFromBackupKey error:&error];
if(!success){
NSLog(#"Error excluding %# from backup: %#", [url lastPathComponent], error);
}
return success;
}
+ (BOOL)addSkipBackupAttributeToFileAtPath:(NSString *)path
{
if (!path) return NO;
return [self addSkipBackupAttributeToURLAtPath:[NSURL fileURLWithPath:path]];
}
in my app i have to store core data database. And i have some groups and folders with default data (audiofiles, maptiles, etc) in the xcode project navigator.
I found a lot about preventing files from being backed up like:
What i have done:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"db.sqlite"];
NSLog(#"%#", storeURL.path);
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
//NSDictionary *options = #{NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES};
/*NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];*/
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[self addSkipBackupAttributeToItemAtURL:storeURL];
return _persistentStoreCoordinator;
}
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
preventing method:
- (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;
}
(The minimum target iOS version is 7.0)
Is this enough? How can i check if the app now prevent backing up the core data database?
Before i added the addSkipBackupAttributeToItemAtURL method i checked the apps storage and found nothing under documents and data. I only found 3.1 MB under backups -> my iphone
- Install and launch your app
- Go to Settings > iCloud > Storage & Backup > Manage Storage
- If necessary, tap "Show all apps"
- Check your app's storage
You are doing it right.
Following the Apple Technical Q&A 1719: How do I prevent files from being backed up to iCloud and iTunes? you should mark with the "do not back up" attribute.
For NSURL objects, add the NSURLIsExcludedFromBackupKey attribute to prevent the corresponding file from being backed up. For CFURLRef objects, use the corresponding kCFURLIsExcludedFromBackupKey attribute.
To know which folder is more appropriated to store you Core Data database you can check the iOS Data Storage Guidelines.
What is the recommended place to save game data if I do not want automatically to be backup to iCloud ? I am using NSUbiquitousKeyValueStore for iCloud, and have custom logic for updates.
So I do not for iCloud backup to mess things up.
By my understanding that is: Library/Caches/.
Because: Documents/ and Library/Preferences/ are backup to iCloud.
Am I correct ?
You shouldn't use /Library/Cache for storing such kind of information. Look what Apple Data Storage Guidelines says:
Data that can be downloaded again or regenerated should be stored in
the /Library/Caches directory.
You can save all your data to /Documents, just flag it so that it wouldn't copy 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;
}
You can use this method in your AppDelegate or wherever you create these files. Note that you can exlude from iCloud backup not only specific files, but directories too.
You can also exclude the specific game data from performing backup to iCloud.
NSURL *url = [NSURL fileURLWithPath:fileName];
assert([[NSFileManager defaultManager] fileExistsAtPath:fileName]);
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;
I'm having trouble synching a simple textfile, I get this error when trying to open it:
{NSFilePath=/private/var/mobile/Library/Mobile Documents/4C224W52W5~com~piso13~opusDomini/currentLogPath, NSUnderlyingError=0xde9b460 "The operation couldn’t be completed. Bad file descriptor"}
This is how I create it
-(BOOL)createLogFolderFile{
NSString *uuid = nil;
CFUUIDRef uuidRef = CFUUIDCreate(nil);
uuid = (NSString*)CFUUIDCreateString(nil, uuidRef);
CFRelease(uuidRef);
NSError *error = nil;
[uuid writeToFile:[self filePath] atomically:NO encoding:NSUTF8StringEncoding error:&error];
if (error) {
NSLog(#"Error trying to create log file %#", error);
return FALSE;
}
else{
return TRUE;
}
}
-(NSString*)filePath{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *iCloudPath = [[fileManager URLForUbiquityContainerIdentifier:nil] path];
return [iCloudPath stringByAppendingPathComponent:LOG_FOLDER_FILE_NAME];
}
This is how I read it:
-(NSString*)readLogFolderFromFile{
NSError *error = nil;
NSString *logFolder = [NSString stringWithContentsOfFile:[self filePath] encoding:NSUTF8StringEncoding error:&error];
if (error) {
NSLog(#"Error when trying to read log folder from file: %#" ,error);
return nil;
}
else{
return logFolder;
}
}
I'm using NSMetadataQuery to search for the file,
The notification query finish gathering info results positive.
Help?
The file was not downloaded. It seems NSMetadataQuery notifies about the existence of the file in the cloud. To actually get the file, extra code is needed:
Inside queryDidFinishGathering notification:
NSMetadataItem *item = [query resultAtIndex:0];
self.metadataItem = item;
BOOL isDownloaded = [[item valueForAttribute:NSMetadataUbiquitousItemIsDownloadedKey]boolValue];
if (!isDownloaded) {
NSError *error = nil;
[[NSFileManager defaultManager] startDownloadingUbiquitousItemAtURL: [item valueForAttribute:NSMetadataItemURLKey] error:&error];
NSLog(#"Start downloading file");
if (error) {
NSLog(#"Error trying to download file: %#", error);
}
else{
[self lookForLogFolderFile];
return;
}
}
The lookForLogFolderFile simply starts the query again.
After several calls my item gets downloaded. You can also use a timer to between each call to start a NSMetadataQuery. In my case, is just a text file with one line.