Invalid value when query on Int in realm - swift

I have a realm declaration like that :
#objc dynamic var roomId = UUID().uuidString
#objc dynamic var roomName = ""
#objc dynamic var roomType = ""
#objc dynamic var floor = 1
#objc dynamic var placeId : String?
I am trying to get a query of all rooms for a specific floor in a specific place from realm DB with this function :
static func getAllRoomNamesAndTypesForQuery (placeName: String? ,room : String? , floor : Int?) -> [[String]] {
var result : [[String]] = [[]]
if placeName != nil , floor != nil {
let placeId = Places.specificPlaceQueries(placeName: placeName)[0] as! String
let allRooms = Users.realm.objects(Rooms.self).filter("placeId == '\(placeId)' AND floor == '\(floor!)'")
var roomNames = [""]
var roomTypes = [""]
for number in 0..<allRooms.count {
roomNames.append(allRooms[number].roomName)
roomTypes.append(allRooms[number].roomType)
}
let sortedRoomNames = roomNames.sorted()
let sortedRoomTypes = roomTypes.sorted()
result = [sortedRoomNames , sortedRoomTypes]
}
return result
}
but it keeps showing me the following strange error
Expected object of type int for property 'floor' on object of type
'Rooms', but received: 1
I dunno how it rejects 1 as Int ?? anyone know where is the problem here??

