CoreData loses relationship - only after reload - ios

I am struggling with a problem since multiple hours - when I try to save some data to my CoreData - everything is fine, BUT when I restart my app, the relationship is lost.
It's an 1:n relation, the inverse relation is here (having multiple inverse relations, and all others are fine)
When I check with a sqlite-viewer, the chances are saved into the database, but when I reload the app, that 1 relation is gone.
There is also another question here, BUT: the solution did not work for me (and it seems that the solution not works for everybody) - and its 3+ years old.
Thanks in advance
Here is more information. The relation is between TaskCraft and Craft, like in this picture:
My CoreData Objects Are:
TaskCraft
#NSManaged var taskcraft: [TaskCraft]?
Craft
#NSManaged var craft: Craft?
My options:
Ill can reproduce the error all the time: When ill save the data to CoreData, my database look like:
When ill reload my app, and there is any CoreData-Fetch, my DB look like:
In this fetch i do not modify any of that NSManagedObjects.

Ok problem solved for me. Ill created (by Xcode) all the NSManagedObjects again, changed all NSSets to the real class names, deleted the simulator cache, and tried again.
Now it works fine, no relation is dropped anymore.
Very strange, but i am happy now.

Related

Should I use NSUserDefault, dictionaries, core data - or something else?

I'm having some issues with the app, that I'm making, which I thought would be a lot easier to explain with some photos, so ... :
Ofcourse the "Create New Person-button" in nr. 1 leads you to number two.
Now, I'm having issues figuring out how to save this data about the person in the "People Diary". The goal is, that when you enter a person's name, add a photo (an enable-camera feature, I will struggle with at a later time...) and add an answer to the question - then you only need to press "Save this person", and then you will be redirected to the AllPersonsInYourDiaryViewController, where there is now a new tableViewCell with this new person's name (maybe with a subtitle containing the answer and the photo shown in miniature in the cell too).
(Naturally you can then enter this cell with the data about the person too - but that comes next.)
So far in the app, I have used NSUserDefault, when allowing the user to create this specifik Diary by the Name "Antons Diary" with the specifik question and so on. But now it came to my attention, that maybe it is smarter to use something else? I tried with dictionaries, but couldn't get this to work properly.
So...: Before I spend hours and hours playing around with one of these ways, will someone smarter than me, tell me what the best approach would be?
If I can give my two cents, the first thing you have to do is to “design” how to represent a person programmatically. You can create a struct or class to do so, even though a struct is more suitable:
struct Person {
var name: String?
var answer: String?
var photo: String?
}
Then you can decide how to save the data of such an object persistently. If you want to use a database, then I would recommend using SQLite with FMDB library. It’s really easy and fast to learn how to use it, and it's also quite handy. I've used it big projects and it works smoothly. I find CoreData too complicated and an overkill based on what you need.
If you don’t want to use a database, your only other way is to save to files, but still, you’ve got options here too. If you encode (see Codable protocol in Swift), you can use NSKeyedArchiver to convert to Data object and write then to disk. If you like using dictionaries, and since the properties you’re going to have for a person are not going to be too many, you could create a dictionary by assigning the properties and their values, and then convert and save as JSON data, or even Plist files. Without any intension to do promotion here, but just to provide some additional help, if you want take a look to a library that I’ve written and that can do all these automatically for you. It’s a protocol that you have to adopt, and then you can instantly convert your struct to a dictionary, JSON or plist and save to files.
No matter which way you’re going to select, save the images as single files to documents directory, and keep their file names only stored to database/file. Based on them, you can build the path to each image (or the URL) easily when needed. Warning: Do not save the full path to the documents directory, especially if you’re testing on Simulator; paths are changing on each build. Save the file name only.
Additionally, if you’re going to use a struct like the one shown above, you could implement small but super convenient functions that will be responsible for saving, loading, or updating your data to the solution (database/file) you’ll eventually select. That way, you’ll have related stuff gathered in one place, and easily accessible (i.e., person.save()).
struct Person {
var name: String?
var answer: String?
var photo: String?
func save() {
…
}
func load() {
…
}
// More functions…
}
Lastly, avoid using UserDefaults, or at least keep just a few non-critical data there. UserDefaults are not meant to keep all data produced by your app. Most importantly, do not use it for saving sensitive data, especially passwords or other stuff like that.
I hope the above will help you make your mind.
I can give you the logic behind coreData and NSUserDefaults, but you will decide which one should be used.
CoreData is usually used as a database. you can create entities and attributes for every entity. Moreover, you can create relations between these entities.
When extracting data from coreData, you can arrange this data using NSSortDescriptor or select a specific record using NSPredicate.
So as you can see CoreData is a database.
While NSUserDefaults is usually used to save a password, username, userID... and such issues that you will regularly use in the app. NSUserDefaults gives you a direct access to the saved variables at any time. However, CoreData will take more time and lines of code to access the entity and make the query.
Now, check which method suits your case more.

