Update model Realm Swift - ios

I'm a new in Realm and ask you to help me.
I created the model:
class UserModel: Object {
dynamic var email = ""
dynamic var facebook_id = ""
dynamic var google_id = ""
dynamic var id = 0
dynamic var name = ""
dynamic var photo = ""
dynamic var someinfo = ""
dynamic var twitter_id = ""
}
When I login to app, I can see my info on UserProfileController. Also I have a EditProfileController where I can change some info about myself. So when I successfully change it, I want to update my Realm model, and try to do this:
let realm = try! Realm()
try! realm.write {
realm.create(UserModel.self, value: ["name": self.editEmail.text!, "email": self.editEmail.text!], update: true)
}
But unfortunately I see this message:
''UserModel' does not have a primary key and can not be updated'
What if I want to update a few property at the same time?
Can I do this with primaryKey? Or...
How it possible to update Realm model without primaryKey?

The problem lie in this update: true, it's used to automatically replace object that have the same primary key in your realm, if your object don't have primary key, set update to false then it will work and always create new object

You need to set a primary key for your UserModel.
https://realm.io/docs/swift/latest/#primary-keys

Related

Update Array [[String: [Dictionary]]] when valueChanged in Firebase

var chatMessages = [[String: ChatMessage]]()
Firebase Chat Messages structure is like this.
-Kjws99ol6qjFt7ET9C
content: "Hehd"
displayName: "John Doe"
fileLength: 0
fileUrl: ""
fromID: "5904ee8cfa"
isRead: false
messageStatus: 2
messageType: "normal"
timestamp: 1494596232
Now on childAdded I'm appending the new message like this
weakSelf.chatMessages.append(newMessage)
//Kjws99ol6qjFt7ET9C - This is the threadID which is stored in String and below value is stored in ChatMessage
But after message isRead by user it value changes and that is identified by childChanged so in childChanged change how to update my Array correctly?
On the childChanged event, the app is passed a snapshot of the updated child, with the (in this case) key being Kjws99ol6qjFt7ET9C and the value being the child node data.
To update the array, find which index in the chatMessages array corresponds to that key and update the value accordingly.
To find it in the array you've set up, which is an array of [String: ChatMessage] dictionaries do the following
let searchKey = "Kjws99ol6qjFt7ET9C"
let index = chatMessages.map( {$0.keys.first!} ).index(of: searchKey)
Once you have the index, you can then update the element in the array with the new data.
{$0.keys.first!} - compiles all of the keys in the chatMessage array into an array
index(of: searchKey) - finds the index of the searchKey we are looking for
Then you can
chatMessage[index] = updated data
If you need any additional code, let me know.
However, I would strongly encourage changing the model to store a ChatMessage class (or struct) in the array
class ChatMessage {
var fbKey = "" // the key like Kjws99ol6qjFt7ET9C
var content = "" // like "Hehd"
var displayName "" // "John Doe"
}
var chatMessages = [ChatMessage]()
it will be easier to maintain and the array search is simplified and faster.
With this use case, to find a specific index do this
let searchKey = "Kjws99ol6qjFt7ET9C"
let index = chatMessages.index(where: { $0.fbKey == searchKey} )
I'm confused as to why you are storing the dictionary inside an array. It looks to me like
var chatMessages = [String: ChatMessage]()
would satisfy your model as each message push id (e.g. -Kjws99ol6qjFt7ET9C) would be unique from the database, so you could have a valid Dictionary by storing this push id as the key.
When you receive a childChanged event, you can then quickly locate the message that has changed by looking up the snapshot.key in the dictionary and updating it.

Adding Objects to single Realm File

Before I begin here is a bit a background for you all so your possible answers won't go beyond my comprehension level:
1. I'm only jolt starting to learn swift and is still learning the in and outs of both realm and Xcode.
2. My only OOP experience has been with java, and at that very low.
So here is my issue:
I'm trying to make a single realm file hold an entire list of "user profile" data(ie. name, age, email). I'm attempting to do this by allowing an IBAction button to cause an object to be saved to the realm file as shown below
#IBAction func signUpButton(_ sender: UIButton) {
let realm = try! Realm()
try! realm.write {
user.userName = userNameTextField.text!
user.passWord = passWordTextField.text!
user.email = emailTextField.text!
user.name = fullNameTextField.text!
user.age = ageTextField.text!
profile.person.append(user)
realm.add(profile)
}
}
The only problem here is that it's not adding object but instead updating the one that was created before, can anyone tell me how I can accomplish this using an IBAction Button?
If an Object has already been added to a Realm file, then you can change its properties by opening up a write transaction and simply modifying its properties in there.
let realm = try! Realm()
let newUser = User()
newUser.userName = userNameTextField.text!
// Add to Realm for the first time
try! realm.write {
realm.add(newUser)
}
// Update its property at a later time
try! realm.write {
newUser.userName = userNameTextField.text!
}
It's not necessary to call realm.add or profile.person.append(user) again if those objects were already previously added.
I'm not sure where user and profile are coming from in your example code there. Since there's no let user = User() inside that method, I'm assuming you're creating single copies of them elsewhere in the view controller class.
If profile has already been added to the Realm, you shouldn't be calling realm.add(profile) again, as that will add a second copy (And calling append each time probably won't break, but it's not recommended).
To work out if profile is already inside a Realm file, you can check by using profile.realm != nil. To check if user already belongs in profile, you can user Realm's inverse relationships feature.
class User: Object {
dynamic var userName = ""
dynamic var password = ""
dynamic var email = ""
dynamic var name = ""
dynamic var age = ""
let profile = LinkingObjects(fromType: Profile.self, property: "person")
}
If your object have primary key, you can only add new by fetch then delete it, then add new one, else you can only update it (primary key is used to prevent duplication)
If you want to have 2 object that have similar property value, then just simply remove the primary key from the object class

