Overview

In this example, we’re using some of the custom code features in Qwikly to build a clone of the iOS calculator app. In particular, we’ll look at:

  1. Using the Qwikly UI to:
    • Add 'Custom Action' interactions for the calculator buttons
  2. Editing the UIViewController subclass in our generated app to:
    • Listen for our custom actions
    • Add custom code to perform the calculator’s calculations
    • Update the calculator display

Setting up the custom actions

Each button in the calculator will trigger a custom action. Doing this in the Qwikly UI is easy:

  1. Select the view
  2. In the Interactions Inspector, click 'Add Interaction'
  3. In the Action field, select 'Custom Action'
  4. In the Selector field, enter a value

For our Calculator example, we’ll need the following custom actions set up:

  • 'add'
  • 'subtract'
  • 'multiply'
  • 'divide'
  • 'equals'
  • 'clear'
  • 'numeral0'
  • 'numeral1'
  • 'numeral9'

The View Controller file

The CalculatorUiViewController class contains all of the custom code driving our calculator. Let’s take a look at each section individually.

Next, we’ll set up some variables to keep track of our calculator’s state:

// the operators we will use
enum Operator {
  case Add
  case Multiply
  case Subtract
  case Divide
}

// the current result, as displayed by the calculator
var currentResult = ""

// the array of operands for our calculations
var operands = [Float]()

// the array of operators that will operate on our operands
var operators = [Operator]()

Our calculator will work by pushing float numbers and operators onto a stack. When the user presses ‘equals’, the calculator will perform the operations on the operands and display the result. So, we’ll need a function to get the last operand in our stack:

// get the last number in our operand stack
func lastOperand() -> Float? {
  return operands.popLast()
}

Next, we’ll set up an event listener for our ‘numeral’-type custom actions. This listener will be called whenever the user taps a numeric button:

// add a single digit character to our displayed result
func appendDigitToResult(s: String) {
  currentResult += s
  _resultLabel.text = currentResult
}

override func numeral0() {
  appendDigitToResult("0")
}

override func numeral1() {
  appendDigitToResult("1")
}

...

override func numeral9() {
  appendDigitToResult("9")
}

Now it’s time to set up event listeners for 'add', 'subtract', and the other operations at our user’s disposal. Notice that 'equals' is a special case. When that action is received, it will call a calculateResult function to perform the calculations before displaying the result in the UI.

// push the current result on to the operands stack
// and push the given operator on to the operators stack
func handleOperator(op: Operator) {
  saveCurrentResult()
  operators.append(op)
}

// push the current result on to the operands stack
// and reset the current result variable
func saveCurrentResult() {
  operands.append((currentResult as NSString).floatValue);
  currentResult = ""
}

override func add() {
  handleOperator(.Add)
}

override func subtract() {
  handleOperator(.Subtract)
}

override func multiply() {
  handleOperator(.Multiply)
}

override func divide() {
  handleOperator(.Divide)
}

Let’s look at that calculateResult method now:

func calculateResult() {
  // loop through the operators stack
  // for each operator, perform the corresponding operation
  // (e.g. add the last two operands for 'add')
  while let currentOp = operators.popLast() {
    switch (currentOp) {
    case .Add:
      operands.append(lastOperand()! + lastOperand()!)
    case .Multiply:
      operands.append(lastOperand()! * lastOperand()!)
    case .Subtract:
      let nums = (lastOperand()!, lastOperand()!)
      operands.append(nums.1 - nums.0)
    case .Divide:
      let nums = (lastOperand()!, lastOperand()!)
      operands.append(nums.1 / nums.0)
    }
  }
}

Finally, we just need to handle our ‘clear’ custom action:

override func clear() {
  currentResult = ""
  _resultLabel.text = currentResult

  operands = [Float]()
  operators = [String]()
}

That’s it! After a few minutes in the Qwikly UI and just a few lines of code, we are able to use our custom UI to build a functional Calculator!

For reference, here is the full CalculatorUiViewController file:

