Realm inverse-relationships support for creation with a dictionary value - ios

I'd like to implement Realm inverse-relationships support for creation with a dictionary.
But the assertion fails: the dog from the dictionary was not created.
import RealmSwift
class Dog: Object {
dynamic var name: String?
dynamic var owner: Person?
}
class Person: Object {
dynamic var name: String?
let dogs = LinkingObjects(fromType: Dog.self, property: "owner")
}
func sample() -> Person? {
// Get the default Realm
let realm = try? Realm()
let sampleValue: [String: Any] = ["name": "Harry", "dogs": [["name": "Belle"]]]
var person: Person? = nil
try? realm?.write {
person = realm?.create(Person.self, value: sampleValue, update: false)
}
assert(person?.dogs.isEmpty == false)
return person
}
Note: RealmSwift (2.1.2)

LinkingObjects is a lookup mechanism, and not an actual representation of an on-disk store. As such, it's not possible to insert data into it via a write transaction.
However, if you redesign your schema, so Person has a List of Dog objects, and Dog itself defines a LinkingObjects to determine its parents, then your code of inserting a Person and Dog in the same dictionary should work. :)
class Dog: Object {
dynamic var name: String?
let owners = LinkingObjects(fromType: Person.self, property: "dogs")
}
class Person: Object {
dynamic var name: String?
let dogs = List<Dog>()
}

Workaround I found was to create each entity separately.
try? realm.write {
person = realm.create(Person.self, value: sampleValue, update: false)
let dogsValue = sampleValue["dogs"] as? [[String: Any]]
dogsValue?.forEach {
var dogValue = $0
dogValue["owner"] = person
realm.create(Dog.self, value: dogValue, update: false)
}
}
I hope there are some easier ways.

Related

How to create model like firebase structure using RealmSwift in iOS swift?

I have been trying to create model like firebase database structure. I can able to create normal string, bool and int value but not able to do array and dictionary.
Here is my firebase structure screenshot:
Here i am trying to add groupMembers and to in model like firebase structure.
Here is my Model i tried to create with array and dictionary:
import Foundation
import RealmSwift
class RealmMessages: Object {
#objc dynamic var messageText : String?
#objc dynamic var sender: String?
let chatCreatedDateTimee = List<timeStampValue>()
#objc dynamic var chatId: String?
#objc dynamic var from: String?
#objc dynamic var groupMemberss : [String: String]!
let groupMemebersCount = RealmOptional<Int>()
#objc dynamic var task: Bool = false
#objc dynamic var to: Array = [String]()
}
class timeStampValue: Object {
let timestamp = RealmOptional<Int>()
}
Here is my contoller code: Trying to add value into realm database.
var dic : [String : String] = [:]
var cont = ["one", "two", "three"]
var oneVal = ["909090": "SELF", "808080": "Other"]
override func viewDidLoad() {
super.viewDidLoad()
let realm = try! Realm()
print("realm location:::\(String(describing: Realm.Configuration.defaultConfiguration.fileURL))")
let myMessage = RealmMessages()
myMessage.messageText = "Diva"
myMessage.sender = "yIvq1mQxjfZpjs1ybRTTlDOmUKV2"
let timevalue = timeStampValue()
timevalue.timestamp.value = 123123131
myMessage.chatId = "+918000080000"
myMessage.from = "+918000080000"
myMessage.groupMemberss = oneVal
myMessage.to = cont
try! realm.write {
realm.add(myMessage)
}
}
How to get groupMemberss and to structure in realm database like firebase. And how to create array and dictionary in realm
There are a number of solutions but here's two.
Assuming the data has been read in and the data from the groupMembers snapshot is sitting in a dictionary var that looks like this
let groupMembersDict = [
"919": "participant",
"111": "observer",
"222": "participant"
]
To store that in Realm, you can work with primitives and store each key and value in a separate List (think: Array) or you can leverage a managed Realm object and store those in a List.
If you want to keep the data within an object; here's what it would look like.
class GroupData: Object {
#objc dynamic var num = ""
#objc dynamic var type = ""
convenience init(withNum: String, andType: String) {
self.init()
self.num = withNum
self.type = andType
}
}
Here's the main object showing both options; either option 1: store the key value pairs in two arrays, or option 2: use the above GroupData object to store the key value pairs together
class Messages: Object {
#objc dynamic var messageText = ""
//option 1: two lists of primative strings that can be accessed like an array.
// downside is managing two lists
let groupNum = List<String>()
let groupType = List<String>()
//option 2: a list of members using another Realm object
let groupNumType = List<GroupData>()
}
And some code to create two messages, one of each type
let msg0 = Messages()
msg0.messageText = "My message"
for member in groupMembersDict {
msg0.groupNum.append( member.key )
msg0.groupType.append( member.value )
}
let msg1 = Messages()
msg1.messageText = "This message"
for member in groupMembersDict {
let aGroup = GroupData(withNum: member.key, andType: member.value)
msg1.groupNumType.append(aGroup)
}
store them in realm
realm.add(msg0)
realm.add(msg1)
read them both in an display the message from option 2. Option 1 would be just iterating over the arrays to print the group data
let messageResults = realm.objects(Messages.self)
for msg in messageResults {
print(msg.messageText)
for group in msg.groupNumType {
print(group.num, group.type)
}
}
Keep in mind that all managed properties must be primitives: NSString, NSDate, NSData, NSNumber or List, Results, RLMLinkingObjects, or subclasses of RLMObject like the GroupData shown above.

