1

I have a parent class, that has children (Child Class), that have Puppets (Puppet Class). The data is displayed in a list of parents that navigates to a list of children, that navigates to a list of puppets.

Within the views, the user is able to add parents, children and puppets or remove them.

I want to store the data locally so that every change is save. I think storing the Parent class is enough, because of parents.children.puppets. Thus I need to conform to Codable anyhow and decode and encode my data anyhow.

The AppState should load local data or [] instead of the current dummy data. On every parent (or child or puppet) change, I want to store the parents locally in the most efficient way. Same for receiving the data.

class Parent: ObservableObject, Hashable {

    static func == (lhs: Parent, rhs: Parent) -> Bool {
        lhs.id == rhs.id
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }

    let id = UUID()
    let name: String
    @Published var children: [Child]

    init(name: String, children: [Child]? = nil) {
        self.name = name
        self.children = children ?? []
    }

    func remove(child: Child) {
        self.children.remove(child)
    }

    func add(child: Child) {
        self.children.append(child)
    }
}

class Child: ObservableObject, Identifiable, Hashable {
    static func == (lhs: Child, rhs: Child) -> Bool {
        return lhs.id == rhs.id
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }

    let id = UUID()
    let name: String
    @Published var puppets: [Puppet]

    init(name: String, puppets:[Puppet]? = nil) {
        self.name = name
        self.puppets = puppets ?? []
    }

    func remove(puppet: Puppet) {
        self.puppets.remove(puppet)
    }

    func add(puppet: Puppet) {
        self.puppets.append(puppet)
    }
}

struct Puppet: Identifiable, Hashable {
    let id = UUID()
    let name: String
}

class AppState: ObservableObject {
    @Published var parents: [Parent]
    init() {
        self.parents = [
            Parent(name: "Foo", children: [Child(name: "bar", puppets: [Puppet(name: "Tom")])]),
            Parent(name: "FooBar", children: [Child(name: "foo", puppets: nil)])
        ]
    }
}

extension Array where Element: Identifiable {
    mutating func remove(_ object: Element) {
        if let index = self.firstIndex(where: { $0.id == object.id}) {
            self.remove(at: index)
        }
    }
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
T. Karter
  • 638
  • 7
  • 25

2 Answers2

0

If you're not bound to any database or format, I'd recommend using CoreData (as suggested in the comments).

From Apple documentation:

Use Core Data to save your application’s permanent data for offline use, to cache temporary data, and to add undo functionality to your app on a single device.

This way your model doesn't have to conform to Codable.

You can find more information in these links:

pawello2222
  • 46,897
  • 22
  • 145
  • 209
0

I like very much pawello2222's detailed response. If you continue to develop on iOS, you will eventually need to learn CoreData.

That said, if you don't have time to do that for your current project, personally, I would recommend using RealmSwift and creating Realm Parent, Child and Puppet objects. It would take you an hour to learn how to install and use RealmSwift.

Here's a start:

import RealmSwift

class Puppet: Object {
    @objc dynamic var id = UUID()
    @objc dynamic var name: String = ""
}

But if you want to save a small number of objects you could use UserDefaults - none of your objects are using unsupported types, so just declare them as codable and let the compiler do the conversion automatically.

Conforming class to codable protocol in swift4

It is quite straightforward.

Mozahler
  • 4,958
  • 6
  • 36
  • 56