iPhone应用程序因iCloud数据存储指南而被拒绝

时间:2012-07-07 12:24:17

标签: ios iphone app-store

由于涉嫌违反iCloud数据存储指南,最近拒绝了对我的某个应用程序的严重错误修正更新。

以下是我的应用程序存储数据的方式(自我的应用程序的第一个版本于2009年获得批准以来一直没有问题):

  1. 启动时,它会将“启动”SQLite3数据库从应用程序包复制到Documents文件夹。
  2. 数据库包含基本架构和一些示例数据,因此用户可以看到如何使用该应用程序。它很小 - 只有不到3MB。
  3. 然后,用户的未来工作仅保存在此数据库文件中。他们可能会删除或保留样本,他们可能会添加大量自己的数据,但该数据库文件将始终存在。
  4. 由于同样的原因,今年早些时候的更新遭到拒绝,但当我向他们提供上述解释时,应用程序状态从“已拒绝”更改为“审核中”更改为“处理App Store”。他们没有给我任何解释,所以我认为这只是对评论员的误解。

    这一次,审稿人回答了我的解释,只是说非用户生成的数据不应该存储在iCloud中,而且我的更新仍处于“已拒绝”状态。

    但我不明白我应该在这做什么。因为所有用户的工作都保存在数据库中,所以我不能选择将其从iCloud备份中排除或将其存储在Cache文件夹中。此外,我不能真正干净地将“用户生成的”与“非用户生成的”数据分开,因为该应用程序使用相同的数据库文件。尽管数据库的文件名和目录位置保持不变,但最初的非用户生成的数据将很快被用户自己的数据替换。

    即使数据库中没有样本数据,任何数据库支持的应用程序在启动时仍然必须生成一个空数据库 - 即使它唯一拥有的是应用程序的数据库模式。

    这一定是一个非常常见的问题,但不幸的是,我不能仅仅关闭备份 - 用户将大量工作投入到我们应用中存储的数据中,而iCloud备份对他们来说非常重要。

    此时我有哪些选择?这是我能看到的:

    1. 再次联系Apple并尝试解释发生了什么。

    2. 我可以将文件的备份属性设置为NO,然后在用户首次更改时将其切换为YES吗?从技术上来说,这对Apple好吗?

    3. 从数据库中删除我的示例数据。这对于可用性来说真的很糟糕,并且会增加我的支持负担,但是如果能够让我的更新获得批准,我愿意这样做。但是,我仍然需要在启动时创建一个存根数据库来保存空数据库模式,所以我不确定这是否会对批准过程产生影响。

    4. 惊魂。

    5. 有人有什么建议吗?我不得不想象有很多其他应用程序使用数据库的方式与我的相同,但是没有选择只禁用备份。

      令我非常沮丧的是,我所做的任何更改都需要进行另一轮测试和应用审核,这将使我的关键更新再次延迟2-3周。 :/

      更新:可能还有其他选项:我可以将文件保存在Library/而不是Documents/,因为他们的问题似乎是专门使用文件夹?如果文件存储在Library/

      中,是否会备份该文件

      更新2 :我发现最令人困惑的事情是,任何数据库支持的应用程序(即使它使用核心数据,我认为)都必须创建一个至少包含应用程序架构的数据库文件。问题只是我的数据库的大小太大了吗?因为我无法看到任何数据库支持的应用程序如何避免在启动时创建数据库。

      更新3 :我正在使用自定义SQLite交互层 - 而不是核心数据。此外,示例数据包含初始图像,用户在开始使用应用程序时可能会最终删除。

8 个答案:

答案 0 :(得分:5)

3MB似乎存储在数据库中的大量样本数据。如果您拉出图像并存储对数据库中图像的引用,那么您应该能够大量使用这种用法。然后,您可以将数据库的图像获取器代码更改为以下内容:

- (UIImage *)image
{
    NSString *imageName = self.imageName;
    UIImage *image = [UIImage imageNamed:imageName];
    if (!image)
    {
        NSString *imageLibraryPath = ...;
        image = [UIImage imageWithPath:[imageLibraryPath stringByAddingPathComponent:imageName]];
    }
    return image;
}

- (void)setImage:(UIImage *)image
{
    [self setImageData:UIImagePNGRepresentation(image)];
}

