Realm - can not update value 2 times - ios

I try to update value two times and i get run time error. My code is following
My model is
class Achievement: Object {
dynamic var name:String = ""
dynamic var counter:Int = 0
dynamic var happened:Bool = false
convenience init(name:String, counter:Int, happened:Bool) {
self.init()
self.name = name
self.counter = counter
self.happened = happened
}
override class func primaryKey() -> String {
return "name"
}
}
Function is
func checkHelperNerdAch() {
var helperNerd = Achievement()
let realm = try! Realm()
var results = realm.objects(Achievement).filter("name = 'helperHelperNerd'")
for element in results {
helperNerd = Achievement(name: element.name, counter: element.counter, happened: element.happened)
}
helperNerd.counter++
try! realm.write {
realm.add(helperNerd, update: true)
}
if helperNerd.counter == 8 {
let realm = try! Realm()
print("entering if 8")
helperNerd.happened = false
try! realm.write {
realm.add(helperNerd, update: true)
}
print("updated inside if 8")
}
}
App crashes after printing entering if 8
Error is
2015-12-30 15:09:00.307 AppName[3215:640021] * Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first.'
* First throw call stack:
(0x2516c10b 0x24912e17 0xf6bf0b 0x1586c8 0x6cb5c 0x6cd38 0x29556043 0x29545ac3 0x26450c7f 0x26450f71 0x2512f68f 0x2512f27d 0x2512d5eb 0x25080bf9 0x250809e5 0x262ccac9 0x29310ba1 0x6db90 0x24d2f873)
libc++abi.dylib: terminating with uncaught exception of type NSException
What am I doing wrong ?

After first write block your helperNerd object is added to realm, so it is not a standalone object anymore.
Any update of the stored object should happen inside transaction, but you're assigning helperNerd.happened = false outside of write block, hence exception occurs.
So just rewrite the if block as follows:
if helperNerd.counter == 8 {
let realm = try! Realm()
print("entering if 8")
try! realm.write {
helperNerd.happened = false
// note that add() is not needed anymore
// because we're sure object exists in realm
}
print("updated inside if 8")
}

Related

Realm crashing app on delete or write, libc++abi.dylib: terminating with uncaught exception of type NSException

