CardDeath
CardDeath

Reputation: 132

One To Many Relationship setter

this is my first time working with Core Data in swift. I'm really enjoying it but it's also a challenge making sure my Appdelegate saves etc.

The Problem

Basically I am creating an budgeting app. Once a budget ends I need to take the current budget and store it away into a history entity. Now I have 2 different entities that work here:

NewBudgetCreateMO and HistoryBudgetHolderMO. What should happen is that the HistoryBudgetHolder should add a budget (newBudgetCreateMO) into it's One-To-Many relationship. Here is an image of my graph and their relationship.

The data model

Now if I've set this up right I should be allow to have as many NewBudgetCreateMOs in my History as I like by adding them? The code below is the generated code for my History entity which shows that it contains an NSSet

extension HistoryBudgetHolderMO {

@nonobjc public class func fetchRequest() -> NSFetchRequest<HistoryBudgetHolderMO> {
    return NSFetchRequest<HistoryBudgetHolderMO>(entityName: "HistoryBudgetHolder");
}

@NSManaged public var budgets: NSSet?

}

extension HistoryBudgetHolderMO {

@objc(addBudgetsObject:)
@NSManaged public func addToBudgets(_ value: NewBudgetCreateMO)

@objc(removeBudgetsObject:)
@NSManaged public func removeFromBudgets(_ value: NewBudgetCreateMO)

@objc(addBudgets:)
@NSManaged public func addToBudgets(_ values: NSSet)

@objc(removeBudgets:)
@NSManaged public func removeFromBudgets(_ values: NSSet)

}

So I assumed that I could just use "addToBudgets" to add a set piece of data and it does seem to work but for only one instance.

Where I'm doing the adding

So I do a fetch request on the HistoryBudgetHolderMO to see if I have any in the data base. If not then I create a new one from my App Delegate (Please NOTE: I have done the app delegate casting etc in a method above and then have passed the App Delegate and Context to this method)

private func SaveAndDeleteCurrentBudget(context : NSManagedObjectContext, appDele : AppDelegate){
    let fetchHistory : NSFetchRequest<HistoryBudgetHolderMO> = HistoryBudgetHolderMO.fetchRequest()

    //Saves the budget to the history budget. If we don't have oen we created one and add it to that
    do{
        let historyBudgets : [HistoryBudgetHolderMO] = try context.fetch(fetchHistory)
        if historyBudgets.count <= 0{
            let newHistoryBudget : HistoryBudgetHolderMO = HistoryBudgetHolderMO(context: context)
            newHistoryBudget.addToBudgets(budgetData.first!)
            print("entered new historyBudget")
        }else{
            historyBudgets.first!.addToBudgets(budgetData.first!)
        }
        appDele.saveContext()
    }catch{
        print("Error when looking for history fetch result")
    }

   //Deletes all budget data and budget entries that are currently used
    for object in budgetData{
        context.delete(object)
    }

    let fetchAllDataEntries = NSFetchRequest<NSFetchRequestResult>(entityName: "BudgetEntry")
    let deleteReq = NSBatchDeleteRequest(fetchRequest: fetchAllDataEntries)

    do{
        try context.execute(deleteReq)
    }catch{
        print("Error when deleting budget entries")
    }
    appDele.saveContext()
}
  1. I do the fetch request and check if a history entity is there. If not then I create a new one, add the budget entry and then save the context.
  2. If not then I grab the first instance of the history holder (as there should only ever be one as it's just a container) and I add the budget entry and then save.

Where it gets bad

So the first time I do this and it's in state 2 I get a value of Optional(1) which means it has stored one entry of the History. However any more additions after this keep saying it's Optional(1). I've tried looking up countless solutions, tried messing around with the extensions etc. I figured this would be a simple Get/Set operation but It's just not working.

Any help with this would be greatly appreciated.

Thanks for taking the time to read this.

Upvotes: 2

Views: 150

Answers (2)

christopher.online
christopher.online

Reputation: 2774

Your solution seems good now. I also would have suggested to get rid of the HistoryBudgetHolderMO class. May I suggest to add another field/property to the NewBudget class: a creationDate (Date type). That way you can always fetch the latest one (e.g. fetch all of them and sort by creationDate). You could als add an active/historic boolean property to mark Budgets as active/inactive. Another suggestion is try to avoid force unwrapping. Instead of writing

budgetData.first!.attributeName 

try to work with the 'if let' construct

if let budget = budgetData.first {
    budget.attributeName
}

Upvotes: 1

CardDeath
CardDeath

Reputation: 132

Solution For Anyone Interested

As I mentioned before I'm still learning Core Data and I'm grateful for KaraBenNensi for his comment to get me thinking.

Right so there was no need for a "holder" type object. Instead what I have done is I have used the last index of my budgets. So everytime I create a new budget I simply keep them all in the array. So instead of saying:

budgetData.first!.attributeName 

I now use

budgetData.last!.attributeName.

This means that my database will grow but it would have grown with the history holder anyway. Now when I want to display history I just fetch all the results from the budgetData core data model. When I want to display my actual budget I just use .last so I get the most recently created budget.

I hope this helps someone and I'm glad I could figure it out. If anyone needs help in the future just reply to this and I'll try to help (But I'm no expert!)

Upvotes: 0

Related Questions