exocore 0.1.2

Distributed applications framework
Documentation
import SwiftUI
import Exocore

struct DiscoveryView: View {
    @EnvironmentObject var appState: AppState
    @StateObject var state: DiscoveryState = DiscoveryState()

    var body: some View {
        NavigationView {
            VStack {
                discoView()
                Spacer()
                errorView()
                Spacer()
                Button("Generate new node") {
                    appState.node = try? LocalNode.generate()
                    appState.refreshNodeConfig()
                    state.performJoinCell(appState)
                }
            }
            .navigationBarTitle("Cell discovery")
            .onAppear {
                state.performJoinCell(appState)
            }
        }
    }

    @ViewBuilder
    func discoView() -> some View {
        if let pin = state.pin {
            VStack {
                Text("Discovery PIN:")
                    .padding()
                Text(formatPin(pin))
                    .bold()
                    .font(.system(size: 30))
            }
        } else {
            Text("Joining...")
        }
    }

    func errorView() -> Text? {
        if let err = self.state.error {
            return Text("Error: \(err)").foregroundColor(.red)
        }

        return appState.currentError.map {
            Text("Error: \($0)").foregroundColor(.red)
        }
    }
}

class DiscoveryState: ObservableObject {
    private var discovery: Discovery?
    @Published var pin: UInt32?
    @Published var error: String?

    func performJoinCell(_ appState: AppState) {
        guard let node = appState.node else {
            self.error = "No node configured"
            return
        }

        do {
            let discovery = try Discovery()
            self.discovery = discovery
            self.pin = nil
            self.error = nil

            discovery.joinCell(localNode: node) { [weak self] (stage) in
                DispatchQueue.main.async {
                    switch stage {
                    case .pin(let pin):
                        self?.pin = pin
                    case .success(let newNode):
                        appState.node = newNode
                        appState.forceDiscovery = false
                        appState.refreshNodeConfig()
                    case .error(let err):
                        self?.error = err.localizedDescription
                    }
                }
            }
        } catch {
            self.error = error.localizedDescription
        }
    }
}

private func formatPin(_ pin: UInt32) -> String {
    let strPin = pin.description

    var ret = ""
    for (i, char) in strPin.enumerated() {
        if i == 3 || i == 6 {
            ret += " "
        }
        ret += String(char)
    }
    return ret
}

#if DEBUG
struct BootstrapView_Previews: PreviewProvider {
    static var previews: some View {
        DiscoveryView()
    }
}
#endif