Home > Software design >  How can I use combine to merge arrays from different ObservableObjects?
How can I use combine to merge arrays from different ObservableObjects?

Time:07-23

In my app I have a task and a note object that both can contain an array of tags. The tasks and notes are fetched and stored in their respective stores. I pass these two classes down as environment objects. When a user is editing a task or a note, depending on the type, they go to the TaskEditView or NoteEditView. They can add a new tag, however if a tag already exists it will add that existing tag to the object. Because the tags are spread across two different stores how can I merge the two arrays and find if the tag exists without duplicating code?

struct Task: Identifiable {
    var id: String = UUID().uuidString
    var tags: [Tag] = []
    // Other task properties
}

struct Note: Identifiable {
    var id: String = UUID().uuidString
    var tags: [Tag] = []
    // Other note properties
}

struct Tag {
    var title: String
    var color: Color
}

class TaskStore: ObservableObject {
    @Published var tasks = [Task]()
    // Other TaskStore properties and functions
}

class NoteStore: ObservableObject {
    @Published var notes = [Note]()
    // Other NoteStore properties and functions
}

The only solution I have found is to pass the NoteStore and TaskStore down as environment objects and then map the tasks and notes arrays into a combined array of tag objects. However, I do not like this because I have to duplicate this code for the NoteEditView, TaskEditView and any other edit view of an object that contains a list of tags as a property. At the same time I have to pass every store that has objects that contain tags. Ideally I would want to create a class called TagStore that uses combine that subscribes to these two arrays.

let tags = [taskStore.tasks.flatMap { $0.tags }, noteStore.notes.flatMap { $0.tags }]
     .flatMap { $0 }

if let tag = tags.first(where: { $0.title == "History" }) {
    // Tag already exists                       
}

CodePudding user response:

Normally there is a single store environment object so I would recommend merging your objects:

class Store: ObservableObject {
    @Published var tasks: [Task] = []
    @Published var notes: [Note] = []

    // now you can have funcs that work on tasks and notes, e.g.
    //var allTags: [Tag] {
    //}
    // because any change to tasks or notes will fire objectWillChange, these computed vars don't need to be published.
}

Take a look at the Fruta sample's Store object for ideas, it's named Model.

  • Related