Get notified when value in List is updated using realm - swift

I am trying to write an application in OS X using a Realm database. I want to trigger notification when where there is change in value of List which is inside another object
Below is the Class
final class Profile: Object {
#objc dynamic var gradient1 = ""
#objc dynamic var gradient2 = ""
#objc dynamic var fontColor = ""
#objc dynamic var selectedFont = ""
#objc dynamic var selectedTitleFont = ""
#objc dynamic var fontFamily = ""
#objc dynamic var name = ""
#objc dynamic var shortbio = ""
#objc dynamic var avatarSource = ""
#objc dynamic var userid = ""
#objc dynamic var email = ""
var features = List<Features>()
var socialLinkButtons = List<SocialLinkButtons>()
#objc dynamic var appSelectedMetaData : AppMetaData? = nil
override static func primaryKey() -> String?{
return "userid"
}
}
final class Features: Object {
#objc dynamic var uuid = ""
#objc dynamic var id = ""
#objc dynamic var label = ""
#objc dynamic var screen = ""
#objc dynamic var active = false
override static func primaryKey() -> String?{
return "id"
}
convenience init(id: String, uuid: String, label: String, screen: String, active: Bool) {
self.init()
self.id = id
self.uuid = uuid
self.label = label
self.screen = screen
self.active = active
}
}
I want to trigger notifications whenever value inside feature is updated.

You can use Realm Collection Notifications to achieve your goals. You just need to make sure that you store the returned NotificationToken in a variable that doesn't get deallocated until you don't actually need to receive the notifications anymore and that you call .invalidate() on the token when you no longer want to receive notifications.
func observeFeatureChanges(in profile:Profile) -> NotificationToken {
let notificationToken = profile.features.observe { changes in
switch changes {
case .update(_, deletions: let deletionIndices, insertions: let insertionIndices, modifications: let modIndices):
print("Objects deleted from indices: \(deletionIndices)")
print("Objects inserted to indices: \(insertionIndices)")
print("Objects modified at indices: \(modIndices)")
case .error(let error):
print(error)
case .initial(let type):
print(type)
}
}
return notificationToken
}

Related

Realm Append managed object to unmanaged object

I want to append managed object to unmanaged object in realm.
Here is the codes
class Schedule3: Object, ObjectKeyIdentifiable {
#objc dynamic var _id: String = UUID().uuidString
#objc dynamic var _partition: String = ""
let scheduleTags = RealmSwift.List<ScheduleTag3>()
#objc dynamic var title: String = ""
override static func primaryKey() -> String? {
return "_id"
}
}
class ScheduleTag3: Object, ObjectKeyIdentifiable {
#objc dynamic var _id: String = UUID().uuidString
#objc dynamic var _partition: String = ""
#objc dynamic var name: String = ""
override static func primaryKey() -> String? {
return "_id"
}
}
When I add managed ScheduleTag3 object to unmanaged Schedule3 object and append #ObsevedResults(Schedule3.self), I get error 'Object is already managed by another Realm. Use create instead to copy it into this Realm.'
Here is the code of append,
struct SchedulePreview: View {
#ObservedResults(Schedule3.self) var schedules
#ObservedResults(ScheduleTag3.self) var tags
#EnvironmentObject var scheduleModel:ScheduleIDModel
var scheduleTitle:String
var scheduleBudget:Int
var areaTag:ScheduleTag3?
#StateRealmObject var thisSchedule = Schedule3()
var body: some View {
TabView(selection: self.$selection) {
...
.navigationBarItems (
trailing: Text("make")
.onTapGesture {
thisSchedule = scheduleModel.addSchedule(scheduleTitle: scheduleTitle, scheduleBudget: scheduleBudget, areaTag: areaTag)
let scheduleId = thisSchedule._id
let areTagId = areaTag?._id
let thisAreaTag = tags.filter("_id == %#", areTagId!).first
thisSchedule.scheduleTags.append(thisAreaTag!)
$schedules.append(thisSchedule)
}
)
}
}
class ScheduleIDModel: ObservableObject {
...
func addSchedule(scheduleTitle:String, scheduleBudget:Int, areaTag:ScheduleTag3?) -> Schedule3 {
let schedule = Schedule3()
if scheduleTitle != "" {
schedule.title = scheduleTitle
}
schedule.budget = scheduleBudget
schedule._partition = "Public"
return schedule
}
}

Deleting Objects with subclasses in Swift Realm

I have a really simple database in Swift Realm for a todo app:
Items and their parent Categories.
The user can delete both the Items and the Categories with a simple swipe action. The action works fine, there are no issues when deleting Items. If I delete a Category, that works too, but I can still see the Items in the Realm Browser, those remain in the database even though there are no parent anymore. Obviously the user can't see these, they are doing nothing but still, it would be better to get rid of these with the parent Category. Are there any simple ways to do this?
class Category: Object{
#objc dynamic var name: String = ""
#objc dynamic var color: String = ""
#objc dynamic var order = 0
let items = List<Item>()
override static func primaryKey() -> String? {
return "order"
}
static func incrementalIDCat() -> Int {
let realm = try! Realm()
return (realm.objects(Category.self).max(ofProperty: "order") as Int? ?? 0) + 1
}
}
class Item: Object {
#objc dynamic var title: String = ""
#objc dynamic var done: Bool = false
#objc dynamic var dateCreated: Date?
#objc dynamic var order = 0
var parentCategory = LinkingObjects(fromType: Category.self, property: "items")
override static func primaryKey() -> String? {
return "order"
}
static func incrementalIDItem() -> Int {
let realm = try! Realm()
return (realm.objects(Item.self).max(ofProperty: "order") as Int? ?? 0) + 1
}
}
override func updateModel(at indexPath: IndexPath) {
if let categoryForDeletion = self.categories?[indexPath.row] {
do {
try self.realm.write {
self.realm.delete(categoryForDeletion)
}
} catch {
print("Error deleting category, \(error)")
}
}
tableView.reloadData()
}
You just delete items first.
self.realm.delete(categoryForDeletion.items)
self.realm.delete(categoryForDeletion)
Or, with this extension, you can do this.
self.realm.delete(categoryForDeletion, cascading: true)

Swift Realm filter all object with a null value

I have two objects as follow:
class NextAction: Object {
#objc dynamic var title: String = ""
#objc dynamic var notes: String? = ""
#objc dynamic var deadline: Date?
#objc dynamic var deadlineID: String = ""
#objc dynamic var reminder: Date?
#objc dynamic var reminderID: String = ""
#objc dynamic var finished: Bool = false
#objc dynamic var favorite: Bool = false
#objc dynamic var priority: Int = 0
var duration = RealmOptional<Int>()
#objc dynamic var tag: String?
let tags = List<Tag>()
}
class Tag: Object {
#objc dynamic var title: String = ""
let owners = LinkingObjects(fromType: NextAction.self, property: "tags")
}
Not all NextAction objects has a tag, but I want to filter out and show all who are missing one, I have tried
var test = realm.objects(NextAction.self).filter("ANY tags == nil")
But I get this error
'Invalid value', reason: 'Expected object of type Tag for property 'tags' on object of type 'NextAction', but received: (null)'
Your tags cannot be nil.
If you want to find objects whose tags is empty, you can do that this way.
var test = realm.objects(NextAction.self).filter("ANY tags.#count == 0")

Comparing Realm Results and finding the object in a List

I need to loop through the objects in the Realm file and see if it is also listed in the List.
When I look at the file with the Realm Browser, it is there, but the following code always renders false.
Can anyone make out what is going on? (I am initiating Try! Realm earlier in the code, it is there, this is just the section that is bobbling my brian. It runs fine BTW.
Thanks,
Blessings,
—Mark
let totalReg = realm.objects(Registry.self)
let totalList = realm.objects(AllServices.self)
if totalReg.count != 0 && totalList.count != 0 { // when the update releases, I will change totalList == 0
print(totalReg.count) // = 2 records
print(totalList.count) // = 1 but when I click and open the list in Realm Browser it shows both records
let a = AllServices().self
for reg in totalReg {
if a.everything.contains(reg) {
print("Here")
} else {
print("not here") //Each loop goes here.
print(a.everything) //this prints: List<Registry> <0x600001d6e070> ( )
print (reg) //This correctly prints all the properties of the object.
}
}
}
This code always defaults to false, even though the Registry Object is in the List.
class Registry: Object {
#objc dynamic var registryId = UUID().uuidString
#objc dynamic var dateTime:String?
#objc dynamic var proper:String?
#objc dynamic var service:String?
#objc dynamic var place:String?
#objc dynamic var sunEuchAttendance:Int = 0
#objc dynamic var otherServiceAttendance:Int = 0
#objc dynamic var EuchVisitor:Int = 0
#objc dynamic var numberOfCommunions:Int = 0
//People serving
#objc dynamic var presiderOfficiant:String?
#objc dynamic var preacher:String?
#objc dynamic var server:String?
#objc dynamic var memo:String?
#objc dynamic var weekendEuch:Int = 0
#objc dynamic var weekdayEuch:Int = 0
#objc dynamic var privateEuch:Int = 0
#objc dynamic var weekendOffice:Int = 0
#objc dynamic var weekdayOffice:Int = 0
#objc dynamic var burrial:Int = 0
#objc dynamic var marraige:Int = 0
#objc dynamic var other:Int = 0
override static func primaryKey() -> String? {
return "registryId"
}
override static func indexedProperties() -> [String] {
return ["service"]
}
}
//List Model
class AllServices: Object {
var everything = List<Registry>()
var eucharist = List<Registry>()
var weekdayEucharist = List<Registry>()
var office = List<Registry>()
var weekdayOffice = List<Registry>()
var marriage = List<Registry>()
var burial = List<Registry>()
var requiem = List<Registry>()
var nuptial = List<Registry>()
var other = List<Registry>()
}

Class has No Initializers Error Realm Class Swift

I'm getting a Class has no initializers Error. Is this not how I would set up the class. This is for a Realm OBject Database, the Properties I want to store. I'm new to swift and realm, but I figured this is how it would be done. If I take out the init() function and just assign everything values it works, however properties like justTook and all that I don't want start with a value, I need to set it later in the program.
Here is my code:
class MedInfo: Object {
// Info To Be Stored
#objc dynamic var keyID = UUID().uuidString
#objc dynamic var medName: String
#objc dynamic var medDose: String
#objc dynamic var currentDate: Date
#objc dynamic var timeTook: Date
#objc dynamic var lastTook: Date
#objc dynamic var durationOfTime: Date
#objc dynamic var doctorName: String
#objc dynamic var rxDate: Date
#objc dynamic var medInfo: String
#objc dynamic var pictureOfMed: UIImage
override static func primaryKey() -> String? {
return "keyID"
}
convenience init(medName: String, medDose: String, currentDate: Date, timeTook: Date, lastTook: Date, durationOfTime: Date, doctorName: String,rxDate: Date, medInfo: String, pictureOfMed: UIImage) {
self.medName = medName
self.medDose = medDose
self.currentDate = currentDate
self.timeTook = timeTook
self.lastTook = lastTook
self.durationOfTime = durationOfTime
self.doctorName = doctorName
self.rxDate = rxDate
self.medInfo = medInfo
self.pictureOfMed = pictureOfMed
}
You need to set default values for all parameters, as vaguely described in the docs. From here:
When using Realm from Swift, the Swift.reflect(_:) function is used to
determine information about your models, which requires that calling
init() succeed. This means that all non-optional properties must have
a default value.
All the examples show this, so follow them. Your class should thus be declared:
class MedInfo: Object {
// Info To Be Stored
#objc dynamic var keyID = UUID().uuidString
#objc dynamic var medName: String = ""
#objc dynamic var medDose: String = ""
#objc dynamic var currentDate: Date = Date()
#objc dynamic var timeTook: Date = Date()
#objc dynamic var lastTook: Date = Date()
#objc dynamic var durationOfTime: Date = Date()
#objc dynamic var doctorName: String = ""
#objc dynamic var rxDate: Date = Date()
#objc dynamic var medInfo: String = ""
// UIImage not supported, you'll need to store a URL/filename or something else
#objc dynamic var pictureOfMed: UIImage
You must call the init to create the instance.
convenience init(medName: String, medDose: String, currentDate: Date, timeTook: Date, lastTook: Date, durationOfTime: Date, doctorName: String,rxDate: Date, medInfo: String, pictureOfMed: UIImage) {
self.init()
self.medName = medName
self.medDose = medDose
self.currentDate = currentDate
self.timeTook = timeTook
self.lastTook = lastTook
self.durationOfTime = durationOfTime
self.doctorName = doctorName
self.rxDate = rxDate
self.medInfo = medInfo
self.pictureOfMed = pictureOfMed
}
As #ces already correctly quoted from the Realm documentation: "This means that all non-optional properties must have a default value."
This is why I think the best way for you to create your Realm object is to declare your variables as optionals.
This way you will not have to call init. This is the way I create my own Realm objects and the way I've seen most people create them.
So your object would look like this:
class MedInfo: Object {
// Info To Be Stored
#objc dynamic var keyID = UUID().uuidString
#objc dynamic var medName: String?
#objc dynamic var medDose: String?
#objc dynamic var currentDate: Date?
#objc dynamic var timeTook: Date?
#objc dynamic var lastTook: Date?
#objc dynamic var durationOfTime: Date?
#objc dynamic var doctorName: String?
#objc dynamic var rxDate: Date?
#objc dynamic var medInfo: String?
#objc dynamic var pictureOfMed: UIImage?
override static func primaryKey() -> String? {
return "keyID"
}