import UIKit

// This is the view controller for our custom calculator UI.
// This file was generated by Qwikly but will not be overwritten by it.
// When you build your app from Qwikly, it will rewrite the
// `CalculatorUiViewControllerBase`, from which this class is inherited.
class CalculatorUiViewController: CalculatorUiViewControllerBase {

  // enumerate the arithmetic operators we will support
  enum Operator {
    case Add
    case Multiply
    case Subtract
    case Divide
  }

  // the current result string, as displayed by the calculator
  var currentResult = ""

  // the numeric operands for our calculations
  var operands = [Float]()

  // the array of operators that will operate on our operands
  var operators = [Operator]()

  override func viewDidLoad() {
    super.viewDidLoad()
  }

  override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
  }

  // add a single digit character to our displayed result
  func appendDigitToResult(s: String) {
    currentResult += s
    _resultLabel.text = currentResult
  }

  // perform our calculations and store the final result in operands[0]
  func calculateResult() {
    // loop through the operators stack
    // for each operator, perform the corresponding operation
    // (e.g. add the last two operands for 'add')
    while let currentOp = operators.popLast() {
      switch (currentOp) {
      case .Add:
        operands.append(lastOperand()! + lastOperand()!)
      case .Multiply:
        operands.append(lastOperand()! * lastOperand()!)
      case .Subtract:
        let nums = (lastOperand()!, lastOperand()!)
        operands.append(nums.1 - nums.0)
      case .Divide:
        let nums = (lastOperand()!, lastOperand()!)
        operands.append(nums.1 / nums.0)
      }
    }
  }

  // push the current result on to the operands stack
  // and push the given operator on to the operators stack
  func handleOperator(op: Operator) {
    saveCurrentResult()
    operators.append(op)
  }

  // retreive the last operand
  func lastOperand() -> Float? {
    return operands.popLast()
  }

  // push the current result on to the operands stack
  // and reset the current result variable
  func saveCurrentResult() {
    operands.append((currentResult as NSString).floatValue);
    currentResult = ""
  }

  // custom action handler called by the UI to perform
  // the calculations and display the result
  override func equals() {
    saveCurrentResult()
    calculateResult()
    currentResult = "\(operands[0])"
    _resultLabel.text = currentResult
    operands = [Float]()
  }

  // custom action handler called by the UI to clear
  // the operators and operands and reset the calculator
  override func clear() {
    currentResult = ""
    _resultLabel.text = currentResult

    operands = [Float]()
    operators = [Operator]()
  }

  // custom action handler the user taps 'add'
  override func add() {
    handleOperator(.Add)
  }

  // custom action handler the user taps 'subtract'
  override func subtract() {
    handleOperator(.Subtract)
  }

  // custom action handler the user taps 'multiply'
  override func multiply() {
    handleOperator(.Multiply)
  }

  // custom action handler the user taps 'divide'
  override func divide() {
    handleOperator(.Divide)
  }

  // custom action handler the user taps '0'
  override func numeral0() {
    appendDigitToResult("0")
  }

  // custom action handler the user taps '1'
  override func numeral1() {
    appendDigitToResult("1")
  }

  // custom action handler the user taps '2'
  override func numeral2() {
    appendDigitToResult("2")
  }

  // custom action handler the user taps '3'
  override func numeral3() {
    appendDigitToResult("3")
  }

  // custom action handler the user taps '4'
  override func numeral4() {
    appendDigitToResult("4")
  }

  // custom action handler the user taps '5'
  override func numeral5() {
    appendDigitToResult("5")
  }

  // custom action handler the user taps '6'
  override func numeral6() {
    appendDigitToResult("6")
  }

  // custom action handler the user taps '7'
  override func numeral7() {
    appendDigitToResult("7")
  }

  // custom action handler the user taps '8'
  override func numeral8() {
    appendDigitToResult("8")
  }

  // custom action handler the user taps '9'
  override func numeral9() {
    appendDigitToResult("9")
  }

}