exocore 0.1.1

Distributed applications framework
Documentation
import Combine
import SwiftUI
import Exocore

struct ListView: View {
    @EnvironmentObject var appState: AppState
    @ObservedObject var list: ItemList
    @State private var text = ""

    #if DEBUG
    init(mockedItems items: [Item]) {
        self.list = ItemList(defaultItems: items)
    }
    #endif

    init() {
        self.list = ItemList()
    }

    var body: some View {
        VStack {
            HStack {
                Button("Rejoin") {
                    self.appState.forceDiscovery = true
                    self.appState.triggerChanged()
                }
                Button("Connect") {
                    self.list.connect(appState: self.appState)
                }
                Button("Disconnect") {
                    self.list.drop()
                }
            }.padding()

            HStack {
                TextField("", text: $text)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                        .padding()

                Button("Add") {
                    self.list.add(self.$text.wrappedValue)
                    self.$text.wrappedValue = ""
                }.padding()
            }

            List {
                ForEach(list.items, id: \.self.id) { (item) in
                    Text(item.text)
                }.onDelete { (indexSet) in
                    self.list.remove(atOffsets: indexSet)
                }
            }
        }.onAppear {
            self.list.connect(appState: self.appState)
        }
    }
}

class ItemList: ObservableObject {
    var resultStream: QueryStreamHandle?

    @Published var items: [Item] = []

    init(defaultItems items: [Item]? = nil) {
        if let items = items {
            self.items = items
        }
    }

    func connect(appState: AppState) {
        let query = QueryBuilder.withTrait(Exocore_Test_TestMessage.self)
                .count(100)
                .build()
        self.resultStream = ExocoreClient.store.watchedQuery(query: query, onChange: { [weak self] (status, results) in
            DispatchQueue.main.async {
                if let results = results {
                    self?.items = results.entities.map { (result: Exocore_Store_EntityResult) -> Item in

                        var title = "UNKNOWN"
                        if let trt = result.entity.traitOfType(Exocore_Test_TestMessage.self) {
                            title = trt.message.string1
                        }

                        return Item(id: result.entity.id, text: title)
                    }
                } else {
                    self?.items = []
                }

            }
        })
    }

    func add(_ text: String) {
        var msg = Exocore_Test_TestMessage()
        msg.string1 = text

        let mutation = try! MutationBuilder
                .createEntity()
                .putTrait(message: msg)
                .build()

        ExocoreClient.store.mutate(mutation: mutation)
    }

    func remove(atOffsets: IndexSet) {
        let item = self.items[atOffsets.first!]

        let mutation = MutationBuilder
                .updateEntity(entityId: item.id)
                .deleteEntity()
                .build()

        ExocoreClient.store.mutate(mutation: mutation)
    }

    func drop() {
        self.items = []
        self.resultStream = nil
    }
}

struct Item: Decodable, Identifiable {
    var id: String
    var text: String
}

#if DEBUG
struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        ListView(mockedItems: [
            Item(id: "1", text: "Item 1"),
            Item(id: "2", text: "Item 2"),
        ]).environmentObject(AppState())
    }
}
#endif