Swift Remove Object from Realm - swift

I have Realm Object that save list from the JSON Response. But now i need to remove the object if the object is not on the list again from JSON. How i do that?
This is my init for realm
func listItems (dic : Array<[String:AnyObject]>) -> Array<Items> {
let items : NSMutableArray = NSMutableArray()
let realm = try! Realm()
for itemDic in dic {
let item = Items.init(item: itemDic)
try! realm.write {
realm.add(item, update: true)
}
items.addObject(item)
}
return NSArray(items) as! Array<Items>
}

imagine your Items object has an id property, and you want to remove the old values not included in the new set, either you could delete everything with just
let result = realm.objects(Items.self)
realm.delete(result)
and then add all items again to the realm,
or you could also query every item not included in the new set
let items = [Items]() // fill in your items values
// then just grab the ids of the items with
let ids = items.map { $0.id }
// query all objects where the id in not included
let objectsToDelete = realm.objects(Items.self).filter("NOT id IN %#", ids)
// and then just remove the set with
realm.delete(objectsToDelete)

I will get crash error if I delete like top vote answer.
Terminating app due to uncaught exception 'RLMException', reason: 'Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first.'
Delete in a write transaction:
let items = realm.objects(Items.self)
try! realm!.write {
realm!.delete(items)
}

func realmDeleteAllClassObjects() {
do {
let realm = try Realm()
let objects = realm.objects(SomeClass.self)
try! realm.write {
realm.delete(objects)
}
} catch let error as NSError {
// handle error
print("error - \(error.localizedDescription)")
}
}
// if you want to delete one object
func realmDelete(code: String) {
do {
let realm = try Realm()
let object = realm.objects(SomeClass.self).filter("code = %#", code).first
try! realm.write {
if let obj = object {
realm.delete(obj)
}
}
} catch let error as NSError {
// handle error
print("error - \(error.localizedDescription)")
}
}

What you can do is assign a primary key to the object you are inserting, and when receiving a new parsed JSON you verify if that key already exists or not before adding it.
class Items: Object {
dynamic var id = 0
dynamic var name = ""
override class func primaryKey() -> String {
return "id"
}
}
When inserting new objects first query the Realm database to verify if it exists.
let repeatedItem = realm.objects(Items.self).filter("id = 'newId'")
if !repeatedItem {
// Insert it
}

The first suggestion that comes to mind is to delete all objects before inserting new objects from JSON.
Lear more about deleting objects in Realm at https://realm.io/docs/swift/latest/#deleting-objects

do {
let realm = try Realm()
if let obj = realm.objects(Items.self).filter("id = %#", newId).first {
//Delete must be perform in a write transaction
try! realm.write {
realm.delete(obj)
}
}
} catch let error {
print("error - \(error.localizedDescription)")
}

There is an easier option to remove 1 object:
$item.delete()
remember to have the Observable object like:
#ObservedRealmObject var item: Items

var realm = try! Realm()
open func DeleteUserInformation(){
realm.beginWrite()
let mUserList = try! realm.objects(User.self)
realm.delete(mUserList)
try! realm.commitWrite()
}

Related

Prevent delete array of object adding again in realm db

I have an api who fetch data and add it in realm, what i want is whenever i delete realm object and refresh the page and called the api. the object that already delete is not showing/adding again in realm db. How to do it? here is my code
class TickerRealmStorage: RealmStorage, TickerStorage {
private var items = RealmSwift.List<Ticker>()
private var temptTicker: Ticker?
func get() -> RealmSwift.List<Ticker> {
return items
}
func save(messages: [Message]) {
messages.forEach { message in
let ticker = Ticker()
ticker.campaignID = message.campaignID
ticker.isRead = message.isRead
ticker.messageBody = message.body
if let temptTicker = temptTicker, ticker.campaignID == temptTicker.campaignID {
try! realm.write {
realm.delete(ticker)
}
}
items.append(ticker)
}
do {
try realm.write {
realm.add(items, update: .modified)
}
} catch {
fatalError("Failed to save tickers")
}
}
func delete(ticker: Ticker) {
temptTicker = ticker
try! realm.write {
realm.delete(ticker)
}
}
}
Should i add a temptItems so i check if the data already delete not adding into list items.

Saving objects to Realm

I try to save some objects to a Realm, but even after setting the member variables and saving it - after reading the Realm, the objects have their default values.
class Person: Object {
#objc dynamic var Name: String = "test"
#objc dynamic var Age: Int = 0;
}
do {
let realm = try Realm();
var p = Person();
p.Name = "Poirot"
p.Age = 55
try realm.write {
realm.add(p)
print("Person:",p.Name); // <-- Here its correct: "Poirot"
}
} catch let error {
print(error.localizedDescription)
}
do {
let realm = try Realm()
let data = realm.objects(Person.self)
for persons in data {
print("Person:", persons.Name); // <-- Here its wrong: "test"
}
} catch let error {
print(error.localizedDescription)
}
After running this, it would print:
Person: Poirot
Person: test
Can someone explain me this behavior and tell me what I´m doing wrong?
Thank you!
If you run your code in foreground it is fine.
If you are on a background thread (a thread without a RunLoop) than you need to refresh your realm.
let realm = try Realm()
realm.refresh()
let data = realm.objects(Person.self)
for persons in data {
print("Person:", persons.Name); // <-- Here its wrong: "test"
}
Have a look at the Realm threading documentation and scroll down to Refreshing Realms

How To Save Only One Instance Of Class In Realm

