I'm having trouble understanding what my options are to merge 2 or more conflicted versions of a UIDocument stored in iCloud. After detecting the document conflict, I currently use removeOtherVersionsOfItemAtURL to simply keep the latest version of the document's data. However, I would like to support merging conflicted documents.
The only resources and tutorials I found referred to Core Data and persistent stores for merging, and the examples used things like SQLite, which I don't use. In my case, each document is a separate file.
Background: My app uses custom NSObject subclasses (with relevant of properties) to store data. They are encoded and decoded (using NSCoding + NSKeyedArchiver/NSKeyedUnarchiver) inside of a UIDocument. I have set up my app to fully work with iCloud. It can make files ubiquitous and vice-versa, edit and delete them.
Please help me to understand what I can do regarding merging conflicted UIDocument's!
You'll use NSFileVersion to get conflicting versions of the file. When you get into a conflict state, the +unresolvedConflictVersionsOfItemAtURL will give you NSFileVersion instances for any versions not considered "current". You can then use the URL property of NSFileVersion to get the contents of those conflicting versions.
Now you have more than one version of the document. How you resolve the conflict is up to you-- go through the conflicting versions and merge changes in whatever way makes sense for your document structure. If you can resolve it in code, you'll probably take some of the data from one of the conflict versions and copy it into the current version, until the current version contains the final merged version.
Once you've done that, save the document, remove the conflicting versions (removeOtherVersionsOfItemAtURL:error:), and mark the current file version as resolved. You might also want to use removeAndReturnError: to actually get rid of the older version(s).
Some more detail on this can be found in Apple's guide for resolving document version conflicts.
Related
I'm working to get rid of a bunch of warnings in our codebase of the form:
Entity.relationship should have an inverse.
In general, although our code does not reach through these inverses, it sounds like it is a good idea to put them in anyway.
My question is about how I would do this.
Should I make these changes through a lightweight migration? That is, should I be creating another xcdatamodel?
For the given data model, what if we already have multiple versions/migrations. For example supposed we have the ReportsDataModel. And underneath that are ReportsDataModel1, ReportsDataModel2, and ReportsDataModel3. It seems like XCode 7 is giving me the same warnings on each data model. So if I fixed them in a lightweight migration to ReportsDataModel4, it seems like it wouldn't get rid of the previous warnings.
What is the recommended way to get around this issue?
-Arjun
First of all, you would be correct that you should implement the inverse relationships, as Xcode requires them. It's a good database practice, and you can virtually ignore the excess relationships if you're not using them.
Yes, you should be able to perform a lightweight migration, that is, create a new data model and let Xcode infer the changes. Here, Apple states that lightweight migration supports adding relationships.
Regarding your second question, true, creating another data model will not actually solve the warning in the older ones. You must leave the older models in Xcode so it can compute the lightweight migration process. If you erase a model and a user updates from an older version that uses that model, their data will be corrupted. (However, if you haven't published a version of the app with a particular data model, you can delete that data model.)
However, you can try suppressing the inverse relationship warning entirely.
In Xcode, click on your project file.
Click the Build Settings tab.
Search for MOMC.
Set Suppress momc warnings on missing inverse relationships to Yes.
EDIT about getting rid of the warnings on only the old models: This question suggests that you could move the old data model out of Xcode and place it elsewhere, and add a Copy Files action to Build Phases to copy the file back in at compile time. This way the file and it's extraneous warnings could be out of your way. Sorry there isn't a less "hacky" solution.
I created a data model file "ChatModel.xcdatamodeld" in my project. Then I merged branches on github. There're conflicts in "project.pbxproj". I fixed them. Then the error happened:
"/Users/mac/zhongqing-ios/Zhongqing/Zhongqing/Model/ChatModel.xcdatamodeld: Could not create bundle folder for versioned model at '/Users/mac/Library/Developer/Xcode/DerivedData/Zhongqing-chngcirectbawjenegkxtgdfgoux/Build/Products/Debug-iphonesimulator/Zhongqing.app/ChatModel.momd'".
"/Users/mac/zhongqing-ios/Zhongqing/ChatModel.xcdatamodeld: Unable to write VersionInfo.plist for versioned model at '/Users/mac/Library/Developer/Xcode/DerivedData/Zhongqing-chngcirectbawjenegkxtgdfgoux/Build/Products/Debug-iphonesimulator/Zhongqing.app/ChatModel.momd'".
Each time I have to delete the Derived Data so that the project can be run.
And then the error happen again.
Although some files are readable they should be treated like binary files. .pbxprojfiles are good example.
From pro-git
Some files look like text files but for all intents and purposes are to be treated as binary data. For instance, Xcode projects on the Mac contain a file that ends in .pbxproj, which is basically a JSON (plain text javascript data format) dataset written out to disk by the IDE that records your build settings and so on. Although it’s technically a text file, because it’s all ASCII, you don’t want to treat it as such because it’s really a lightweight database — you can’t merge the contents if two people changed it, and diffs generally aren’t helpful. The file is meant to be consumed by a machine. In essence, you want to treat it like a binary file.
I have a huge text file in my application (version 1.0).
Lets assume that a new version (2.0) of this file was just released.
Most of the file remained the same but the new (2.0) version has a few modifications (some lines removed, others added).
I now wish to update the file (1.0) to the new version (2.0), but do not wish to download the whole file again.
I would love to just patch the file with the changes of the new file, thus saving bandwith from downloading the WHOLE new file from my server.
(Similar to the way versioning systems like git or svn act)
How can I do this programmatically? Are there any iOS libraries available?
Thank you
You need to implement some kind of Binary delta compression such as zdelta, or Remote Differential Compression such as the one in rsync.
Personally I'm not aware of such algorithm implemented specifically for iOS, but I'm sure it's possible to find one that is implemented in C/C++ which can be seamlessly used in the iOS environment.
Edit: I also recommend you to read this.
It's actually a big problem... if your API let you ask the data of a file in a specific range, you can just a ask the data range that you need to replace, and seek the file at the range and overwrite the specific data... this mean that you have to take trace about the changes every time you update the files... and your app update has to know the ranges to request... this is not a solution... I hope will be a start point to implement your own solution
to try to get partial conent in a range you need to add to your request header something like this:
Range: bytes=0-999
I think, you can do this by yourself without third party libraries. To achieve this, all you need is 1)Piece of code which will generate the metadata for joining versions of your file (offsets, lengths, and pointers to the data to be changed in older version); 2)piece of code which will do the hard work: read meta and put the parts on right places. Several days of struggling with offsets, and you are done ;) Good luck!
I'm working on enhancing an existing application to use iCloud so the same data can be accessed on multiple devices.
I'm planning to use document-based storage and to use a file package (i.e. a directory of files represented as single file and handled by NSFileWrapper).
My main question is: are file package updates guaranteed to be atomic? If I open the app and a few files within a single document package have been changed, will iOS download them and then inform my app only when all subfiles are present and in-place? Or is there a risk that the files will come in one by one, leaving me with a potentially inconsistent package?
Also, my existing app uses SQLite (not through Core Data, but rather through a custom wrapper). Some parts of the app will clearly require a nice, indexed SQL database for performance. So my plan is to use the iCloud data as a reference store, keep an SQLite database in the Caches directory for performance reasons (or somewhere else strictly local to the device) and update the database based on what's in iCloud. Changes the user makes in the app will be recorded both in iCloud and in the local database. Is this crazy or reasonable?
so my answer to your main question is academic, because i don't have a test-base for testing this, but …
given that your directory is treated as a single-entity, if you coordinate using that entity as the item manipulated by your NSManagedDocument.
i'm basing this answer on my notes from learning about using NSManagedDocument to manage iCloud :
// Conflict
// - what if a device detached from the network changed a document that another device changed?
// and then the detached device re-attached and tried to apply the change and it conflicted?
// one must manage the conflict by looking for the InConflict document state.
// - when it happens, a decision must be made which version of the document to use and/or
// manage changes. probably want to set NSManagedObjectContext's mergePolicy to other than
// default (Error).
// - then update the old, conflicting NSFileVersion to no longer conflict (and remove those
// versions).
(yes, i take notes in objective-c comment format.)
I'm using CoreData for one of my databases, and I'm having trouble with an upgrade to my App.
My old databases are no longer loading ... I get the error "Cocoa error 134190" which I believe means that the inferred mapping model is failing.
When I started editing this version of my App, I created a new model version, and I simply added one new attribute to one entity, and two new attributes to another entity. Nothing was changed or deleted.
I've checked inside my App's directory, and all of the model versions are there.
I even have a snapshot of my previous version of the App, and with that snapshot I can load my core data files.
To debug what is going wrong, I took the exact data model from my previous snapshot and used it in my current version, and I still get the error.
I've even done a "diff" on every file in my ".app" directory packages, and there are only two files that are different between the two. One is the actual binary for the application, and the other is the "Info.plist" file. I looked at the Info.plist files, and they too are basically identical (the new one has a newer bundle version, but no other change).
Any idea why the inferred mapping is broken?
I am getting the same error even when the data models are completely unchanged from my last version which works.
This is extremely frustrating.
Any information as to how to track down problems like this in the future would also be greatly appreciated!!!
I found the answer ... the problem was that in my new version, I was rearranging the locations of my files. I did so by simply moving the database to a new location. What I didn't realize is that coredata databases have some knowledge of their path built in, and you can't simply move them.
I needed to change the location of the database using the NSPersistentStoreCoordinator's method:
migratePersistentStore:toURL:options:withType:error:
My fault, but I really wish that core data error messages weren't so cryptic.
In the next version of my App, I'm going to remove core data completely.
Ron
I don't believe core data actually cares where the database file is, but I could be wrong. In the times I have seen this error, specifically "Cocoa error 134190" while trying to infer a model mapping, it's been the case that there was actually a problem trying to migrate to the next version of the model.
For example, when I found this thread today because I was helping someone with this problem, the real underlying issue was that the type of one of the attributes was accidentally changed. The automatic migration can't handle that sort of change.
A somewhat easy way to look at the changes between two model versions is to diff the description files themselves on the command line with diff:
diff yourNameHere.xcdatamodeld/yourNameHere5.xcdatamodel/contents yourNameHere.xcdatamodeld/yourNameHere4.xcdatamodel/contents
(That assumes you have a core data model description named "yourNameHere" and you're looking at a problem migrating from version 4 to 5. You'll have to adjust for your specific files.) In this diff you should see whatever additional things you've added, but you're really looking for something like a type changing when it shouldn't.