How to avoid "Key paths that include an array property must use aggregate operations" exception when querying a Realm? - swift

I have a Realm object database with the following structure:
class ItemModel: Object {
dynamic var id: Int = 0
dynamic var catId: Int = 0
dynamic var subId: Int = 0
dynamic var itemName: String = ""
dynamic var sortOrder: Int = 0
dynamic var favorite: Bool = false
override static func primaryKey() -> String {
return "id"
}
}
class SubCategoryModel: Object {
dynamic var id: Int = 0
dynamic var catId: Int = 0
dynamic var subCatName: String = ""
dynamic var desc: String = ""
dynamic var sortOrder: Int = 0
let haveUnit = List<ItemModel>()
override static func primaryKey() -> String {
return "id"
}
}
class CategoryModel: Object {
dynamic var id: Int = 0
dynamic var catName: String = ""
dynamic var sortOrder: Int = 0
override static func primaryKey() -> String {
return "id"
}
}
To query the database I'm using the following code:
var data = List<ItemModel>()
let index = indexPath.section
var query = "catId == \(inCategory)"
if (searchTextValue != "") {
query += " AND itemName CONTAINS[c] \(searchTextValue)"
}
if index == 0 {
let unitFavorite = DBManager.realm.objects(ItemModel.self).filter("\(query) AND favorite == true")
data += unitFavorite
} else {
var subquery = "catId == \(inCategory)"
if (searchTextValue != "") {
subquery += " AND ANY haveUnit.itemName CONTAINS[c] %#"
let subCat = DBManager.realm.objects(SubCategoryModel.self).filter(subquery, searchTextValue).sorted(byKeyPath: "sortOrder", ascending: true)
data += subCat[indexPath.section - 1].haveUnit
} else {
let subCat = DBManager.realm.objects(SubCategoryModel.self).filter(subquery).sorted(byKeyPath: "sortOrder", ascending: true)
data += subCat[indexPath.section - 1].haveUnit
}
}
When running this code I'm always seeing the following exception:
Terminating app due to uncaught exception 'Invalid predicate', reason: 'Key paths that include an array property must use aggregate operations'
Can you please help me understand why I'm seeing this exception and what I can do to avoid it?

Related

firebase firestore not accepting my write function from swift encoded json

this is my struct
struct CustomerDetail: Codable {
var customerID: String = ""
var firstname: String = ""
var lastname: String = ""
var age: Int = 0
var birthday: Int = 0
var country: String = ""
var pound: Bool = true
var feetboolean: Bool = true
var currentweight: Int = 0
var desiredweight: Int = 0
var sex: Bool = false
var feet: Int = 0
var inches: Int = 0
var cm: Float = 0.0
}
this is my write function
struct writetofirebase {
var customerdetails: CustomerDetail = CustomerDetail()
let db = Firestore.firestore()
var delegate: writefirebase?
func write(){
print(customerdetails)
do {
if let data = try? JSONEncoder().encode(customerdetails){
try db.collection("collection name").document("document name").setData(from: data)
delegate?.didSuccessfulWrite(true)
}else{
print("failed to encode")
}
} catch let error {
delegate?.didFailWithError(error)
}
}
}
this is my error
https://i.stack.imgur.com/cOpQK.png
this is my firebase structured data
https://i.stack.imgur.com/cOpQK.png
The error is saying that the data it is receiving inside the setData method is not compatible and therefore malformed for the request. You should be updating or setting the data appropriately by defining a custom dictionary or passing the object directly.
db.collection("collection name").document("document name").setData(data, merge: true)
Source: https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document

Nil data returned when copying "working" json data to new struc array