I have added realm integration in my app. Process is that,
1] if list is not empty then reload the tableview , and at the same time call an api, receive its response..
2] check value present against Id or not, if present delete that value from realm, again add value to realm and reload the tableview.. Code is working fine, if i wait for the 2nd step completion. But 2nd step is totally asynchronous..Here is what I have tried
And crash is happening when i change viewcontroller before the completion 2nd step.
public class Features : Object , Codable{
#objc dynamic var intellinectsId : String?
#objc dynamic var serviceName : String?
#objc dynamic var android_icon : String?
#objc dynamic var parentUrl : String?
#objc dynamic var url : String?
#objc dynamic var mobileOrder : String?
enum CodingKeys: String, CodingKey {
case serviceName = "serviceName"
case android_icon = "android_icon"
case parentUrl = "parentUrl"
case url = "url"
case mobileOrder = "mobileOrder"
case intellinectsId = "intellinectsId"
}
required convenience public init(from decoder: Decoder) throws {
self.init()
let values = try decoder.container(keyedBy: CodingKeys.self)
serviceName = try values.decodeIfPresent(String.self, forKey: .serviceName)
android_icon = try values.decodeIfPresent(String.self, forKey: .android_icon)
parentUrl = try values.decodeIfPresent(String.self, forKey: .parentUrl)
url = try values.decodeIfPresent(String.self, forKey: .url)
mobileOrder = try values.decodeIfPresent(String.self, forKey: .mobileOrder)
intellinectsId = try values.decodeIfPresent(String.self, forKey: .intellinectsId)
}
required init() {
}
}
I have created single class for RealmService
class RealmService {
private init() {}
/// To get singleton class
static let shared = RealmService()
/// To get realm object instance
var realm = try! Realm()
/// To create a record or adding new object to database
func create<T: Object>(_ object: T) {
do {
try realm.safeWrite {
//realm.add(object)
realm.create(T.self,value: object)
//realm.create(T.self, value: object, update: .modified)
}
} catch {
post(error)
}
}
/// To update/modify particular record/object in the database
func update<T: Object>(_ object: T, with dictionary: [String: Any?]) {
do {
try realm.write {
for (key, value) in dictionary {
object.setValue(value, forKey: key)
}
realm.add(object, update: .all) //add(object, update: true)
}
} catch {
post(error)
}
}
/// To delete/remove record or object database
func delete<T: Object>(_ object: T) {
do {
try realm.write {
realm.delete(object)
}
} catch {
post(error)
}
}
/// To handle the errors while performing realmDB opration
func post(_ error: Error) {
NotificationCenter.default.post(name: NSNotification.Name("RealmError"), object: error)
}
/// To observe realm error in the particular View controller
func observeErrors(in vc: UIViewController, completionHandler: #escaping(Error?) -> Void) {
NotificationCenter.default.addObserver(forName: NSNotification.Name("RealmError"), object: nil, queue: nil) { (notification) in
completionHandler(notification.object as? Error)
}
}
/// To stop observing errors in particular View Controller
func stopObservingErrors(in vc: UIViewController) {
NotificationCenter.default.removeObserver(vc, name: NSNotification.Name("RealmError"), object: nil)
}
/// To delete complete realm form the app
func completeDeleteRealm() {
let realmURL = Realm.Configuration.defaultConfiguration.fileURL!
let realmURLs = [
realmURL,
realmURL.appendingPathExtension("lock"),
realmURL.appendingPathExtension("note"),
realmURL.appendingPathExtension("management")
]
for URL in realmURLs {
do {
try FileManager.default.removeItem(at: URL)
} catch {
post(error)
}
}
}
}
Now in View controller , I am taking a value from realm, against id, like this
var dashboardList : Results<Features>{
get{
return RealmService.shared.realm.objects(Features.self).filter(NSPredicate(format: "intellinectsId == %#", HelperFunctions().getUserInfoFromDefaults()?.intellinectsId ?? ""))
}
}
.. as given in step 1st, if dashboardList count is > 0 then reload the tableview , simultaneously , call and api to fetch the details and again I have performed 2nd step like below
if let responseList = response.array, responseList.count > 0{
//remove existing data from realm
let removeHMC = RealmService.shared.realm.objects(Features.self).filter(NSPredicate(format: "intellinectsId == %#",intellinectsId))
if let realm = try? Realm(){
try? realm.write {
realm.delete(removeHMC)
}
}
if let featuresList = responseList[0]["features"].array, featuresList.count > 0{
for val in featuresList{
let feature = Features()
feature.intellinectsId = intellinectsId
feature.serviceName = val["serviceName"].string
feature.android_icon = val["android_icon"].string
feature.parentUrl = val["parentUrl"].string
feature.url = val["url"].string
feature.mobileOrder = val["mobileOrder"].string
RealmService.shared.create(feature)
}
}
}
And if i wait for this completion then it works fine. but if i go on next vc. I am getting an error like
libc++abi.dylib: terminating with uncaught exception of type NSException
after looking at the specific issue, I received an error
Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.'
I applied this trick also.. But after deleting value from realm , showing again to the tableview causes this issue. I am unable to figure out. Kindly help
In your response when you are deleting the previous features list you are creating a new instance of Realm() which will cause inconsistencies between those two.
Try removing that and using the same RealmService.shared like this
//remove existing data from realm
let removeHMC = RealmService.shared.realm.objects(Features.self).filter(NSPredicate(format: "intellinectsId == %#",intellinectsId))
RealmService.shared.delete(removeHMC)

iOS - App crash on changing a REALM object property

I am using RealmSwift in a project. I have a model as below
#objc final class Diary : Object, Codable {
#objc dynamic public var id: Int = 1
#objc dynamic public var notes: String = ""
}
public func persistDiary(){
let realm = StorageServiceManager.shared.getRealm()
do{
try realm.write {
realm.add(self)
}
}catch{
debugPrint(error)
}
}
I wrote few Diary objects to the REALM db. I was able to fetch them also using below code
let realm = StorageServiceManager.shared.getRealm()
let notes = realm.objects(Diary.self)
After fetching those objects, I just tried updating a property of an object and the app got crashed. The code for that is as below,
var currentNotes = notes[0]
currentNotes.id = 2//This line leads to the crash
currentNotes.notes = "testing"
Console message: libc++abi.dylib: terminating with uncaught exception of type NSException
Any help will be great, Thanks.
You need to update your object inside a write transaction. Your code should look something like:
let realm = try! Realm()
let notes = realm.objects(Diary.self)
if let currentNotes = notes[0] {
try! realm.write {
currentNotes.id = 2//This line leads to the crash
currentNotes.notes = "testing"
}
}
To make a copy of your object, you can do it like this:
let currentNoteCopy = Diary(value: notes[0])
currentNoteCopy.id = 2

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.

