Let me first clarify that I’m making a macOS app and not an iOS app. I don’t think matters for the most part but maybe it does. It’s an application that runs commands in a terminal for each row listed on the left hand side of the menu in order to apply a compliant setting to the user’s system based on that particular row and its contents. I’ll get more into this later. It’s only one main view with separate views within. The top of the view (I’m going to call the menu) has four buttons. A “report all”, “fix all” and the other two buttons are irrelevant. Below that is a vertically split view (HStack) and on the left has a list of what I call security controls and on the right has the description of the security control i.e. what it does and what’s entailed to set this in the system along with a report and fix button within this section.
The point of this application is to apply government STIG settings to a user’s system. Each setting (Security control I’ll call SC) is separated into a row on the left of the menu and its details on the right of the menu. The user then can either run a report all at the top with those initial four buttons where the click of the buttons runs a check on each SC and checks if the system is in compliance or click on each security control in the left side of the menu and then run a single report from the report button on the right side of the screen in the description section. I’ve been successful in making the single report for each SC work and this then updates the row on the left with either a yellow ! or a green “Check mark” indicating the status of the SC. My issue now is the report all (the fix all is the same idea so no need to clarify this).
I’ve made a view called SCRow that just handles each SC row on the left side of the menu its own view for easier handling. Each SCRow is a NavigationLink that points to a destination of another view I called SCDescription (the right side of the menu) so that when each row is clicked the right side updates with the description information for each SC along with its own report and fix button. Each SCRow object contains a gray ? initially and using @State and @Binding variables between the SCRow and SCDescription View I was able to successfully update the gray ? in each row to either a green check or yellow ! depending on the report results.
My issue now is: How do I get the reportAll button to communicate with the SCRow views. The reportAll button and it’s cohorts get created at the very beginning of the main view so I’m having trouble understanding how it’s going to know about a view (SCRow) when they’re being created at different times. I put all four top buttons in a separate view called TopButtons and I attempted to make the instantiation of the TopButtons() view a variable such as var topButtons = TopButtons() and then pass that as a parameter to SCRow but I don’t think that’s going to do it either. Basically the push of the report all button at the top of the menu needs to update each row further down in the menu as it runs a report on each SC and to somehow track a view that hasn’t been created yet when the top button is created. I can’t wrap my head around it.
This is my main view:
import SwiftUI
import Foundation
struct Stom3Menu: View {
var path = ""
var fix = false
var report = false
var valueIndex = 0
let optionsarray = ["-f", "-r", "-h", "-m", "-V"]
let version = "0.8.10"
var driver = Driver()
let checkAscii = "\u{2714}"
@State var icon: String = "?"
var body: some View {
driver.CheckoutBranch()
//ruleList is an SCPRules object with an object value of rules, a dictionary of security controls
let ruleList = driver.GatherRules()
//keys is an array of strings of the id of each rule and values contains the rest of the yaml file as an array of SCPRule
let keys = ruleList.rules.map{$0.key}
//values is an array of SCPRule objects
@State var values = ruleList.rules.map{$0.value}
return VStack {
TopButtons(rules: values, icon: $icon)
HStack {
NavigationView {
List {
Text("Security Control").font(.largeTitle).fontWeight(.medium).multilineTextAlignment(.center)
Divider()
ForEach(values.indices) { index in
SCRow(item: values[index], id: values[index].id)
}
}
}
}
}
}
}
This is my SCRow view:
import SwiftUI
struct SCRow: View, Identifiable {
@State var item: SCPRule
var id: String
@State var icon: String = "?"
let checkAscii = "\u{2714}"
var body: some View {
NavigationLink (destination: SCDescription(item: $item, id: item.id, icon: $icon)){
HStack {
if self.icon == "?" {
Text(self.icon).font(.title).padding().background(Color.gray).clipShape(Circle())
} else if self.icon == "!" {
Text(self.icon).font(.title).padding().background(Color.yellow).clipShape(Circle())
} else if self.icon == checkAscii {
Text(self.icon).font(.title).padding().background(Color.green).clipShape(Circle())
}
Text(item.id).font(.body).padding()
}
}
}
}
SCDescription View
import SwiftUI
struct SCDescription: View {
@Binding var item: SCPRule
let id: String
@Binding var icon: String
let checkAscii = "\u{2714}"
var body: some View {
VStack {
Section {
Text("Description")
.font(.largeTitle)
.fontWeight(.medium)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
}
Spacer()
Divider()
Spacer()
Section {
VStack {
Text(item.title)
Spacer()
Divider()
ScrollView {
Text(item.discussion)
}
}
}
Section {
VStack {
Text("Results").font(.title)
//SCStatus(status: status)
}
}
Section {
HStack {
Button(action: {
let result = item.checkRule()
if result != true {
icon = "!"
} else {
icon = self.checkAscii
}
}, label: {
Image("system-search").resizable().scaledToFit()
Text("Report")
})
Button(action: {
item.fixRule()
let result = item.checkRule()
if result != true {
icon = "!"
//status = "This rule is not in compliance"
} else {
icon = self.checkAscii
//status = "This rule is compliant!"
}
}, label: {
Image("system-software-update").resizable().scaledToFit()
Text("Fix")
})
}
}
}
}
}
TopButtons View:
import SwiftUI
struct TopButtons: View {
var rules: [SCPRule]
@Binding var icon: String
let checkAscii = "\u{2714}"
var body: some View {
HStack{
Button(action: {
for var rule in rules {
var result = rule.checkRule()
if result != true {
icon = "!"
} else {
icon = self.checkAscii
}
}
}, label: {
Image("system-search").resizable().scaledToFit()
Text("Report All")
})
Button(action: {
}, label: {
Image("system-software-update").resizable().scaledToFit()
Text("Fix All")
})
Button(action: {
}, label: {
Image("cancel").resizable().scaledToFit()
Text("Stop Run")
})
Button(action: {
}, label: {
Image("help").resizable().scaledToFit()
Text("Help")
})
}
}
Any help would be greatly appreciated!
EDIT: Sorry for the formatting of some of my code. I’m not sure how to make all of it appear correctly within scrollable regions. I will make those changes if anyone knows how to fix it