Weird. I swear this was working but then it just stopped working .. or ... Please ignore the i+i ,I will clean this up...
I don't have a clue why but myrecords?[i].title is returning nil. The json.releases[i].date_adde is working fine and full of data. I can "print" it and get a result. but when I go to copy it to the myrecords it is returning nil.
I download the data from JSON, that works fine. then I try to copy the data to a struc array I can get to in other parts of my app and now my myrecords data is empty. what the heck am I doing wrong?
import Foundation
var numberOfRecords : Int = 0
struct routine {
var dateadded : String
var title : String
var artist : String
var year : Int
var recordlabel : String
}
var myrecords: [routine]?
//-------------------------------------------------------------
struct Response: Codable {
var pagination: MyResult
var releases: [MyReleases]
}
public struct MyResult: Codable {
var page: Int
var per_page: Int
var items: Int
}
public struct MyReleases: Codable {
var date_added: String
var basic_information: BasicInformation
}
public struct BasicInformation: Codable {
var title: String
var year: Int
var artists : [Artist]
var labels: [Label]
}
public struct Artist: Codable {
var name: String
}
public struct Label: Codable {
var name: String
}
let url = "https://api.discogs.com/users/douglasbrown/collection/folders/0/releases?callback=&sort=added&sort_order=desc&per_page=1000"
public func getData(from url: String) {
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
guard let data = data, error == nil else {
print("something went wrong")
return
}
//HAVE DATA
var result: Response?
do {
result = try JSONDecoder().decode(Response.self, from: data)
}
catch {
print("Converion Error:\(error.localizedDescription)")
}
guard let json = result else {
return
}
numberOfRecords = json.pagination.items
var i: Int
i = -1
for _ in json.releases {
i = i + 1
myrecords?[i].dateadded = json.releases[i].date_added
myrecords?[i].title = json.releases[i].basic_information.title
myrecords?[i].artist = json.releases[i].basic_information.artists[0].name
myrecords?[i].year = json.releases[i].basic_information.year
myrecords?[i].recordlabel = json.releases[i].basic_information.labels[0].name
print(">>>>>>\(myrecords?[i].dateadded)")
}
})
task.resume()
}
You haven't initialized myrecords array.
Otherwise, you cannot use subscript like myrecords[i] when you don't know the capacity of array, it can be out of index.
First, initialize your array.
var myrecords: [routine]? = []
Second, append new element to array instead of using subscript
for _ in json.releases {
let newRecord = routine()
newRecord.dateadded = json.releases[i].date_added
newRecord.title = json.releases[i].basic_information.title
newRecord.artist = json.releases[i].basic_information.artists[0].name
newRecord.year = json.releases[i].basic_information.year
newRecord.recordlabel = json.releases[i].basic_information.labels[0].name
myrecords.append(newRecord)
}
This is the answer. :) THANK YOU All for pointing me in the right direction
struct Routine {
var dateadded : String
var title : String
var artist : String
var year : Int
var recordlabel : String
}
var myRecords: [Routine] = []
var i : Int
i = -1
for _ in json.releases {
var newRecord = Routine.self(dateadded: "", title: "", artist: "", year: 0, recordlabel: "")
i = i + 1
newRecord.dateadded = json.releases[i].date_added
newRecord.title = json.releases[i].basic_information.title
newRecord.artist = json.releases[i].basic_information.artists[0].name
newRecord.year = json.releases[i].basic_information.year
newRecord.recordlabel = json.releases[i].basic_information.labels[0].name
myRecords.append(newRecord)
}
print(">>>>\(myRecords[0].dateadded)")
I will clean up the bad code too but it works and that is good! :)

How to use variable outside the loop, after initializing inside the loop?

