第一次应用程序更新,用户数据丢失(存储在文档目录中)

发布于 2024-10-31 12:21:53 字数 1148 浏览 1 评论 0原文

我的第一个应用程序更新昨晚刚刚上线,我收到投诉称该更新导致用户创建的数据(其中一些)消失。我已经能够重现该问题,但无法说出原因。

在 Documents 目录中,我保存了一个关键文件,它告诉我所有用户文件的“标题”及其文件名(完整路径)。那么用户的所有文件也都在Documents目录中。

当更新发生时,密钥文件仍然存在(至少,我认为这是因为数据显示在应用程序的第一个屏幕中 - 应用程序在更新后会完全退出并重新启动,对吧?),但是当用户尝试时要导航到实际文件,没有任何数据,并且用户输入的任何新数据都不会保存。

当我不小心使用无效的文件名(其中包含不正确的字符)时,它的行为与调试时的行为完全相同 - 它从未保存。但随着更新,这些文件在旧版本中已正确保存,但在新版本中却以某种方式失败。

这是我的第一个应用程序,也是我的第一次更新,我在这里很茫然。我暂时已经下架了该应用程序(不知道你不能简单地恢复到旧版本!哎呀!)并且非常感谢有关在哪里/如何查找问题以及如何编写的任何想法一个很好的更新,不会丢失数据。 (到目前为止,我发现的只是“将数据保存在文档目录中”,这就是我已经在做的事情。)

我确实将原始应用程序保存在自己的项目中,我可以返回并再次使用它。当我开始进行更新时,我复制了整个目录,我确实想知道这是否可能是问题所在?我确实更改了保存所有 XCode 文件的目录的名称,并使用了 Project>Rename 功能。这会以某种方式产生这种效果吗?

该应用程序(原始版和更新版)在 4.2 及更高版本上运行(如果有的话)。

解决方案:

我相信我已经解决了这个问题。就像我在问题开始时所说的那样,我将用户文件的完整路径保存在我的关键文件中。显然,更新后不保证完整路径相同(我确信这已记录在某处,但我没有遇到它)。

因此,用户文件已传输到更新的新文档目录,但我在旧的绝对路径中查找它们,即不在我的沙箱中。

通过在应用程序的 didFinishLaunching 中放置一个循环以拉出文件路径的有问题的文件(我一直在本地查找并且它仍在工作)并将它们削减为文件名来修复。每当执行文件操作时,都必须以编程方式找到并添加文档路径。

就其价值而言,对我来说实际上是有好处的,因为无法恢复到商店中以前的二进制文件,因为重新安装原始版本不会解决用户的问题,但我相信它至少可以解决最初。我确实希望有一种方法可以禁止更新,但仍然允许新的购买(因为问题不影响新的购买)。不过,这种情况可能还不够普遍,不足以证明这一点。

My first app update just went live last night and I've gotten a complaint that the update caused the user-created data (some of it) to disappear. I have been able to reproduce the problem, but can't tell why.

In the Documents directory, I have saved one key file which tells me the "title" of all the user's files and their filenames (full path). Then all the user's files are also in the Documents directory.

When the update occurs, the key file is still there (at least, I think it is because the data shows up in the first screen of the app - apps do fully quit and relaunch after updating, right?), but when the user tries to navigate into the actual files, there is no data and any new data the user types in is never saved.

It's acting exactly as it did in debugging when I was accidentally using an invalid filename (with improper characters in it) - it never saved. But with the update, these files had been saved properly in the old version but are somehow failing in the new version.

This is my first app and my first update and I'm quite at a loss here. I've pulled the app from sale for the moment (didn't know you couldn't simply revert to an old version! yikes!) and would deeply appreciate any ideas as to where/how to look for the problem and how to write a GOOD update that won't lose the data. (So far, all I've found is "save data in the Documents directory", which is what I was already doing.)

I do have the original app saved in its own project and I can go back and work from that again. I copied that whole directory when I started working on the update and I do wonder if that could somehow be the problem? I did change the name of the directory that holds all the XCode files and used the Project>Rename function. Could that have this effect somehow?

The app (both original and update) is running on 4.2 and above, if that matters.

RESOLUTION:

I believe I have figured out this problem. Like I said near the beginning of my question, I was saving the FULL PATH of the user files in my key files. Apparently, the full path is NOT guaranteed to be the same after an update (I'm sure this is documented somewhere, but I hadn't run across it).

So, the user files HAD been transported to the update's new Documents directory, but I was looking for them at the old absolute path, i.e. not in my sandbox.

Fixed by putting a loop in app's didFinishLaunching to pull out the offending file of filepaths (THAT, I had always looked for locally and it was still working) and chop them down to just fileNAMES. The Documents path has to be found and added programmatically whenever file operations are performed.

For what it's worth, it was actually good for me that there is no way to revert to a previous binary in the store because re-installing the original version would not have fixed the user's problem, but I would have believed it had, at least initially. I do wish there was a way to have disallowed UPDATES, but still allowed new purchases (since the problem did not affect new purchases). Probably not a common enough situation to warrant it, though.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

