iCloud和Core Data预填充数据库

时间:2012-04-25 09:04:45

标签: ios core-data icloud

我有一个带有预先填充的.sqlite文件的应用程序,该文件在首次打开应用程序时被复制到用户的Documents目录中。这个文件是12.9MB。现在两次,我的应用程序已被拒绝,因为使用此拒绝注释将目标更改为iOS5:

  

二进制被拒绝2012年4月24日上午10:12

     

拒绝理由:

     

2.23应用必须遵循iOS数据存储指南,否则将被拒绝

     

2012年4月24日上午10:12来自Apple。

     

2.23

     

我们发现您的应用不符合iOS数据存储指南,这是App Store审核指南所要求的。

     

特别是,我们发现在内容下载中,您的应用程序存储了12.81 MB。要检查应用存储的数据量:

     
      
  • 安装并启动您的应用
  •   
  • 转到设置> iCloud>存储&备份>管理存储
  •   
  • 如有必要,请点按“显示所有应用”
  •   
  • 检查应用的存储空间
  •   
     

iOS数据存储指南指出,只有用户使用您的应用创建的内容(例如文档,新文件,编辑等)可以存储在/ Documents目录中 - 并由iCloud备份。

     

您的应用使用的临时文件只应存储在/ tmp目录中;请记得在用户退出应用程序时删除存储在此位置的文件。

     

可以重新创建但必须保持应用程序正常运行的数据 - 或者因为客户希望它可供脱机使用 - 应标记为“不备份”属性。对于NSURL对象,请添加NSURLIsExcludedFromBackupKey属性以防止备份相应的文件。对于CFURLRef对象,请使用相应的kCFURLIsExcludedFromBackupKey属性。

     

有关详细信息,请参阅技术问答1719:如何阻止文件备份到iCloud和iTunes?

     

有必要修改您的应用以满足iOS数据存储指南的要求。

我已尝试按照“数据存储指南”中的建议设置“不备份”属性,但又被拒绝了。

我在我的应用中没有使用iCloud,而且设置> iCloud>等根本没有用。

我无法使用Cachestmp目录,因为用户在创建后会修改数据库。

我似乎介于摇滚和硬地之间,Apple不允许这种应用程序运行。

有没有人遇到这个问题并设法克服它?

编辑17-5-12 我还没有设法让这个应用程序获得批准。有没有人设法做到这一点?

编辑1-7-12 出于同样的原因,我的应用程序再次遭到拒绝。我不知道该怎么做,当然这是一种常见的用途。

编辑11-9-12 应用程序现已批准 - 请参阅下面的解决方案。我希望它可以帮助别人。

4 个答案:

答案 0 :(得分:9)

好的,这是我设法获得批准的解决方案(最后!)

这是设置“跳过备份”属性的代码 - 请注意,5.0.1及以下版本和5.1及以上版本不同。

#include <sys/xattr.h>
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    if (&NSURLIsExcludedFromBackupKey == nil) { // iOS <= 5.0.1
        const char* filePath = [[URL path] fileSystemRepresentation];

        const char* attrName = "com.apple.MobileBackup";
        u_int8_t attrValue = 1;

        int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
        return result == 0;
    } else { // iOS >= 5.1
        NSError *error = nil;
        [URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
        return error == nil;
    }
}

这是我的persistentStoreCoordinator

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (__persistentStoreCoordinator != nil)
    {
        return __persistentStoreCoordinator;
    }

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"store.sqlite"];

    NSError *error;

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent:@"store.sqlite"];

    // For iOS 5.0 - store in Caches and just put up with purging
    // Users should be on at least 5.0.1 anyway
    if ([[[UIDevice currentDevice] systemVersion] isEqualToString:@"5.0"]) {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
        NSString *cacheDirectory = [paths objectAtIndex:0];
        NSString *oldStorePath = [storePath copy];
        storePath = [cacheDirectory stringByAppendingPathComponent:@"store.sqlite"];
        storeURL = [NSURL URLWithString:storePath];

        // Copy existing file
        if ([fileManager fileExistsAtPath:oldStorePath]) {
            [fileManager copyItemAtPath:oldStorePath toPath:storePath error:NULL];
            [fileManager removeItemAtPath:oldStorePath error:NULL];
        }
    }
    // END iOS 5.0

    if (![fileManager fileExistsAtPath:storePath]) {
        // File doesn't exist - copy it over
        NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"store" ofType:@"sqlite"];
        if (defaultStorePath) {
            [fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
        }
    }

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    [self addSkipBackupAttributeToItemAtURL:storeURL];

    return __persistentStoreCoordinator;
}

