阅读背景:

由于iCloud数据存储准则,iPhone应用程序被拒绝

来源:互联网 

A critical bugfix update to one of my apps was recently rejected due to an alleged violation of the iCloud Data Storage Guidelines.

最近,我的一个应用程序的一个关键补丁更新被拒绝了,原因是它违反了iCloud的数据存储指南。

Here's how my app stores data (hasn't been a problem since the first version of my app was approved in 2009):

下面是我的应用程序存储数据的方式(自从我的第一个应用程序在2009年被批准以来,一直没有出现问题):

  1. On startup, it copies a "starter" SQLite3 database from the app bundle to the Documents folder.
  2. 在启动时,它将“starter”SQLite3数据库从应用程序包复制到Documents文件夹。
  3. The database contains the basic schema and some example data so the user can see how to use the app. It's small - just under 3MB.
  4. 这个数据库包含基本的模式和一些示例数据,用户可以看到如何使用这个应用程序。
  5. The user's future work is then saved only in this database file. They may delete or keep the samples, they may add tons of their own data, but that database file will always be there.
  6. 然后用户将来的工作只保存在这个数据库文件中。他们可能会删除或保留样本,他们可能会添加大量自己的数据,但是那个数据库文件将始终存在。

An update earlier this year was rejected for the same reason, but when I gave them the explanation above, the app status changed from "Rejected" to "In Review" to "Processing for App Store." They didn't send me any explanation, so I thought it was just a misunderstanding on the reviewer's part.

今年早些时候的一个更新也因为同样的原因被拒绝了,但是当我给他们上面的解释时,应用的状态从“拒绝”变成了“审查”变成了“应用商店的处理”。他们没有给我任何解释,所以我认为这只是对审稿人的误解。

This time, the reviewer responded to my explanation by simply saying that non-user-generated data shouldn't be stored in iCloud and my update remains in the "Rejected" state.

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

But I don't understand what I'm supposed to do here. Because all of the user's work is kept in the database, it's not an option for me to exclude this from iCloud backup or store it in a Cache folder. Also, I can't really cleanly separate "user-generated" from "non-user-generated" data because the app works from the same database file. The initial, non-user-generated data will be quickly replaced with the user's own data, though the filename and directory location of the database will remain the same.

但是我不明白我在这里应该做什么。因为所有用户的工作都保存在数据库中,所以我无法将其从iCloud备份中排除,或者将其存储在缓存文件夹中。另外,我也不能将“用户生成”和“非用户生成”数据清晰地分开,因为应用程序使用的是同一个数据库文件。初始的、非用户生成的数据将很快被用户自己的数据所取代,尽管数据库的文件名和目录位置将保持不变。

And even if there were no sample data in the database, any database-backed app will still have to generate an empty database when it starts - even if the only thing it holds is the app's database schema.

即使数据库中没有示例数据,任何数据库支持的应用程序在启动时仍必须生成一个空数据库——即使它只包含应用程序的数据库模式。

This must be a very common problem, but unfortunately it's not okay for me to just turn off backup - users put a lot of work into the data they store in my app, and iCloud backup is very important to them.

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

What options do I have at this point? Here's what I can see:

现在我有什么选择?我看到的是:

  1. Contact Apple again and try to explain what's going on.

    再次联系苹果,试着解释到底发生了什么。

  2. Can I set the file's backup attribute to NO and then only toggle it to YES when the user makes their first change? Is that okay technically and okay with Apple?

    我可以将文件的备份属性设置为NO,然后在用户进行第一次更改时将其切换为YES吗?这在技术上和苹果都没问题吧?

  3. Remove my example data from the database. This will be really bad for usability and will increase my support load, but I'm willing to do it if it will get my update approved. However, I'll still have to create a stub database at startup to hold the empty database schema, so I'm not sure if that will even make a difference in the approval process.

    从数据库中删除示例数据。这对可用性非常不利,并且会增加我的支持负载,但是如果它能让我的更新获得批准的话,我愿意这么做。但是,我仍然需要在启动时创建一个存根数据库来保存空的数据库模式,因此我不确定这是否会对审批过程产生影响。

  4. Cry.

    哭了起来。

