RealmSwift: How to create To-One Relationships properly? - ios

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

Related

Is it possible to use dynamic variable for a class member variable

I have a class like
class Person {
var address:String
var number:String
var houseNo:String
var licenceNo:String
....
}
let jone = Person()
jone.number = "123456"
So in this i need to initialize the variable of person class one by one. And i have approx 30 variables in person class.
Is not there s simple way to do this ?
like I have all the keys coming from backend like "number = 123456". Is not there a way that i run a for loop and use something like.
for key in keys {
john."\(key)" = dict[key]
}
Is not there a way to shorten this lengthy procedure ?
You can try out this code
extension NSObject{
// fetch all class varible
func property() -> Mirror.Children {
return Mirror(reflecting: self).children
}
func propertyList() {
for (name, value) in property() {
guard let name = name else { continue }
print("\(name): \(type(of: value)) = '\(value)'")
}
}
}
Your class, set value like below code it's helpfull
class Person: NSObject {
var address:String = ""
var number:String = ""
var houseNo:String = ""
var licenceNo:String = ""
init(with response: [String: AnyObject]) {
for child in self.property() {
if let key = child.label, let value = response[key] {
self.setValue(value, forKey: key)
}
}
}
}
person.propertyList()
// Display all class property list
address: String = ''
number: String = ''
houseNo: String = ''
licenceNo: String = ''
Why don't use a Person method for load the backend response into a new Person object?
Somethind like that:
let jone = Person()
jone.loadDataFrom(response)
Or use a Person static method
let jone = Person.loadDataFrom(response)
static func loadDataFrom(response:Object) -> Person {
let p = Person()
...
set response data
...
return p
}

Realm inverse-relationships support for creation with a dictionary value

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.

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

sort array of multiple inheritance objects by date

I'm having an array array: [AnyObject] which contain a number of different inheritance objects for instance Dog or Cat. I would like to sort these by date how can i do this?
The classes could for instance look like this
class Pet {
var: NSDate = NSDate()
}
class Dog: Pet {
}
class Cat: Pet {
}
So far i've created this class, which is suppose to handle the sorting and return a new array with the objects sorted
class FavoriteSorter {
func sortOrganizationsByDistanceFromLocation(orgArray:[AnyObject]) -> [AnyObject] {
let sortedArray = orgArray.sort {
}
return sortedArray
}
}
Try this out:
class Pet {
var myDate = NSDate()
var title = String()
}
class Dog: Pet {
}
class Cat: Pet {
}
var dog1 = Dog()
dog1.myDate = NSDate()
dog1.title = "Dog 1"
var dog2 = Dog()
dog2.myDate = NSDate(timeIntervalSinceNow:NSTimeInterval(10))
dog2.title = "Dog 2"
var cat1 = Cat()
cat1.myDate = NSDate(timeIntervalSinceNow:NSTimeInterval(20))
cat1.title = "Cat 1"
var cat2 = Cat()
cat2.myDate = NSDate(timeIntervalSinceNow:NSTimeInterval(15))
cat2.title = "Cat 2"
var arrayOfObjs = [cat1, cat2, dog1, dog2]
// Sorting array
func sortOrganizationsByDistanceFromLocation(array:[AnyObject]) -> [AnyObject] {
return array.sort{ ($0.0 as! Pet).myDate.compare(($0.1 as! Pet).myDate) == NSComparisonResult.OrderedAscending }
}
let sortedArray = sortOrganizationsByDistanceFromLocation(arrayOfObjs) as! [Pet]
print(sortedArray[0].title); // Prints Dog 1
print(sortedArray[1].title); // Prints Dog 2
print(sortedArray[2].title); // Prints Cat 2
print(sortedArray[3].title); // Prints Cat 1
class FavoriteSorter {
func sortOrganizationsByDistanceFromLocation(array:[AnyObject]) -> [AnyObject] {
return array.sort{ ($0.0 as! Pet).date.compare(($0.1 as! Pet).date) == NSComparisonResult.OrderedAscending }
}
}
As mentioned by vacawama it would be better to pass [Pet] instead of [AnyObject] to avoid crashing your app in case you pass another object type:
class FavoriteSorter {
func sortOrganizationsByDistanceFromLocation(array:[Pet]) -> [Pet] {
return array.sort{ $0.date.compare($1.date) == NSComparisonResult.OrderedDescending }
}
}
class Pet {
var date = NSDate()
}
class Dog: Pet { }
class Cat: Pet { }
let dog = Dog()
let cat = Cat()
let date01 = NSDate()
let date02 = NSDate().dateByAddingTimeInterval(3600)
dog.date = date01
cat.date = date02
let pets = [dog,cat]
pets.description // "[Dog, Cat]"
let sortedPets = FavoriteSorter().sortOrganizationsByDistanceFromLocation(pets)
sortedPets.description // "[Cat, Dog]"

Resources