Search code examples
iosswiftrealmrealm-mobile-platform

Saving different categories with Realm, Swift


I'm doing a finance app. I'm saving data with realm, but i can't understand one thing. After the user entered, how much money he spent, he can choose a category: Supermarket, Transport and others. So the question is how can i save user's data by categories. RealmModel:

class Items: Object {
    @objc dynamic var personID = UUID().uuidString
    @objc dynamic var categoryAmount = ""
    @objc dynamic var categoryName = ""

    override static func primaryKey() -> String? {
        return "personID"
    }  
}
class RealmModel {
    static let shared = RealmModel()
    private let realm = try! Realm()
    let myPrimaryKey = "Primary-Key"

    func addAmount(newItem: String) {
        let categoryAmount = realm.object(ofType: Items.self, forPrimaryKey: myPrimaryKey)
        let new = Items()
        new.personID = "My-Primary-Key"
        new.categoryAmount = newItem
    
        try! realm.write {
            realm.add(new, update: .modified)
        }
    }
    
    func updateAmount(editedItem: Items, newItem: String) {
        try! realm.write {
            editedItem.categoryAmount = newItem
        }
    }
    
    func getAmount() -> Items? {
            let amount = realm.objects(Items.self).last
            return amount
        }
    
    func setAmount(newItem: String) {
        if let data = getAmount() {
            updateAmount(editedItem: data, newItem: newItem)
        } else {
            addAmount(newItem: newItem)
        }
    }
}

Button:

@IBAction func supermarketBTN(_ sender: Any) {
    RealmModel.shared.setAmount(newItem: "\((Double(exileAmount?.categoryAmount ?? "" ) ?? 0) + (Double(self.amountTF.text ?? "") ?? 0))")
    self.exileAmount = RealmModel.shared.getAmount()
    delegate?.updateAmount(amount: self.amountTF.text ?? "")
    dismiss(animated: true, completion: nil)
    
    }

How should I use categoryName to save data by categories?


Solution

  • There are 100 different way to approach this. Let me present one possibility at a high level.

    The first issue is to change how your objects are structured. You need two; one to store the user and the other to store their category choice and amount spent

    class UserClass: Object {
       @objc dynamic var userId = "" //however you want to define id's
       @objc dynamic var userName = ""
    }
    

    and then

    class SpentMoneyClass: Object {
       @objc dynamic var _id = UUID().uuidString
       @objc dynamic var category = ""
       @objc dynamic var byUserId = ""
       @objc dynamic var amount = 0.0
    }
    

    Your database will contain users and each user object has a userId. As the user spends money, create a new SpentMoneyClass and fill in the properties - say User Artim spends money on Travel

    let spend = SpentMoneyClass()
    spend.category = "Travel"
    spend.byUserId = artimUser.userId
    spend.amount = 100.00
    

    write the object to realm

    This structure enables you to query for all money spent by Artim, all Travel money spent by all users and a more granular query for all travel money spent by Artim or specfic amounts spent - you can leverage the aggregate function .sum to get totals really easy:

    let travelTotal: Double = realm.object(SpentMoneyClass.self)
                                   .filter("byUserId == x and category == 'Travel'")
                                   .sum(ofProperty: "amount")
    

    gives the total spent by Artim on Travel (x = Artims user id)

    *note I left out primary keys and _id properties required by MongoDB Realm to keep the length short