So instead of using user defualts I want to persist some settings using Realm.
I've created a class for the settings
import Foundation
import RealmSwift
class NutritionSettings: Object {
#objc dynamic var calories: Int = 0
#objc dynamic var proteins: Int = 0
#objc dynamic var carbohydrates: Int = 0
#objc dynamic var fats: Int = 0
}
But in my view controller I don't know how to save just one instance of it
I've tried
let realm = try! Realm()
let settings = NutritionSettings()
do {
try realm.write{
settings.calories = cals!
settings.carbohydrates = carbs!
settings.fats = fats!
settings.proteins = proteins!
}
} catch {
print("error saving settings")
}
Since I know doing realm.add would just add another NutritionSettings object which is not what I want. I was unable to clarify anything using the documentation. Any help would be appreciated thanks.
I faced a similar issue in my project when I tried to save a user session object. If you want to save a unique object, override the primaryKey() class method and set the unique key for it.
#objcMembers class NutritionSettings: Object {
static let uniqueKey: String = "NutritionSettings"
dynamic var uniqueKey: String = NutritionSettings.uniqueKey
dynamic var calories: Int = 0
override class func primaryKey() -> String? {
return "uniqueKey"
}
}
Then to receive the object just use the unique key.
// Saving
let settings = NutritionSettings()
settings.calories = 100
do {
let realm = try Realm()
try realm.write {
realm.add(settings, update: .modified)
}
} catch {
// Error handling
}
// Reading
var settings: NutritionSettings?
do {
let realm = try Realm()
let key = NutritionSettings.uniqueKey
settings = realm.object(ofType: NutritionSettings.self, forPrimaryKey: key)
} catch {
// Error handling
}
if let settings = settings {
// Do stuff
}
Hope it will help somebody.
If you look at the example realm provides https://realm.io/docs/swift/latest you can see that in order to only save one object you still have to do an add. Once you have added the object to the database you can fetch that object and do a write that modifies the internal properties
let realm = try! Realm()
let settings = NutritionSettings()
settings.id = 1
do {
try realm.write{
realm.add(settings)
}
} catch {
print("error saving settings")
}
Next you can fetch and modify that single instance that you saved
let realm = try! Realm()
let settings = realm.objects(NutritionSettings.self).filter("id == 1").first
do {
try realm.write{
settings.calories = cals!
settings.carbohydrates = carbs!
settings.fats = fats!
settings.proteins = proteins!
}
} catch {
print("error saving settings")
}

Swift Realm change only one object value

class User: Object {
#objc dynamic var id = ""
#objc dynamic var dateFirstStart:TimeInterval = 0
//dates
#objc dynamic var dateLastStart:TimeInterval = 0
#objc dynamic var dateLastAppClose:TimeInterval = 0
#objc dynamic var dateLastDataUpdateCheck:TimeInterval = 0
#objc dynamic var dateLastFilesUpdateCheck:TimeInterval = 0
override class func primaryKey() -> String? {
return "id"
}
}
Do I really have to create a function for each value to change? Like this:
func updateUserDateFirstStart(date:Date){
do {
let realm = try Realm()
try realm.write {
let user = getUser()
user. dateLastStart = Date().timeIntervalSince1970
}
} catch let error as NSError {
print("ERROR \(error.localizedDescription)")
}
}
What I want is something like
let user = getUser()
user.dateLastStart = Date().timeIntervalSince1970
dataManager.updateUser(user)
And in my DataManager:
func updateUser(user:User){
do {
let realm = try Realm()
try realm.write {
realm.add(user, update: true)
}
} catch let error as NSError {
print("ERROR \(error.localizedDescription)")
}
}
But if I do it as you can see in my wishtohave solution I always get an Attempting to modify object outside of a write transaction error.
I tried to create a complete new Object and use the id from the object I want to change. This works but would need even more lines of code.
You can use KVO to update one value in realm object
how to call
let user = getUser()
self.update(ofType:user, value: Date().timeIntervalSince1970 as AnyObject, key: "dateLastStart")
Helper func
func update(ofType:Object,value:AnyObject,key:String)->Bool{
do {
let realm = try Realm()
try realm.write {
ofType.setValue(value, forKeyPath: key)
}
return true
}catch let error as NSError {
fatalError(error.localizedDescription)
}
return false
}

RealmDB does not get populated

New to Realm... so hopefully a simple fix!
I have a data object of:
class GasFile : Object {
#objc dynamic var gasFilename : String = ""
// #objc dynamic var gasCategory : String? = ""
}
In my ViewController I have the following function:
func PopulateRealmWithFilenames() {
let fm = FileManager.default
var path = Bundle.main.resourcePath!
path += "/NBTFiles"
//let items = try! fm.contentsOfDirectory(atPath: path)
let items : [String] = try! fm.subpathsOfDirectory(atPath: path)
for item in items {
do {
print("item for realm is: \(item)")
try self.realm.write {
let newGasFile = GasFile()
newGasFile.gasFilename.append(item)
print("newGasFile written ok")
}
} catch {
print("Error writing new item to Realm \(error)")
}
}
}
This sets up the RealmDB ok, but never gets populated.
The aim of the function is to save all the filenames (not paths) of the files stored in a folder called NBTFiles.
I get the 'newGasfile written ok printed out' so it is getting into the loop.
datatype error ?
(I know I could just put it into an Array, but I want to use realm)
how do you want to add new object to realm without using adding function ?
you should use realm.add(object: newGasFile)
try this
func PopulateRealmWithFilenames() {
let fm = FileManager.default
var path = Bundle.main.resourcePath!
path += "/NBTFiles"
//let items = try! fm.contentsOfDirectory(atPath: path)
let items : [String] = try! fm.subpathsOfDirectory(atPath: path)
for item in items {
do {
print("item for realm is: \(item)")
try self.realm.write {
let newGasFile = GasFile()
newGasFile.gasFilename.append(item)
realm.add(object: newGasFile)
print("newGasFile written ok")
}
} catch {
print("Error writing new item to Realm \(error)")
}
}
}