Want to code a function that will take an array of strings named straar and an integer named k. It will return k times longest strings merged together.
code:
import UIKit
func longestConsec( strarr: [String], k: Int) -> String {
var strerr : [String] = Array()
strerr = strarr
var longest : Int
longest = 0
var longestString : String
var finalBoss : String
finalBoss = ""
for v in 1...k{
for long in strerr{
if (long.count > longest){
longest = long.count
longestString = long
}
}
finalBoss += longestString
for lang in 0...strerr.count{
if (strerr[lang] == longestString){
strerr.remove(at:lang)
}
}
}
return finalBoss
}
there are some problem with the code. The var 'longestString' needs to be initialized before being used. And in the last for loop fails because the index is mayor than the count of items. Here's the code with some corrections.
func longestConsec( strarr: [String], k: Int) -> String {
var strerr = strarr
var longest : Int
longest = 0
var longestString : String = ""
var finalBoss : String
finalBoss = ""
for _ in 1...k {
for long in strerr {
if (long.count > longest) {
longest = long.count
longestString = long
}
}
finalBoss += longestString
for lang in 1 ..< strerr.count {
if (strerr[lang] == longestString) {
strerr.remove(at:lang)
}
}
}
print("finalBoss: \(finalBoss)")
return finalBoss
}
longestConsec(strarr: ["Eleven", "Two", "Three"], k: 3)

junction and join in Realm in swift

I have three models to save Contcacts and Categories. and a junction table to save Contacts in different Categories as CategoryContacts.
Category :
import UIKit
import Foundation
import RealmSwift
class Category: Object, IEntity
{
override class func primaryKey() -> String? {
return "CategoryId"
}
static func KeyName() -> String
{
return primaryKey()!
}
dynamic var CategoryId: Int64 = 0
dynamic var Name = ""
dynamic var AvatarName = ""
dynamic var CreationDateTime = ""
dynamic var LocalContactCount: Int32 = 0
dynamic var ServerContactCount: Int32 = 0
dynamic var UserId: Int64 = 0
}
Contacts as :
import UIKit
import Foundation
import RealmSwift
class CustomerClubContact: Object, IEntity
{
override class func primaryKey() -> String? {
return "CustomerClubContactId"
}
static func KeyName() -> String
{
return primaryKey()!
}
dynamic var CustomerClubContactId: Int64 = 0
dynamic var Prefix = ""
dynamic var FirstName = ""
dynamic var LastName = ""
dynamic var Mobile = ""
dynamic var BirthDay = ""
dynamic var AvatarName = ""
dynamic var UserId: Int64 = 0
dynamic var ErrorMessage = ""
dynamic var IsMembershipCanceled = false
dynamic var IsDeleted = false
}
and the junction table is :
import UIKit
import Foundation
import RealmSwift
class CategoryContacts: Object, IEntity
{
override class func primaryKey() -> String? {
return "Id"
}
static func KeyName() -> String
{
return primaryKey()!
}
dynamic var Id: Int64 = 0
dynamic var CategoryId: Int64 = 0
dynamic var CustomerClubContactId: Int64 = 0
}
Now I want to get all contacts which are present in a special category. I don't know if it is possible in realm db or not? How Can I do it in realm db in swift
I found the solution.
For those who encounter in the future
import UIKit
import Foundation
import RealmSwift
class CustomerClubContact: Object, IEntity
{
override class func primaryKey() -> String? {
return "CustomerClubContactId"
}
static func KeyName() -> String
{
return primaryKey()!
}
dynamic var CustomerClubContactId: Int64 = 0
dynamic var Prefix = ""
dynamic var FirstName = ""
dynamic var LastName = ""
dynamic var Mobile = ""
dynamic var BirthDay = ""
dynamic var AvatarName = ""
dynamic var UserId: Int64 = 0
dynamic var ErrorMessage = ""
dynamic var IsMembershipCanceled = false
dynamic var IsDeleted = false
let CategoryContactList = List<CategoryContacts>()
}
import UIKit
import Foundation
import RealmSwift
class Category: Object, IEntity
{
override class func primaryKey() -> String? {
return "CategoryId"
}
static func KeyName() -> String
{
return primaryKey()!
}
dynamic var CategoryId: Int64 = 0
dynamic var Name = ""
dynamic var AvatarName = ""
dynamic var CreationDateTime = ""
dynamic var LocalContactCount: Int32 = 0
dynamic var ServerContactCount: Int32 = 0
dynamic var UserId: Int64 = 0
let CategoryContactList = List<CategoryContacts>()
}
import UIKit
import Foundation
import RealmSwift
class CategoryContacts: Object, IEntity
{
override class func primaryKey() -> String? {
return "Id"
}
static func KeyName() -> String
{
return primaryKey()!
}
dynamic var Id: Int64 = 0
dynamic var CategoryId: Int64 = 0
dynamic var CustomerClubContactId: Int64 = 0
}
then to insert
let c1 = Category()
c1.CategoryId = 1
c1.Name = "category 1"
categoryBiz.insert(item: c1)
let c2 = Category()
c2.CategoryId = 2
c2.Name = "category 2"
categoryBiz.insert(item: c2)
let cat1 = CategoryContacts()
cat1.Id = 100
cat1.CategoryId = 1
cat1.CustomerClubContactId = 10
//categoryContactBiz.insert(item: cat1)
let cat2 = CategoryContacts()
cat2.Id = 101
cat2.CategoryId = 2
cat2.CustomerClubContactId = 11
//categoryContactBiz.insert(item: cat1)
let con1 = CustomerClubContact()
con1.CustomerClubContactId = 10
con1.FirstName = "f1"
con1.LastName = "l2"
con1.CategoryContactList.append(cat1)
contactBiz.insert(item: con1)
let con2 = CustomerClubContact()
con2.CustomerClubContactId = 11
con2.FirstName = "f2"
con2.LastName = "l2"
con2.CategoryContactList.append(cat2)
contactBiz.insert(item: con2)
and to fetch Data with join i did
func FetchAllEligibleWithCategoryId(categoryId: Int64)-> Results<RealmEntityType>?
{
do
{
let object = realm.objects(CustomerClubContact.self).filter(" any CategoryContactList.CategoryId == \(categoryId) AND IsDeleted = \(false) And IsMembershipCanceled = \(false) ")
return object
}
catch
{
print(error.localizedDescription)
}
return nil
}