铜锣湾横着走 2024-11-07 12:21:53

只是为了让互联网路人对此有更多了解。是的,OP最后回答了他自己的问题。您绝对不应该在文档目录中存储文件的绝对 URL,否则可能会导致数据丢失。这是因为当您更新应用程序时,通过更改 .plist 文件中的版本号,iOS 会使用不同的十六进制名称为该应用程序创建一个新目录。现在,您的绝对 URL 引用了错误的位置,并且不会返回正确的文件。

在部署到设备之前,您可以在自己的计算机上看到此内容,方法是导航到

~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/ ,

您将看到名称如下的文件夹:

6AA4B05C-8A38-4469- B7BE-5EA7E9712510
CB43C5F3-D720-49C3-87D4-EBE93FFD428B

这些文件夹内将是 iOS 应用程序的标准文件系统布局。即

文档
图书馆
tmp
YourApp.app

通过引用整个 URL,您将看到类似

~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/6AA4B05C-8A38-4469-B7BE-5EA7E9712510/Documents/MyVeryImportantUserData.txt

但是,当您更新应用程序,应用程序将尝试的新 URL 为:

~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/CB43C5F3-D720-49C3-87D4-EBE93FFD428B/Documents/MyVeryImportantUserData.txt

为空。

相反,您应该始终在获得文档目录文件路径前缀后仅保存文件路径来进行动态引用。

/**
 Returns the path to the application's Documents directory.
 */
