COMP 1601 2022W Assignment 1 Solutions 1a. [1] What is the purpose of the Spacer() calls at the beginning and end of the AnalysisResult body? A: They add extra vertical spacing above and below the result text. 1b. [1] What does the result area say when the app starts? A: It says "Please Select a Mode". 1c. [2] Can you ever make it display the the initial message after you've selected something from the analysis menu? Why or why not? A: You can't ever get the result area to say "Please Select a Mode" because this message is only displayed with the analysisMode string is set to a value that isn't a key in the analysis dictionary, and selecting menu items can't ever do that because the menu is generated from the keys of the analysis dictionary. (We can get an initial value that isn't in the dictionary because analysisMode is initialized manually on line 11.) 2. [1] Why doesn't the AppTitle view use a @Binding decorator for title? A: The title doesn't use a @Binding decorator because title isn't referring to a state variable passed in from ContentView. Instead, title is associated with a constant string. We don't need a @State variable here because the title of the application won't change at runtime. 3. [2] How often are the analysis mode functions called (at minimum)? How do you know? A: The analysis mode functions are at least called every time we type in a letter, because we can see the character count change instantly as we type. 4. [2] What happens if we add a line analysisMode = "Count" between lines 13 and 14? Why? A: We get an error "Type '()' cannot conform to 'View'". We get this error because we are attempting to assign a value to a state variable, and that is not allowed in a SwiftUI view body. 5a. [1] What is the value of availModes? A: An array of "String": ["Count", "Empty", "Upper Case", "Pets Mentioned"] 5b. [2] If we replace lines 62-66 with the following for loop, Xcode will report an error. What error will it report? Why doesn't the ForEach generate the same error? for mode in availModes { Button(mode, action: { analysis = mode }) } A: We get the error "Closure containing control flow statement cannot be used with result builder 'ViewBuilder'". We get this error because the body of the for loop is a closure here, and that isn't allowed. Conceptually again we are trying to dynamically modify the view here as it is being built, and that isn't allowed in SwiftUI. The ForEach doesn't have this problem because it is specifically designed to create a collection of SwiftUI views, rather than run arbitrary code wrapped inside of a closure. (For those who are curious: The body of a View is created using ViewBuilder, which is a kind of Swift result builder. Result builders are the Swift mechanism that allows for the creation of domain-specific languages that can be embedded inside of a Swift program. They are created using a struct decorated with @resultBuilder. Result builders are described in the Swift programming language guide under "Advanced Operators." Result builders are beyond the scope of this course.) 5c. [2] Can the value of availModes change during the execution of the program? Why or why not? A: The value of availModes cannot change because 1) it is declared with "let" and 2) it is based on analysis which is an immutable dictionary (it is also declared with "let"). 5d. [2] How could you add a menu option "Other" that set the analysisMode to "Other" without changing the analysis dictionary? What would this new menu option do? A: You could create an Other menu item by adding the following code between lines 61 and 62: Button("Other", action: { analysisMode = "Other" }) When this menu item is selected, the results area will go back to saying "Please Select a Mode" (because "Other" does not have an entry in the analysis dictionary). 6. [2] How could you change the analysis dictionary so it doesn't use any more closures (anonymous functions)? Your changes should preserve the program's functionality. A: We can replace the analysis declaration (lines 94-99) with something like following: let analysis: [String: (String) -> String] = [ "Count": countCharacters, "Empty": checkEmpty, "Upper Case": countUpper, "Pets Mentioned": countPetsMentioned ] We would then define countCharacters and checkEmpty as follows: func countCharacters (s: String) -> String { return String(s.count) } func checkEmpty (s: String) -> String { return (s == "") ? "Yes" : "No" } 7. [2] How could you add a new analysis mode "Pets Count" that counts the total number of occurrences of the pet names Roshi, Tab, and Shift (and any other ones you want to add)? Unlike "Pets Mentioned", this one should count duplicates, e.g., "Roshi Roshi" should return a count of 2, not 1. A: First, define a function countPets as follows: func countPets(s: String) -> String { var count = 0 for p in pets { count += s.components(separatedBy:p).count - 1 } return String(count) } And then we add this function to the analysis dictionary, inserting the following line between lines 97 and 98: "Pets Count": countPets, At first I tried solving it by iterating through the strings and doing character by character comparisons, but with unicode strings that isn't so easy and in fact is not at all recommended. So instead I found this components trick here: https://stackoverflow.com/questions/31746223/number-of-occurrences-of-substring-in-string-in-swift