Swift 계산기 만들기 v0.4

MVC 모델 적용 및 Enum, Closure 활용

연산 처리를 담당하는 클래스 (모델)

class CalculatorModel {
    
    // 연산 타입 정의 : 단일, 이항, 등호
    // Associated Value 값에 클로저 대입
    private enum OperatoinCase {
        case unary((Double) -> Double)
        case binary((Double,Double) -> Double)
        case equal
    }

    
    // 연산 기호 : 연산 타입 딕셔너리 생성
    private var operDic: [String:OperatoinCase] = [
        "+": .binary({(num1, num2) -> Double in return num1 + num2}),
        "-": .binary({(num1, num2) -> Double in return num1 - num2}),
        "*": .binary({(num1, num2) -> Double in return num1 * num2}),
        "/": .binary({(num1, num2) -> Double in return num1 / num2}),
        "cos": .unary(cos),
        "√": .unary(sqrt),
        "±": .unary({(num1: Double) -> Double in return -num1}),
        "=": .equal
    ]

    
    // 연산을 기다리는 옵셔널 Double 변수 생성
    var leftNumber: Double? 
    
    // 연산 기호 클릭 시 leftNum에 디스플레이 값 대입
    func setNumber(displayNum: Double){
        leftNumber = displayNum
    }
    
    // 바이너리 연산을 위한 스트럭트 WaitingBinary의 인스턴스
    private var waitingBinary: WaitingBinary?
    
    // 연산 클릭시 연산 수행, 연속 연산을 위한 축적값 operand 생성
    var operand: Double?
    func perfomrOperation(mathSymbol: String) {
        if let operationCase = operDic[mathSymbol] {
            switch operationCase {
            case .unary(let function):
                if leftNumber != nil {
                    leftNumber = function(leftNumber!)
                }
            case .binary(let binaryFunction):
            if leftNumber != nil && operand == nil {
                operand = leftNumber!
                waitingBinary = WaitingBinary(firstNum: operand!, waitingFunc: binaryFunction)
            }else if leftNumber != nil && operand != nil {
                operand! += leftNumber!
                waitingBinary = WaitingBinary(firstNum: operand!, waitingFunc: binaryFunction)
            }else if operand != nil && leftNumber == nil {
                waitingBinary = WaitingBinary(firstNum: operand!, waitingFunc: binaryFunction)
            }
            leftNumber = nil
            case .equal:
                getResult()
                operand = nil
            }
    	}
    }
    
    // 바이너리 값 저장하고 있을 스트럭트
    private struct WaitingBinary {
        let firstNum: Double
        let waitingFunc: (Double,Double) -> Double
        func doBinaryOp(with secondNum: Double) -> Double {
            return waitingFunc(firstNum, secondNum)
        }
    }

    // 바이너리 연산 값 구하기
    private func getResult() {
        if waitingBinary != nil && leftNumber != nil {
            leftNumber = waitingBinary!.doBinaryOp(with: leftNumber!)
        }
    }
    
    // 뷰콘트롤러에 넘겨줄 연산 완료된 값
    var returnValue: Double? {
        return leftNumber
    }
    
    
}

뷰 콘트롤러

class UpgradeCalculatorViewControler: UIViewController {

    //MARK: - UI Property
    @IBOutlet weak var displayLB: UILabel!

    //MARK: - IBAction
    //숫자 입력
    var isTyping: Bool = false
    @IBAction func digit(_ sender: UIButton) {
        let currentDigit = sender.currentTitle!
        if isTyping {
            let textCurrentlyInDisplay = displayLB!.text!
            displayLB!.text = textCurrentlyInDisplay + currentDigit
        }else {
            displayLB!.text = currentDigit
            isTyping = true
        }
    }
    
    // 리셋
    @IBAction func resetHandler(_ sender: UIButton) {
        displayValue = 0.0
        isTyping = false
        calModel.setNumber(displayNum: displayValue)
    }
    
    // 전시된 값을 get 하거나 set하는 displayValue
    var displayValue: Double {
        get {
            return Double(displayLB.text!)!
        }
        set {
        	displayLB.text! = String(newValue)
        }
    }
    
    // operation
    var calModel = CalculatorModel()
    @IBAction func operation(_ sender: UIButton) {
        if isTyping { // check
            calModel.setNumber(displayNum: displayValue)
            isTyping = false
        }
        
        guard let symbol = sender.currentTitle else { return }
        calModel.perfomrOperation(mathSymbol: symbol)

        if calModel.returnValue != nil {
            displayValue = calModel.returnValue!
        }
    }

}