- (NSString *)applicationDocumentsDirectory {
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

对于原始发布者来说,您可以通过发布新的更新来保存用户丢失的数据,其中您修改了代码以获取绝对 URL 并通过字符串的文档部分修剪所有内容。然后,您可以使用正确的文档目录 url 作为文件路径的前缀,应用程序就会找到数据。

Just to shed some more light on this for internet passer-bys. Yes, the OP answered his own question at the very end. You should never store absolute URLs for files in your documents directory and doing so may cause data loss. This is because when you update an app, by changing its version # in the .plist file, iOS creates a new directory for that app with a different hexadecimal name. Now your absolute URL is referencing the incorrect location and won't return the proper file.

You can see this on your own computer before you even deploy to a device by navigating to

~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/

There you'll see folders with names like:

6AA4B05C-8A38-4469-B7BE-5EA7E9712510
CB43C5F3-D720-49C3-87D4-EBE93FFD428B

Inside those folders will be the standard file system layout for iOS apps. i.e.

Documents
Library
tmp
YourApp.app

By referencing the entire URL, you'll see something like,

~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/6AA4B05C-8A38-4469-B7BE-5EA7E9712510/Documents/MyVeryImportantUserData.txt

However when you update the app the new URL that the app will try will be:

~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/CB43C5F3-D720-49C3-87D4-EBE93FFD428B/Documents/MyVeryImportantUserData.txt

which is empty.

Instead you should always make a dynamic reference by only saving the filepath after you've gotten the documents directory filepath prefix.

/**
 Returns the path to the application's Documents directory.
 */
- (NSString *)applicationDocumentsDirectory {
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

To the original poster, you could have saved your users lost data by issuing a new update where you modified your code to take the absolute url and trim everything through the documents portion of the string. Then you could prefix the filepath with the proper documents directory url and the app would have found the data.

纵山崖 2024-11-07 12:21:53

问题海报和第一个答案都有助于澄清到底发生了什么。

因此,根据他们的观察,我开始意识到(似乎是)通向 Documents 文件夹的路径中的文件夹被重命名。这导致我找到了一个解决方案,我只保存文件的文件名,并创建一个实用程序类来获取已保存且现在存储在 当前 文件名的完整路径字符串应用程序更新后的>当前文档文件夹:

#import "StringUtils.h"

@implementation StringUtils

+ (NSString *)getFullDocumentUrl:(NSString *)fileName
{
    return [NSString stringWithFormat:@"%@/%@",[self applicationDocumentsDirectory],fileName];
}

+ (NSString *)applicationDocumentsDirectory
{
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

@end

Both the question poster and the first answer were helpful in clarifying what exactly was going on.

So building on their observations, I came to realize that there was (what appears to be) a renaming of the folders within the path that lead up to the Documents folder. This lead me to a solution where I'm saving only the fileNames of the files and creating a utility class for grabbing the current fullPath string of the fileName that had been saved and is now stored in the current Documents folder after the app updates:

#import "StringUtils.h"

@implementation StringUtils

+ (NSString *)getFullDocumentUrl:(NSString *)fileName
{
    return [NSString stringWithFormat:@"%@/%@",[self applicationDocumentsDirectory],fileName];
}

+ (NSString *)applicationDocumentsDirectory
{
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

@end
憧憬巴黎街头的黎明 2024-11-07 12:21:53

我遇到了同样的问题。下面是一些示例代码,用于通过迭代文档目录中的文件并替换文件路径信息并保留文件名来修复文件名。在执行最后一行更改文件名之前,我建议使用 NSLog 来确保这正是您在 UpdatedName 中想要的内容。

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
    NSFileManager *fm = [NSFileManager defaultManager];
    NSArray *filenames = [fm contentsOfDirectoryAtPath:documentsPath error:nil];

    //Match on the filepath leading up to docs directory
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^/\\S*Documents/"
                                                                           options:NSRegularExpressionCaseInsensitive
                                                                             error:nil];
    for (NSString *fileName in filenames)
    {
        NSRange fullLength = NSMakeRange(0, [fileName length]);
        //swap out the prepended directory structure with an empty string
        NSString *updatedFileName = [regex stringByReplacingMatchesInString:fileName
                                                                    options:0
                                                                      range:fullLength
                                                               withTemplate:@""];
        NSString *currentName = [NSString stringWithFormat:@"%@/%@",documentsPath,fileName];
        NSString *updatedName = [NSString stringWithFormat:@"%@/%@",documentsPath,updatedFileName];
        [fm moveItemAtPath:currentName toPath:updatedName error:nil];
    }

I ran into the same problem. Below is some example code to fix your filenames by iterating through the files in your documents directory and substituting out the filepath information while leaving the filenames. Before executing the last line to change the filenames, I would recommend using NSLog to make sure this is exactly what you want in your updatedName.

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
    NSFileManager *fm = [NSFileManager defaultManager];
    NSArray *filenames = [fm contentsOfDirectoryAtPath:documentsPath error:nil];

    //Match on the filepath leading up to docs directory
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^/\\S*Documents/"
                                                                           options:NSRegularExpressionCaseInsensitive
                                                                             error:nil];
    for (NSString *fileName in filenames)
    {
        NSRange fullLength = NSMakeRange(0, [fileName length]);
        //swap out the prepended directory structure with an empty string
        NSString *updatedFileName = [regex stringByReplacingMatchesInString:fileName
                                                                    options:0
                                                                      range:fullLength
                                                               withTemplate:@""];
        NSString *currentName = [NSString stringWithFormat:@"%@/%@",documentsPath,fileName];
        NSString *updatedName = [NSString stringWithFormat:@"%@/%@",documentsPath,updatedFileName];
        [fm moveItemAtPath:currentName toPath:updatedName error:nil];
    }
流星番茄 2024-11-07 12:21:53

获取当前文档目录路径

NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String

To get the current Document Directory path

NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
表情可笑 2024-11-07 12:21:53

您可以使用此功能来查找新的文档目录,

例如:这是您第一次构建应用程序并将照片保存到此网址

“file:///var/mobile/Containers/Data/Application/D79E1373-883C-412B-B84E- 5C1E47BD0BEC/Documents/Temp/Photo_65650205603482916.jpg”

在您重建应用程序之后,文档直接被更改为

file:///var/mobile/Containers/Data/Application/E387590D-B279-4C38-A601-65C6F7BC917D/Documents/

所以您使用旧网址时无法上传

上传时,您可以执行以下操作:

// you have photoURl 

func getURLtoUpload(photoUrl:URL) {
    let urlStr = photoUrl.lastPathComponent

    let fileURL = self.getDocumentsDirectory().appendingPathComponent("Temp").appendingPathComponent("\(urlStr)")
    // your url to upload here 
    let url = URL(string:fileURL)
}


func getDocumentsDirectory() -> URL {

    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let documentsDirectory = paths[0]
    return documentsDirectory
}

You can user this function to find new dicrectorty of documents

ex: this is first time you build app and save photo to this url

"file:///var/mobile/Containers/Data/Application/D79E1373-883C-412B-B84E-5C1E47BD0BEC/Documents/Temp/Photo_65650205603482916.jpg"

after that you rebuild app the doc direct is chaged to

file:///var/mobile/Containers/Data/Application/E387590D-B279-4C38-A601-65C6F7BC917D/Documents/

so you can't not upload when use old url

When upload you can do this:

// you have photoURl 

func getURLtoUpload(photoUrl:URL) {
    let urlStr = photoUrl.lastPathComponent

    let fileURL = self.getDocumentsDirectory().appendingPathComponent("Temp").appendingPathComponent("\(urlStr)")
    // your url to upload here 
    let url = URL(string:fileURL)
}


func getDocumentsDirectory() -> URL {

    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let documentsDirectory = paths[0]
    return documentsDirectory
}
风启觞 2024-11-07 12:21:53

如果您想永久保存文件的位置,请使用 NSURL 的书签功能。书签是一种不透明的数据结构,包含在 NSData 对象中,用于描述文件的位置。尽管路径和文件引用 URL 在应用程序启动之间可能很脆弱,但书签通常可用于重新创建文件的 URL,即使文件被移动或重命名也是如此。

https://stackoverflow.com/a/53274341/945906

If you want to save the location of a file persistently, use the bookmark capabilities of NSURL. A bookmark is an opaque data structure, enclosed in an NSData object, that describes the location of a file. Whereas path- and file reference URLs are potentially fragile between launches of your app, a bookmark can usually be used to re-create a URL to a file even in cases where the file was moved or renamed.

https://stackoverflow.com/a/53274341/945906

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文