Appending to a Realm List Only if Unique Primary Key - ios

I am implementing Realm in my iOS project and wondering if there is a way to append an Object to a List only if the Object's primary key is unique. Currently I have something like:
let realm = try! Realm()
let message = RealmMessage()
message.id = 99999
message.desc = "Please Help!"
let chatroom = realm.objects(RealmChatRoom.self)[0]
try! realm.write {
chatroom.messages.append(message)
}
However this will crash if the messages is already in the list.
I know that complete objects can be updated using something like:
try! realm.write {
realm.add(chatRoom, update: .modified)
}
But does something like this exist for append? I.e. only write if unique key otherwise overwrite?

List stores the object references and its elements auto-update with message update. So if chatroom.messages contains a message, no need to append it again. As a result you can use this code for updating:
try! realm.write {
realm.add(message, update: .modified)
if !chatroom.messages.contains(message) {
chatroom.messages.append(message)
}
}

Related

How to remove all models that are not contained in the current array in Realm

I have the following case, I get data when loading the application, it is an array of ChatUserPersonalConversationModel models, I store this array in Realm as a separate model. What is the best way to do the following when I get an array and if the previous model is not contained in the resulting array, then I delete it from Realm.
I wrote the following code, it works for me, but I think that it can do better.
func updateChatUserPersonalConversationModels(_ chatUserPersonalConversationModels: [ChatUserPersonalConversationModel]) {
DispatchQueue.main.async {
do {
let realm = try Realm()
let existChatUserPersonalConversationModels = realm.objects(ChatUserPersonalConversationModel.self)
for existChatUserPersonalConversationModel in existChatUserPersonalConversationModels {
if !chatUserPersonalConversationModels.contains(where: { (newChatUserPersonalConversationModel) -> Bool in
return newChatUserPersonalConversationModel.id == existChatUserPersonalConversationModel.id
}) {
try realm.write {
realm.delete(existChatUserPersonalConversationModel)
}
}
}
try realm.write {
realm.add(chatUserPersonalConversationModels, update: true)
}
} catch {
debugPrint(error.localizedDescription)
}
}
}
Realm doesn't have any built-in functionality to achieve what you want. An alternative to what you have is to leverage Swift's Set type.
First, you should make sure equality and the hash value property are defined for your model class, and you should probably have a primary key (which seems to be id).
Next, turn your arrays of existing and new models into Sets. Set has an initializer that takes any sequence of objects, so you can pass arrays and Realm Lists alike into it.
Next, use the subtract(_:) method on Set to get a new set containing just the elements you want to delete:
// itemsToDelete contains models in oldModels that aren't in newModels
let itemsToDelete = oldModels.subtract(newModels)
Finally, you can use the Realm delete(_:) method that takes a sequence to delete all the obsolete models at once:
try! realm.write {
realm.delete(itemsToDelete)
}
I hope this helps. Note that this isn't a panacea; if your arrays are huge you may want to consider whether this approach uses too much memory.

iOS Swift 3 Storing NSArray Values To Realm

In my Application we are using Realm Storage for storing values locally. I can Able To Store my Array Values As Sting. But Not Able To Store and Retrive Values As Array. Is It Possible To Store NSArray Values To Realm Object and Retrieve It As NSArray.
Here Is The Code I Have Used To Store String Values:
class PracticeView: Object
{
dynamic var PracticeArray = ""
}
And Usage:
let realm:Realm = try! Realm()
let PracticeDetails = PracticeView()
PracticeDetails.PracticeArray = "Test String Values"
try! realm.write
{
realm.add(PracticeDetails)
}
print("Values In Realm Object: \(PracticeDetails.PracticeArray)")
//Result Will Be
Values In Realm Object: Test String Values
No, Realm cannot store native arrays (Whether Objective-C NSArray objects or Swift arrays) as properties of the Object model classes.
In Realm Swift, there is an object called List that lets you store an array of Realm Object instances as children. These still can't be strings though, so it's necessary to encapsulate the strings in another Realm Object subclass.
class Practice: Object {
dynamic var practice = ""
}
class PracticeView: Object {
let practiceList = List<Practice>()
}
let newPracticeView = PracticeView()
let newPractice = Practice()
newPractice.practice = "Test String Value"
newPracticeView.practiceList.append(newPractice)
let realm = try! Realm()
try! realm.write {
realm.add(newPracticeView)
}
For more information, I recommend checking out the 'To-Many Relationships' section of the Realm Swift documentation. :)

issue when appending the RLMArray, update RLMArray in RLMObject and link it with object in swift realm 3.0

want some method like which do update the existing followers, and if not exists do add it to DB and link them to user something like
GETS CRASH OVER append in write block, due to duplicate primary key,
also, it works perfectly if no followers has been added in HKUser Table, once it comes to update it crashes
import UIKit
import RealmSwift
class HKUser: Object{
dynamic var full_name = ""
dynamic var email: String?
dynamic var user_id: String?
let followers = List<HKUser>()
override static func primaryKey() -> String? {
return "user_id"
}
}
I want to update the connection of a user in DB also, so I want to do some thing like
//1. updated the userFollower array with required data
let userFollowers:[HKUser] = []
//2. now need to link it with my user object and update it in db
if let user = realmWrapper.sharedInstance.getUser(forID: id) {
try! realm.write {
//want some method like which do update the existing followers,
//and if not exists do add it to db and link them to user something like
//realm.add(user, update: true)
user.followers.append(contentsOf: followers)
/**********
GETS CRASH OVER HERE,
due to duplicate primary key,
it works perfect if no followers has been added in HKUser Table,
once it comes to update it crashes
**********/
}
}
List<T>.append() method save to the Realm implicitly if the objects are unmanaged. That is why the duplicated primary key exception happens.
To avoid this, you can add or update the unmanaged objects before appending to the List. Then you append the objects to the List.
try! realm.write {
...
realm.add(followers, update: true) // Add or update the objects first
user.followers.append(contentsOf: followers)
...
}
Maybe it's not the case (not enough code in your question to tell) but it looks like you prepare the userFollowers array in step 1 to contain ALL the current followers for that specific user.
If this is the case then you'll end up re-adding all the existing followers not only the new ones, hence the duplicate keys.

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.

Resources