Realm/Swift: compilation error: Missing argument for parameter 'forPrimaryKey' in call

I'm trying to get my head around swift & realm, so I've created some kind of a test pad programme.
My model is defined like so
class RealmRecord: Object {
// properties
dynamic var id: Int = 0;
dynamic var text: String = ""
dynamic var var1: Double = 0.0
dynamic var var2: Int = 0
dynamic var var3: Double = 0.0
dynamic var var4: Int = 0
dynamic var cdate: Date = Date()
dynamic var cusr: String = ""
dynamic var mdate: Date = Date.distantPast
dynamic var musr: String = ""
dynamic var mcnt: Int = 0
// methods
convenience init(id: Int? = 0, text: String? = "", var1: Double? = 0.0,
var2: Int? = 0, var3: Double? = 0.0, var4: Int? = 0,
cusr: String? = "") {
self.init()
self.id = id!
self.text = text!
self.var1 = var1!
self.var2 = var2!
self.var3 = var3!
self.var4 = var4!
self.cdate = Date()
self.cusr = cusr!
self.mdate = Date.distantPast
self.musr = ""
self.mcnt = 0
} // init
override static func primaryKey() -> String? {
return "id"
} // primaryKey
} // RealmRecord
Persisting the data is accomplished by
try recRealm?.write {
recRealm?.add(self.rec, update: true)
} // try
But when adding the data retrieval via
if let inrec = self.recRealm?.object(RealmRecord.self) {
return inrec
} else {
return List<RealmRecord>()
} // if/else
I'm receiving an error message while compiling the code, reading
DataRealmRecord.swift:84:39: Missing argument for parameter 'forPrimaryKey' in call
Looking at the Realm documentation reveals only retrieving all persisted data--apparently without having a primary key defined--, or, alternatively, a single object, specified by the primary key.
Sifting through The Net brings up pretty much the same.
Given the model above, how can I retrieve all persisted data?
-- Sil68
EDIT
I've also defined a class facilitating this Realm model of mine, which basically carries out the following steps:
generate some random data;
persist data via the Realm model;
read data in again;
compare generated with read data.
The code
import Foundation
import RealmSwift
class DataRealmRecord {
// properties
private(set) var recDBPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
private(set) var recDBSubPath = "Persistency"
private(set) var recDBFile = "data.realm"
private(set) var recRealm: Realm?
private(set) var recRealmCfg: Realm.Configuration?
private(set) var rec = List<RealmRecord>()
private(set) var startTime = 0.0
private(set) var stopTime = 0.0
private(set) var runTime = 0.0
private(set) var outLog = ""
private(set) var realmOk = false
// methods
init() {
// assemble destination folder/database name
do {
try FileManager.default.createDirectory(atPath: recDBPath + "/" +
recDBSubPath,
withIntermediateDirectories: true,
attributes: nil)
recDBFile = recDBPath + "/" + recDBSubPath + "/" + recDBFile
realmOk = true
} catch let error as NSError {
outLog += error.localizedDescription
realmOk = false
} // do/try/catch
// configure realm database
if (realmOk) {
self.recRealmCfg = Realm.Configuration(fileURL: Foundation.URL(string: self.recDBFile))
do {
self.recRealm = try Realm(configuration: self.recRealmCfg!)
realmOk = true
} catch let error as NSError {
outLog += error.localizedDescription
realmOk = false
} // do/try/catch
} // if
} // init
// generate test data
func generateData(noRecs: Int? = 1000, simDat: SimulateData?) {
for i in 1...noRecs! {
let realmRec = RealmRecord(id: i,
text: String(format: "Record #%04d", i),
var1: simDat?.datnorm[i - 1] ?? 1.1,
var2: simDat?.datpois[i - 1] ?? 2,
var3: simDat?.datunif[i - 1] ?? 3.3,
var4: simDat?.datbern[i - 1] ?? 4,
cusr: "me")
self.rec.append(realmRec)
} // for
} // generateData
// retrieve test data from persistent storage
func loadData() -> List<RealmRecord> {
if let inrec = self.recRealm?.object(RealmRecord.self) {
return inrec
} else {
return List<RealmRecord>()
} // if/else
} // loadData
// save test data to persistent storage
func saveData() {
do {
try recRealm?.write {
recRealm?.add(self.rec, update: true)
} // try
} catch let error as NSError {
outLog += error.localizedDescription
} // do/try/catch
} // saveData
// compare two data sets
func compareData(rec1: List<RealmRecord>, rec2: List<RealmRecord>) -> Bool {
var rc = false
if rec1.count == rec2.count {
rc = true
for i in 0..<rec1.count {
rc = rc && (rec1[i] == rec2[i])
} // for
} // if
return rc
} // compareData
// run a full evaluation cycle
// (1) generate test data;
// (2) save test data to persistant storage;
// (3) retrieve test data from persistant storage;
// (4) compare generated data with retrieved data.
func fullCycle(noRecs: Int? = 1000, simDat: SimulateData?, prnData: Bool? = false) {
// start execution time measurement
self.startTime = Double(CFAbsoluteTimeGetCurrent())
// execute the full cycle
self.generateData(noRecs: noRecs, simDat: simDat) // (1)
self.saveData() // (2)
let rec2 = self.loadData() // (3)
let cmpRec = compareData(rec1: self.rec, rec2: rec2) // (4)
// stop execution time measurement & calculate elapsed time
self.stopTime = Double(CFAbsoluteTimeGetCurrent())
self.runTime = self.stopTime - self.startTime
} // fullCycle
} // DataRealmRecord
Issue at hand is, this code fails to compile due to the error message mentioned above (in method loadData()).
The Swift compiler is telling you that it thinks you're trying to call Realm.object(ofType:forPrimaryKey:), which retrieves a single object based on the value of its primary key. It sounds like you really want to call Realm.objects(_:) to retrieve all objects of a given type. Note that this returns a Results<T>, not a List<T>.