Save model state between viewControllers with Realm - it breaks relation - swift

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]))

Related

Returning variables from Model to other ViewControllers

I am making a weather application. I basically created a class where I will only get data from API and return them as needed. I have variables like cityName, currentWeather etc.
Problem is sometimes API doesn't provide them all so I need to check if they are nil or not before return them. What comes to my mind is set variables like this first:
private var cityNamePrivate: String!
and then
var cityNamePublic: String {
if cityNamePrivate == nil {
//
}
else { return cityNamePrivate }
But as you can as imagine, its very bad because I have a lots of variables. Is there any better logic for this? Should I just return them and check later in wherever I send them?
The problem here is that you will have many variables to deal with. It's not just returning them from the API, it's also dealing with them in your app and perhaps in some processing code.
One solution is to have a big class or struct with many properties. This will work very well, is straightforward to implement but will require lots of repetitive code. Moreover, it will require to change your API and all your code, whenever some new properties are made available by the remote web service.
Another approach is to have replace the big inflexible class or struct, with a dynamic container, e.g. an array of items [ Item ] or a dictionary that associates names to items [ String : Item ]. If the data is just strings, it's straightforward. If it's several types, you may have to have to implement a small type system for the elements. Example:
var x : [ String: String] = [:]
x["city"]="Strasbourg"
x["temperature"]="34°C"
struct Item {
var name : String
var value : String
}
var y : [ Item ] = [Item(name:"city",value:"Strasbourg"),
Item(name:"temperature", value:"34°C")]
The other advantage of this approach is that you stay loyal to the semantics: an information that is not available in the API (e.g. "unknown") is not the same as a default value. I.e. if the weather API does not return a temperature, you will not display 0 in your app. because 0 is not the same as "unknown". While strings are more robust in this matter, the absence of a name is not the same as an empty name.
This last remark suggests that in your current scheme, of having a big data transfer object, you should consider to keep the properties as optional, and move the responsibility for the processing of unknown data to your app.

Swift Combine Pipeline to compare draft model

I have a VielModel in SwiftUI handling my person model. To be able to store draft persons in the editor in the View(s), I have two objects:
#Published var person: Person
#Published var draftPerson: Person
In the UI, I am only changing the draftPersons until the user clicks on "Save", which stores the draftPerson as the person. In the onAppear method of the editor, I reset the draftPerson to the person.
Now I want to disable the "Save" button of the Editor and therefor introduced a bool "modified" in the VM. Using a pipeline, I want to set the modified to true, if and as long as the draftPerson is not equal to person, by doing the following:
$draftPerson.map { draftPerson in
return draftPerson != self.person
}
.assign(to: \.modified, on: self)
.store(in: &cancellables)
It looks like it is working on first glance, but if I change something in a textField, the value of modified is only set to true after the second change in the field. Vice versa, if I delete the typed values, it is only set back to false after I delete one more character as were originally there.
Question 1:
Is there another "best practice" to handle changes in draft objects and deactivating the "Save" button in SwiftUI?
Question 2:
Why is the pipeline "one change behind"?
Thanks a lot for your input.
Edit: I created a separate part of the App focusing only on the pipeline and realized that it is indeed working as intended if I remove my other pipelines. I have to check now in detail. Nevertheless, I will stick with my first question:
Is there anything I can do better?
Please find the code here on Github
You could declare another #Published property and combine the two person and draftPerson publishers and publish whether they are the same, like this:
#Published var person: Person
#Published var draftPerson: Person
#Published var saveDisabled: Bool = true
public init() {
// all init code
Publishers.CombineLatest($person, $draftPerson)
.map { $0 == $1 }
.assign(to: &$saveDisabled)
}
But essentially it is not needed and a computed property will do the same job:
var saveDisabled: Bool {
person == draftPerson
}
Because both person and draftPerson are marked #Published each time one of them changes the View will be notified of the change so it will also pick new value of saveDisabled.

Communicating "which playing card" between View and Model - best way?

For fun I'm building a FreeCell solitaire game from scratch in Swift. I've got my model for the deck set up to build the deck and each Card struct has a suit, rank, and description (e.g., 4♠️) plus some other properties. The model for the game simply stores what cards are in what columns. A change in the view (moving a card) will alert the controller to modify what cards are in what columns.
When a user taps on the card (a subclass of UIView including a label that contains card.description) the label.text will be sent to the controller to identify the card in the model. I'm wondering what the best way to do this is.
The most obvious way I can think of is to build dictionary where the keys are descriptions and the values are cards. I can write a function in my DeckBuilder class to build the dictionary for me, of course. But since the description already exists as a property of the Card struct, this seems a little redundant and clunky-ish.
Another method would be to, every time a card is selected, iterate through the deck of cards in the model and say "if selectedCard.description == tryCard.description { //this is the right card! } " but that seems absurdly inelegant and theoretically too computationally expensive (although in reality I'm sure it takes no time at all).
What I'd love to do is have the controller say "get the Card that has this String as its description property." Similar to dictionary lookup, but without an actual dictionary.
Is this last solution possible? If not, what do you think is best?
Thankya!
You should not use the text of a label as an indication of a value at some location; this is like storing your model in your view. Instead you have come internal structure that represents the state of the columns. Each column can be an array of cards. The columns themselves can be in an array of columns:
struct Card {}
var deck = [Card]()
var columns: [[Card]] = [[Card]](repeating: [], count: 7)
deck.shuffle()
for i in 0..<columns.count {
for j in 0..<i {
columns[i].append(deck.draw())
}
}
Now if you have a click on a view. you just need to know what column and what index in that column was clicked (you can do this based on the frame of the view or by assigning a tag to each view). You can now get the value of the card but looking at the columns array: columns[selectedColumn][selectedRow]
I would create two enums, one for the rank and one for the suit.
enum Rank {
case value(Int)
case jack
case queen
case king
case ace
}
enum Suit {
case diamond
case heart
case spade
case club
}
This is way safer and more readable than a dictionary. In your struct Card you can add these two properties.
struct Card {
var rank: Rank
var suit: Suit
}
It's up to you if you want to compare the Strings, compare the enums or even have your Card struct conform to the Equatable protocol. I would probably go for the Equatable protocol but I do not think that there are big differences between these options.

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)

Different types of the same object

So, I'm in Xcode and programming a small program from a friend.
I want to initialize several instances of an object, put them in an array and then iterate through it (via a function that returns a string). Each object adds some text to that string when it's iterated, depending on the variables of the object.
Let's say the class is Tree. The variables in the class are name (string), height(int) and a hasLeaves(bool)(weather it currently has leaves on it or not). I could easily print:
"This is a name that is height meters tall and has leaves.
The problem is that I want the string to be a bit different, depending on which kind of tree it is. Like this:
The oak(name) is big and lifeful, it's height meters tall and has leaves.
Apple trees(name) carries some apples, it's height meters tall and has leaves.
If you ever visit Sweden you should check out the firs(name), they are height tall and haven't got leaves.
I don't want you to write the code for me, but give me a clue. I don't know what to look for. I was thinking about creating a subclass for each Tree, but every subclass would only appear once in the program and I don't know if it's necessary or not.
As you recognize, I'm having a hard time even formulating this question, but if you understand my intentions I'm glad for any clue I can get.
Edit: So this is my attempt to show it in code:
Class:
class tree {
var treeHeight: Int?
var treeWidth: Int?
var hasLeaves: Bool
var treeString: String
init (height: Int?, width: Int?, leaves: Bool, theString: String) {
self.treeHeight = height
self.treeWidth = width
self.hasLeaves = leaves
self.treeString = theString
}
}
Main function:
var oak: tree = tree(height: 1, width: 2, leaves: true, theString:"Oh, the mighty oak")
var appleTree: tree = tree(height: 1, width: 2, leaves: false, theString: "Yummy Apples")
var treeArray: Array = [oak, appleTree]
var resultString = "This is the stories of the trees: "
for tree in treeArray {
if tree.hasLeaves == true {
resultString = resultString + tree.theString
}
}
So, I want the string added to the "resultString" to be different, depending on what kind of tree it is, but I don't want to set that string in the initialization of the object, but rather from what "kind" of tree it is. Does that make it easier to understand?
I want the string (treeString) to be static depending on what "kind" of tree it is. So if it is an oak, the string is always "Oh, the might oak".
It sounds like you want a tree class with some properties like leaves, etc. Maybe you also want to subclass leaves with additional properties like color, etc. I recommend the WWDC 2014 video:
http://youtu.be/W1s9ZjDkSN0
Somewhere around 30 minutes they have a demo of a Car class with RaceCar at subclass.
Regarding creating the objects, you can build each object individually and then collect them in an array as one option. For example, maybe in a form on your app the user inputs data for a class or subclass and then you create an object, store to a master array which you then archive to a file.
So, if anyone stumbles in to this question, this is what I learned:
I was looking for subclasses and protocols. There are methods to determining whether an object is of a certain subclass, and in my case, I could have a protocol called "Tree" with certain methods and/or methods, and then make subclasses to this protocol, called "oak" and "fir".