Mobile App Dev 2021W: Tutorial 2: Difference between revisions
Created page with "==Code== * [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/code/Convert2Cmd.zip Convert2Cmd.zip] * [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/code/Converter2.z..." |
No edit summary |
||
(17 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
In this tutorial you'll be playing with [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/code/Convert2Cmd.zip Convert2Cmd] and [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/code/Converter2.zip Converter2]. Convert2Cmd and Converter2 both make use of the unit converters defined in Converter.swift; Convert2Cmd provides a simple command line, menu-based interface to the converters, while Converter2 uses them in a simple SwiftUI-based iOS application. By studying two interfaces for the same functionality, you can see how SwiftUI-based interfaces compare with command line ones. | |||
Submit your answers by 11:59 PM on Sunday, January 31, 2021 via cuLearn. Please submit a text file following [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/templates/tut2-template.txt this template]. ''Note that you only need to cite sources that weren't covered in lecture and aren't listed below.'' | |||
==Key Concepts== | |||
Below are the key concepts covered in this tutorial, with links to (mostly) Apple documentation on them. | |||
* Swift [https://docs.swift.org/swift-book/LanguageGuide/Functions.html functions] & [https://docs.swift.org/swift-book/LanguageGuide/Closures.html closures] | |||
* Swift [https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html dictionaries & arrays] | |||
* Writing simple command line synchronous interface in Swift | |||
* [https://developer.apple.com/documentation/swiftui/declaring-a-custom-view Declaring custom views] in SwiftUI | |||
* SwiftUI [https://developer.apple.com/documentation/swiftui/state @State] and [https://developer.apple.com/documentation/swiftui/binding @Binding]. ([https://shensheng.medium.com/swiftui-difference-of-state-binding-environment-and-environmentobject-and-when-to-use-them-ff80699f45b7 This article] by Shen Sheng is also helpful. | |||
* SwiftUI [https://www.hackingwithswift.com/quick-start/swiftui/how-to-create-views-in-a-loop-using-foreach ForEach] (hackingwithswift.com) | |||
You can supplement these with other web resources. In particular, the WWDC videos related to SwiftUI are good, such as [https://developer.apple.com/videos/play/wwdc2020/10119/ this 2020 introduction to SwiftUI]. There are also videos from WWDC 2019 and before, such as this video on [https://developer.apple.com/videos/play/wwdc2019/216/ SwiftUI Essentials]. While you do not need to consult outside material to do the assignments in this class, another perspective can be helpful. | |||
==Common Problems== | |||
* If you get an error saying "No account for team" and/or "No signing certificate" (and you created a signing certificate in Tutorial 1), you may need to change the deployment team or the deployment version (if you aren't running Big Sur and are building a command line program). You can do this by selecting the program at the top of the file view on the left (i.e., Convert2Cmd, Converter2) and then changing the deployment target under Info or the team under Signing and Capabilities. | |||
* If you cannot see a SwiftUI preview when editing ContentView.swift, you may need to validate project settings and update to recommended settings. You get here by selecting the warning icon near the top right of the Xcode window. | |||
==Questions== | |||
===Part A: Converter.swift=== | |||
# If you changed Conversions:8 to <tt>func formatConversion(from: Double) -> String {</tt> (removing the underscore), what else would have to be changed to make this declaration correct? Why? | |||
# The "inch to cm" conversion is coded differently from the others. What is different about it? How would you change it to be similar to the rest? | |||
# When is the <tt>init</tt> method of the Converter struct (Conversions:16) called? | |||
# Is the type declaration in Conversions:28 (the declaration of the conversions dictionary) necessary? Why? | |||
# Add a conversion for miles to kilometers. | |||
===Part B: main.swift (Convert2Cmd)=== | |||
# What type is <tt>convAvail</tt>? | |||
# What is the i+1 for on main:8? | |||
# How could you change the conversion message to be of the form "Boss, I think 0 Celsius is 32 Farenheit!" with only making changes to main.swift, not Conversions.swift? | |||
# Why is the test on main:25 needed? Why main:26? | |||
===Part C: ContentView.swift (Converter2)=== | |||
# Can you combine all of the separate views into one view, getting rid of ConvMenu and ConvOutput? Show your attempt and say what happens. | |||
# Why is there an ! on ContentView:43? How could you get rid of it? | |||
# Add a menu to the bottom of the screen, identical to the one at the top. How hard was this to do? | |||
# How could you have values from previous conversions used for new conversions? For example, if you entered in 32 for Fahrenheit, how could you keep that 32 when you switched to inches? | |||
# Could the ForEach() on ContentView:27 have been used in main? Explain. | |||
==Code== | ==Code== | ||
Line 4: | Line 51: | ||
* [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/code/Converter2.zip Converter2.zip] | * [https://homeostasis.scs.carleton.ca/~soma/mad-2021w/code/Converter2.zip Converter2.zip] | ||
=== | ===Conversions.swift=== | ||
<syntaxhighlight lang="swift" line> | <syntaxhighlight lang="swift" line> | ||
Line 47: | Line 94: | ||
InToCM) | InToCM) | ||
] | ] | ||
</syntaxhighlight> | |||
===main.swift (Convert2Cmd)=== | |||
<syntaxhighlight lang="swift" line> | |||
// main.swift (Convert2Cmd) | |||
var convAvail = conversions.keys.sorted() | |||
func getChoice() -> String? { | |||
print("Available Conversions:") | |||
for i in 0..<convAvail.count { | |||
print(" \(i+1). \(convAvail[i])") | |||
} | |||
print("Enter choice: ", terminator: "") | |||
if let choice = readLine() { | |||
if let c = Int(choice) { | |||
if ((c > 0) && (c <= convAvail.count)) { | |||
return convAvail[c-1] | |||
} | |||
} | |||
} | |||
return nil | |||
} | |||
let getConversion = {(_ choice: String) in | |||
let conv = conversions[choice]! | |||
print("\nEnter \(conv.convFrom): ", terminator: "") | |||
if let vs = readLine() { | |||
if let v = Double(vs) { | |||
let res = conv.formatConversion(v) | |||
print(res) | |||
return | |||
} else { | |||
print("You didn't enter a number!") | |||
} | |||
} | |||
} | |||
if let choice = getChoice() { | |||
getConversion(choice) | |||
} else { | |||
print("You didn't make a valid choice!") | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 114: | Line 206: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | ==Solutions== | ||
[https://homeostasis.scs.carleton.ca/~soma/mad-2021w/solutions/tut2-solutions.txt Tutorial 2 solutions] | |||
Latest revision as of 16:38, 1 February 2021
In this tutorial you'll be playing with Convert2Cmd and Converter2. Convert2Cmd and Converter2 both make use of the unit converters defined in Converter.swift; Convert2Cmd provides a simple command line, menu-based interface to the converters, while Converter2 uses them in a simple SwiftUI-based iOS application. By studying two interfaces for the same functionality, you can see how SwiftUI-based interfaces compare with command line ones.
Submit your answers by 11:59 PM on Sunday, January 31, 2021 via cuLearn. Please submit a text file following this template. Note that you only need to cite sources that weren't covered in lecture and aren't listed below.
Key Concepts
Below are the key concepts covered in this tutorial, with links to (mostly) Apple documentation on them.
- Swift functions & closures
- Swift dictionaries & arrays
- Writing simple command line synchronous interface in Swift
- Declaring custom views in SwiftUI
- SwiftUI @State and @Binding. (This article by Shen Sheng is also helpful.
- SwiftUI ForEach (hackingwithswift.com)
You can supplement these with other web resources. In particular, the WWDC videos related to SwiftUI are good, such as this 2020 introduction to SwiftUI. There are also videos from WWDC 2019 and before, such as this video on SwiftUI Essentials. While you do not need to consult outside material to do the assignments in this class, another perspective can be helpful.
Common Problems
- If you get an error saying "No account for team" and/or "No signing certificate" (and you created a signing certificate in Tutorial 1), you may need to change the deployment team or the deployment version (if you aren't running Big Sur and are building a command line program). You can do this by selecting the program at the top of the file view on the left (i.e., Convert2Cmd, Converter2) and then changing the deployment target under Info or the team under Signing and Capabilities.
- If you cannot see a SwiftUI preview when editing ContentView.swift, you may need to validate project settings and update to recommended settings. You get here by selecting the warning icon near the top right of the Xcode window.
Questions
Part A: Converter.swift
- If you changed Conversions:8 to func formatConversion(from: Double) -> String { (removing the underscore), what else would have to be changed to make this declaration correct? Why?
- The "inch to cm" conversion is coded differently from the others. What is different about it? How would you change it to be similar to the rest?
- When is the init method of the Converter struct (Conversions:16) called?
- Is the type declaration in Conversions:28 (the declaration of the conversions dictionary) necessary? Why?
- Add a conversion for miles to kilometers.
Part B: main.swift (Convert2Cmd)
- What type is convAvail?
- What is the i+1 for on main:8?
- How could you change the conversion message to be of the form "Boss, I think 0 Celsius is 32 Farenheit!" with only making changes to main.swift, not Conversions.swift?
- Why is the test on main:25 needed? Why main:26?
Part C: ContentView.swift (Converter2)
- Can you combine all of the separate views into one view, getting rid of ConvMenu and ConvOutput? Show your attempt and say what happens.
- Why is there an ! on ContentView:43? How could you get rid of it?
- Add a menu to the bottom of the screen, identical to the one at the top. How hard was this to do?
- How could you have values from previous conversions used for new conversions? For example, if you entered in 32 for Fahrenheit, how could you keep that 32 when you switched to inches?
- Could the ForEach() on ContentView:27 have been used in main? Explain.
Code
Conversions.swift
// Conversions.swift
struct Converter {
var convFrom = "From Type"
var convTo = "To Type"
var convert: (_ from: Double) -> Double?
func formatConversion(_ from: Double) -> String {
if let to = self.convert(from) {
return "\(from) \(self.convFrom) is \(to) \(self.convTo)."
} else {
return "Converting \(from) \(convFrom) to \(self.convTo) failed."
}
}
init(_ from: String, _ to: String, _ f: @escaping (_ from: Double) -> Double?) {
self.convFrom = from
self.convTo = to
self.convert = f
}
}
func InToCM(_ inch: Double) -> Double {
let cm = 2.54 * inch
return cm
}
let conversions: [String: Converter] =
["F to C": Converter("Farenheit", "Celsius",
{F in return ((F - 32.0)*(5/9))}),
"C to F": Converter("Celsius", "Farenheit",
{C in return ((9/5)*C + 32.0)}),
"km to mi": Converter("kilometers", "miles",
{k in
let m = k * 0.6213712
return m
}),
"inch to cm": Converter("inches", "centimeters",
InToCM)
]
main.swift (Convert2Cmd)
// main.swift (Convert2Cmd)
var convAvail = conversions.keys.sorted()
func getChoice() -> String? {
print("Available Conversions:")
for i in 0..<convAvail.count {
print(" \(i+1). \(convAvail[i])")
}
print("Enter choice: ", terminator: "")
if let choice = readLine() {
if let c = Int(choice) {
if ((c > 0) && (c <= convAvail.count)) {
return convAvail[c-1]
}
}
}
return nil
}
let getConversion = {(_ choice: String) in
let conv = conversions[choice]!
print("\nEnter \(conv.convFrom): ", terminator: "")
if let vs = readLine() {
if let v = Double(vs) {
let res = conv.formatConversion(v)
print(res)
return
} else {
print("You didn't enter a number!")
}
}
}
if let choice = getChoice() {
getConversion(choice)
} else {
print("You didn't make a valid choice!")
}
ContentView.swift (Converter2)
// ContentView.swift (Converter2)
import SwiftUI
struct ContentView: View {
@State var convMode: String? = nil
@State var fromS = ""
var body: some View {
VStack{
ConvMenu(convMode: $convMode,
fromString: $fromS)
Spacer()
ConvOutput(convMode: $convMode,
fromString: $fromS)
Spacer()
Spacer()
}
}
}
struct ConvMenu: View {
@Binding var convMode: String?
@Binding var fromString: String
var body: some View {
let availConversions = [String](conversions.keys)
Menu("Conversions menu") {
ForEach(availConversions, id: \.self) {
conversion in
Button(conversion, action: {
convMode = conversion
fromString = ""
})
}
}
}
}
struct ConvOutput: View {
@Binding var convMode: String?
@Binding var fromString: String
var body: some View {
if let mode = convMode {
let conv = conversions[mode]!
TextField("Enter \(conv.convFrom)", text: $fromString)
Spacer()
if let from = Double(fromString) {
Text(conv.formatConversion(from))
}
} else {
Text("Please select a conversion")
Text("type from the menu above.")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}