Getting views to interact with each other

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

Code formatting:

To format the code nicely, place 3 back-ticks ``` on the line above your code and 3 back-ticks ``` on the line below your code. Like this:

```
Code goes here
```

The 3 back-ticks must be the ONLY characters on the line. The back-tick character is located on the same keyboard key as the tilde character ~ (which is located below the Esc key - QWERTY keyboard).

Alternatively after you paste in your code, select that code block and click the </> button on the toolbar. This does the same thing as manually typing the 3 back ticks on the line above and below your code.

1 Like

Thank you for the info!

@liquidoshin

Are you familiar with a ViewModel (Observable Object) and how you can enable each View in the hierarchy to have access to it?

As an observation is seems to me that maybe you need to have an ObservableObject which each of your SC entries has access to. Configure it so that there is a function in that ObservableObject related to each SC entry which runs a report related to that SC entry. Also in that ObservableObject have a reportAll function which can call each of the functions for each SC entry in sequence.

Before I go any further, does that make any sense?

This is kind of ridiculous but I can’t seem to find where to edit my post. I clicked the pencil icon at the top but that just shows my history. I saw your other response but I want to make this look right. Any info for me?

It looks like it may not be possible since I’ve already edited it once. I’m guessing there’s a limit to how many times a new user can edit their post. All my other comments have a pencil icon below the post to allow an edit but not the original post.

Chris, thank you so much for being patient with me. I’ve read some about Observable objects in my books and on other forums. This makes sense in a general sense but I don’t have the knowledge to implement this currently. Any help would be greatly appreciated.

@liquidoshin

I edited the original post for you (since I have moderator capabilities) and it now looks like the way you need it to be.

I did try to make up an App using the code you supplied but there are a couple of definitions missing to make it work.

  • Driver
  • SCPRrule

There are a number of other assets I don’t have so that kind of makes it a bit hard to see what is going on and then come up with an answer to help you out.

If you have your project on Github then maybe you could share a link to it here (or with me via a Private Message if you prefer).

If you don’t have it on Github then the other way (maybe) is to compress the project at the top level folder and post it to either DropBox or Google Drive and create a share link and post here in a reply (or with me via a Private Message if you prefer).

You’re being incredibly helpful and I appreciate it. This is a government related project so I’ll need to create a new project with certain things stripped out and replaced with more generalized url’s etc. Give me some time and I will either put it here or message you with it. Thanks for your help and your patience.

Hey Chris, not sure if you remember me and my issue but I finally got around to pushing a sanitized version (all my company specific urls and info removed) up to GitHub. Here is the URL:

When you clone it you may have to modify some settings in Xcode because I’m not sure if they carry over in an Xcode file or not. You may need to:

You may notice a few files that don’t do anything and/or aren’t implemented. Those were files that I had added just messing around with the idea of observable objects as you mentioned. But I never made it any further with those than creating the files. If you could look over this and let me know what you think I would greatly appreciate it. Please let me know if you’re not able to run it for some reason.