Fix uneccessary copy of NSManagedObject

I'm sorry the title may mislead you, since I'm not so good at English. Let me describe my problem as below (You may skip to the TL;DR version at the bottom of this question).
In Coredata, I design a Product entity. In app, I download products from a server. It return JSON string, I defragment it then save to CoreData.
After sometimes has passed, I search a product from that server again, having some interaction with server. Now, I call the online product XProduct. This product may not exist in CoreData, and I also don't want to save it to CoreData since it may not belong to this system (it come from other warehouse, not my current warehouse).
Assume this XProduct has the same properties as Product, but not belong to CoreData, the developer from before has designed another Object, the XProduct, and copy everything (the code) from Product. Wow. The another difference between these two is, XProduct has some method to interact with server, like: - (void)updateStock:(NSInteger)qty;
Now, I want to upgrade the Product properties, I'll have to update the XProduct also. And I have to use these two separately, like:
id product = anArrayContainsProducts[indexPath.row];
if ([product isKindOfClass:[XProduct class]] {
// Some stuff with the xproduct
}
else {
// Probably the same display to the cell.
}
TL;DR
Basically, I want to create a scenario like this:
Get data from server.
Check existed in CoreData.
2 == true => add to array (also may update some data from server).
2 == false => create object (contains same structure as NSManagedObject from JSON dictionary => add to array.
The object created in step 4 will never exist in CoreData.
Questions
How can I create an NSManagedObject without having it add to NSMangedObjectContext and make sure the app would run fine?
If 1 is not encouragement, please suggest me a better approach to this. I really don't like to duplicate so many codes like that.
Update
I was thinking about inheritance (XProduct : Product) but it still make XProduct the subclass of NSManagedObject, so I don't think that is a good approach.
There are a couple of possibilities that might work.
One is just to create the managed objects but not insert them into a context. When you create a managed object, the context argument is allowed to be nil. For example, calling insertNewObjectForEntityForName(_:inManagedObjectContext:) with no context. That gives you an instance of the managed object that's not going to be saved. They have the same lifetime as any other object.
Another is to use a second Core Data stack for these objects, with an in-memory persistent store. If you use NSInMemoryStoreType when adding the persistent store (instead of NSSQLiteStoreType), you get a complete, working Core Data stack. Except that when you save changes, they only get saved in memory. It's not really persistent, since it disappears when the app exits, but aside from that it's exactly the same as any other Core Data stack.
I'd probably use the second approach, especially if these objects have any relationships, but either should work.

Breeze Offline Mode Issues

UPDATE: Here is a plnkr to illustrate the problem
http://plnkr.co/edit/bwQL3o?p=preview
Scenario in offline mode
You create an entity and store it in the localstorage
Breeze generates a tempKey: EmployeeId: -1 and populates a tempKeys array with the EmployeeId: -1. Every thing is all good and great.
Later on, (the application was terminated the device was turned off...) you import the stored data and create a new entity.
So Breeze loads the stored data sees the TempKeys of EmployeeId: -1 and generates a new EmployeeId: -2
Every thing is still good and great.
The problem
When you store this new data set in the localstorage the tempKeys
array only contains the EmployeeId: -2 entry.
Later on, when you import the stored data and try to create a new Employee you get
an Error:
A MergeStrategy of 'Disallowed' does not allow you to attach an entity
when an entity with the same key is already attached:
Employee:#Context--1
The question
Why is Breeze not keeping track of the current TempKeys? Is this a Bug? How do we fix this Scenario ? Any help would be greatly appreciated.
Ok, this was a bug and has now been fixed in the breeze.js repo on GitHub. This fix will also go out on the next full release of Breeze.js ( probably sometime next week). ... and thanks for finding this and providing the plunkr.

Magical Record persistance issue

I have following Problem:
First up... i use MagicalRecord for the whole CoreData thing
I have two Entities: A and B
They have a relation between each other
I create one instance of Entity A
I create several instances ob Entity B and set the relation
I don't call [[NSManagedObjectContext defaultContext] save];
It's fine
The relation is ok... I can check it using the findByAttribute method
If I call this save then the relation is destroyed...
the same check using findByAttribute does not find results any more
I have absolutely no clue what I am doing wrong or if it's a bug in CoreData / MagicalRecord...
I made a sample project showing the problem.
https://github.com/bliblablo/MagicalRecordsProblem
You can see the problem by following this steps:
click "create"
click "add"
click "check"
see the log output for results
click "save"
click "check" again and see the problem in the log :)
Any help is really appreciated!!!
Thanks a lot!
Sounds like the problem I was having with temporary ObjectID's not getting refreshed in the default context.
See my answer on NSPredicate not executed for details.
If you haven't already sorted it out, try checking the ObjectID of the NSManagedObject instances at various points. Especially if you (or Magical Record) are using the object as part of an NSPredicate to do the later fetches.
I think this is not MR bug. It is a bug from core data. See this post.
http://wbyoung.tumblr.com/post/27851725562/core-data-growing-pains

Strange CoreData error ... value returned from insertNewObjectForEntityForName: appears to be corrupt

I have a very strange error happening in an App that has been working for a long time.
I can no longer create one of my entities in my CoreData model.
When I create one particular entity in my model and try to print it using NSLog( #"%#", obj ), I get this strange message:
2011-11-08 13:03:05.936 iLearnFast[31541:15503] -[__NSCFNumber objectID]: unrecognized selector sent to instance 0xa069e20
When I loop over the attributes / relationships for this object and print them out, one particular one to one relationship returns a strange value from [obj valueForKey:]. The value it returns is the same pointer / object that is mentioned in the above error message.
I thought I might have been corrupting memory somewhere, but I inserted the code to create the entity at the very beginning of my executable as soon as datastructures are initialized, and I get the same problem. I am extremely confident that I have not made any memory errors at this point (and a memory error would be more random ... I can create thousands of objects, and always the same entity has the same problem with the same relationship, and no other entities ever have a problem).
After narrowing down the problem to this one relationship, I found that I could make the error go away by renaming the relationship to anything else. The relationship has been called "file" since my App was created.
I can make my code work again by renaming the property, although it messes up my automatic lightweight migration, but now I have to deal with figuring out how to do a more complicated migration.
If anyone has any ideas as to what might be going wrong, I would really appreciate it.
This is baffling me, and really feels like a bug in Apple's SDK.
I'm currently using XCode 4.2 and tried both the SDKs for both iOS5.0 and iOS4.3 and both had the same behaviour.
Ron
2011-11-08 13:03:05.936 iLearnFast[31541:15503] -[__NSCFNumber
objectID]: unrecognized selector sent to instance 0xa069e20
I guess you have a leak in your code. This line means that you trying to access objectID property which contains in your custom NSManagedObject, but for some reason that object no longer lives. Try to check your code on memory leaks.
I saw this same issue arise in a Swift project using Core Data. The issue only raised it's head when I pushed the app onto a device (it had been working fine in the simulator for some time).
The issue centred around a relationship between two entities. To illustrate see problem image:
After looking around SO for sometime I decided to rename the relationships, captain and player. See fix image:
Only after renaming both 'ends' of the relationship did the error go away.
I had same error message and in my case reason was that I had relation property teacher and created read-only property isTeacher.
This issue caused by Objective-C name conventions: CoreData was confused in getting teacher property. Instead of accessing real relation and giving me real object it taking isTeacher getter with BOOL type, cast it to NSNumber and try to deal with it like CoreData relation and call objectID.
After renaming isTeacher to isTeacherLicence problem gone.

Resources