【问题标题】:iCloud and Core Data pre-filled databaseiCloud 和 Core Data 预填充数据库
【发布时间】:2012-05-05 22:50:24
【问题描述】:

我有一个带有预填充 .sqlite 文件的应用程序,该文件在首次打开应用程序时被复制到用户的 Documents 目录中。该文件为 12.9MB。自从将目标更改为 iOS5 后,我的应用已两次被拒绝,并附有此拒绝说明:

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

拒绝原因:

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

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

2.23

我们发现您的应用未遵循 App Store 审核指南所要求的 iOS 数据存储指南。

特别是,我们发现在内容下载时,您的应用存储了 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 应用程序现已获得批准 - 请在下面查看我的解决方案。我希望它可以帮助别人。

【问题讨论】:

  • 我也面临这个问题。我担心你又被拒绝了。您是否尝试在整个文件夹上设置属性?我担心苹果只是拒绝不是核心数据的数据模型。如果您有更多信息,请发布您的发现。
  • 我为此被拒绝了 3 次。我没有尝试将属性设置为整个文档文件夹,因为它不应该有所作为。您的应用是否因此被拒绝?
  • 是的。您是否考虑过将数据库文件保存到 /Library?也许只是因为它位于 /Documents 文件夹中,而这仅适用于用户生成的内容。应该可以编写自己的 /Database 文件夹。
  • @LeeProbert 我是否可以将 /Library 文件夹用于用户生成的内容?问题是一个文件是两者的混合。
  • 我注意到适用于 iOS 的 Google Analytics SDK 已更新,其 SQL lite 文件已从 /Documents 移至 /Library。我刚刚以相同的更改重新提交了我的应用程序。会告诉你进展如何。

标签: ios core-data icloud


【解决方案1】:

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

这是设置跳过备份属性的代码 - 请注意,对于 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 用户的清除。

这是苹果本月批准的。

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

【讨论】:

    【解决方案2】:
    #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;
    }
    

    【讨论】:

    • 感谢您的回答 - BTW 缩进代码有 4 个空格以正确格式化。您是说我应该将属性设置为整个文档文件夹吗?这是我正在使用的确切方法,除了我将其应用于文件。
    • 另外.. 此代码不支持 5.1 及更高版本。您只应该使用该方法在 5.0.1 中设置属性。
    【解决方案3】:

    你说你不使用 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。

    【讨论】:

    【解决方案4】:

    我一直在研究这个,发现这篇关于这个主题的非常有趣的文章:http://iphoneincubator.com/blog/data-management/local-file-storage-in-ios-5

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

    我建议你硬着头皮使用 /Cache。最糟糕的用户情况是他们的设备内存不足,并且应用程序在删除数据库文件时被清理。在这种情况下,当您的应用程序再次启动时,它应该将捆绑的数据库文件复制到 /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 存储空间。

    【讨论】:

    • 我注意到 Google Analytics iOS SDK 也将其 sql 文件复制到了 /Documents 文件夹,因此我查看了是否有更新。有... feeds.feedburner.com/ga-dev-changelog-collection-ios 他们现在保存到 /Library 而不是 /Caches 文件夹中,而只是在根目录中。
    • 不幸的是,丢失数据不是我的应用程序的选项。该应用程序附带了许多乐谱/和弦表,用户可以创建这些的播放列表以进行演奏。这些播放列表可以保存并在以后调用,所以我不希望数据被删除。
    • 是他们保存在设备上的所有内容,还是您也在同步到远程服务器?如果你不是,那我还是会考虑的。查看 Parse.com 作为此选项。然后你可以安全地清理缓存并让它从远程服务器重新同步。
    • 保存到 /Library 文件夹是否不需要标记不备份?
    • 我相信 iTunes 会备份 /Library 文件夹,但不确定这是否与 iCloud 备份 /Documents 文件夹并使用存储空间相同。
    猜你喜欢
    • 2012-04-25
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 2011-10-26
    • 2013-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多