how to update realm object based on the certain property in Realm? - ios

I have products in my Realm database like this
I want to update my realm database based on productID, so I don't need to add another product over and over again. let say I want to update quantity of product that has productID = "a" to be 5.
I have tried to write something like this.
let selectedProductID = "a"
let productsInRealmDatabase = realm.objects(Product.self)
let productIndex = productsInRealmDatabase.index(where: {$0.productID == selectedProductID})
if let productIndex = productIndex {
do {
try realm.write {
var productRealm = productsInRealmDatabase[productIndex]
productRealm.quantity = 5
productsInRealmDatabase[productIndex] = productRealm // ERROR HERE
}
} catch {
// error Handling
}
}
but I got error in : productsInRealmDatabase[productIndex] = productRealm
Error Message: Cannot assign through subscript: subscript is get-only
so how to update realm object based on the certain property in Realm?

You should use Realm's own filter method which accepts an NSPredicate and returns an auto-updating Results instance rather than Swift's filter when operating on Realm collections. Than either update the properties of the fetched prouduct or create a new one and save that to Realm.
let selectedProductID = "a"
let productsInRealmDatabase = realm.objects(Product.self)
let matchingProduct = productsInRealmDatabase.filter("productID == %#", selectedProductID).first
if let matchingProduct = matchingProduct {
do {
try realm.write {
matchingProduct.quantity = 5
}
} catch {
// error Handling
}
} else {
let newProduct = Product()
newProduct.productID = selectedProductID
newProduct.quantity = 5
do {
try realm.write {
realm.add(newProduct)
}
} catch {
// error Handling
}
}
If you want your Products to be unique based on their productID property, you use also set productID as the primaryKey of your Object subclass.
class Product:Object {
#objc dynamic var productID = ""
...
override static func primaryKey() -> String? {
return "productID"
}
}

Try this -
let selectedProductID = "a"
let productsInRealmDatabase = realm.objects(Product.self)
let filteredProducts = productsInRealmDatabase.filter("productID = \(selectedProductID)")
do {
try realm.write {
filteredProducts.forEach { product in
product.quantity = 5
}
}
} catch {
// error Handling
}

While inserting data to your database inside the insert function mark update key as true and then try updating the value. eg:
static func insertData() {
//Your insertion code//
try! realm.write {
realm.add(request, update: true)
}
}
static func updateData(productId: String, quantity: Int) {
let product = self.getProductData(prodId: productId)
let realm = try! Realm()
try! realm.write {
product?.quantity = quantity
}
}
Hope this helps you out.

Related

Can't pass variable value from firebase firestore to another class SWIFT