You shouldn't be using String interpolation when creating NSPredicates, since even though it is supported, it is really easy to mess up the predicate format. Simply use %# for substituting variable values into the predicate.
let allRooms = Users.realm.objects(Rooms.self).filter("placeId == %# AND floor == %#",placeId, floor)
Some further improvements to your code: don't use nil check, then force unwrapping, use optional binding when working with Optionals.
if let placeName = placeName , let floor = floor {
Also don't add an initial value to Arrays when creating them, instead of var roomNames = [""] and var roomTypes = [""], do
var roomNames = [String]()
var roomTypes = [String]()

Can you try
let allRooms = Users.realm.objects(Rooms.self).filter {
$0.placeId == placeId
&& $0.floor == floor
}

First :
set this
var roomNames = [""];
var roomTypes = [""];
to this
var roomNames = [String]()
var roomTypes = [String]()

Related

Filter Realm Results array returns elements that should be filtered

Any ideas why this filter is not working correctly ?
for item in activeItems {
print("item.product: \(item.product), \(item.spaceRequired)")
}
returns
item.product: nil, 40.0
Filtering where product is nil
let f1 = activeItems.filter{$0.product != nil}
print("f1: \(f1)")
print("f1.count: \(f1.count)")
returns a count of ZERO but the array still appears to contain an item
f1: LazyFilterSequence<Results<AssortmentItem>>(_base: Results<AssortmentItem> <0x109ce2c90> (
[0] AssortmentItem {
...
f1.count: 0
And then filtering and mapping just spaceRequired
let f11 = f1.filter{$0.product!.isProduct == true}.map({$0.spaceRequired})
print("f11: \(f11)")
returns the same array with a single item
f11: LazyMapSequence<LazyFilterSequence<Results<AssortmentItem>>, Double>(_base: Swift.LazyFilterSequence<RealmSwift.Results<Merchandise_Manager.AssortmentItem>>(_base: Results<AssortmentItem> <0x109ce2c90> (
[0] AssortmentItem {
And then trying to reduce crashes
let w = f11.reduce(0,+)
This seems to fix the problem
let width = Array(activeItems.filter{$0.product != nil}).filter{$0.product!.isProduct == true}.map({$0.spaceRequired}).reduce(0,+)
Is this a bug in Swift 5 or in Realm ?
EDIT: It looks like this is a bug in Realm's handling of things.
To be a bit cleared below is a more complete set of the Realm objects.
import Foundation
import RealmSwift
let activeDate: NSDate = Date() as NSDate
let defaultWidth: Double = 40.0
class MyObject: Object {
#objc dynamic var number: Int = 0
#objc dynamic var name: String?
let items = List<ChildObject>()
}
extension MyObject {
var activeItems: Results<ChildObject> {
let activeDate = activeDate // Some globally defined value
let active = items.filter("startDate <= %# && (endDate >= %# || endDate == nil)", activeDate, activeDate).sorted(byKeyPath: "number")
return active
}
/// Works Correctly
var totalWidth: Double {
let width = Array(activeItems.filter{$0.product != nil}).filter{$0.product!.isProduct == true}.map({$0.spaceRequired}).reduce(0,+)
let width2 = Array(activeItems.filter{$0.product == nil}.map({$0.spaceRequired})).reduce(0,+)
return width+width2
}
/// Crashes
var totalWidth: Double {
let width = activeItems.filter{$0.product != nil}.filter{$0.product!.isProduct == true}.map({$0.spaceRequired}).reduce(0,+)
let width2 = activeItems.filter{$0.product == nil}.map({$0.spaceRequired}).reduce(0,+)
return width+width2
}
}
class ChildObject: Object {
#objc dynamic var parent: MyObject?
#objc dynamic var number: Int = 0
#objc dynamic var product: Product?
#objc dynamic var name: String?
#objc dynamic var spaceRequired: Double = 40.0
#objc dynamic var startDate: NSDate?
#objc dynamic var endDate: NSDate?
}
extension ChildObject {
var spaceRequired: Double {
if let p = product {
return p.width
} else {
return defaultWidth
}
}
}
class Product: Object {
#objc dynamic var isProduct: Bool = false
#objc dynamic var width: Double = 30.0
}
There's a couple of issues at work here but the main problem is that Realm Results are live updating; while you can filter data using the Swifty
let f1 = activeItems.filter{$0.product != nil}
it's going to give intermittent results since Realm doesn't know which items are filtered or not as .filter { is not a Realm function and Realm won't know what to update within the results.
You should generally use the built in Realm filtering mechanism
let results = realm.objects(ItemClass.self).filter("product != nil")
Those results will be live updating - if an object leaves the filter parameter, the results follow that. If an object matches the filter the results are updated as well.
I believe this Github issue #2138 provides some more light on the issue.
If you absolutely need static data, then I would suggest extending the Results class to return an array; like this
extension Results {
func toArray() -> [Element] {
return compactMap { $0 }
}
}
Keeping in mind this will use more memory as Realm objects are lazy loaded and and array isn't.
EDIT:
There's some additonal information in the question so I crafted up a simple example trying to replicate the issue. There's a HouseClass object which contains a List of RoomClass objects and then the HouseClass is extended to return the total width of all of the rooms in its list.
class RoomClass: Object {
#objc dynamic var room_name = ""
#objc dynamic var width = 0
#objc dynamic var length = 0
#objc dynamic var belongs_to_house: HouseClass!
}
class HouseClass: Object {
#objc dynamic var house_id = NSUUID().uuidString
#objc dynamic var house_name = ""
let rooms = List<RoomClass>()
override static func primaryKey() -> String? {
return "house_id"
}
}
extension HouseClass {
var totalWidth: Int {
let width = Array(rooms).map {$0.width}.reduce(0,+)
return width
}
var anotherTotalWidth: Int {
let width = rooms.map {$0.width}.reduce(0,+)
return width
}
}
and then the code to get all of the houses and output their room widths based on two different functions (see the HouseClass extension)
let houseResults = realm.objects(HouseClass.self)
for house in houseResults {
let w0 = house.totalWidth
print(w0)
let w1 = house.anotherTotalWidth
print(w1)
}
I added 100 houses each with three rooms and ran the above code several times without crash.
Count of f1 is 0 so map is not worked.
You can optimize your width calculation as following
let width = activeItems
.filter { $0.product?.isProduct ?? false }
.map { $0.spaceRequired }
.reduce(0,+)

Get associated object value from filter query using Realm swift

So Im pretty new to realm and i feel my question is very basic but i cant find the answer to it.
Basically I'm trying to query Realm for all playerName associated with a specific TeamID (ie. TeamID is not the primary key), however Im having trouble finding the solution. I keep getting a Value of type 'Results<playerInfoTable>' has no member 'playerName' error.
Below is my Realm class:
`class playerInfoTable: Object {
#objc dynamic var playerID: Int = 0
#objc dynamic var playerName: String = ""
#objc dynamic var jerseyNum: Int = 0
#objc dynamic var TeamID: String = ""
#objc dynamic var goalCount: Int = 0
#objc dynamic var shotCount: Int = 0
override class func primaryKey() -> String {
return "playerID"
}
}`
And the Code I'm using the query Realm:
let mainPlayerFilter = NSPredicate(format: "teamID == %#", "1")
let mainPlayerStrings = realm.objects(playerInfoTable.self).filter(mainPlayerFilter)
let mainPlayerTeamName = mainPlayerStrings.playerName
Solution!
let mainPlayerFilter = NSPredicate(format: "TeamID == %#", String(homeTeam!))
let mainPlayerStrings = realm.objects(playerInfoTable.self).filter(mainPlayerFilter)
let mainPlayerTeamName = mainPlayerStrings.value(forKeyPath: "playerName") as! [String]
I suppose you get the error when you declare the mainPlayerTeamName constant. Try the following:
let mainPlayerTeamName = mainPlayerStrings.first?.playerName
And I noticed that in your playerInfoTable class you declare "teamID" as "TeamID", while in your predicate you refer to it as "teamID". Decide which one you want because NSPredicate is case sensitive by default.

How to change Realm singleton attribute value

I have a Realm object:
class TransactionDB: Object {
dynamic var transactionID : Int = -1
dynamic var registrationPlate : String = ""
dynamic var locationID : Int = 0
dynamic var time : String = ""
dynamic var subscription : String = ""
dynamic var startTime : NSDate = NSDate()
dynamic var endTime : NSDate = NSDate()
dynamic var status : Int = -2
dynamic var requestType : Int = -1
var extensions : List<ExtensionDB> = List<ExtensionDB>()
dynamic var price : Double = 0
dynamic var currency : String = ""
private dynamic var test : Int = 10
override static func primaryKey() -> String? {
return "transactionID"
}
class var sharedInstance : TransactionDB {
struct Singleton {
static let instance = TransactionDB()
}
return Singleton.instance
}
static func saveOrUpdate {
// ......
}
and a singleton version for it. So I have one object over many controllers when screens change.
A few days back I was using some older Objective-C version of Realm but now I changed to the Swift-only version 1.0.2 and I'm trying to fix all the problems.
So now it came to part that when I try to call stuff like:
TransactionDB.sharedInstance.time = ""
I get an exception. However, after I do the following, it works:
let realm = try! Realm()
try! realm.write {
TransactionDB.sharedInstance.time = ""
}
So am I creating the singleton wrong or is this just the way it has to be done? Because, for me, it is a little annoying that I would always have to use a try block when I want to change the value of some attribute.
Take a look at the first line of the Realm docs for the write section.
It states:
All changes to an object (addition, modification and deletion) must be done within a write transaction.
So yea, it's just how you have to do it.

How do I set NSUserDefault settings as Instance Variables?

When I try to just set a constant based on the settings like below, it results in Optional("value").
let accesstoken = NSUserDefaults.standardUserDefaults().stringForKey("accessToken")
let userId = NSUserDefaults.standardUserDefaults().stringForKey("userId")
If I do it like the below, I get an error saying variable used within its own initial value. I can't seem to win here. What am I doing wrong?
var accesstoken = String()
var userId = Int()
if let atString = NSUserDefaults.standardUserDefaults().stringForKey("accessToken") {
accesstoken = atString
}
if let userIdString = NSUserDefaults.standardUserDefaults().stringForKey("userId") {
userId = userIdString
}
You can achieve what you want with a read only computed property combined with the nil coalescing operator "??". Try like this:
var accessToken: String {
return NSUserDefaults().stringForKey("accessToken") ?? ""
}
var userId: String {
return NSUserDefaults().stringForKey("userId") ?? ""
}
or if you need an Int for your userID
var userId: Int {
return NSUserDefaults().integerForKey("userId")
}

Class to Struct causing reassigning of variable not to work [duplicate]

This question already has answers here:
Cannot assign property in method of struct
(2 answers)
Closed 7 years ago.
sorry to bother with something that is probably pretty easy.
Here is my code now the only difference is instead of 'struct' it was a 'class'. Now it's causing the changeAmount function to no longer work. I have tried declaring it as 'let' etc, but it shows other errors. If anyone has a fix that would be great.
struct Product {
var ID : String = ""
var name : String = ""
var type : String = ""
var price : Double = 0.00
var image : String = ""
var amount : Int = 1
var imageURL : String = ""
var thumbnailURL : String = ""
var description : String = ""
init(id:String, name:String, description:String, type:String, price:Double, image:String, imageURL:String, thumbnailURL:String, amount:Int = 1) {
self.ID = id
self.name = name
self.description = description
self.type = type
self.price = price
self.image = image
self.imageURL = imageURL
self.thumbnailURL = thumbnailURL
self.amount = amount
}
func changeAmount(a:Int){ self.amount = a }
}
If a function will change the contents of a struct, you need to declare it as mutating:
mutating func changeAmount(a:Int){
self.amount = a
}