Anyone have any advice? I have to imagine that there are lots of other apps that use a database the same way mine does, but don't have the option of just disabling backup.

有人有什么建议吗?我不得不想象有很多其他的应用程序使用数据库的方式和我的一样,但是不能只禁用备份。

It's also very depressing that any changes I make will require another round of testing and app review, which will set back my critical update an additional 2 - 3 weeks. :/

同样令人沮丧的是,我所做的任何改变都需要新一轮的测试和应用审查,这将使我的关键更新推迟2 - 3周。:/

UPDATE: There may be another option: can I simply save the file in Library/ instead of Documents/, since their problem seems to be specifically with the use of the Documents folder? Will the file be backed up if it's stored in Library/?

更新:可能还有另一种选择:我是否可以简单地将文件保存在库/而不是文档/,因为他们的问题似乎是专门使用文档文件夹的?如果文件存储在库/中,是否会进行备份?

UPDATE 2: The thing I find most confusing is that any database-backed app (even if it uses Core Data, I assume) will have to create a database file containing at least the app's schema. Is the problem just that the size of my database is too big? Because I can't see how any database-backed application can avoid having to create a database at startup.

更新2:最让我困惑的是,任何数据库支持的应用程序(我猜,即使它使用核心数据)都必须创建一个包含至少应用程序模式的数据库文件。问题是我的数据库太大了吗?因为我看不出任何数据库支持的应用程序如何能够避免在启动时创建数据库。

UPDATE 3: I'm using a custom SQLite interaction layer - not Core Data. Also, the example data consists of starter images, which the user will probably end up deleting as they start using the app.

更新3:我使用的是自定义的SQLite交互层,而不是Core Data。此外,示例数据由starter图像组成,用户在开始使用应用程序时可能会删除这些图像。

8 个解决方案

#1


5  

3MB seems like quite a lot of sample data stored in a database. If you pull out the images and store references to the images in the database instead you should be able to get this usage down a lot. You can then change your database's image getter code to something like this:

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

The self.fileName would be backed by your database.

自我。文件名将由您的数据库支持。

By reducing your data storage in this way you should be able to get approved as you are not storing relatively large amounts of data on the phone. The images can also be in the app bundle this way and do not need to duplicated at all.

通过以这种方式减少数据存储,您应该能够获得批准,因为您没有在电话上存储相对大量的数据。这些图像也可以以这种方式放在app bundle中,根本不需要复制。

#2


4  

I have a really stupid idea. But maybe it is valid.

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

You can show a popup on startup asking "Would you like me to create some demo data". When the user clicks "Yes", its user generated data.

您可以在启动时显示一个弹出窗口,询问“是否需要我创建一些演示数据”。当用户单击“Yes”时,它的用户生成数据。

#3


3  

One work around would be to read the SQL file out of the bundle until the user tries to edit something. Then copy it over. Since the file is small it should not even require a spinner.

其中一个工作就是从包中读取SQL文件,直到用户尝试编辑某样东西为止。然后复制它。由于该文件很小,因此甚至不需要旋转器。

I had this exact problem with an App recently. In our case we we using core data and copied the SQL file on startup as well.

我最近有一个应用程序的问题。在我们的例子中,我们使用core data并在启动时复制SQL文件。

Unfortunately you have to target iOS 5.

不幸的是,你必须针对iOS 5。

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

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

#4


0  

