Save To-Many Relationships with Realm - ios

the Problem is, that I can add relationships to my object and it works temporally but realm doesn't save the changes and when I quit the app and restart ist, the relationships are gone.
In the first view I create the main object. Here is the class:
class UserListClass: Object {
dynamic var UserListName = ""
let Medis = List<MediClass>()
override static func primaryKey() -> String? {
return "UserListName"
}
}
And the code how I create the object:
let realm = try! Realm()
let newList = UserListClass ()
newList.UserListName = textField.text!
try! realm.write {
realm.add(newList)
}
This works without problems, and the object I add is also there when I restart the app.
When the user click on Edit it will open the next view:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showUserlistEdit" {
let editViewController = segue.destinationViewController as! MediUserlistEditViewController
editViewController.userlist = userlist
}
}
In the next view the code add selected entries to the list, I tried two versions, but both have the result, that it saves the relationships only for the runtime of the app. When I restart it, they are away.
First try:
let realm = try! Realm()
try! realm.write {
userlist.Medis.append(medi)
realm.add(userlist, update: true)
}
Second try:
let realm = try! Realm()
try! realm.write {
userlist.Medis.append(medi)
}

The problem is the
let realm = try! Realm()
The documentation specify
// Get the default Realm
let realm = try! Realm()
// You only need to do this once (per thread)
You did on both view controllers and there is no guarantee that the path to the default Realm will be the same. It might work on the actual device but not on simulator.
Try this and delete all other occurrences
// Put this inside your Real class Definition/Declaration file
let realm = try! Realm()
Hope this help

Related

Realm Swift - Object not updating when using modal

Working Code
Inside my tableView's didSelectRowAt IndexPath method, I have a call that updates the UserSave model, which is in the default realm. The function is structured like so:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let UserRealm = UserSave()
let realm = try! Realm()
try! realm.write {
UserRealm.articleLink = newsLink[indexPath.row]
UserRealm.articleBody = newsData[indexPath.row]
UserRealm.articleTitle = newsTitle[indexPath.row]
UserRealm.articleAuthor = newsSrc[indexPath.row]
}
performSegue(withIdentifier: "webKitSegue", sender: self)
}
When this is run, the realm updates with the new values, as it should.
Problem Code
I have a second model, UserPrefs, which is also a part of the default realm. It is called inside function exrefresh(writeToRealm: String). The function looks like this:
func exrefresh(passed: String) {
let UserRealm = UserPrefs()
let realm = try! Realm()
try! realm.write {
UserRealm.fetchUrl = passed
}
self.refreshControl!.beginRefreshing()
self.refreshControl!.sendActions(for: .valueChanged)
}
When this function runs, however, the realm maintains its default values, and does not update with the new one.
Models
// UserSave
import RealmSwift
class UserSave: Object {
#objc dynamic var articleTitle = "Default Title"
#objc dynamic var articleAuthor = "Default Author"
#objc dynamic var articleLink = "https://example.com"
#objc dynamic var articleBody = "Default Body"
}
// UserPrefs
import RealmSwift
class UserPrefs: Object {
#objc dynamic var applicationDark = false
#objc dynamic var fetchUrl = "https://example.com/"
}
The Issue
I can update the UserSave model just fine, though I am unable to change the values in UserPrefs, even though both are in the default realm. I use the same code (with the names substituted) to update both models, and only one works properly. I have the .realm file pulled up in the Realm Browser, and am able to watch as UserSave changes. I have followed the guide from realm.io, and their code only works on one model.
Thanks for any help in advance.
Your question mentions default values. Assuming the objects already exist, you would need to get that object to then be able to update it. Otherwise Realm would not know what object you're referring to.
Here's the code to write an object.
func exrefresh(passed: String) {
let UserRealm = UserPrefs()
UserRealm.fetchUrl = passed
let realm = try! Realm()
try! realm.write {
realm.add(UserRealm)
}
}
If you are going to only ever have one object of that type, then here's the code to update it.
let realm = try! Realm()
let results = realm.objects(UserPrefs.self)
let theOnlyOne = results.first!
try! realm.write {
theOnlyOne.fetchUrl = passed
}
This assumes you have proper error checking to know they object exists before updating.

Save User using Realm Object

I am creating a project. I want if the userID already exist, it doesn't add the user. But somehow my code isn't working properly.
This is my Realm Model Object (User.swift):
import Foundation
import RealmSwift
class User: Object {
#objc dynamic var userID = Int()
#objc dynamic var username = ""
#objc dynamic var full_name = ""
#objc dynamic var myBool = Bool()
override static func primaryKey() -> String? {
return "userID"
}
}
And this is the button to add users:
#IBAction func add(_ sender: Any) {
let myUser = User()
let JSON_userID = Int(arc4random_uniform(5)) // This is temporary. I am going to get code from JSON, but using random for testing purpose.
if (myUser.userID != JSON_userID) {
myUser.userID = JSON_userID
myUser.username = "myUsername"
myUser.full_name = "My Name"
let realm = try! Realm()
try! realm.write {
realm.add(myUser)
}
}
else {
print("Already exist")
}
}
Sometimes it runs the code, but most of the times it crashes with error:
libc++abi.dylib: terminating with uncaught exception of type NSException.
As you defined a primary key in your User object, Realm can handle this automatically if you set the update parameter to true inside the write closure.
let realm = try! Realm()
try! realm.write {
realm.add(myUser, update: true)
}
If the update parameter is not set or false, Realm will throw an exception when you try to add an object with an existing primary key.
This makes the if / else condition useless. It can be removed.
If you need to know if the user already exists, you can request the Realm with the primary key value:
realm.object(ofType: User.self, forPrimaryKey: JSON_userID)
The result will be nil if the user does not exist.

