Child Context - Deleting Object Without Save? - swift

I'm creating a recipe object inside of a child context. I'm adding ingredients, which are a relational object to recipe.
lazy var childRecipe: Recipe = { return Recipe(context: self.childContext) }()
let newIngredient = Ingredient(context: self.childContext)
newIngredient.parent = childRecipe
newIngredient.percentage = 0.0
I'm trying to figure out how to remove/delete an ingredient that's already connected to childRecipe. This is what I've tried, but it's not working.
var ingredients = getIngredients()
let ingredientToDelete = ingredients[indexPath.row]
self.childContext.delete(ingredientToDelete)
I believe this isn't working because the context isn't being saved. But my problem is that I'm not ready to save. I just want to remove the ingredient that's not needed.
Thank you.

My cellForRowAt method had a section that taps into Recipe and gets it's relational Ingredient objects for display.
Turns out what I needed to do was remove the parent relation of Ingredient to Recipe. Which then allowed the section to return the correct number of rows. This then allowed the ingredientToDelete to be marked for delation without issue.
let ingredientToDelete = ingredients[indexPath.row]
newIngredient.parent = nil
self.childContext.delete(ingredientToDelete)

Related

Update Realm objects - SwiftUI

I need to update all the objects in the database, when I try it, it says:
Thread 1: "Attempting to modify a frozen object - call thaw on the Object instance first."
I have the following:
#ObservedRealmObject var meal: MealTracking
#ObservedResults(MealTracking.self) var mealTracking
mealTracking is the one that contains everything and meal is the single object in the current view.
So once I update the name on that single object (meal), I want to update the name on all other objects. So I'm doing:
for (index, meal) in self.mealTracking.enumerated() {
$mealTracking.wrappedValue[index].mealName = ""
}
and I get the error of not being able to update the objects.
I also tried it like $mealTracking[index].mealName.wrappedValue = "" and gives another error: Referencing subscript 'subscript(_:)' requires wrapped value of type 'Results<MealTracking>'
For a the single object on the current view I can update it with:
$meal.mealName.wrappedValue = "some stuff", the problem is when attempting to update All objects
How can I update all the objects?
#ObservedRealmObject is a frozen object. If you want to modify the properties of an #ObservedRealmObject directly in a write transaction, you must .thaw() it first.
In your case something like :
mealName.thaw()?.name = ""
More information :
https://www.mongodb.com/docs/realm/sdk/swift/swiftui/
https://www.mongodb.com/community/forums/t/freeze-frozen-objects-and-thawing/15706/5

How to tell NSPredicate skip entities before context.save()

How to prevent firing controller(:didChangeContentWith: diff) before viewContext was saved?
After creating an NSManagedObject object i'm getting controller(:didChangeContentWith: diff) fired, and after saving context too, so it fires twice for the same object.
First of all, I create NSFetchedResultsController and set fetchRequest.predicate
fetchedResultsController.fetchRequest.predicate = nil
Then when I add item:
let newItem = Item(context: context) //controller fires right after this #1
...
...
context.save() // Fires here too #2
First time objectID is wrong yet, second time it's correct and set up according to the context. So, is there any way to tell predicate to skip entities, that was created before context.save()?
The only way I've found for now is filtering results. It works for me because I have own custom logic of UI updating, not sure it will work for everyone.
lists = fetchLists().filter { !$0.objectID.isTemporaryID }
Where lists is [NSManagedObject] (subclass). So when CoreData was changed, I simply fetch with needed predicate and filter out all temporary IDs. When item gets correct ID, controller(:didChangeContentWith: diff) fires again and everything works fine.
Still looking for the way to fetch with skipping isTemporaryID objects.

How to get the values from Realm List to be used in a UITableview