Just add the following attribute and Apple will let you pass (if you are not using

只要添加以下属性,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 and two persistent stores should be the way to go. But for you, when first copying the file set the com.apple.MobileBackup file attribute. Unset it after the user makes his first modification

应该使用CoreData和两个持久存储。但是对你来说,当你第一次复制文件的时候。MobileBackup文件属性。在用户进行第一次修改后取消设置。

#6


0  

As the ultimate solution, I suggest using CoreData, with your canned data as an additional read-only persistent store.

作为最终的解决方案,我建议使用CoreData,将您的压缩数据作为一个额外的只读持久存储。

However, in the mean-time, you need a simple, quick solution. Create a simple screen that loads sample data into the user's database. Put a link in your "system" or "tool" screen so it can be done any time.

然而,在此期间,您需要一个简单、快速的解决方案。创建一个简单的屏幕,将示例数据加载到用户的数据库中。在“系统”或“工具”屏幕上放置一个链接,以便在任何时候都可以进行。

Specifically, though, bring it up when the app is first run. You are asking the user if they want sample data in their document, with the size of the data, so they are the one creating the document. Also mention that the sample data can be deleted or replaced at any time.

具体地说,当应用程序第一次运行时,请将它提出来。您正在询问用户是否希望在文档中使用具有数据大小的示例数据,因此他们是创建文档的人。还要注意,可以随时删除或替换示例数据。

This should get you past the review.

这应该能让你通过审查。

#7


0  

Using your "private" SQL data access layer

Since you are not using Core Data, but a custom access layer, I can only suggest not to place the "initial seed" database in the iCloud folder, but write a custom procedure that generates it at runtime (eg. when you first launch the app).

由于您没有使用核心数据,而是使用自定义访问层,所以我只能建议不要将“初始种子”数据库放在iCloud文件夹中,而是编写一个自定义过程,在运行时生成它(例如)。当你第一次启动应用程序时)。

The best approach should be placing your real SQL file in the common application bundle, outside the iCloud shared folder. Through your procedure you will have to read all the contents of this file and recreate a clone in the shared folder, so that this new file will appear as user-generated content and not bundled content. This will require a bit of useless overhead, but I think it's required to avoid Apple from rejecting your app.

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

I suggest you not to backup all the images and leave them in the main bundle, if possible, since they are already present in every downloaded app, and then will uselessly occupy precious space on iCloud. Try to place in iCloud only what is strictly necessary.

我建议你不要备份所有的图片,如果可能的话,把它们放在主包中,因为它们已经出现在每个下载的应用程序中,然后将毫无用处地占用iCloud上的宝贵空间。尽量只在iCloud中放置那些严格必要的东西。

In case you are planning to use Core Data and iCloud together.

First of all, placing a custom SQLite3 database file in iCloud is dangerous for a couple of reasons, the first being the fact that you will never be able to appropriately handle merge issues between different data if your app is being used on different devices concurrently.

首先,在iCloud中放置一个定制的SQLite3数据库文件是危险的,原因有二:首先,如果你的应用同时在不同的设备上使用,你将永远无法恰当地处理不同数据之间的合并问题。

I cannot give you a complete solution here, since it will require too much time and space. But I suggest you to look at the Session 227 talk from this year's Apple WWDC. They talk about using Core Data and iCloud together, also addressing the same issue you are facing right now (i.e. initial database seeding and synching through iCloud). If you access the WWDC page through your Apple Developer portal, you will be able to get a complete copy of the sample project.

我不能在这里给你一个完整的解决方案,因为它需要太多的时间和空间。但我建议你看看今年苹果全球开发者大会上的第227次会议。他们讨论将核心数据和iCloud结合在一起,也解决了你现在面临的问题(即通过iCloud进行初始的数据库播种和同步)。如果您通过Apple Developer门户访问WWDC页面,您将能够获得示例项目的完整副本。

#8


-1  

If you're using Core Data, you can keep the sample data in the app bundle. Open it as a read-only persistent store on the same coordinator as the read/write store in your docs directory, and Core Data will make sure things stay in the right place on save. This is explained in the "Best Practices for Using Core Data" video from WWDC 2012.

如果使用Core Data,可以将示例数据保存在app bundle中。在与文档目录中的读/写存储相同的协调器上,将其作为只读持久存储打开,而核心数据将确保保存时保持在正确的位置。这在《使用核心数据的最佳实践》视频中得到了解释。

If you're using SQLite directly, it's not so easy but you can implement your own logic for doing same.

如果直接使用SQLite,这并不容易,但是可以实现自己的逻辑。


分享到: