Realm #count query issue - ios

I have tried to use the #count function to get data according to that but it somehow crashed without crash report.
Here is the code
class PSMedia: Object {
#objc dynamic var id = ""
#objc dynamic var promotional_status = false
var promotions = List<String>()
}
And here is that query that caused an issue
realm.objects(PSMedia.self).filter("promotions.#count <= 5")
What's wrong here? I have followed realm swift doc and used the #count function same as described in that doc.

I am pretty sure #count does not work on Lists of primitives. Realm didn't used to support lists of primitives at all, it now does but there is some missing functionality.
EDIT: Release 10.7 added support for filters/queries as well as aggregate functions on primitives so the below info is no longer completely valid. However, it's still something to be aware of.
Change your promotions to be a list of other realm objects
class PromotionClass: Object {
#objc dynamic var promotion = ""
}
and then
class PSMedia: Object {
#objc dynamic var id = ""
#objc dynamic var promotional_status = false
let promotions = List<PromotionClass>()
}
then this will work
realm.objects(PSMedia.self).filter("promotions.#count <= 5")
EDIT
Yep, that's correct. It's not supported. Here's the Github link for that issue
Swift docs should make it clear that filtering by someListOfPrimitives.#count isn't supported #6079

Related

How to write MongoDB Realm Schema?

I'm trying to enable Sync with MongoDB Realm on my iOS app. The app already uses Realm DB, but I'm having trouble creating the schemas for my classes due to some less primitive variables. How would you write the following classes as schema (I'm mostly having trouble with List<Assignment> and the LinkingObjects variables)?
class Course: Object{
#objc dynamic var _id: ObjectId = ObjectId.generate()
#objc dynamic var _partitionKey: String = ""
#objc dynamic var name: String = ""
var days = List<String>()
let assignments = List<Assignment>()
}
class Assignment: Object{
#objc dynamic var _id: ObjectId = ObjectId.generate()
#objc dynamic var _partitionKey: String = ""
#objc dynamic var name: String = ""
var parentCourse = LinkingObjects(fromType: Course.self, property: "assignments")
}
With Realm, you don't need to 'write schemas' - it's done for you based on the properties of your classes.
For example, when an Realm object is defined in your app, when the app first Sync's to MongoDB Realm Cloud the objects schema is auto-generated. So for example let say I have an app that tracks people and their dogs. If I create the following Object in my app
class Person: Object {
#objc dynamic var _id = ObjectId.generate()
#objc dynamic var _partitionKey = ""
#objc dynamic var name = ""
}
and run the app, as soon as the app 'speaks' to MongoDB Realm, an object will be created on the server and in your local file with schema based on those Person properties. Nothing more is required or even needed
On the topic of primitives, Realm Collections do not support primitives (very well).
EDIT: Release 10.7 added support for filters/queries as well as aggregate functions on primitives so the below info is no longer completely valid. However, it's still something to be aware of.
So they should be avoided. If you want to store objects in Lists, create a Realm object with the primitive as the property.
In your question you have
var days = List<String>()
this should be changed to
class DayClass: Object {
#objc dynamic var date = ""
}
let days = List<DayClass>()
Realm does not support queries on a List of primitives and also doesn't support aggregative function on primitives (yet)
What that means is by storing another object within the List, you can use functions like .sum, .min, .max, .count etc. on those objects.
You code is very close and LinkingObjects are perfect. So just a slight change for the List and everything should work as is.

Retrieving object properties with relation to their parent categories

I am new to realm and iOS development so I apologize in advance if something isn’t explained properly or is just incorrect.
I have 2 Realm Object classes:
class Category: Object {
#objc dynamic var name: String = ""
#objc dynamic var color: String = ""
let trackers = List<Tracker>()
}
and
class Tracker: Object {
#objc dynamic var timeSegment: Int = 0
var parentCategory = LinkingObjects(fromType: Category.self, property:
"trackers")
}
I’m able to store new timeSegment properties consistently; however, the issue is that I cannot retrieve & display a collection of timeSegment values relating to their parentCategory. setting
var entries : Results<Tracker>?
produces all results for every category, which is the only result i'm able to pull so far after testing.
Any help is appreciated, and can follow up with any additional details. Thanks
You need to call objects on your Realm object with a filter for fetching only results that match a predicate. The realm object in this code is an instance of the Realm class.
func getTrackersWithName(_ name: String) -> Results<Tracker> {
return realm.objects(Tracker.self).filter("name = \"\(name)\"")
}
This tells Realm to fetch all objects that match the filter predicate. In this case, the filter predicate matches any object where the value of the "name" property matches the string that is passed into the method.

Realm is creating multiple entries for nested objects on update

I'm experiencing trouble understanding how updating objects works in Realm. I'd appreciate help in helping me to understand how updating nested objects work and why it doesn't work the way I expect it.
I started using Realm just recently, and here's what I want to use it for: I have a set of key value pairs stored on my server, that serve as localized values for strings used in my iOS app. On app launch every now and then I want to update my strings, so I pull them from the server and store them locally in realm on my iOS device. I want to have only ONE instance of those strings on my device.
Here are the classes:
import RealmSwift
public class LocalizedStrings: Object {
dynamic var id = 1
dynamic var version: String = ""
let assets = List<LocalizedString>()
override public static func primaryKey() -> String? {
return "id"
}
}
public class LocalizedString: Object {
dynamic var key: String = ""
dynamic var value: String = ""
}
Here's how I update the LocalizedStrings object:
realm.add(localizedStrings, update: true)
Here's how I access my strings:
func getLocalizedString(forKey key: String) -> String {
var result = key
try! realm.write {
let queryResult = realm.objects(LocalizedString.self).filter("key == %#", key)
// print(queryResult)
if queryResult.count == 1 {
result = queryResult[0].value(forKey: "value") as! String
}
}
return result
}
Now, I would expect, that whenever I update my LocalizedStrings, that the localizedStrings.assets list would get updated with new values. But instead, the assets are not updated, the list reference gets updated and I end up having multiple instances of the same string, which is not what I would expect from an update function. When I try to access a particular LocalizedString, it turns out there's multiple instances:
(...)
[19] LocalizedString {
key = update;
value = Update;
},
[20] LocalizedString {
key = update;
value = Update;
}
Perhaps I'm missing something obvious and I would really appreciate if someone could point me in the right direction, so I'd be able to achieve the behavior I'm looking for (which would be having the nested object actually updated, rather than having unnecessary duplicates of my objects).
Thanks!
Ok, so this answer helped me figure out, what was wrong with my setup. I was missing primaryKey in LocalizedString class.
From the answer above on how realm.add(object, update: true) works:
Documentation :
parameter object: The object to be added to this Realm.
parameter update: If true, the Realm will try to find an existing copy of the object (with the same primary
key), and update it. Otherwise, the object will be added.
So the same thing happens with nested objects. They can not be updated unless they have primaryKey.

Computed properties using Realm LinkedObject instances return nil

I'm experiencing slightly unusual behaviour when attempting to use computed properties to access linked Objects in a Realm Object subclass.
final class Patient: Object {
dynamic var name: String = ""
var parameters = List<Parameter>()
}
final class Parameter: Object {
dynamic var name: String = ""
dynamic var patient: Patient? {
return LinkingObjects(fromType: Patient.self, property: "parameters").first
}
}
The patient property on the Parameter class returns nil but, if you replace the code with the following, we get the expected behaviour:
var p = LinkingObjects(fromType: Patient.self, property: "parameters")
var q: Patient? {
return p.first
}
I suspect this is something to do with Realm's internal representation of LinkingObject. The code I used originally was referenced in a previous StackOverflow question and was accepted as a functional solution thus I guess it worked then so perhaps something has changed? Xcode 7, Swift 2.2
When Realm added the ability to query inverse relationships, that became the syntax to specify them. See https://realm.io/news/realm-objc-swift-0.100.0/ and https://realm.io/docs/swift/latest/#inverse-relationships for details.

Optional Int property contains nil instead of zero during migration

I am trying to perform Realm.IO migration from the following model:
public class RealmBuddy: Object {
dynamic var id = 0
let contactId = RealmOptional<Int>()
dynamic var name = ""
let topics = List<RealmTopic>()
override static func primaryKey() -> String? {
return "id"
}
}
to the following:
public class RealmBuddy: Object {
dynamic var id = 0
dynamic var contactId: String? = nil
dynamic var name = ""
let topics = List<RealmTopic>()
override public static func primaryKey() -> String? {
return "id"
}
}
As you can see, the single change is in the type of contactId: optional Int should be converted to optional String. I've implemented it like this:
if (oldSchemaVersion < 1) {
migration.enumerate(RealmBuddy.className()) { oldObject, newObject in
if let contactId = oldObject!["contactId"] as? Int {
newObject!["contactId"] = "\(contactId)"
}
}
}
However, I found that after migration my entities which had no contactId (i.e. nil value) now have contactId equal to "0" string. It appears that oldObject!["contactId"] returns 0 instead of nil.
I haven't find any info or examples on how to perform this simple task so I am not sure if I retrieve/cast the value properly (tried to cast to RealmOptional<Int> also), so I decided to ask about it here before filing an issue on github. Is it a bug or expected behavior? If it is expected behavior, how to get value properly?
PS: My project is open-source because I am new to iOS and develop it for self-education, so I pushed it to a repo where the issue can be easily reproduced (please note that it is in migration_issue branch). I also made a unit test using existing .realm file which migrates to new schema during test. Hope that will help.
PPS: I tried 0.102.1 and 0.103.1 versions of the library.
PPPS: It was mentioned that Realm Browser doesn't display any contactId with nil value in my sample .realm file. I think it is just Realm Browser issue, so I made another unit test in separate branch to prove that old database has nil value.
When I open default-v0.realm in the Realm Browser I don't see any rows with a null contactId. If you're expecting there to be one then the problem is in the code that initially populated the Realm and not the migration code.
It was a bug. See https://github.com/realm/realm-cocoa/pull/3643. Fixed in 0.103.2.
Optional properties of scalar types were getting the wrong Objective-C
type code, leading to the dynamic accessors taking the non-optional
code path. This resulted in nil values being returned as 0.

Resources