junction and join in Realm in swift - 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
}

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! :)

Filtering multidimensional array in uitableview - swift

Here is my model
class BusinessProfile: NSObject {
var title: String?
var website: String?
var associatedOrganization: String?
var companyName: String?
var productList: [BusinessProfileProduct]?
}
class BusinessProfileProduct: NSObject{
var productName: Double?
var modelNumber: String?
var hsCode: String?
}
Here are my array variables in view controller.
var businessProfileArray = [BusinessProfile]()
var tempBusinessProfileArray = [BusinessProfile]()
I already have filtered businessProfileArray on the basis of companyName like below:
tempBusinessProfileArray = businessProfileArray.filter({ (BusinessProfile) -> Bool in
return (BusinessProfile.companyName!.lowercased().contains(searchText.lowercased()))
})
But I am unable to filter businessProfileArray on the basis of productName or hsCode from nested array of BusinessProfileProduct.
Note: businessProfileArray contains array of businessProfileProduct
Any help from anyone? Thanks in advance.
You can do something similar to this
func filterArr(searchText:String) -> [BusinessProfile] {
var filteredArr = [BusinessProfile]()
for profile in businessProfileArray {
var isMatched = false
if let matchedProduct = profile.productList.filter ({$0.companyName.lowercased().contains(searchText.lowercased())}).first {
isMatched = true
print(matchedProduct.companyName)
}
if isMatched {
filteredArr.append(profile)
}
}
return filteredArr
}
this will return all the profiles in which there is a match of searchText with product's companyName however it will not remove the extra products which does not match the searchText

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

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?

Swift: The proper way to initialize model class with a lot of properties

How do you initialize your classes/structs with a lot of properties?
This question could probably be asked without Swift context but Swift brings a flavour to it, so I add Swift tag in headline and tags.
Let's say you have a User class with 20 properties. Most of them should not be nil or empty. Let's assume these properties are not interdependent. Let's assume that 33% of it should be constant (let) by the logic of the class. Let's assume that at least 65% of them do not have meaningful default values. How would you design this class and initialize an instance of it?
So far I have few thoughts but none of it seems to be completely satisfactory to me:
put all of the properties linearly in the class and make huge init method:
class User {
// there is 20 properties like that
let id : String
let username : String
let email : String
...
var lastLoginDate : Date
var lastPlayDate : Date
// then HUUUUGE init
init(id: String,
username: String,
...
lastPlayDate: Date) {
}
}
try to group properties into sub types and deal with smaller inits separately
class User {
struct ID {
let id : String
let username : String
let email : String
}
struct Activity {
var lastLoginDate : Date
var lastPlayDate : Date
}
let id : ID
...
var lastActivity : Activity
// then not so huge init
init(id: ID,
...
lastActivity: Activity) {
}
}
another solution is to break requirements a bit: either declare some of the properties optional and set values after init or declare dummy default values and set normal values after init, which conceptually seems to be the same
class User {
// there is 20 properties like that
let id : String
let username : String
let email : String
...
var lastLoginDate : Date?
var lastPlayDate : Date?
// then not so huge init
init(id: String,
username: String,
email: String) {
}
}
// In other code
var user = User(id: "1", username: "user", email: "user#example.com"
user.lastLoginDate = Date()
Is there a nice paradigm/pattern how to deal with such situations?
You can try the builder pattern.
Example
class DeathStarBuilder {
var x: Double?
var y: Double?
var z: Double?
typealias BuilderClosure = (DeathStarBuilder) -> ()
init(buildClosure: BuilderClosure) {
buildClosure(self)
}
}
struct DeathStar : CustomStringConvertible {
let x: Double
let y: Double
let z: Double
init?(builder: DeathStarBuilder) {
if let x = builder.x, let y = builder.y, let z = builder.z {
self.x = x
self.y = y
self.z = z
} else {
return nil
}
}
var description:String {
return "Death Star at (x:\(x) y:\(y) z:\(z))"
}
}
let empire = DeathStarBuilder { builder in
builder.x = 0.1
builder.y = 0.2
builder.z = 0.3
}
let deathStar = DeathStar(builder:empire)
Example taken from here: https://github.com/ochococo/Design-Patterns-In-Swift
If you are looking for a bit more Java like solution, you can try something like this.
Alternative Example
final class NutritionFacts {
private let servingSize: Int
private let servings: Int
private let calories: Int
private let fat: Int
private let sodium: Int
private let carbs: Int
init(builder: Builder) {
servingSize = builder.servingSize
servings = builder.servings
calories = builder.calories
fat = builder.fat
sodium = builder.sodium
carbs = builder.carbs
}
class Builder {
let servingSize: Int
let servings: Int
private(set) var calories = 0
private(set) var fat = 0
private(set) var carbs = 0
private(set) var sodium = 0
init(servingSize: Int, servings: Int) {
self.servingSize = servingSize
self.servings = servings
}
func calories(value: Int) -> Builder {
calories = value
return self
}
func fat(value: Int) -> Builder {
fat = value
return self
}
func carbs(value: Int) -> Builder {
carbs = value
return self
}
func sodium(value: Int) -> Builder {
sodium = value
return self
}
func build() -> NutritionFacts {
return NutritionFacts(builder: self)
}
}
}
let facts = NutritionFacts.Builder(servingSize: 10, servings: 1)
.calories(value: 20)
.carbs(value: 2)
.fat(value: 5)
.build()
Example taken from: http://ctarda.com/2017/09/elegant-swift-default-parameters-vs-the-builder-pattern