App Crashes SIGABRT when trying to use Realm

I am trying to save a simple object with Realm but the app keeps crashing when trying to make a write transaction even when it's wrapped in a Do Catch block.
let theme = Theme()
theme.name = "Custom Theme"
theme.backgroundColor = backgroundColor
theme.accentColor = accentColor
theme.numberColor = numColor
theme.functionColor = funcColor
// Add to the Realm inside a transaction
do {
try Realm().write {
do {
try Realm().add(theme, update: true)
} catch {
print("Error saving data")
}
}
} catch {
print("Realm.write error")
}
Here is the object 'Theme'
class Theme : Object {
dynamic var name = ""
dynamic var backgroundColor = ""
dynamic var accentColor = ""
dynamic var numberColor = ""
dynamic var functionColor = ""
override static func primaryKey() -> String? {
return "name"
}
}
Here is a screenshot of the crash
SIGABRT Crash
EDIT: The code above that causes the crash is only executed when a button is clicked. There is no console output either. I am bringing realm in via CocoaPods.
Ah, it might have something to do with the way you're creating the realm instances, try this:
let realm = try! Realm()
do {
try realm.write {
do {
try realm.add(theme, update: true)
} catch {
print("Error saving data")
}
}
} catch {
print("Realm.write error")
}
Though, usually you won't need to wrap your transactions into a do-catch block:
let realm = try! Realm()
try! realm.write {
realm.add(theme, update: true)
}

Realm errors: RLMArray and Migration

I ran into some errors when I'm trying to compile my realm code, this is the first version of the code:
import RealmSwift
class Test: Object {
dynamic var name = ""
dynamic var dict = Dictionary<String, Int>()
dynamic var owner: TestList?
}
class TestList: Object{
dynamic var name = ""
let tests = List<Test>().filter("ALL Test.dict[hello] != nil")
}
//ViewController
import RealmSwift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let realm = Realm()
let test1 = Test()
test1.name = "test1"
test1.dict = ["hello": 1]
realm.write{realm.add(test1)}
let test2 = Test()
test2.name = "test2"
test2.dict = ["nihao": 2]
realm.write{realm.add(test2)}
let test3 = Test()
test3.name = "test3"
test3.dict = ["hello": 3]
realm.write{realm.add(test3)}
}
#IBOutlet weak var label: UILabel!
#IBAction func set(sender: UIButton){
let test = Test()
let realm = Realm()
test.name = "not using CoreData"
realm.write{realm.add(test)}
text = "abc"
}
#IBAction func show(sender: UIButton){
let test = Realm().objects(TestList)
var str = ""
println("got to here")
for i in test{
str += " \(i.name)"
}
label.text = str
}
}
With this setup I got an error in my log that says:
Terminating app due to uncaught exception 'RLMException', reason: 'This method can only be called on RLMArray instances retrieved from an RLMRealm'
When I removed the filtering in my TestList object, the error became: Terminating app due to uncaught exception 'RLMException', reason: 'Migration is required for object type 'Test' due to the following errors:
- Property 'dict' has been added to latest object model.
- Property 'owner' has been added to latest object model.'
Am I understanding Realm's documentation completely wrong?
The first error is right. You can't filter on properties. Furthermore Realm doesn't allow Dictionary properties at the moment. You would need to explicitly model that as a list property of a dedicated Realm object entity, which has fields to hold a String and an Int.
class Test: Object {
dynamic var name = ""
dynamic var dict = List<TestRelatedThing>()
dynamic var owner: TestList?
}
class TestRelatedThing : Object {
dynamic var key: String
dynamic var value: Int
}
class TestList: Object{
dynamic var name = ""
let tests = List<Test>()
}
You can then query at runtime by:
Realm().objects(TestList).filter("ANY tests.key == %#", "hello")
Hint: The aggregate operator ALL is not supported for Realm. See our predicate cheat sheet for a full list of supported operators. Here it makes anywhere more sense to existential quantify that operation, because of the changed data structure, which doesn't allow an entry of TestRelatedThing with it's value property to equal nil to exist in the first place. So you can just make sure that there is an entry with key "hello" instead.
For the second error: I guess you tried to run your code before and added after that first run further fields to your model objects.
You can reset the simulator state or might just add one of the following lines of code to ensure to start with a fresh Realm on each run while development.
# Swift 1.2
var error: NSError?
NSFileManager.defaultManager().removeItemAtPath(Realm.defaultPath, error:&error)
# Swift 2
try! NSFileManager.defaultManager().removeItemAtPath(Realm.defaultPath)

Resources