How can I read the value of a Realm variable in an iOS project?

I have just started coding. I am making an iOS app with Swift that needs to be able to store two variables even when the app is exited. For this, I am using Realm. I am able to write those two variables, but when I try to read them, I cannot build my project.
I have tried many tutorats, but failed each time I tried to apply them to my problem.
Here is my Realm class:
class Info: Object {
dynamic var name = ""
dynamic var gender = ""
}
How can I read the value of these two variables in my ViewController?
When I try to read the variable, I have an error on the line
guard let info = realm.objects(Info.self).first else {return}
of my ViewController:
import UIKit
import RealmSwift
class SettingsViewController: UIViewController {
let realm = try! Realm()
guard let info = realm.objects(Info.self).first else {return}
let name = info.name
let gender = info.gender
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You can access Realm variables like this:
let realm = try! Realm()
guard let info = realm.objects(Info.self).first else {return}
let name = info.name
let gender = info.gender
However, if you don't plan on storing more complex data on storage, I would suggest you just use UserDefaults, for such a simple task it is easier to use.
You should also change the « » to proper " signs if you want to store String literals.
For the edited part of your question:
guard let info = realm.objects(Info.self).first else {return} has to happen inside your viewDidLoad function. You cannot access realm, which is an instance property of your class, before your class is actually initialised, hence the error you get. If you need info to be an instance property, initialise it as an optional and access it inside viewDidLoad.
First option - if you only need access to info inside viewDidLoad:
class SettingsViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let realm = try! Realm()
guard let info = realm.objects(Info.self).first else {return}
let name = info.name
let gender = info.gender
}
}
Second option - if you need to access info everywhere in your class instance:
class SettingsViewController: UIViewController {
let realm = try! Realm()
var info: Info?
override func viewDidLoad() {
super.viewDidLoad()
info = realm.objects(Info.self).first else {return}
let name = info.name
let gender = info.gender
}
}
If this is your initial view controller, declaring realm as an instance property might prove to be problematic, since your class might be instantiated before your application(didFinishLaunchingWithOptions) function finished execution and hence your realm schema won't be set up properly by the time you try to access it in SettingsViewController. If this is the case, just move the line let realm = try! Realm() inside viewDidLoad.

Realm swift thread safe variable

I am new to Realm and I am wondering if it is good idea to store primary key of object in View controller and object as stored property.
class VC: ViewController{
public var id:Int!
private var customer:Customer{
get{
return DBO.loadCustomer(for: id)
}
}
}
class DBO{
public static func loadCustomer(for id: Int) -> Customer{
let realm = try! Realm()
return realm.object(ofType: Customer.self, forPrimaryKey: id)!
}
}
I am assuming that this because of this my app will be thread safe, because every time thread will try to access customer it will fetch object from db. But I wonder if this is efficient for single object and array of objects.
As of Realm 2.2 you could use thread-safe references (see here):
Now, you can create thread-safe references for all types that were
previously thread-confined within a Realm, and passing objects between
threads becomes a simple, three-step process:
Initialize a ThreadSafeReference with the thread-confined object.
Pass that ThreadSafeReference to a destination thread or queue.
Resolve this reference on the target Realm by calling
Realm.resolve(_:). Use the returned object as you normally would.
And a code sample, from the source above:
let realm = try! Realm()
let person = Person(name: "Jane") // no primary key required
try! realm.write {
realm.add(person)
}
let personRef = ThreadSafeReference(to: person)
DispatchQueue(label: "com.example.myApp.bg").async {
let realm = try! Realm()
guard let person = realm.resolve(personRef) else {
return // person was deleted
}
try! realm.write {
person.name = "Jane Doe"
}
}
But, your approach should also work fine.

Re-initializing a Realm object in a different thread

Is running a query the only way of re-initializing a Realm Object for use in a different thread? Is there no way to somehow grab some kind of reference for use with Realm, so that the query doesn't have to be completely from scratch? It would be nice to have some kind of guarantee that the object is for the same record (if it exists and the query for that "ref" is successful), without having to add a primary key just for this one purpose.
This outlines my situation:
func construct(name: String, tokens: [String]) -> Document {
let doc = Document()
let realm = try! Realm()
try! realm.write {
realm.add(doc)
}
DispatchQueue.global(qos: .userInitiated).async {
let realm = try! Realm()
// Some long running task to convert `tokens` into `[Blurb]`
// var blurbs: [Blurb]
// What's the recommended way of re-initing that new `Document`?
// let sameDoc = .....
try! realm.write {
sameDoc.blurbs.append(blurbs)
}
}
return doc
}

Resources