简体   繁体   English

iCloud和Core Data预填充数据库

[英]iCloud and Core Data pre-filled database

I have an app with a pre-filled .sqlite file that is copied into the user's Documents directory when the app is first opened. 我有一个带有预先填充的.sqlite文件的应用程序,该文件在首次打开应用程序时被复制到用户的Documents目录中。 This file is 12.9MB. 这个文件是12.9MB。 Twice now, my app has been rejected since changing target to iOS5 with this rejection note: 现在两次,我的应用程序因为拒绝备注将目标更改为iOS5而被拒绝:

Binary Rejected Apr 24, 2012 10:12 AM 二进制被拒绝2012年4月24日上午10:12

Reasons for Rejection: 拒绝的理由:

2.23 Apps must follow the iOS Data Storage Guidelines or they will be rejected 2.23应用必须遵循iOS数据存储指南,否则将被拒绝

Apr 24, 2012 10:12 AM. 2012年4月24日上午10:12 From Apple. 来自Apple。

2.23 2.23

We found that your app does not follow the iOS Data Storage Guidelines, which is required per the App Store Review Guidelines. 我们发现您的应用不符合iOS数据存储指南,这是根据App Store审核指南所要求的。

In particular, we found that on content download, your app stores 12.81 MB. 特别是,我们发现在内容下载时,您的应用程序存储了12.81 MB。 To check how much data your app is storing: 要检查应用存储的数据量:

  • Install and launch your app 安装并启动您的应用
  • Go to Settings > iCloud > Storage & Backup > Manage Storage 转至设置> iCloud>存储和备份>管理存储
  • If necessary, tap "Show all apps" 如有必要,请点按“显示所有应用”
  • Check your app's storage 检查您应用的存储空间

The iOS Data Storage Guidelines indicate that only content that the user creates using your app, eg, documents, new files, edits, etc., may be stored in the /Documents directory - and backed up by iCloud. iOS数据存储指南指出,只有用户使用您的应用创建的内容(例如,文档,新文件,编辑等)可以存储在/ Documents目录中 - 并由iCloud备份。

Temporary files used by your app should only be stored in the /tmp directory; 应用程序使用的临时文件只应存储在/ tmp目录中; please remember to delete the files stored in this location when the user exits the app. 请记得在用户退出应用程序时删除存储在此位置的文件。

Data that can be recreated but must persist for proper functioning of your app - or because customers expect it to be available for offline use - should be marked with the "do not back up" attribute. 可以重新创建但必须保持应用程序正常运行的数据 - 或者因为客户希望它可供脱机使用 - 应标记为“不备份”属性。 For NSURL objects, add the NSURLIsExcludedFromBackupKey attribute to prevent the corresponding file from being backed up. 对于NSURL对象,请添加NSURLIsExcludedFromBackupKey属性以防止备份相应的文件。 For CFURLRef objects, use the corresponding kCFURLIsExcludedFromBackupKey attribute. 对于CFURLRef对象,请使用相应的kCFURLIsExcludedFromBackupKey属性。

For more information, please see Technical Q&A 1719: How do I prevent files from being backed up to iCloud and iTunes?. 有关详细信息,请参阅技术问答1719:如何防止文件备份到iCloud和iTunes?。

It is necessary to revise your app to meet the requirements of the iOS Data Storage Guidelines. 有必要修改您的应用程序以满足iOS数据存储指南的要求。

I have tried setting the "do not back up" attribute as recommended in the Data Storage Guidelines, but is was rejected again. 我已尝试按照“数据存储指南”中的建议设置“不备份”属性,但又被拒绝了。

I do not use iCloud in my app, and Settings > iCloud > etc. shows no usage at all. 我不在我的应用程序中使用iCloud,而设置> iCloud>等根本没有显示任何用法。

I cannot use the Caches or tmp directories as the database is modified by the user after creation. 我无法使用Cachestmp目录,因为用户在创建后会修改数据库。

I seem to be between a rock and a hard place here with Apple not allowing this kind of app to function at all. 我似乎介于摇滚和硬地之间,Apple不允许这种应用程序运行。

Has anyone had this problem and managed to overcome it? 有没有人遇到过这个问题并设法克服它?

EDIT 17-5-12 I still haven't managed to get this app approved yet. 编辑17-5-12我还没有设法让这个应用程序获得批准。 Has anyone managed to do this? 有没有人设法做到这一点?

EDIT 1-7-12 My app has just been rejected again for the same reason. 编辑1-7-12由于同样的原因,我的应用程序再次被拒绝。 I am at a loss as to what to do here, as surely it is a common use scenario. 我不知道该怎么做,当然这是一个常见的使用场景。

EDIT 11-9-12 App now approved - please see my solution below. 编辑11-9-12应用程序现已批准 - 请参阅下面的解决方案。 I hope it can help someone else. 我希望它可以帮助别人。

OK, here is the solution I managed to get approved (finally!) 好的,这是我设法获得批准的解决方案(最后!)

This is the code for setting the Skip Backup attribute - note that it is different for 5.0.1 and below and 5.1 and above. 这是用于设置“跳过备份”属性的代码 - 请注意,对于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;
    }
}

And here is my persistentStoreCoordinator 这是我的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;
}

Note that I made the decision to just store in Caches and put up with purging for iOS 5.0 users. 请注意,我决定只在Caches存储,并忍受对iOS 5.0用户的清除。

This was approved by Apple this month. 本月苹果批准了这一点。

Please don't copy and paste this code without reading and understanding it first - it may not be totally accurate or optimised, but I hope it can guide someone to a solution that helps them. 请不要复制并粘贴此代码而不首先阅读和理解它 - 它可能不完全准确或优化,但我希望它可以引导某人找到帮助他们的解决方案。

#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;
}

You said that you don't use iCloud. 你说你不使用iCloud。 In that case, you should simply move your sqlite file to a directory with suffix .nosync . 在这种情况下,您只需将sqlite文件移动到后缀为.nosync的目录.nosync That should do it! 应该这样做!

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. HTH。

I've been looking into this and have found this very interesting article on the subject : http://iphoneincubator.com/blog/data-management/local-file-storage-in-ios-5 我一直在研究这个问题并找到了关于这个主题的非常有趣的文章: http//iphoneincubator.com/blog/data-management/local-file-storage-in-ios-5

I believe that you will continue to be rejected if you try to use the /Documents folder to store your DB file. 我相信如果您尝试使用/ Documents文件夹存储数据库文件,将继续被拒绝。

I would suggest you bite the bullet and use /Cache. 我建议你咬紧牙关并使用/ Cache。 The worst user case scenario would be that their device runs low on memory and the app gets cleaned removing the DB file. 最糟糕的用户案例场景是他们的设备内存不足,应用程序清理后删除了DB文件。 In this case when your app launches again it should copy over the bundled DB file into /Cache and the user would sync to go and grab the excess data from your remote server. 在这种情况下,当您的应用程序再次启动时,它应该将捆绑的DB文件复制到/ Cache中,并且用户将同步以从远程服务器获取多余的数据。 That is assuming this is how your app works. 这假设这是您的应用程序的工作方式。

To get your app to move your DB file from /Documents to /Cache you can just tell NSFileManager to do this for you ... 要让您的应用程序将数据库文件从/ 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);
    }

This will prevent your existing users from using their iCloud storage unnecessarily. 这将阻止现有用户不必要地使用其iCloud存储。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM