CoreData - creating new entity - Why don't you need to save this? - ios

I figured you can create new entities (in swift 3) like this:
let person = Person(context: persistentContainer.viewContext)
person.name = "Some Name"
This seems to be it. It saves the new person permanently (I think so, at least).
Why don't you need to call saveContext()of AppDelegate. swift (or persistentContainer.viewContext.save() which is basically the same, right?)?
Every time you change some entity, you need to save it. Why isn't this the case when creating new entities?
Thanks in Advance !!!

According to your comments on your question, you ARE calling saveContext().
Go into your AppDelegate and check out applicationWillTerminate, saveContext() is called there.
In short, if you want to persist the data then yes, you need to call saveContext()

for your anser you have to understand the Core Data stack
https://developer.apple.com/library/ios/documentation/DataManagement/Devpedia-CoreData/coreDataStack.html#//apple_ref/doc/uid/TP40010398-CH25-SW1
Changes that you make to your managed objects are not committed to the parent store until you save the context.

Related

Is it possible to make copy of Realm object?

I want to refresh the Realm database in the background thread like this:
(Because I have got fresh data from Webservice)
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[realm deleteAllObjects]; // !!
[Pubs createOrUpdateInRealm:[RLMRealm defaultRealm] withJSONArray:data];
[realm commitWriteTransaction];
Problem is, that meanwhile I delete & renew the objects in Realm db, user can open some Detail ViewController pointing to some Realm object (Pubs) which has been deleted meanwhile so the exception is thrown.
I don't see any solution for this, except always when I would like to access the Realm object from Detail controller or its property I would need to always do something like this:
(That means always get Realm object, but that can probably fail too)
pub = [Pubs objectsWhere:[NSString stringWithFormat: #"pubId = %lu", (long)_selectedPubId]].firstObject;
But I am not using this solution. I am thinking best would be if I could call in Detail view controller something like this:
pub = [Pubs objectsWhere:[NSString stringWithFormat: #"pubId = %lu", (long)_selectedPubId]].firstObject;
pub = [pub safeCopy];
So the PubRealmObject can be meanwhile deleted, but the pub object will solo exist in the memory (only for the purpose to access its data properties).
So did someone try something similar?
Or maybe even using some iOS SDK way like this?
I need to only access the data properties as I say, not operate with realm object methods like delete or update the object in the db.
Btw I tried to call the update of Realm db in the main thread, but the problem is it takes like 5-7 seconds (only 1000 JSON objects) so it lags the application. That's why I am thinking the background update & safe copying of object could be better.
But I am thinking that it can fail even while copying the object, so what is the solution for this? (background update vs safe access of Realm object)
It's usually not a good design pattern to have a view controller relying on a data model that can be deleted out from underneath it. It's possible to check if a Realm object has been deleted to avoid exceptions by checking its object.invalidated property.
In any case, to create a detached copy of a Realm object, all you need to do is:
RLMObject *copiedObject = [[RLMObject alloc] initWithValue:object];
This will make a copy of the object, but it will not be inserted into any Realm instance. Please note that if the object links to any other Realm objects, these will not be copied as well; the new object will just be pointing at the existing copies.
But I still feel like I need to mention that you could probably just make your implementation of updating Realm from your web service a bit smarter to avoid the need to do this.
If your objects implement a primary key, then when you call createOrUpdateInRealm, the existing objects will be updated with the new values.
Good luck!
With Swift:
Previously answered here
As of now, Dec 2020, there is not proper solution of this issue. We have many workarounds though.
Here is the one I have been using, and one with less limitations in my opinion.
Make your Realm Model Object classes conform to codable
class Dog: Object, Codable{
#objc dynamic var breed:String = "JustAnyDog"
}
Create this helper class
class RealmHelper {
//Used to expose generic
static func DetachedCopy<T:Codable>(of object:T) -> T?{
do{
let json = try JSONEncoder().encode(object)
return try JSONDecoder().decode(T.self, from: json)
}
catch let error{
print(error)
return nil
}
}
}
Call this method whenever you need detached / true deep copy of your Realm Object, like this:
//Suppose your Realm managed object: let dog:Dog = RealmDBService.shared.getFirstDog()
guard let detachedDog = RealmHelper.DetachedCopy(of: dog) else{
print("Could not detach Note")
return
}
//Change/mutate object properties as you want
detachedDog.breed = "rottweiler"
As you can see we are piggy backing on Swift's JSONEncoder and JSONDecoder, using power of Codable, making true deep copy no matter how many nested objects are there under our realm object. Just make sure all your Realm Model Classes conform to Codable.
Though its NOT an ideal solution, but its one of the most effective workaround.

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.

Access Parse Object ID right after instantiation

I'm creating two PFObjects at the same time that should reference each other's object IDs when they're saved. In the example below, the second object is supposed to save the first object's object ID in an array.
let objectForFirstClass = PFObject(className:"ClassOne")
let objectForSecondClass = PFObject(className: "ClassTwo")
objectForSecondClass.setObject([objectForFirstClass.objectId!], forKey: "classOneObjectArray")
The last line is causing the error because objectForFirstClass.objectId is nil. I'd assume this is because the object hasn't been saved yet. How can I fix this?
You want to save after creating the first object, and in the completion handler, create the second one with a reference to the first one.
You can use saveAllInBackground:block: for this.
Correct, the object id is assigned by the server when saved. I'd be tempted to write some cloud code to do what you want so you can send some details and the cloud code will create and connect the objects, then return both of them to you. You can of course do the same thing locally in your app, there's just more network comms.
You should also consider using pointers or relationships. These are better for querying, though the same save requirements apply before you can set the connections.

Do I have to save() my NSManagedObjectContext?

I'm quite new to iOS development and have the following question:
I'm using CoreData and I add a Element like this:
NSEntityDescription.insertNewObjectForEntityForName("Foo", inManagedObjectContext: moc) as! Foo
After restarting my App, it's still there. The question is:
When I should use the NSManagedObjectContext.save() function?
You call save when you want to persist your changes to the disk.
Your method inserts a new object into the managedObjectContext. But the managedObjectContext is really just a temporary place to put things. When you create an object in a context, that doesn't automatically persist those changes to the Persistent Store until you call save on it.

Best way to have a single Entity using Magicalrecord

I'm looking for the best solution to implement this behavior:
I have an Entity called Customer and this will have only a single entry on Core Data, because the Customer will be only ONE.
What's the best solution to implement this? Is everytime check if the Entity exists before creating?
Many thanks
As mentioned, you can use for single object [NSUserDefaults standardUserDefaults].
But if you prefer use CoreData, write this:
Customer* customer = [Customer MR_findFirst];
if (customer != nil)
{
//do something with it
} else
{
[Customer MR_importFromObject:JSONToImport];
}
BDW:
MR_importFromObject method automatically check if exists entity with specific id (for id key it use attribute of your entity name plus "ID". (in your case "customerID") or key that named "mappedKeyName".
And if entity with this key already exist - Magical Record just update this entity.
So, if you specify this value it in your entity, just write:
[Customer MR_importFromObject:JSONToImport];
If there's only a single instance, the best solution is usually to not put it in Core Data. It gives you very little, and adds complexities like the one you're seeing. Save the necessary information in a property list, or even in user defaults.
Checking the entity exists before creating a new one is a good idea.
You can fetch all entities of your customer entity type and delete them all before adding a new one is another method.
You could also have a simple method that gets the current customer or creates one and then update all it's properties.
It sort of depends on how you get the data and what you want to happen with the related objects.

Resources