- (void)setImageData:(NSData *)imageData
{
    NSString *imageLibraryPath = ...;
    NSString *fileName = self.uniqueId; //or something else, UUID maybe?
    NSString *filePath = [imageLibraryPath stringByAddingPathComponent:imageName];
    [imageData writeToFile:filePath atomically:YES]; // maybe dispatch_async this into the background?
    self.fileName = fileName;
}

self.fileName将由您的数据库支持。

通过以这种方式减少数据存储,您应该能够获得批准,因为您没有在手机上存储相对大量的数据。图像也可以通过这种方式存在于应用程序包中,根本不需要复制。

答案 1 :(得分:4)

我有一个非常愚蠢的想法。但也许它是有效的。

您可以在启动时显示一个弹出窗口,询问“您希望我创建一些演示数据吗”。当用户单击“是”时,其用户生成数据。

答案 2 :(得分:3)

一种解决方法是在用户尝试编辑内容之前从包中读取SQL文件。然后复制它。由于文件很小,甚至不需要微调器。

我有一个确切的问题 最近的应用。在我们的例子中,我们使用核心数据并在启动时复制SQL文件。

不幸的是你必须以iOS 5为目标。

https://developer.apple.com/library/ios/qa/qa1719/_index.html

答案 3 :(得分:0)

使用“私有”SQL数据访问层

由于您不使用Core Data,而是使用自定义访问层,我只建议不要将“初始种子”数据库放在iCloud文件夹中,而是编写一个在运行时生成它的自定义过程(例如,当您首先启动应用程序。)

最好的方法应该是将您的真实SQL文件放在iCloud共享文件夹之外的公共应用程序包中。通过您的过程,您必须阅读此文件的所有内容并在共享文件夹中重新创建克隆,以便此新文件将显示为用户生成的内容而不是捆绑的内容。这将需要一些无用的开销,但我认为需要避免Apple拒绝您的应用。

我建议你不要备份所有图像,如果可能的话,将它们留在主包中,因为它们已经存在于每个下载的应用程序中,然后在iCloud上无用占用宝贵的空间。尝试只在必要的地方放置iCloud。

如果您计划一起使用Core Data和iCloud。

首先,将自定义SQLite3数据库文件放在iCloud中是危险的,原因有两个,第一个原因是,如果您的应用程序在不同的地方使用,您将永远无法正确处理不同数据之间的合并问题同时使用设备。

我不能在这里给你一个完整的解决方案,因为它需要太多的时间和空间。但我建议你看一下今年Apple WWDC上的Session 227讲座。他们一起讨论使用Core Data和iCloud,同时解决您面临的同一问题(即初始数据库播种和通过iCloud进行同步)。如果您通过Apple Developer门户访问WWDC页面,您将能够获得示例项目的完整副本。

答案 4 :(得分:0)

只需添加以下属性,Apple即可让您通过(如果您不使用

    #include <sys/xattr.h>
    ...
    - (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
    {
    int result = -1;
    @try {
            const char* filePath = [[URL path] fileSystemRepresentation];
            const char* attrName = "com.apple.MobileBackup";
            u_int8_t attrValue = 1;
            result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
    } @catch (NSException * e) {
            DebugLog(@"Exception: %@", e);
    }
    return result == 0;
    }

    - (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    ... 
    NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
    [self addSkipBackupAttributeToItemAtURL:storeUrl];
    ...

答案 5 :(得分:0)

CoreData和两个持久存储应该是可行的方法。 但对于您来说,首次复制文件时设置com.apple.MobileBackup文件属性。在用户进行第一次修改后取消设置

答案 6 :(得分:0)

作为最终的解决方案,我建议使用CoreData,将您的固定数据作为附加的只读持久存储。

但是,在平均时间内,您需要一个简单,快速的解决方案。创建一个简单的屏幕,将示例数据加载到用户的数据库中。在“系统”或“工具”屏幕中放置一个链接,以便随时可以完成。

具体而言,请在首次运行应用时启动它。您询问用户是否需要文档中的样本数据,以及数据的大小,因此他们是创建文档的人。另请注意,可以随时删除或替换样本数据。

这可以让你通过审核。

答案 7 :(得分:-1)

如果您使用的是Core Data,则可以将示例数据保留在应用包中。在与docs目录中的读/写存储相同的协调器上将其作为只读持久性存储打开,Core Data将确保在保存时保持正确的位置。这在WWDC 2012的“使用核心数据的最佳实践”视频中进行了解释。

如果你直接使用SQLite,那就不那么容易,但你可以实现自己的逻辑来做同样的事情。