So I have this function in class Functions :
struct Prices {
var standardPrice: Int!
}
// FUNC PRICING
class Functions {
private var PricingRef: CollectionReference!
var price = Prices()
func getPrice() -> Prices {
PricingRef = Firestore.firestore().collection("ProductXYZ")
PricingRef.getDocuments { (snapshot, error) in
if let err = error {
debugPrint("Error fetching data \(err)")
}
else {
guard let snap = snapshot else { return }
for document in snap.documents {
let data = document.data()
let std = data["standard"] as! String
self.price.standardPrice = Int(std)!
print(self.price.standardPrice!) // This print the intended result
}
}
}
return price
}
}
Then I want to pass the standardPrice value to this class, called PriceList :
class PriceList: UITableViewController {
var price = Prices()
var newStandardPrice = 0
func Price() {
price = Functions().getPrice()
newStandardPrice = price.standardPrice // always error with value nil
}
I always have that error where newStandardPrice is nil.
but the print(self.price.standardPrice!) shows number of result I want.
So as far as I know, the problem here is because it takes time for the firebase firestore to get the data from database.
How do I get the value of standardPrice after its assigned with the new price from firebase database?
Any help will be appreciated
Thankyou
you need to use completion handler because its async function
func getPrice(completion:#escaping (Prices?,Error?)-> Void) {
PricingRef = Firestore.firestore().collection("ProductXYZ")
PricingRef.getDocuments { (snapshot, error) in
if let err = error {
debugPrint("Error fetching data \(err)")
completion(nil,err)
}
else {
guard let snap = snapshot else { return }
for document in snap.documents {
let data = document.data()
let std = data["standard"] as! String
self.price.standardPrice = Int(std)!
print(self.price.standardPrice!) // This print the intended result
completion(self.price.standardPrice,nil)
}
}
}
}
How to use
Functions().getPrice { (price, error) in
if let err = error {
// do something if you get error
} else if let getPrice = price {
// use price
self.price = getPriice
}

Realm List not saving data

I am fetching data from FireBase and save it inside my realm but it is not working as expected :
for doc in docs {
let shopData = doc.data()
let newShop = RLMShop()
newShop.shopName = shopData["name"] as? String ?? "Empty Name"
self.saveShop(shop: newShop) // My issue is here
}
My saveShop function :
func saveShop(shop: RLMShop) {
do {
try realm.write {
realm.add(shop)
}
} catch {
print("Error saving shop \(error)")
}
}
Calling save function is not saving my object.
The problem you have is that you are creating a RLMShop object but it is not linked to a RLMShopsCategory object, therefore your shopsList will not contain the new object.
// Fetch the RLMShopsCategory that you wish to add the RLMShop too
// Using Primary Key here just as an example
let shopsCategory = realm.object(ofType: RLMShopsCategory.self, forPrimaryKey: "YourKey")
for doc in docs {
let shopData = doc.data()
let newShop = RLMShop()
newShop.shopName = // setting the properties etc
// This is the part you are missing
// You need to append the newShop to your shopsCategory object
try! realm.write {
shopsCategory.shopsList.append(newShop)
}
}

How to avoid adding the same data model that has the same primary key in realm database?

I have one to many relationship between two models, Product and WishList like the code below
class Product : Object {
#objc dynamic var productID : String = ""
#objc dynamic var name : String = ""
#objc dynamic var unitPrice: Double = 0.0
#objc dynamic var imagePath : String = ""
#objc dynamic var quantity = 0
#objc dynamic var hasBeenAddedToWishList : Bool = false
var parentCategory = LinkingObjects(fromType: WishList.self, property: "products")
convenience init(productID : String, name: String, unitPrice: Double, imagePath: String, quantity: Int = 1, hasBeenAddedToWishList: Bool = false) {
self.init()
self.productID = productID
self.name = name
self.unitPrice = unitPrice
self.imagePath = imagePath
self.quantity = quantity
self.hasBeenAddedToWishList = hasBeenAddedToWishList
}
override static func primaryKey() -> String? {
return "productID"
}
}
and WishList:
class WishList : Object {
#objc dynamic var userID: String = ""
var products = List<Product>()
}
I try to add or remove product to WishList using the code below when love button in the image above is pressed :
// 1. get the wishlist based on UserID
let allWishList = realm.objects(WishList.self)
let theWishList = allWishList.filter("userID CONTAINS[cd] %#", userID).first
guard let userWishList = theWishList else {return}
// 2. modify Wishlist data in Realm.
if loveIconHasBeenFilled {
guard let index = userWishList.products.index(where: {$0.productID == selectedProduct.productID}) else {return}
do {
// remove data from realm database
try realm.write {
userWishList.products.remove(at: index)
}
} catch {
// error Handling
}
} else {
do {
// add product to wishlist model in realm database
try realm.write {
userWishList.products.append(selectedProduct)
}
} catch {
// error Handling
}
}
and here is the data in Realm Browser
and the problem is ....
when I run the app for the first time, I can add, and then remove, and then add the product again to the wishlist, and the number of product in the realm database still be the same (all have unique productID)
but when I restart the app, and try to click that love button to add the product to wishlist again, it throws an error
'RLMException', reason: 'Attempting to create an object of type
'Product' with an existing primary key value 'a'
this error is triggered because of this line of code userWishList.products.append(selectedProduct) , when adding the product to WishList, it automatically adds Product in the realm database. so because I keep adding the same product that has the same productID (primary key) it will throw that error.
so, my question is, how to avoid addition in Product if it has the same productID (primary key), it is better if i can just update the product in realm database when adding the product to the wishlist using this line of code: userWishList.products.append(selectedProduct)
You could check the property hasBeenAddedToWishList of the selected product and only add it if the property is false.
if loveIconHasBeenFilled {
//your logic to remove already added products
} else if !selectedProduct.hasBeenAddedToWishList { //<--- check if the product already exists in wishlist if not you add it
do {
// add product to wishlist model in realm database
try realm.write {
userWishList.products.append(selectedProduct)
}
} catch {
// error Handling
}
}

Removing Custom Objects from List in UserDefaults

I am trying to remove an element from a list which is stored in NSUserDefaults. The getAll function is implemented below:
func getAllOrders() -> [Order] {
var orders = [Order]()
if let userDefaults = UserDefaults(suiteName: "group.com.johndoe.SoupChef.Shared") {
if let ordersData = userDefaults.data(forKey: "Orders") {
orders = try! JSONDecoder().decode([Order].self, from: ordersData)
}
}
return orders
}
And here is the code for deleting the order.
func delete(order :Order) {
var persistedOrders = getAllOrders()
persistedOrders.removeAll { persistedOrder in
persistedOrder.identifier.uuidString == order.identifier.uuidString
}
}
After deleting the order in the code above when I call getAllOrders I still see all the elements, meaning I don't see the order being deleted.
That's because you don't save your changes. Once you've performed the removal you need to turn persistedOrders back into JSON and then:
userDefaults.set(json, forKey:"Orders")
You need to use jsonEncoder and encode the edited array then store it again the user defaults
func delete(order :Order) {
var persistedOrders = getAllOrders()
persistedOrders.removeAll { persistedOrder in
persistedOrder.identifier.uuidString == order.identifier.uuidString
}
do {
let data = try JSONEncoder().encode(persistedOrders)
userDefaults.set(data, forKey:"Orders")
}
catch {
print(error)
}
}
You have to store correctly your UserDefaults
UserDefaults.standard.set(json, forKey:"Orders")
Now, you can remove them using:
UserDefaults.standard.removeObject(forKey: "Orders")

Loop through Realm Object fields in Swift

I have a Realm Object
class CoursesModel: Object {
dynamic var courseName = ""
dynamic var par3Field = 0
dynamic var par4Field = 0
dynamic var par5Field = 0
}
When somebody enters the course name I want to check whether it already exists before writing it to Realm.
Can you please tell me what I'm doing wrong because it doesn't seem to loop through.
class func compareCourse(name : String) -> Bool {
let c = name
do
{
let realm = try Realm()
let course = realm.objects(CoursesModel)
for course in course {
if course == c {
print("course = \(course)")
print("c = \(c)")
return true
}
else {
return false
}
}
}
catch
{
// return nil
}
return false
}
Any help will be greatly appreciated.
EDIT - WORKING CODE HERE
class func compareCourse(name : String) -> Bool {
let c = name
do
{
let realm = try Realm()
let course = realm.objects(CoursesModel)
for course in course {
let a = course.courseName
print("Model Course = \(a)")
print("Passed Course = \(c)")
if a == c {
return true
}
}
}
catch
{
// return nil
}
return false
}
You are returning in both branches of the loop, which immediately exits out of the function. You do not want to return false on the first failure, but only after all have failed (I think).

Resources