11

我有一个带有预填充 .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 > 等根本没有使用。

我不能使用Cachesortmp目录,因为数据库在创建后被用户修改。

我似乎处于困境和艰难的境地,Apple 根本不允许这种应用程序运行。

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

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

编辑 1-7-12 我的应用程序刚刚因为同样的原因再次被拒绝。我不知道在这里做什么,因为它肯定是一个常见的使用场景。

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

4

4 回答 4

9

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

这是设置 Skip Backup 属性的代码 - 请注意,对于 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 用户存储并忍受清除。

这是苹果本月批准的。

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

于 2012-09-11T15:30:53.113 回答
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;
}
于 2012-04-25T11:40:19.730 回答
1

你说你不使用 iCloud。在这种情况下,您只需将sqlite文件移动到带有 suffix 的目录即可.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。

于 2012-08-02T07:13:19.053 回答
0

我一直在研究这个,发现这篇关于这个主题的非常有趣的文章: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 存储。

于 2012-07-02T14:06:15.747 回答