Adding new Object to existing List in Realm

I have two classes. First looks like that:
class Person: Object {
dynamic var owner: String?
var dogs: List<Dogs>()
}
and second class which looks like that:
class Dogs: Object {
dynamic var name: String?
dynamic var age: String?
}
and now in ViewController in 'viewDidLoad' I create object Person with empty List and save it in Realm
func viewDidLoad(){
let person = Person()
person.name = "Tomas"
try! realm.write {
realm.add(Person.self)
}
}
it works great and I can create Person, problem begins when I try to read this data in SecondViewController in ViewDidLoad doing it:
var persons: Results<Person>?
func viewDidLoad(){
persons = try! realm.allObjects()
}
and try to add new Dog to List doing it in button action:
#IBAction func addDog(){
let newDog = Dogs()
newDog.name = "Rex"
newDog.age = "2"
persons[0].dogs.append(newDog)
// in this place my application crashed
}
Here my app is crashing with an information: Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first. How can I add new Dog to List and how can I update person[0]?
I use SWIFT 3.0
The persons property is of type Results<Person>, which is a collection containing Person objects that are managed by a Realm. In order to modify a property of a managed object, such as appending a new element to a list property, you need to be within a write transaction.
try! realm.write {
persons[0].dogs.append(newDog)
}
Write something like this:
if let person = persons?[0] {
person.dogs.append(newDog)
}
try! realm.write {
realm.add(person, update: true)
}
Please check how are you getting realm. Each time you call defaultRealm, you are getting new realm.
Side Note: Besides adding the code inside the write transaction which solves your issue, you could query Person by name as follow...
#IBAction func addDog(){
let newDog = Dogs()
newDog.name = "Rex"
newDog.age = "2"
let personName = realm.objects(Person.self).filter("name = 'Tomas'").first!
try! realm.write {
personName.dogs.append(newDog)
}
}
Add object for Realm Database
class Task : Object {
#objc dynamic var id : Int = 0
#objc dynamic var name = ""
#objc dynamic var phone = ""
#objc dynamic var address = ""
}
#IBAction func buttonSave(_ sender: Any) {
let realm = try! Realm()
let user = Task()
user.id = 0
user.name = (txtName.text! as NSString) as String
user.phone = (txtPhone.text! as NSString) as String
user.address = (txtAddress.text! as NSString) as String
try! realm.write {
realm.add(user)
print("user:",user.name)
}
}

RealmSwift: How to create To-One Relationships properly?

