Building a Simple Calculator App with Swift and UIKit
A calculator is a small project with enough moving parts to teach the fundamentals of iOS development. You will create a screen with UIKit, connect buttons to actions, store the current number, perform arithmetic, and update the display after every operation. The finished app can be simple, but the decisions behind it are useful in larger projects.
This project is also a practical way to become comfortable with Swift and Xcode. You do not need a complex design or an external library. A clean calculator built for an iPhone used in Sydney, Brisbane, or Perth can teach layout, state management, error handling, accessibility, and testing in one compact exercise.
Choose The Calculator’s Scope
Start with four operations: addition, subtraction, multiplication, and division. Include decimal input, a clear button, and an equals button. This gives the app a useful feature set without creating a confusing first project. Scientific functions, memory buttons, percentage calculations, and expression history can come later.
Create a new iOS project in Xcode using the App template. Select Swift as the language and UIKit for the interface. You can choose Storyboard or a fully programmatic layout; programmatic UIKit is a strong choice if you want to understand views and Auto Layout directly. Set the deployment target to a version that supports the iPhones you want to reach in the Australian App Store.
A calculator should represent its values consistently. Use Double for a beginner-friendly implementation, while remembering that floating-point numbers can produce results such as 0.30000000000000004. For a basic learning app, formatting the visible result to a sensible number of decimal places is acceptable. A financial app dealing with Australian dollars, GST, or cents would need more careful decimal arithmetic.
Build The Interface With UIKit
The main screen needs a display at the top and a grid of buttons beneath it. A vertical UIStackView can hold the display and a set of horizontal button rows. Stack views reduce the amount of constraint code and make the layout adapt to different iPhone sizes, including compact devices and larger Pro Max screens.
Create a UILabel for the result and configure it for right alignment, a large font, and a starting value of "0". Buttons can use titles such as 7, 8, 9, ÷, and +. Use a dark background for the display, a contrasting colour for operation buttons, and a larger accent colour for equals. Keep text large enough for comfortable use on a tram ride in Melbourne or while standing in a busy Brisbane station.
private let displayLabel: UILabel = {
let label = UILabel()
label.text = "0"
label.textColor = .white
label.font = .systemFont(ofSize: 52, weight: .light)
label.textAlignment = .right
label.adjustsFontSizeToFitWidth = true
return label
}()
Each button should have a visible title, a reasonable height, and enough spacing to prevent accidental taps. Add rounded corners and a highlighted background when the user presses a button. Use the safe area so the bottom row does not conflict with the home indicator on modern iPhones.
Connect Buttons And Track Input
The calculator needs a small amount of state. Store the number currently displayed, the first number in an operation, the selected operator, and whether the next digit should begin a new number.
private var firstValue: Double?
private var pendingOperation: String?
private var shouldResetDisplay = false
When a digit button is pressed, append its value to the display. If shouldResetDisplay is true, replace the old result instead. The decimal button should add a period only when the current text does not already contain one. This prevents input such as 12.4.6.
@objc private func numberTapped(_ sender: UIButton) {
guard let digit = sender.currentTitle else { return }
if shouldResetDisplay || displayLabel.text == "0" {
displayLabel.text = digit
shouldResetDisplay = false
} else {
displayLabel.text?.append(digit)
}
}
@objc private func decimalTapped() {
guard !(displayLabel.text?.contains(".") ?? true) else { return }
displayLabel.text?.append(".")
}
For each operation button, save the displayed number and operator. If the user enters 12, taps +, and enters 8, the app should retain 12 while the second value is being entered. A single pendingOperation property is enough for a basic calculator. Later, you can add a full expression parser if you want the app to support sequences such as 2 + 3 × 4.
Perform Calculations Safely
The equals action converts the display text into a Double, applies the selected operation, and writes the result back to the label. Keep the arithmetic in a separate method so the button action remains easy to read and maintain.
@objc private func equalsTapped() {
guard
let firstValue,
let operation = pendingOperation,
let secondValue = Double(displayLabel.text ?? "")
else { return }
let result: Double
switch operation {
case "+":
result = firstValue + secondValue
case "−":
result = firstValue - secondValue
case "×":
result = firstValue * secondValue
case "÷":
guard secondValue != 0 else {
displayLabel.text = "Error"
shouldResetDisplay = true
return
}
result = firstValue / secondValue
default:
return
}
displayLabel.text = format(result)
pendingOperation = nil
self.firstValue = nil
shouldResetDisplay = true
}
The format method keeps the interface readable by removing unnecessary zeroes. A result such as 7.0 should usually appear as 7, while 7.5 should remain 7.5.
private func format(_ value: Double) -> String {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 8
formatter.minimumFractionDigits = 0
return formatter.string(from: NSNumber(value: value)) ?? "\(value)"
}
Consider the difference between immediate calculation and normal mathematical precedence. A simple button calculator often evaluates 2 + 3 when the next operator is tapped, then continues from 5. That behaviour is easy to implement and matches many handheld calculators. If you want standard precedence, such as multiplication before addition, the architecture must store and parse a complete expression.
Handle Errors And Accessibility
The clear button should reset every property as well as the display. It should be safe to press at any time, including directly after an error. Division by zero should never crash the application; show "Error" and make the next number replace that text.
@objc private func clearTapped() {
displayLabel.text = "0"
firstValue = nil
pendingOperation = nil
shouldResetDisplay = false
}
Accessibility deserves attention even in a small personal project. Set an accessibility label for every button, especially when the visible symbol is unfamiliar. VoiceOver users should hear “multiply” rather than only “times symbol”. Avoid relying on colour alone to distinguish operators, and check that Dynamic Type or a large accessibility setting does not hide the display.
The Australian market includes users with different languages, abilities, and device preferences. If you later add currency conversion for Australian dollars, use locale-aware formatting rather than manually adding $. A value such as $1,234.50 should be generated with NumberFormatter and an en_AU locale, particularly if the app ever handles GST calculations or prices from Australian retailers.
Test, Polish, And Prepare The App
Run the app in the iPhone Simulator and on a physical device if possible. Test ordinary calculations such as 8 + 4, negative results such as 3 − 9, decimal values such as 1.5 × 2, and long numbers that may not fit comfortably on screen. Test pressing equals repeatedly, starting with an operator, entering several decimal points, and dividing by zero.
Use XCTest for the calculation logic once the basic interface works. A small calculator can have a separate model with methods such as add, subtract, multiply, and divide. Unit tests then verify the arithmetic without requiring taps. UI tests can check that entering 9, ×, 6, and = produces 54.
Polish the app by adding haptic feedback, a launch icon, and a short name that fits beneath the icon. Test light and dark mode, landscape orientation, and different Dynamic Type settings. If you publish through App Store Connect, prepare screenshots that reflect the Australian storefront and write clear metadata rather than claiming features the app does not have. Local testing across networks and time zones is useful when working between Perth, Adelaide, and the east coast, especially for future apps that include dates or scheduled data.
Swift and UIKit become easier to understand when a project has a visible result. Each button action demonstrates event handling, each property represents state, and each layout constraint contributes to a responsive interface. You can extend this calculator with percentage support, calculation history, keyboard input, or a SwiftUI version after the UIKit foundation is reliable.
The key lesson is to keep the first version small: build a clear interface, store state deliberately, handle invalid input safely, and test the behaviour users depend on. A simple calculator made with Swift and UIKit can become a compact portfolio project that proves you understand the essential habits of iOS development.