Using the (modified) examples in the Realm Swift documentation:
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
let puppies = List<Puppies>()
}
class Person: Object {
dynamic var name = ""
dynamic var picture: NSData? = nil // optionals supported
let dogs = List<Dog>()
}
class Puppies: Object {
dynamic var name = ""
}
Let's assume that the Person.name = Bob, and that Bob has several dogs added to his dogs List. I have added another model class called Puppies, which would represent puppies that belong to Bob's dogs. (Apparently Bob owns a kennel.)
How would I get the values to display the names of Bob's dogs and the number of puppies belonging to each dog in a UITableview?
More specifically, what is the code to extract the property values of the List of dogs that belong to Bob. I assume that once I get those values it won't be difficult to list them in the tableview cells.
I decide to use the slightly modified example from the documentation instead of my own code so that those who read this won't have to try and interpret my code, and be able to focus on the solution.
I have been able to save my data and believe I have made the relationships between the objects link properly, but don't know how to get the values of the List objects, based on the primary key I have in my top level model. The problem I have is that (using the example above): the puppies know what dog they belong to, and the dog knows the person it belongs to, but the inverse relationships don't seem to work.
(By the way; I used the LinkingObject examples in the documentation in a playground and it throws and error. I'm not sure if the examples are incomplete in some way.)
In the Realm Browser (displaying the Person object) I can see the data as entered but the link that shows [Dog] has a 0 next to it and when I click on the link, the table that shows is blank. Maybe solving that issues will be the answer to make everything else work.
Please excuse my ignorance. I'm still learning.
Thanks to Ahmad F. for pointing me in the right direction.
Here is the answer:
I did not know how to append to the list property in each of the object classes. Following the example above, it is done by creating a variable that holds the Person object. Then the realm.write function would look something like this.
newDog = Dog()
newDog.name = "Phydeaux"
.....
try! realm.write {
currentPerson?.dogs.append(newDog)

Combining two element locators [duplicate]

I am trying to keep my pageObjects in Protractor as clean as possible, but have run up against some behavior in selecting sub-elements.
This works:
var parent = element(by.css('.parent-class'));
parent.element(by.css('.child-class')).getText();
However this does not:
var parent = element(by.css('.parent-class'));
var child = element(by.css('.child-class'));
parent.child.getText();
Is there someway to do something like the second example? I'd rather not have the element locators spread throughout the methods on my pageObjects, but it seems thats the only way to locate subelements?
In actual application I have a long list of cards, from which I filter down to just the one I am looking for. I then want to do things with subelements of the card.
You could use the locator() function to get the locator of the child element and use it to find a child of the parent. This is similar to the solution you provided in your comment, but allows you to define all properties on your page object as web elements instead of a mix of elements and locators:
var parent = element(by.css('.parent-class'));
var child = element(by.css('.child-class'));
parent.element(child.locator()).getText();
I have a lot of the following code:
var parent = element(by.css('.parent-class'));
var child = parent.element(by.css('.child-class'));
child.getText();
But as far as I understood you have a lot of children and you don't want to list all the variants.
Additionally to Nathan Thompson answer you can have a helper function in page object to find subelement:
function getCard(parent, child) { // Or getChild()
return element(by.css(parent)).element(by.css(child));
}
function getCardText(parent, child) { // Or getChildText
return getCard(parent, child).getText();
}
So then you can just write in spec:
expect(cardPage.getCardText('.parent-class', '.child-class')).toBe('...');
I wanted to mention that using 5.2.2 version this implementation is bit different.
To get actual selector value you must use following code
let selector = child.locator().value
This is because locator returns an object which contains selector and other properties, but in this case, you only need selector.
here is what is returned by method locator()
name(name) {
return By.css('*[name="' + escapeCss(name) + '"]');
}
{ using: 'css selector', value: '.child-class' }
Here is how it should be implemented now.
var parent = element(by.css('.parent-class'));
var child = element(by.css('.child-class'));
parent.element(child.locator().value).getText();
//short hand
var parent = $('.parent-class');
var child = $('.child-class')
parent.$(child.locator().value).getText();

Save model state between viewControllers with Realm - it breaks relation

I use Realm in my swift project.
Here are my objects, Meal and Reaction - with a mapping of one to many:
enum DishType: String{
case Breakfast
case Second_breakfast
case Brunch
case Elevenses
case Lunch
case Tea
case Dinner
case Supper
case Snack
}
class Meal : Object{
dynamic var date: NSDate = NSDate()
dynamic var dishType = DishType.Breakfast.rawValue
var dishTypeEnum: DishType{
get{
return DishType(rawValue: dishType)!
}
set{
dishType = newValue.rawValue
}
}
dynamic var foodItems: String = ""
var reactions = List<Reaction>()
}
enum Category: String{
case Apetit
case Energy
case Emotion
}
enum Type: String{
case Positive
case Negative
}
class Reaction: Object{
dynamic var category = Category.Apetit.rawValue
dynamic var text: String?
dynamic var selected: Bool = false
dynamic var type = Type.Positive.rawValue
.....
}
I need to save state from one viewController to the other.
I select some reactions, which I have to see selected when returning to the reactions screen.
I was thinking saving into Realm, but I also have to clear all reactions state when entering a new set of data.
When deleting from Reaction, it seems that it also breaks the relation. I have Meal objects saved (which also have a relation with Reaction).
I do not know how to distinguish between Reactions that indicate state and the ones that are in relation, from a saved Meal.
I was thinking saving Reactions for state, just in NSUserDefaults. But like this I will combine both frameworks.
What to you think? How do you save state?
I was thinking saving into Realm, but I also have to clear all reactions state when entering a new set of data. When deleting from Reaction, it seems that it also breaks the relation. I have Meal objects saved (which also have a relation with Reaction).
When you make the modifications to the Reaction objects, these modifications will appear where ever the reactions are linked. So you likely don't want to make modifications to them unless it's a rewording. (e.g. fixing a typo "You don't want desert" => "You don't want dessert" 😉) Also if you delete them, they will be also deleted from Meal objects where they were linked.
I was thinking saving Reactions for state, just in NSUserDefaults. But like this I will combine both frameworks.
That's not necessary. You can solve your use-case entirely by using Realm.
The solution is that you don't include an attribute selected in Reaction. Instead you just add the selected reactions to the Meal. In your view controller that means, that you render a cell for each Reaction by querying for all of them with let reactions = realm.objects(Reaction).
You can then figure out whether you need to add the checkmark, by checking whether they are included in the Meal's reactions like this:
meal.reactions.contains(reaction)
If a reaction for a meal is selected, you add it to the meal:
meal.reactions.add(reactions[indexPath.row])
If a reaction is deselected, you remove it from the meal:
meal.reactions.removeAtIndex(meal.reactions.indexOf(reactions[indexPath.row]))