请注意,我决定只在Caches存储,并忍受对iOS 5.0用户的清除。

本月苹果公司批准了这项计划。

请不要复制并粘贴此代码而不首先阅读和理解它 - 它可能不完全准确或优化,但我希望它可以引导某人找到帮助他们的解决方案。

答案 1 :(得分:4)

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#include <sys/xattr.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

//Put this in your method

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSURL *pathURL= [NSURL fileURLWithPath:documentsDirectory];

    iOS5 = NO;
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"5.0.1")) {
        iOS5 = YES;
    }

    // Set do not backup attribute to whole folder
    if (iOS5) {
        BOOL success = [self addSkipBackupAttributeToItemAtURL:pathURL];
        if (success) 
            NSLog(@"Marked %@", pathURL);
        else
            NSLog(@"Can't marked %@", pathURL);
    }
}

- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    const char* filePath = [[URL path] fileSystemRepresentation];
    const char* attrName = "com.apple.MobileBackup";
    u_int8_t attrValue = 1;
    int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
    return result == 0;
}

答案 2 :(得分:1)

你说你不使用iCloud。在这种情况下,您只需将sqlite文件移动到后缀为.nosync的目录即可。应该这样做!

NSString *dataFileName = @"my.sqlite";
NSString *dataFileDirectoryName = @"Data.nosync";
NSString *documentsDirectoryPath = [self applicationDocumentsDirectory];
NSFileManager *fileManager = [NSFileManager defaultManager];

if ([fileManager fileExistsAtPath:[documentsDirectoryPath stringByAppendingPathComponent:dataFileDirectoryName]] == NO) {
    NSError *fileSystemError;
    [fileManager createDirectoryAtPath:[documentsDirectoryPath stringByAppendingPathComponent:dataFileDirectoryName]
           withIntermediateDirectories:YES
    attributes:nil
         error:&fileSystemError];

    if (fileSystemError != nil) {
        NSLog(@"Error creating database directory %@", fileSystemError);
    }
}

NSString *dataFilePath = [[documentsDirectoryPath
                           stringByAppendingPathComponent:dataFileDirectoryName]
                          stringByAppendingPathComponent:dataFileName];

// Move your file at dataFilePath location!

HTH。

答案 3 :(得分:0)

我一直在研究这个问题并找到了关于这个主题的非常有趣的文章:http://iphoneincubator.com/blog/data-management/local-file-storage-in-ios-5

如果您尝试使用/ Documents文件夹存储数据库文件,我相信您将继续被拒绝。

我建议你咬紧牙关并使用/ Cache。最糟糕的用户案例场景是他们的设备内存不足,应用程序清理后删除了DB文件。在这种情况下,当您的应用程序再次启动时,它应该将捆绑的DB文件复制到/ Cache中,并且用户将同步以从远程服务器获取多余的数据。这假设这是你的应用程序的工作方式。

要让您的应用程序将数据库文件从/ Documents移动到/ Cache,您可以告诉NSFileManager为您执行此操作...

#define FILE_MANAGER     [NSFileManager defaultManager]
#define DOCUMENTS_PATH   [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0]
#define CACHES_PATH      [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex: 0]
#define DB_DOCS_PATH     [DOCUMENTS_PATH stringByAppendingPathComponent: @"database.db"]
#define DB_PATH          [CACHES_PATH stringByAppendingPathComponent: @"database.db"]

if([FILE_MANAGER moveItemAtPath: DB_DOCS_PATH toPath:DB_PATH error:&error])
    {
        NSLog(@"SUCCESSFULLY MOVED %@ to %@",DB_DOCS_PATH,DB_PATH);
    }

这将阻止现有用户不必要地使用其iCloud存储空间。