Let's assume I have:
class Dog: Object {
dynamic var race = ""
dynamic var name = ""
override static func primaryKey() -> String? {
return "race"
}
}
class Person: Object {
dynamic var name = ""
dynamic var address = ""
dynamic var dog: Dog?
override static func primaryKey() -> String? {
return "name"
}
}
First I create a Dog and save it:
let dog = Dog()
dog.race = "Dalmatian"
try! realm.write {
realm.add(dog, update: true)
}
Now I create a Person in a different class. The docs are quite a bit unclear about this scenario. Do I need to save changes for the Dog first before creating the relationship?:
let person = Person()
person.name = "Jim"
// retrieve dog from realm:
if let dog = realm.objectForPrimaryKey(Dog.self, key: "Dalmatian") {
dog.name = "Rex" // Owner gives dog a new name
// Question:
// Saving changes to Rex: is this step neccessary?
try! realm.write {
realm.add(dog, update: true)
}
person.dog = dog
}
try! realm.write {
realm.add(person, update: true)
}
No, and it will cause a crash
if let dog = realm.objectForPrimaryKey(Dog.self, key: "Dalmatian") {
dog.name = "Rex" // Owner gives dog a new name
person.dog = dog
}
if you want update the dog's name, write like this:
if let dog = realm.objectForPrimaryKey(Dog.self, key: "Dalmatian") {
try! realm.write({
dog.name = "Rex"
})
person.dog = dog
}
see more: Realm.io/updating-objects
You can setup a whole object graph as unmanaged objects and persist them all by one call. So you don't need to persist the Dog first and retrieve it again to be able to use it in a relationship.
let dog = Dog()
dog.race = "Dalmatian"
let person = Person()
person.name = "Jim"
person.dog = dog
try! realm.write {
realm.add(person, update: true)
}

How to determine an inverse relationship of a to-one relationship using Realm?

Is it possible in Realm to obtain the inverse relation of a to-one relationship?
For example:
class Dog: Object {
dynamic var name: String?
dynamic var owner: Person?
}
class Person: Object {
dynamic var name: String?
let dog = LinkingObjects(fromType: Dog.self, property: "owner").first
}
let person = Person()
person.name = "Harry"
try! realm.write {
realm.add(person)
}
let dog = Dog()
dog.name = "Fido"
dog.owner = person
try! realm.write {
realm.add(dog)
}
print(person.dog?.name) // -> result is nil
And also:
let arbitraryPerson = realm.objects(Person).filter("name contains 'Harry'").first!
let dogOwned = arbitraryPerson.dog
print(dogOwned?.name) // -> result is nil
It seems that ownedDog is always nil. Why could the inverse relation not be determined? Or should dog always be of list<T> type (i.e. to-many relationship) to force a relation between these objects? In this situation I get it working, but it feels not correct to force a to-many relationship when it isn't.
The LinkingObjects object has to be a property of your model object for Realm to know what object it goes with:
class Person: Object {
dynamic var name: String?
let _dogs = LinkingObjects(fromType: Dog.self, property: "owner")
var dog: Dog? { return _dogs.first }
}
As far as I understand it, relations in Realm are not bi-directional by default, as you might be used to from Core Data. So if you want both objects to know of the relation, you have to set it both ways;
dog.owner = person
person.ownedDog = dog

RealmSwift - How can I store data in list

I'm new in programming and I would like to know how I can store data in a List with RealmSwift.
Considering the following model:
import RealmSwift
class ScanResults: Object{
dynamic var id = 0
dynamic var resource = ""
dynamic var scanDate = ""
let ScanResultsDetail = List<ScanResultsDetails>()
}
class ScanResultsDetails: Object{
dynamic var scanner = ""
dynamic var result = ""
}
This is an example how I store new ScanResults:
let newResults = ScanResults()
newResults.id = newResults.IncrementaID()
newResults.resource = "Test"
newResults.scanDate = "19.01.2016"
do{
try uiRealm.write({ () -> Void in
uiRealm.add(newResults)
})
}
catch{
}
My Question is now, how can I store data in the list? I can't figure it out.. Can you give me an example?
I don't see that you append any object to ScanResultsDetail in your example
Here is quick example based on swift source code (docs):
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
class Person: Object {
dynamic var name = ""
let dogs = List<Dog>()
}
let realm = try! Realm() // Create realm pointing to default file
// Link objects
let person = Person()
person.name = "Tim"
person.dogs.append(mydog)
try! realm.write {
realm.add(person)
}

Resources