0

Two ViewModels, one is a store of roles containing skills:

final class RolesStore: ObservableObject {
  @Published var roles: [Role] = []
.....

A role model is:

struct Role: Codable, Identifiable {
    let id: String
    var name: String
    var skills: [Skill]
}

The other ViewModel is a store of skills:

final class SkillStore: ObservableObject {
    @Published var skills: [Skill] = []
.....

The following shows what i want to do, remove a skill from SkillStore (handy), and also have the deleted skill automatically removed from any role that happen to have the skill in the RoleStore:

remove handy skill

As you can see, removing the skill handy doesn't remove it from the role Ansatt. I am not sure how to do that, so I prepared a Xcode Playground anyone can clone from Github: https://github.com/imyrvold/roleSkills

There must be a way of having SkillStore having a dependency on the skills in SkillStore or is there no other way than looping through all the roles and remove the skill from all the roles that have this skill?

Ivan C Myrvold
  • 680
  • 7
  • 23

1 Answers1

0

I found a solution to this, when I remembered that a class could subscribe to changes in another class with the magic of Combine. So if I could have RoleStore class subscribe to changes in SkillStore, then RoleStore could update and remove the skill in its roles when a user deletes a skill.

To do that, RolesStore needs to have a reference to SkillStore:

import SwiftUI
import PlaygroundSupport

var skillStore = SkillStore(skills: MySkills.skills())
var roleStore = RoleStore(roles: MyRoles.roles(), skillStore: skillStore)

struct ModelsView: View {
    @ObservedObject var skillStore: SkillStore
    @ObservedObject var roleStore: RoleStore
....

SkillStore is updated with a PassthroughSubject that will send the deleted skill (deletedPublisher):

import Foundation
import Combine

public final class SkillStore: ObservableObject {
    @Published public var skills: [Skill] = [] {
        didSet {
            let oldSkills = Set(oldValue)
            let uniqueSet = oldSkills.subtracting(self.skills)
            if let deletedSkill = uniqueSet.first {
                deletedPublisher.send(deletedSkill)
            }
        }
    }
    private var cancellables: Set<AnyCancellable> = []
    let deletedPublisher = PassthroughSubject<Skill, Never>()

    public init(skills: [Skill]) {
        self.skills = skills
    }

    public func delete(skills: [Skill]) {
        for skill in skills {
            if let index = self.skills.firstIndex(where: { $0 == skill }) {
                self.skills.remove(at: index)
            }
        }
    }
}

and finally RoleStore removes the deleted skill from all the roles that have this skill:

import Foundation
import Combine
import SwiftUI

public final class RoleStore: ObservableObject {
    @Published public var roles: [Role] = []
    @ObservedObject var skillStore: SkillStore
    private var cancellables: Set<AnyCancellable> = []

    public init(roles: [Role], skillStore: SkillStore) {
        self.roles = roles
        self.skillStore = skillStore
        self.skillStore.deletedPublisher.sink { skill in
            for roleIndex in self.roles.indices {
                let skills = self.roles[roleIndex].skills.filter { $0 != skill }
                self.roles[roleIndex].skills = skills
            }
        }
        .store(in: &cancellables)
    }
}

I have updated the Playground with the changes.

Ivan C Myrvold
  • 680
  • 7
  • 23