I am a beginner on programming and especially on iOS development
currently I am trying to use realm as the local database in an ecommerce app. I have Product and WishList object of Realm like below
The product:
class Product : Object {
#objc dynamic var productID : Int = 0
#objc dynamic var name : String = ""
#objc dynamic var categoryID : Int = 0
#objc dynamic var categoryName : String = ""
#objc dynamic var unitPrice: Double = 0.0
#objc dynamic var quantityInCart = 0
#objc dynamic var quantityFromServer = 0
#objc dynamic var descriptionProduct : String = ""
#objc dynamic var hasBeenAddedToWishList : Bool = false
#objc dynamic var hasBeenAddedToCart : Bool = false
#objc dynamic var isNewProduct : Bool = false
#objc dynamic var productWeight : String = ""
#objc dynamic var weightUnit : String = ""
#objc dynamic var minimumOrderQuantity = 0
#objc dynamic var maximumOrderQuantity = 0
override static func primaryKey() -> String? {
return "productID"
}
}
the Wishlist:
class WishList : Object {
var products = List<Product>()
}
my app can change the user. so let say if User A put some products on the wishlist (populates the WishList object with the product), after that he performs Log Out,
then it will make the User B's wishlist object will already have object after User B login.
so I think I need to insert userID both on Product and Wishlist object, and I have to also remove the primary key on Product object.
override static func primaryKey() -> String? {
return "productID"
}
so when I perform query or filter from realm database I can filter based on productID and also userID.
do I have the correct approach using way like that ? or is there a better approach? because to I am not comfortable having userID as the property of Product object like code below:
class Product : Object {
#objc dynamic var userID : Int = 0 <--- like this
#objc dynamic var productID : Int = 0
#objc dynamic var name : String = ""
#objc dynamic var categoryID : Int = 0
#objc dynamic var categoryName : String = ""
#objc dynamic var unitPrice: Double = 0.0
#objc dynamic var quantityInCart = 0
#objc dynamic var quantityFromServer = 0
#objc dynamic var descriptionProduct : String = ""
}
class WishList : Object {
#objc dynamic var userID : Int = 0 <--- and like this
var products = List<Product>()
}
and with this approach, I will also have 'duplicate' Product data on Product object with the same productID, but the userID on the property of the Product is different
You shouldn't add userID to either the Product or the Wishlist. A Product should be independent of specific users, so it should have no relationship to a user. On the other hand, a Wishlist is specific to a user, but instead of manually having to query the userID property of a specific Wishlist, you should take advantage of Realm's built-in relationships and declare a one-to-one relationship between a User and a Wishlist (you can either declare the relationship on WishList or on User, both work just fine).
So your Product definition should be left unchanged with its primaryKey being intact as well. Your Wishlist should look like the following:
class WishList : Object {
#objc dynamic var user: User?
let products = List<Product>()
}
Or if you want to access the WishList of a specific User from the user, you can modify your User like so:
class User: Object {
#objc dynamic var wishList: WishList?
...
}
Related
I am using IceCream Library and want to achieve the following functionality.
In app delegate, I have written
syncEngine = SyncEngine(objects: [
SyncObject(type: Recipient.self, uListElementType: String.self),
SyncObject(type: SMSSchedule.self, uListElementType: Recipient.self),
SyncObject(type: Template.self),
SyncObject(type: ContactsGroup.self, uListElementType: Recipient.self)
])
where Recipient class is...
#objc class Recipient: Object {
#objc dynamic var rec_id = ""
#objc dynamic var firstName = ""
#objc dynamic var lastName = ""
var phoneNumbers = List<String>()
#objc dynamic var email = ""
#objc dynamic var colorTag = "#FFFFFFFF"
#objc dynamic var isDeleted = false // IceCream requirement
override static func primaryKey() -> String? {
return "rec_id"
}
// initialization code
}
Look at the phoneNumbers property. It is a list of string objects. However the SyncObject doesn't accept String as uListElementType. How can I solve this issue?
Can I use same model class for storing array data of multiple type array of dictionary with same keys?
Let's say for example I have a model class named ProductDetail used to store the product detail with key id, name and image and showing them in UITableViewController.
Now I have a different class named categories with same above mentioned keys.
Here is my model class:
class TrendingProductsData: NSObject {
var id : Int! = 0
var name : String! = ""
var image : String! = ""
}
My question is can I use the ProductDetail model to store categories data as well?
How about using a super model for common properties and extending the ones you have. Here is what I mean:
class BaseModel: NSObject {
var id : Int = 0
var name : String = ""
var image : String = ""
func setData(data: Any) {
// Parse id, name and image from data
}
}
class ProductDetail: BaseModel {
// Add your other properties and/or functions
var productProvider: String = "" // I added this to be an example
override func setData(data: Any) {
super.setData(data: data) // Since the key-value pairs are the same id, name and image will be parsed at BaseModel
// Parse extra values such as productProvider
}
}
class Categories: BaseModel {
// Add your other properties and/or functions
var categorySubtitle: String = "" // I added this to be an example
override func setData(data: Any) {
super.setData(data: data) // Since the key-value pairs are the same id, name and image will be parsed at BaseModel
// Parse extra values such as categorySubtitle
}
}
This way you can create both ProductDetail and Categories models with common properties and if the need occurs you can add separate properties and functions.
If you want to more complex structure like subCategory etc. it could be in a better way. But I think basically these classes are what you want.
Product can have detail only if isCategory is false.
class Product {
var id : Int = 0
var name : String = ""
var image : String = ""
var isCategory: Bool = false
var productDetail: ProductDetail? = nil
}
class ProductDetail {
var description : String = ""
var price : Decimal?
}
I have wishlistVC that has collection view like the picture below:
I have product realm model like this:
class Product : Object {
#objc dynamic var productID : String = ""
#objc dynamic var name : String = ""
#objc dynamic var unitPrice: Double = 0.0
#objc dynamic var quantity = 0
#objc dynamic var descriptionProduct : String = ""
#objc dynamic var hasBeenAddedToWishList : Bool = false
#objc dynamic var hasBeenAddedToCart : Bool = false
#objc dynamic var isNewProduct : Bool = false
var imagePaths = List<String>()
}
and WishList realm model like this:
class WishList : Object {
#objc dynamic var userID: String = ""
var products = List<Product>() // many to many relationship
static func getWishListFromRealmDatabase() -> WishList {
let userID = "1"
let allWishList = RealmService.shared.realm.objects(WishList.self)
let theWishList = allWishList.filter("userID CONTAINS[cd] %#", userID).first
if let userWishList = theWishList {
return userWishList
} else {
// WishList never setted up before in Realm database container
// then create WishList in realm database
let newWishList = WishList()
newWishList.userID = userID
newWishList.products = List<Product>()
RealmService.shared.save(object: newWishList)
return newWishList
}
}
static func addProductToWishListRealmDatabase(userWishList: WishList, selectedProduct: Product) {
// to check wheter the selected product from user is already in WishList or not
if userWishList.products.filter("productID == %#", selectedProduct.productID).first == nil {
RealmService.shared.save(expression: {
selectedProduct.hasBeenAddedToWishList = true
userWishList.products.append(selectedProduct)
})
}
}
}
when the user tap that love button, here is the code used to add product to WishList:
func addProductToWishListRealmDatabase(userWishList: WishList, selectedProduct: Product) {
// to check wheter the selected product from user is already in WishList.products or not
if userWishList.products.filter("productID == %#", selectedProduct.productID).first == nil {
// write in realm database
RealmService.shared.save(expression: { // <-- this is just a wrapper to avoid write do try catch block all over the place
selectedProduct.hasBeenAddedToWishList = true
userWishList.products.append(selectedProduct)
})
}
}
and here is the full simplified code of my WishListVC:
class WishListVC : UIViewController {
#IBOutlet weak var wishListCollectionView: UICollectionView!
private var userWishList : WishList?
private var products = List<Product>()
private var selectedProduct : Product?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
userWishList = WishList.getWishListFromRealmDatabase() // the definition is in the code above
guard let userWishList = userWishList else {return}
products = userWishList.products
}
}
extension WishListVC : ListProductCellDelegate {
func likeButtonDidTapped(at selectedIndexPath: IndexPath, productHasBeenLiked: Bool, collectionView: UICollectionView) {
guard let userWishList = userWishList else {return}
let selectedProduct = products[selectedIndexPath.item]
if productHasBeenLiked {
WishList.removeProductFromWishListRealmDatabase(userWishList: userWishList, selectedProduct: selectedProduct)
} else {
WishList.addProductToWishListRealmDatabase(userWishList: userWishList, selectedProduct: selectedProduct)
}
wishListCollectionView.reloadData()
self.wishListCollectionView.isHidden = userWishList.products.isEmpty
}
}
if I append a product to the wishlist model like the code above, it will affect to the Product.self in realm database, it will keep adding the product in 'Product Realm Database', as you can see in the image below, there are 9 data in Product, but as you can see some of the product have the same productID, there are 3 products that have 'a' as the productID .
so how to avoid adding the product that has the same productID in 'Product Realm Database' (Product.self) when I modify the WishList by appending the product to wishlist.products ?
I have also tried to add primary key using:
override static func primaryKey() -> String? {
return "productID"
}
but It makes crash with message:
*** Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to create an object of type 'Product' with an existing
primary key value 'a'.'
it will throw error because I add productID = 'a'
what should I do ? how to append product to the WishList model but I also can avoid adding the same product that has the same productID to the Product Realm database model (Product.self) ?
do I use the wrong approach to add the product to the wishlist?
I am stuck in a problem. Let's assume I have this Realm Model:
class Table: Object {
dynamic var id = 0
dynamic var x: Int = 0
dynamic var y: Int = 0
dynamic var width:Int = 0
dynamic var height: Int = 0
dynamic var text: String = ""
dynamic var color: String = ""
dynamic var type: String = ""
let food = List<Food>()
override static func primaryKey() -> String {
return "id"
}
}
class Food : Object {
dynamic var id: Int = 0
dynamic var name = ""
dynamic var ingredients: String = "" // bigger text field
dynamic var size: Int = 0 // none, small, medium, big size
dynamic var price: Float = 0.0
dynamic var category: Category?
let additionalIngredients = List<Ingredient>()
override static func primaryKey() -> String {
return "id"
}
}
Let's say I have one table and added 2 times the same food on that table like so :
try! realm.write(){
table.food.append(food) // A
table.food.append(food) // B
realm.add(table, update: true)
}
If I change the additionalIngredients for food A , also at the same food B changes its values. I am doing that changes with this transaction :
try! realm.write(){
table.food.first!.additionalIngredients.removeAll()
for ingredient in ingredientsToAdd{
table.food.first!.additionalIngredients.append(ingredient)
}
realm.add(table, update: true)
}
I guess I am doing something wrong regarding the reference/instance, can someone give me a hint?
Thanks in advance!
List.append() adds the object itself to the list and not a copy of the object, so you only have one Food object.
I've got an Article and a Category model linked by a many-to-one relationship. However, the Category model has a unique constraint on the id property because it's the primary key as you can see below.
class Article: Object
{
dynamic var id: String = ""
dynamic var title: String = ""
dynamic var category: Category()
override static func primaryKey() -> String? {
return "id"
}
}
class Category: Object
{
dynamic var id: String = ""
dynamic var title: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
This will work until an Article got the same Category and throw an exception because of the unique constraint.
How am I supposed to implement this kind of relationship ? Is there any built-in way to persist only the Category id and retrieve the corresponding Category ?
Thanks
As you can read in Realm doc (0.92.1), you have to use a List<Object> for a many-to-one relationship.
See this link :
http://realm.io/docs/swift/latest/
class Dog: Object {
dynamic var name = ""
dynamic var owner: Person? // Can be optional
}
class Person: Object {
... // other property declarations
let dogs = List<Dog>()
}
let someDogs = Realm().objects(Dog).filter("name contains 'Fido'")
jim.dogs.extend(someDogs)
jim.dogs.append(rex)
So in your case, I guess it should be something like that :
class Article: Object
{
dynamic var id: String = ""
dynamic var title: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
class Category: Object
{
dynamic var id: String = ""
dynamic var title: String = ""
dynamic var articles = List<Article>()
override static func primaryKey() -> String? {
return "id"
}
}
If your Realm version is older :
class Category: Object
{
...
dynamic var categories = RLMArray(objectClassName: Article.className())
}