Can I see the contents of Core Data managed objects in the stack variables area? - ios

I'm developing for iOS 6.1 and I'm learning Core Data.
I've got a managed object abc.myString that I've stuffed a string into:
abc.myString = #"the String";
When I see abc.myString in the variables area, if I look inside, I see only NSManagedObject and if I drill deeper, I see a lot of stuff I don't understand but I don't see my string.
But, after loading abc.myString, I can do this to verify what I put in:
NSLog( #"contents = %#", abc.myString );
Is this expected behavior; that I cannot see into abc.myString in the variables area?

Yes, this is expected behaviour. NSManagedObject doesn't store its' fields in instances variables. Think of NSManagedObject as a front end to the persistent store's data. NSManagedObject doesn't actually store the data it fetches and update the persistent store.
You can still access the data by using the debugger. In the console type the follow to log an object:
po variableNameOrAddressOfTheObjectYouWantToLog
You can also set up break points to do this logging automatically.

Yep, Xcode will not display the contents of the managed object in the variables list, but you can right click & use Print Description of 'xyz' to print the contents into the console.

Related

Is there a way to access properties of an x-coredata:// object returned from an NSFetchRequest?

TL;DR: Is there a way to programmatically read/recall (NOT write!) an instance of a Core Data entity using the p-numbered "serial number" that's tacked on to the instance's x-coredata:// identifier? Is this a good/bad idea?
I'm using a method similar to the following to retrieve the instances of an Entity called from a Core Data data store:
var managedContext: NSManagedObjectContext!
let fetchRequest : NSFetchRequest<TrackInfo> = TrackInfo.fetchRequest()
fetchResults = try! managedContext.fetch(fetchRequest)
for (i, _) in Global.Vars.numberOfTrackButtons! {
let workingTrackInfo = fetchResults.randomElement()!
print("current track is: \(workingTrackInfo)")
The list of tracks comes back in fetchResults as an array, and I can select one of them at random (fetchResults.randomElement()). From there, I can examine the details of that one item by coercing it to a string and displaying it in the console (the print statement). I don't list the code below, but using workingTrackInfo I am able to see that instance, read its properties into other variables, etc.
In the console, iOS/Xcode lists the selected item as follows:
current track is: <MyProjectName.TrackInfo: 0x60000374c2d0> (entity:
TrackInfo; id: 0xa7dc809ab862d89d
<x-coredata://2B5DDCDB-0F2C-4CDF-A7B9-D4C43785FDE7/TrackInfo/p22>;
data: <fault>)
The line beginning with x-coredata: got my attention. It's formatted like a URL, consisting of what I assume is a UUID for the specific Core Data store associated with the current build of the app (i.e. not a stable address that you could hardcode; you'd need to programmatically look up the Core Data store, similar to the functions we use for programmatically locating the Documents Folder, App Bundle, etc.) The third item is the name of the Entity in my Core Data model -- easy enough.
But that last number is what I'm curious about. From examining the SQLite database associated with this data store, it appears to be a sort of "instance serial number" associated with the Z_PK field in the data model.
I AM NOT interested in trying to circumvent Core Data's normal mechanisms to modify the contents of a managed object. Apple is very clear about that being a bad idea.
What I AM interested in is whether it's possible to address a particular Core Data instance using this "serial number".**
In my application, where I'm randomly selecting one track out of what might be hundreds or even thousands of tracks, I'd be interested in, among other things, the ability to select a single track on the basis of that p-number serial, where I simply ask for an individual instance by generating a random p-number, tack it on to a x-coredata:// statement formatted like the one listed above, and loading the result (on a read-only basis!) into a variable for further use elsewhere in the app.
For testing purposes, I've tried simply hardcoding x-coredata://2B5DDCDB-0F2C-4CDF-A7B9-D4C43785FDE7/TrackInfo/p22 as a URL, but XCode doesn't seem to like it. Is there some other data Type (e.g. an NSManagedObject?) that allows you to set an x-coredata:// "URL" as its contents?
QUESTIONS: Has anyone done anything like this; are there any memory/threading considerations why grabbing instance names in this manner is a bad idea (I'm an iOS/Core Data noob, so I don't know what I don't know; please humor me!); what would the syntax/method for these types of statements be?
Thanks!
You are quite close.
x-coredata://2B5DDCDB-0F2C-4CDF-A7B9-D4C43785FDE7/TrackInfo/p22
is the uriRepresentation() of the NSManagedObjectID of the record.
You get this URL from an NSManagedObject with
let workingTrackInfo = fetchResults.randomElement()!
let objectIDURL = workingTrackInfo.objectID.uriRepresentation()
With this URL you can get the managed Object ID from the NSPersistentStoreCoordinator and the coordinator from the managed object context.
Then call object(with: on the context to get the object.
let persistentStoreCoordinator = managedContext.persistentStoreCoordinator!
if let objectID = persistentStoreCoordinator.managedObjectID(forURIRepresentation: objectIDURL) {
let object = managedContext.object(with: objectID) as! TrackInfo
print(object)
}

Best way to save a dictionary containing bunch of Core Data objects?

I wish to save a dictionary containing some Core Data objects (bunch of different entities). The objects also have quite a few relationships (and inverse relationships) defined. What would be the best way to go about it?
I tried using NSKeyedArchiver and writing to a file. While that works great, when trying to read from the file using NSKeyedUnarchiver, it fails on one of the classes with the error
-[SomeEntity initWithCoder:]: unrecognized selector sent to instance
EDIT - More details
I have a bunch of objects, each having properties based on which they can be filtered. The properties are in themselves Core Data entity objects since they have a complex structure.
I wish to save the filters the user has selected so that the next time they view the objects, the objects can be filtered as per their previous selection.
Say there are 3 filters, Filter A, B and C and each can have 5 different values. Now the user might select Filter A1, A2, B1 and C3 (or a different combination). My question, how do I save these selected filters (A1, A2, B1 and C3 in this case) using Core Data?
Let me see if I understand your question: You have a collection of managedObjects that are already saved in a context. They may already be persisted in the SQL database. You want to save that collection ALSO to another file for other purposes. You have already considered saving the information of this collection inside core-data in some way and have already rejected it. You have also considered simply saving the query generation tokens to save the state of the database as it currently is, but that also is not what you want. The point is to have a file that contains a copy of some of the managedObjects organized in a way that you can get the data back without using the SQL database that was already designed exactly for that purpose.
Solution 1: Turn each managed object in a dictionary.
You can get every attribute and every property of every object by getting a managed object's entity and then accessing the attributesByName and
relationshipsByName property of the entity. From there you make a simple loop to put each property into a dictionary. I also suggest you store the objectID and point to the objectID when encoding the relationships. Then replace the managedObject in your dictionary with dictionary that contains all the attributes and relationship. This new dictionary should be easy to archive and unarchive.
This make sure that the data when you unarchive is exactly how you left it. When you unarchive you will get a COPY of data and if the managed objects have changed in your database since then, you will get the OLD values. Also these copies are not core-data object because they are unconnected to a managed Object Context.
Solution 2: Just save the Managed Object's ObjectId.
Replace every managed object in your collection with the object's objectId. This dictionary can be easily archived. When you unarchive it replace every objectId with a core data object (if found) using existingObjectWithID: on the context. If entities have been deleted then you won't get them back. If entities have changed then you will get the NEW values.
Solution 3: Don't do any of this
It seems to me that you may not be aware core-data are already saved in a database. If you have some collection of managedObjects, you should be able to recreated it from your database. If you aren't able to, then you should add properties and/or relationships that will allow you to so.
Try like this :
ARCHIVE :
NSDictionary *yourDictData = [NSDictionary dictionaryWithObject:json forKey:#"key"]; // This is for example. Here you have to replace ur dictionary
NSData *myData = [NSKeyedArchiver archivedDataWithRootObject:yourDictData];
UNARCHIVE :
NSDictionary *myData = [NSKeyedUnarchiver unarchiveObjectWithData:yourDictData];

Magical Record - context (not saving)

I'm completely unsure what's the problem. I'm creating a new object and inserting it into context:
Object *object = [Object MR_createEntity];
object.name = #"blahblah";
NSManagedObjectContext *context = [NSManagedObjectContext MR_defaultContext];
[context MR_saveToPersistentStoreAndWait];
According to the docs I shouldn't use + MR_contextForCurrentThread so I don't:
In particular, do not use +MR_contextForCurrentThread from within any of the +[MagicalRecord saveWithBlock:…] methods — the returned context may not be correct!
However if I open my app data I can see 3 files (1)appname.sqlite, (2)appname.sqlite-shm and (3)appname.sqlite-wal. As far as I know 2&3 are only cache. When I open file 1 accompanied by 2 and 3 I can see all my data. However when I open only file 1 (copied somewhere far from 2&3) there is no data in my database, so I suppouse something isn't working ok.
The problem is visible only on the device not on the simulator and only if you copy .sqlite to your computer from app data. If you copy all 3 files you can see all the records, but it suggest that everything is saved in cache instead of sqlite.
Maybe I should exchange [Object MR_createEntity] to [Object MR_createInContext:context] because the object is created but not inserted into persistent store?
The code you've given here is correct. The shm file is called the Shared Memeory log. And wal is called the Write Ahead Log. They are there to help SQLite perform saves and fetches faster. That said, you should always be able to open up the sqlite file and see whats in the data store.
With that, some things to look at are to enable the logs. Likely your data is not saving because you have specified a property as required and did not fill it in. Also, you may be looking in the wrong place o

CoreData fault - how to get data

I've researched tons of questions and documents about CoreData returning faults instead of actual values:
Relationship 'whiskers' fault on managed object (0xb7abab0)
This happens when I'm trying to get the count for the number of whiskers, such as:
self.numWhiskersLabel.text = [NSString stringWithFormat:#"%d", cat.whiskers.count];
Even if I try to log the whiskers set directly I still get a fault:
NSLog(#"whiskers: %#", cat.whiskers);
I understand that "Core data will not return full object until there is a need to access the actual value of that object. Each of your returned objects will be a 'fault' until this point." That's great, but there is a need to access the actual value at this point. I need the value right now! So how do I get out of this oxymoron? How can accessing the count of a Set not be considered needing the value?
I didn't get any feedback from my comment so I'm just going to assume whiskers is a set of NSManagedObjects
The set wont be loaded initially because internally it's coming from another table in the db. When you access .whiskers.count it still doesn't need to go and get the data yet, because all you're wanting is the number of whiskers in the set.
When you pull a whisker out of the set, then it will be faulted, try doing
NSLog(#"whiskers: %#", [cat.whiskers.anyObject anyProperty]);
That should give you a loaded NSManagedObject.
This is an error condition. Something is wrong with that NSManagedObject instance. Either it was deleted before you accessed it or you are trying to touch it from the wrong thread.
Please edit your question and show the code that is accessing that NSManagedObject.
Also, what happens when, in the debugger, you just do a po cat? Do you see the full Cat object or is that giving a fault error as well?

Core Data fetch request and unsaved changes

i have a problem. My iOS app is behaving really strange when it comes to fetching some data and having unsaved changes. For your interest the whole behavior appears while syncing some data with a web server. I wanted to do a full sync and then save the changes. I tried some workarounds but none of them was working well enough.
To the problem itself:
I sync some entities with a web server. They are organized into zones (their parent), which themselves are in a building. So for each entity i query if a matching zone already exists, and if not i create a new one. The problem now is that i'm unable to fetch those zones if they were just created (so a new but identical zone is created everytime). I also have the problem that i cannot fetch the correct building anymore once it is changed by adding a newly created zone to it, the result for the exact same query is suddenly empty.
I have ensured that [fetch setIncludePendingChanges:YES] is set, and i'm also using normal result mode not NSDictionaryResultType (see: NSDictionaryResultType expression not taking into account newly inserted objects).
I hope somebody can help.
A Fetch request fetches data from a context that has saved data in the persistent store from which the context is fetching. When you create a new Managed Object, you create it in your context (a.k.a. your scratch book) but not in your persistent store, yet. So before you can fetch a newly created object, you must save the changes of that context into your store.
Assuming that I understand your description right: I think your predicate for fetching your data is pretty complex, which forces core data to read from the persistent store. Thus, modifications in the managed object context are ignored.
For example we have a data model like
Category 1---n Icon
and we want to fetch all categories which
have icons (more than zero),
have icons whose attribute usable is TRUE
have icons whose attribute enabledByAdmin is TRUE
we use a predicate like this:
NSArray *predicates = #[[NSPredicate predicateWithFormat:#"icons.#count > 0"],
[NSPredicate predicateWithFormat:#"ANY icons.usable = 1"],
[NSPredicate predicateWithFormat:#"ANY icons.enabledByAdmin = 1"]];
NSCompoundPredicate *cp;
cp = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType
subpredicates:predicates];
This complex predicate forces core data to read from the persistent store, directly.
My solution is to save the managed object context and fetch the data afterwards.

Resources