Do I need to write all the child objects in a class in realm as well?

Following the example code as shown:
// Define your models like regular Swift classes
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
class Person: Object {
dynamic var name = ""
dynamic var picture: NSData? = nil // optionals supported
let dogs = List<Dog>()
}
// Use them like regular Swift objects
let myperson = Person()
let mydog = Dog()
mydog.name = "Rex"
myperson.dogs.append(mydog)
// Persist your data easily
let realm = try! Realm()
try! realm.write {
// do I need to add this statement??
realm.add(mydog)
realm.add(myperson)
}
Do I need to persist the mydog object as well, or that Realm is smart enough to know that it is a new child object of the myperson and it will persist it for me?
No you do not need to persist the actual dog object, if you already persist an object containing it.
For the users who are wondering about cascading delete, the answer is NO.
Realm supports cascading write but NOT delete. For delete you may have to query all the relations and delete one by one.

Realm iOS relationship from mysql json results

I want to use Realm to my iOS app but I have a problem with the relationship. What I want to achieve is a relationship between the following two RLMobjects :
class Catalogue: RLMObject {
dynamic var ID = ""
dynamic var greekName = ""
dynamic var deutschName = ""
dynamic var createdAt = NSDate()
dynamic var updatedAt = NSDate()
override class func primaryKey() -> String? {
return "ID"
}
}
class Products: RLMObject {
dynamic var foodName = ""
dynamic var foodDescription = ""
dynamic var foodPrice = ""
dynamic var createdAt = NSDate()
dynamic var updatedAt = NSDate()
dynamic var category: Catalogue?
}
I am retrieving all my data from a server in JSON format and the problem is that
I can not set the category as relationship to Catalogue ID.
In my database the category field is a foreign key to the Catalogue ID.
Does anyone knows how can I do that in Realm?
Thank you in advance.
Rather than storing the Catalogue ID in the dynamic var category: Catalogue? relationship field you will need to find the Catalogue object and just store that directly. This is how you link objects and is an important and powerful part of using NoSQL type DB's like Realm.
I would also add an array of products relationship on Catalogue so that you can link all the products to the Catalogue itself.
You can see more discussion about this here if that wasn't fully clear. Hope this helps

How to properly handle create and update for realm.io relationships

I've got two RLMObjects:
class Timeline: RLMObject {
dynamic var entries = RLMArray(objectClassName: Entry.className())
dynamic var id = 0
dynamic var title = ""
dynamic var coverPhoto = ""
dynamic var body = ""
override class func primaryKey() -> String {
return "id"
}
}
class Entry: RLMObject {
dynamic var id :Int = 0
dynamic var timelineID :Int = 0
dynamic var name :String = ""
dynamic var caption :String = ""
dynamic var body :String = ""
dynamic var imageURL :String = ""
override class func primaryKey() -> String {
return "id"
}
}
As you can see there is a To-Many relationship between a Timeline and an Entry. A timeline has many Entries.
the JSON fetch and assignment of my timelines works just fine, they are being fetched and set with:
realm.beginWriteTransaction()
Timeline.createOrUpdateInDefaultRealmWithObject(timelineObject)
realm.commitWriteTransaction()
My problem arises when I'm trying to fetch the entries (which is a separate JSON request/response) for a given timeline and set them.
Fetching and creating them like this works just fine:
realm.beginWriteTransaction()
Entry.createOrUpdateInDefaultRealmWithObject(entryObject)
realm.commitWriteTransaction()
But of course that doesn't associate them with my Timeline object. So then I tried to get my Timeline object and then adding the Entry object to my timeline by doing:
let timelineObject = Timeline(forPrimaryKey: id)
timelineObject.entries.addObject(entryObject)
(inside of a transaction ofc).
This works fine for the initial fetch. But when I try to refetch your data, you get the following RLMException:
'RLMException', reason: 'Can't set primary key property 'id' to existing value 59.'
Am I doing something wrong, or is this a bug? It seems like there should be an instance method on RLMObject that creates or updates a product, like it's class method does?
Solution
As segiddins comment suggests, you add the return from the class method: createOrUpdate... and then add that return to your object (in my case, the timelineObject.entries.addObject(entryReturn).
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
for entry in json["entries"] {
...
//Process and map the JSON
...
let entryReturn = Entry.createOrUpdateInDefaultRealmWithObject(entryObject)
timelineObject.entries.addObject(entryReturn)
}
realm.commitWriteTransaction()
Try adding the Entry object that gets returned from the createOrUpdate call.

Resources