๊ณ์ฐ๊ธฐ ์ฑ์ ๋ง๋ค๋ฉด์ Advanced Swift ๋ฅผ ๋ฐฐ์๋ณด์.
1. ๋จผ์ ์๋ ๋งํฌ์์ Skeleton Project ๋ฅผ ๋ค์ด๋ฐ์์ฃผ์.
https://github.com/appbrewery/Calculator-Advanced-Swift-iOS13
2. ์ ๋ ฅํ ๋ฒํผ ์ซ์๋ฅผ ์ถ๋ ฅํ๋ ๊ณ์ฐ๊ธฐ ์ฝ๋๋ฅผ ์์ฑํด์ฃผ์.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var displayLabel: UILabel!
private var isFinishedTypingNumber: Bool = true
@IBAction func calcButtonPressed(_ sender: UIButton) {
//What should happen when a non-number button is pressed
isFinishedTypingNumber = true
}
@IBAction func numButtonPressed(_ sender: UIButton) {
//What should happen when a number is entered into the keypad
if let numberValue = sender.currentTitle {
if isFinishedTypingNumber {
displayLabel.text = numberValue
isFinishedTypingNumber = false
}
else{
displayLabel.text! += numberValue
}
}
}
}
numButtonPressed ํจ์์์ sender ๋ก ๋๋ฅธ ๋ฒํผ์ text ๋ค์ ๊ฐ์ ธ์จ๋ค.
๋งจ์ฒ์ 0์ผ ๋, 0์ด ์์์ด์ง๋ ์ค๋ฅ๋ฅผ ๋ง๊ธฐ ์ํด isFinishedTypingNumber ๋ณ์๋ฅผ ์ ์ธํ๋ค.
3. ์ฐ์ฐ ์ฝ๋๋ฅผ ์์ฑํด์ฃผ์.
1) ๋จผ์ ์ซ์์ ๋ถํธ๋ฅผ ๋ฐ๊ฟ์ฃผ๋ +/- ๋ฒํผ์ ๊ตฌํํด๋ณด์.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var displayLabel: UILabel!
private var isFinishedTypingNumber: Bool = true
@IBAction func calcButtonPressed(_ sender: UIButton) {
//What should happen when a non-number button is pressed
isFinishedTypingNumber = true
guard let number = Double(displayLabel.text!) else {
fatalError("cannot convert display label text to a Double.")
}
if let calcMethod = sender.currentTitle {
switch calcMethod {
case "+/-":
displayLabel.text = String(number * -1) // ์ผ์ชฝ์ด ๋ฌธ์์ด์ด๋ฏ๋ก String์ผ๋ก ํ๋ณํ ํด์ค์ผ ํ๋ค.
break
case "AC":
displayLabel.text = "0"
break
case "%":
displayLabel.text = String(number / 100)
break
default:
break
}
}
}
@IBAction func numButtonPressed(_ sender: UIButton) {
//What should happen when a number is entered into the keypad
if let numberValue = sender.currentTitle {
if isFinishedTypingNumber {
displayLabel.text = numberValue
isFinishedTypingNumber = false
}
else{
displayLabel.text! += numberValue
}
}
}
}
์ฐ์ฐ๋ฒํผ์ calcButtonPressed ํจ์์ ์ฐ๊ฒฐ๋์ด ์์ด์ ์ฌ๊ธฐ์ ๊ตฌํํ๋ฉด ๋๋ค.
displayLabel.text ๋ถ๋ถ์ Double ๋ก ๊ฐ์ผ ์ด์ ๋ text ๋ฅผ ์ซ์๋ก ๋ฐ๊ฟ ์ ์๋ ๊ฒฝ์ฐ๊ฐ ๋ง๊ธฐ ๋๋ฌธ์ Double ๋ก ๊ฐ์ ํ๋ณํ ํด์ค ๊ฒ์ด๋ค.
๊ฐ์ ํ๋ณํ ํ์ ๋ ์๋ฌ๊ฐ ๋์ ์ฑ์ด ๋ฉ์ถ๋ ๊ฒ์ guard ๋ฅผ ์ด์ฉํด์ ๋ง์๋ค. guard ๋ฌธ๋ฒ์ if ๋ฌธ๊ณผ ๋์ผํ์ง๋ง ๋ฌด์กฐ๊ฑด else ๋ฌธ์ ์จ์ critical ์๋ฌ๋ฅผ ๋ง๊ณ ๋น ๋ฅด๊ฒ ์ข ๋ฃ์ํฌ ์ ์๋ค. ๋ฐ๋์ return ์ด๋ throw, fatalError ๋ฑ์ ๋ช ์ํด์ฃผ์ด์ผ ํ๋ค.
P.S) +/- ๋ฒํผ๊ณผ ์ฐ๊ฒฐ๋ ํจ์ ๋ณ๊ฒฝ ํ์
+/- ๋ฒํผ์ด calButtonPressed ์ ๋ฌถ์ฌ ์์ด์ ๊ทธ๋ฅ ์ซ์๋ถํธ๋ฅผ ๋ฐ๊พธ๋๊ฒ ์๋๋ผ ์ฐ์ฐ์ ํ๋ ค๊ณ ํ๊ธฐ์ ์๋ฌ๊ฐ ๋๋ค.
์ฐ๊ฒฐ๋ ํจ์๋ฅผ numButtonPressed ํจ์๋ก ๋ฐ๊ฟ์ค๋ค. ์ฆ ์ฐ๊ฒฐ๋ ๋ฒํผํจ์๋ฅผ ์ ๊ฑฐํด์ค๋ค.
๋ฒํผ์ ctrl + ๋๋๊ทธํ์ฌ view controller ๋ก ๊ฐ๋ค๋๋ค.
sent Events ๋ชฉ๋ก ์ค์ numButtonPressed ํจ์๋ฅผ ์ฒดํฌํ๋ค.
calButtonPressed() -> numButtonPressed() ๋ก ๋ฐ๊พธ๋ฉด ์ ์๋์ํ๋ค.
2) .(์์์ ) ๋ฒํผ์ ๊ตฌํํด๋ณด์.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var displayLabel: UILabel!
private var isFinishedTypingNumber: Bool = true
@IBAction func calcButtonPressed(_ sender: UIButton) {
//What should happen when a non-number button is pressed
isFinishedTypingNumber = true
guard let number = Double(displayLabel.text!) else {
fatalError("cannot convert display label text to a Double.")
}
if let calcMethod = sender.currentTitle {
switch calcMethod {
case "+/-":
displayLabel.text = String(number * -1) // ์ผ์ชฝ์ด ๋ฌธ์์ด์ด๋ฏ๋ก String์ผ๋ก ํ๋ณํ ํด์ค์ผ ํ๋ค.
break
case "AC":
displayLabel.text = "0"
break
case "%":
displayLabel.text = String(number * 0.01)
break
default:
break
}
}
}
@IBAction func numButtonPressed(_ sender: UIButton) {
//What should happen when a number is entered into the keypad
if let numberValue = sender.currentTitle {
if isFinishedTypingNumber {
displayLabel.text = numberValue
isFinishedTypingNumber = false
}
else{
switch numberValue {
case ".":
guard let currentDisplayValue = Double(displayLabel.text!) else{
fatalError("Cannot convert display label text to a Double !")
}
let isInt = floor(currentDisplayValue) == currentDisplayValue
if !isInt{
return
}
break
default:
break
}
displayLabel.text! += numberValue
}
}
}
}
numButtonPressed ํจ์์์ ๊ตฌํํด์คฌ๋ค. ์ ์๋ฉด ์์๋ก ๋ฐ๊พธ๊ณ ์์๋ฉด return.
P.S) command + option + ์ผ์ชฝํ์ดํ๋ฅผ ๋๋ฅด๋ฉด ์ฝ๋๋ฅผ ์ ์ ์ ์๋ค.
3) ์์์ ๋ฐฐ์ด getter์ setter๋ฅผ ์ฌ์ฉํด์ ์ฝ๋ ๋ฆฌํฉํ ๋ง์ ํด๋ณด์.
guard let number ๋ถ๋ถ๊ณผ guard let currentDisplayValue ๋ถ๋ถ์ด ์ค๋ณต๋๋ค.
์ด ๋ถ๋ถ์ ์ ์ญ๋ณ์๋ก ๋นผ์ ๋ณ์ get ์์ฑ์ผ๋ก ๊ฐ์ ธ์ค์.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var displayLabel: UILabel!
private var isFinishedTypingNumber: Bool = true
private var displayValue: Double {
get {
guard let currentDisplayValue = Double(displayLabel.text!) else {
fatalError("cannot convert display label text to a Double.")
}
return currentDisplayValue
}
}
@IBAction func calcButtonPressed(_ sender: UIButton) {
//What should happen when a non-number button is pressed
isFinishedTypingNumber = true
if let calcMethod = sender.currentTitle {
switch calcMethod {
case "+/-":
displayLabel.text = String(displayValue * -1) // ์ผ์ชฝ์ด ๋ฌธ์์ด์ด๋ฏ๋ก String์ผ๋ก ํ๋ณํ ํด์ค์ผ ํ๋ค.
break
case "AC":
displayLabel.text = "0"
break
case "%":
displayLabel.text = String(displayValue * 0.01)
break
default:
break
}
}
}
@IBAction func numButtonPressed(_ sender: UIButton) {
//What should happen when a number is entered into the keypad
if let numberValue = sender.currentTitle {
if isFinishedTypingNumber {
displayLabel.text = numberValue
isFinishedTypingNumber = false
}
else{
switch numberValue {
case ".":
let isInt = floor(displayValue) == displayValue
if !isInt{
return
}
break
default:
break
}
displayLabel.text! += numberValue
}
}
}
}
์ฝ๋์ ๋ฆฌ๋ฅผ ์๋ฃํ ๋ชจ์ต.
๋ํ displayValue ๊ฐ์ ๊ฐ์ ธ์์ ์ฐ์ฐ์ ์ํํด์ displayLabel.text ๋ก ๋ฃ๋ ๋ถ๋ถ์
set ๋ฅผ ์ด์ฉํด์ displayValue ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์๋์ผ๋ก displayLabel ๊ฐ์ ๊ฐฑ์ ํด์ฃผ์.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var displayLabel: UILabel!
private var isFinishedTypingNumber: Bool = true
private var displayValue: Double {
get {
guard let currentDisplayValue = Double(displayLabel.text!) else {
fatalError("cannot convert display label text to a Double.")
}
return currentDisplayValue
}
set {
displayLabel.text = String(newValue)
}
}
@IBAction func calcButtonPressed(_ sender: UIButton) {
//What should happen when a non-number button is pressed
isFinishedTypingNumber = true
if let calcMethod = sender.currentTitle {
switch calcMethod {
case "+/-":
displayValue *= -1
break
case "AC":
displayLabel.text = "0"
break
case "%":
displayValue *= 0.01
break
default:
break
}
}
}
@IBAction func numButtonPressed(_ sender: UIButton) {
//What should happen when a number is entered into the keypad
if let numberValue = sender.currentTitle {
if isFinishedTypingNumber {
displayLabel.text = numberValue
isFinishedTypingNumber = false
}
else{
switch numberValue {
case ".":
let isInt = floor(displayValue) == displayValue
if !isInt{
return
}
break
default:
break
}
displayLabel.text! += numberValue
}
}
}
}
displayValue *= -1
displayValue *= 0.01
์ฝ๋๊ฐ ์ด๋ ๊ฒ ๊ฐ๋จํด์ก๋ค.
4) MVC ๋์์ธ ํจํด๋ ์ ์ฉํด๋ณด์.
MVC ๊ตฌ์กฐ๋ฅผ ์ํด Model, View, Controller ํด๋๋ฅผ ๋ง๋ค์ด์ฃผ๊ณ Model ์ญํ ์ ํ CalculatorLogic.swift ํ์ผ์ ๋ง๋ค์ด์ค๋ค.
์ด ๋, View ํด๋์๋ Main storyboard ๋ฅผ ๋ฃ์ด์ฃผ๊ณ Controller ํด๋์๋ ViewController.swift ํ์ผ์ ๋ฃ์ด์ฃผ์.
ํ์ฌ๋ ์์ ๊ฐ์ด View ๋จ์ธ ViewController.swift ์ ๋ชจ๋ ์ฝ๋๊ฐ ๋ค์ด ์๋ค.
์ฌ๊ธฐ์ ๊ณ์ฐ ์ฝ๋๋ฅผ ๋นผ์ Model ๋จ์ธ Calculator.swift ํ์ผ๋ก ์ฎ๊ฒจ์ฃผ์.
CalculatorLogic.swift
import Foundation
class CalculatorLogic {
var number: Double
init(number: Double){
self.number = number
}
//return ๊ฐ์ด double ํ์ด ์๋ ๊ฒฝ์ฐ๋ฅผ ์ํด optional ์ถ๊ฐํด์ค.
func calculate(symbol: String) -> Double? {
switch symbol {
case "+/-":
return number * -1
case "AC":
return 0
case "%":
return number * 0.01
default:
return nil
}
}
}
CalculatorLogic ํด๋์ค๋ฅผ ๋ง๋ค๊ณ ๊ณ์ฐ์ ํ์ํ ์ซ์๋ฅผ number ๋ก ๊ฐ๊ณ ์๊ฒ ํ๋ค.
init ํจ์๋ฅผ ๋ง๋ค์ด์ number ๊ฐ์ด CalculatorLogic ๊ฐ์ฒด๊ฐ ํธ์ถ ๋ ๋ ์ ์ฉ๋๋๋ก ํ์.
calculate ํจ์๊ฐ ViewController ํด๋์ค์ ์๋ calcButton ํจ์๋ถ๋ถ์ด๋ค.
symbol(๊ณ์ฐ ๋ถํธ)๋ฅผ ๋ง๋๋ฉด number ๊ฐ์ ์ฐ์ฐ์ ํด์ return ํ๋ค.
์ด๋, return ๊ฐ์ด double ํ์ด ์๋ ๊ฒฝ์ฐ๋ฅผ ์ํด ?(optional) ๋ฅผ ์ถ๊ฐํด์ค์ผ ํ๋ค. default return ์ nil !
ViewController.swift
@IBAction func calcButtonPressed(_ sender: UIButton) {
//What should happen when a non-number button is pressed
isFinishedTypingNumber = true
if let calcMethod = sender.currentTitle {
let calculator = CalculatorLogic(number: displayValue)
guard let result = calculator.calculate(symbol: calcMethod) else {
fatalError("The result of the calculation is nil.")
}
displayValue = result
}
}
viewController ํด๋์ค์ ์๋ calcButtonPressed ํจ์์์ ์์ ๊ฐ์ด CalculatorLogic ํด๋์ค๋ฅผ ํธ์ถํ๊ณ CalculatorLogic ํด๋์ค์ calculate ํจ์๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
CalculatorLogic ๊ฐ์ฒด ์ ์ธ ์ number ๊ฐ์ผ๋ก displayValue ๋ฅผ ์ ๋ฌํ๋ค.
calculate ํจ์์ calcMethod ๊ฐ์ symbol ์ ๋๊ฒจ์ฃผ๊ณ ์ฐ์ฐํ๊ฒ ํ๋ค.
์ด ๋, calculate ํจ์์ ๊ฒฐ๊ณผ๊ฐ์ด ์๊ฑฐ๋ ์๋ฌ๊ฐ ์์ ๊ฒฝ์ฐ๋ฅผ ๋๋นํ์ฌ guard else ๋ฌธ์ ์ฌ์ฉํด์ค๋ค.
5) CalculatorLogic ํด๋์ค๋ฅผ Struct ๋ก ๋ฐ๊พธ์
import Foundation
struct CalculatorLogic {
private var number: Double?
mutating func setNumber(_ number: Double){
self.number = number
}
func calculate(symbol: String) -> Double? {
if let n = number {
switch symbol {
case "+/-":
return n * -1
case "AC":
return 0
case "%":
return n * 0.01
default:
return nil
}
}
return nil
}
}
CalculatorLogic ํด๋์ค๋ฅผ Struct ๋ก ๋ฐ๊ฟจ๋ค. Struct ๋ก ๋ฐ๊พธ๋ฉด์ ํ์์๋ init ํจ์๋ฅผ ์ง์ ๋ค.
struct ์ ์ญ๋ณ์๋ ํญ์ private access level ์ ๊ฐ๊ณ ์๊ฒ ํ๋๊ฒ ์ข๋ค. number ๊ฐ์ setNumber ํจ์๋ฅผ ์ด์ฉํด์๋ง ๋ณ๊ฒฝ๋๋๋ก ๊ฐ์ธ์. ์ด ๋, mutating ํค์๋๋ก ์ ์ญ๋ณ์๋ฅผ ๋ณ๊ฒฝํ๋ ํจ์์์ ๋ช ์ํด์ฃผ์.
number ๊ฐ ์๋ ๊ฒฝ์ฐ(์ฌ์ฉ์๊ฐ ์ซ์๋ฅผ ์ ๋ ฅํ์ง ์์์ ๋)์ ์ฐ์ฐ์ ํ์ง ๋ง์์ผ ํ๋ฏ๋ก if ๋ฌธ์ ์ถ๊ฐํด์ฃผ์๋ค.
private var calculator = CalculatorLogic()
@IBAction func calcButtonPressed(_ sender: UIButton) {
//What should happen when a non-number button is pressed
isFinishedTypingNumber = true
calculator.setNumber(displayValue)
if let calcMethod = sender.currentTitle {
guard let result = calculator.calculate(symbol: calcMethod) else {
fatalError("The result of the calculation is nil.")
}
displayValue = result
}
}
CalculatorLogic Class ๊ฐ ์๋ CalculatorLogic Struct ๋ ์์ ๊ฐ์ด ์ ์ธ ๋ฐ ์ฌ์ฉํ ์ ์๋ค.
์ ์ธํ๊ณ setNumber ํจ์๋ก ๊ฐ์ ๋ฃ์ด์ ์ด๊ธฐํํ์.
6) CalculatorLogic ํด๋์ค์ Tuple ์ ์ ์ฉํด๋ณด์.
import Foundation
struct CalculatorLogic {
private var number: Double?
private var intermediateCalculation: (n1: Double, calcMethod: String)?
mutating func setNumber(_ number: Double){
self.number = number
}
mutating func calculate(symbol: String) -> Double? {
if let n = number {
switch symbol {
case "+/-":
return n * -1
case "AC":
return 0
case "%":
return n * 0.01
case "=":
return performTwoNumCalculation(n2: n)
default:
intermediateCalculation = (n1: n, calcMethod: symbol)
}
}
return nil
}
private func performTwoNumCalculation(n2: Double) -> Double{
if let n1 = intermediateCalculation?.n1,
let operation = intermediateCalculation?.calcMethod {
switch operation {
case "+":
return n1 + n2
case "-":
return n1 - n2
case "x":
return n1 * n2
case "÷":
return n1 / n2
default:
fatalError("The operation passed in does not match any of the cases.")
}
}
return 0.0
}
}
์ซ์์ ์์ ๋ฌธ์์ด์ ๋ด๋ intermediateCalculation ํํ ๋ณ์๋ฅผ ์ ์ธํด์ฃผ์
์ด ๋ณ์๊ฐ ๊ฐ์ด ์์ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ์ optional(?) ์ ์ถ๊ฐํด์ฃผ์ด์ผ ํ๋ค.
calculate(symbol: String) ํจ์์์ +/-, AC, %, = ๊ธฐํธ๊ฐ ์๋ +, -, x, ÷ ๊ธฐํธ์ด๋ฉด ๋ ์ซ์์ ์ฐ์ฐ์ด ํ์ํ๋ฏ๋กperformTwoNumCalculation ํจ์๋ฅผ ๋ง๋ค์ด์ ์ฐ์ฐํด์ ๊ฐ์ return ํ์.
performTwoNumCalculation ํจ์์ n2 ๊ฐ์ ์ ๋ฌํ๊ณ intermediateCalculation ํํ ๋ณ์์ ์ ์ฅ๋ n1 ์ซ์์ ์์๊ธฐํธ์ ํจ๊ป ๊ณ์ฐํด์ค๋ค. ์ด ๋, default ๋ฌธ์๋ ํน์ ๊ฐ์ ๋ณด๋ด๊ธฐ๋ณด๋ค fatalError๋ฅผ ๋ฟ๋ ค์ ์น๋ช ์ ์ธ ์๋ฌ๋ฅผ ๋ง์.
@IBAction func calcButtonPressed(_ sender: UIButton) {
//What should happen when a non-number button is pressed
isFinishedTypingNumber = true
calculator.setNumber(displayValue)
if let calcMethod = sender.currentTitle {
// guard let result = calculator.calculate(symbol: calcMethod) else {
// fatalError("The result of the calculation is nil.")
// }
if let result = calculator.calculate(symbol: calcMethod) {
displayValue = result
}
}
}
ViewController ํด๋์ค์ calcButtonPressed ํจ์ ๋ด์ guard let ๊ตฌ๋ฌธ์ if let ์ผ๋ก ๋ฐ๊ฟ์ฃผ์.
guard let ์กฐ๊ฑด๋ฌธ์ ๋ชจ๋ ์์ธ์ฌํญ์ cash ๋ฅผ ๋ธ๋ค.
if let ์กฐ๊ฑด๋ฌธ์ ์ฌ์ฉ์๊ฐ ์ด์ ์ซ์๋ ์ฐ์ฐ์์ด ์ค์๋ก = ๋ฒํผ์ ๋๋ ๋ค๋ฉด crash ๋๋๊ฒ ์๋๋ผ ๊ทธ๋ฅ ์ฝ๋๋ฅผ ๋ฌด์ํ๋ค.
๋ฐ๋ผ์ ์ด ์ํฉ์์ guard let ์ด ๊ตฌ์ง ํ์์๊ณ if let ๊ตฌ๋ฌธ์ด ๋ ๋ง๋ค.
์ต์ข ์ฑ.
'๐ฑ App Development Study > iOS๐' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Udemy iOS & Swift Bootcamp] App Design (0) | 2023.07.29 |
---|---|
[Udemy iOS & Swift Bootcamp] Swift Structures, Access Levels, Tuples (0) | 2023.07.27 |
[Udemy iOS & Swift Bootcamp] Advanced Swift Properties (Computed/Observed Properties) (0) | 2023.07.25 |
[Udemy iOS & Swift Bootcamp] Todo App(Local Data Persistance) (0) | 2023.07.18 |
[Udemy iOS & Swift Bootcamp] Project Catalyst (0) | 2023.07.17 |