Getting Started with iOS Development for Frontend Developers: Swift Basics
Prerequisites
You need:
- A Mac computer
- An Apple ID, which you can register for free on Apple's official website
- Xcode, available for download from the Mac App Store
Prractice Environment Setup
To practice Swift syntax without creating a full iOS project, you can use Xcode's Playground:
- Open Xcode
- Go to
File > New > Playground - Select the Blank template
- Save the file to your local machine to start practicing. Playground shows you the execution result of your code in real time on the right panel.
Swift Basic Syntax
Comments
Swift uses the same comment syntax as JavaScript:
- Single line comments start with
// - Multi-line comments are wrapped in
/* */
// This is a single line comment
/*
This is a
multi-line comment
*/
Variables
Variables are declared with the var keyword. It is best practice to explicitly declare the type of the variable, though Swift can automatically infer the type if you omit it.
To explicitly declare a type, add a colon followed by the type name after the variable name.
// Explicit type declaration
var greeting: String = "Hello World"
// Type inference by Swift
var autoGreeting = "Hi there"
Variable values can be modified after declaration, but the type of a variable cannot be changed once declared, and you cannot re-declare the same variable name in the same scope. Both of the following examples will throw a compile error:
// Error: Cannot assign an Int to a String variable
var myText: String = "test"
myText = 123
// Error: Variable already declared
var myValue: Int = 10
var myValue: Int = 20
Constants
Constants are declared with the let keyword. You can assign a value when declaring the constant, or assign it later before using it. Once assigned, the value cannot be modified.
// Assign value at declaration
let defaultSiteName: String = "My Blog"
// Declare first, assign later
let apiBaseUrl: String
apiBaseUrl = "https://api.example.com"
The following code will throw an error because you cannot modify a constant after assignment:
// Error: Cannot mutate a constant
let appName: String = "DemoApp"
appName = "NewDemoApp"
Naming Rules for Variables & Constants
- Allowed characters: letters, numbers, underscores, even Chinese characters and emojis, though non-ASCII characters are not recommended for production code.
- Cannot start with a number, but can start with an underscore.
- Special characters other than underscore are not allowed.
- You can use Swift reserved words as variable names by wrapping them in backticks, though this is not recommended.
// Using a reserved word as a variable name (not recommended in production)
var `let` = "Hello"
print(`let`)
Print Output
To output values to the console, use the print() function:
var message: String = "Hello Swift"
print(message)
Common Data Types
Integer: Int
Used to store whole numbers, declaerd with the Int type:
var userAge: Int = 28
Floating Point: Float & Double
Used to store numbers with decimal points. Double has double the precision of Float, and is the default choice for most use cases.
Like JavaScript, Swift can have precision issues with floating point arithmetic:
var pi: Double = 3.1415926
var a: Double = 0.1
var b: Double = 0.2
print(a + b) // Output: 0.30000000000000004
String: String
Strings in Swift must be wrapped in double quotes (single quotes are not allowed). Common string operations:
Check if string is empty: isEmpty
var emptyStr = ""
print(emptyStr.isEmpty) // true
Get string length: count
var helloStr = "Hello"
print(helloStr.count) // 5
Concatenate strings with +
var str1 = "Hello"
var str2 = "World"
print(str1 + " " + str2) // "Hello World"
Append to string
var msg = "Hello"
msg.append(Character("!"))
print(msg) // Hello!
// Alternative:
msg = msg + "!!"
print(msg) // Hello!!!
Boolean: Bool
Boolean type only has two possible values: true and false, used for logical judgments:
var isLoggedIn: Bool = true
var isDisabled: Bool = false
Tuples
Tuples allow you to group multiple values of different types into a single compound value. You can name each element, or access them by index.
// Define a tuple with named elements: (username, age)
var user: (name: String, age: Int) = ("Alice", 28)
// Access via dot syntax with element names
print(user.name) // Alice
print(user.age) // 28
// Access via numeric index
print(user.0) // Alice
print(user.1) // 28
// Decompose a tuple into separate variables
let (userName, userAge) = user
print(userName) // Alice
print(userAge) // 28
// Ignore unused elements with _
let (_, onlyAge) = user
print(onlyAge) // 28
Array: Array
Arrays are ordered collections of same-type values, accessed by zero-based index.
Create an Array
// Empty integer array
var emptyNums: [Int] = []
// String array with initial values (type inferred)
var fruits = ["Apple", "Banana", "Orange"]
// Explicit type declaration
var explicitFruits: [String] = ["Apple", "Banana", "Orange"]
var genericFruits: Array<String> = ["Apple", "Banana", "Orange"]
// Create array with repeated values
var fiveApples = Array(repeating: "Apple", count: 5)
Common Array Operations
- Access and modify elements by index:
var fruits = ["Apple", "Banana", "Orange"]
print(fruits[0]) // Apple
fruits[0] = "Apricot"
print(fruits) // ["Apricot", "Banana", "Orange"]
- Get range of elements, modify range:
var chars = ["h", "e", "l", "l", "o"]
print(chars[1...3]) // ["e", "l", "l"]
// Replace range with new values (number of elements can differ)
chars[1...3] = ["a", "a"]
print(chars) // ["h", "a", "a", "o"]
// Delete range by assigning empty array
chars[1...2] = []
print(chars) // ["h", "o"]
- Check if array is empty, get element count:
var emptyArr: [Int] = []
var fullArr = [1, 2, 3]
print(emptyArr.isEmpty) // true
print(fullArr.count) // 3
- Add elements:
var nums = [1, 2, 3]
nums.append(4) // Add single element
nums.append(contentsOf: [5, 6, 7]) // Add multiple elements
print(nums) // [1, 2, 3, 4, 5, 6, 7]
- Remove elements:
var nums = [1, 2, 3, 4, 5]
nums.removeFirst() // Remove first element → [2, 3, 4, 5]
nums.removeFirst(2) // Remove first 2 elements → [4, 5]
nums.removeLast() // Remove last element → [4]
nums.removeLast(1) // Remove last 1 element → []
// Always check if array is not empty before removing elements, or you will get a crash!
- Check if array contains a value:
var fruits = ["Apple", "Banana", "Orange"]
print(fruits.contains("Apple")) // true
print(fruits.contains("Grape")) // false
Important Note: You can read elemants from arrays declared with
let, but you cannot modify, add, or remove elements from aletarray. Any mutation operation on aletarray will throw a compile error.
Set: Set
Sets are unordered collections of unique values, ideal for checking membership quickly.
// Create a set of integers
var uniqueNums: Set<Int> = [1, 2, 3, 2, 1]
print(uniqueNums.count) // 3 (duplicates are automatically removed)
// Common operations
print(uniqueNums.contains(2)) // true
uniqueNums.insert(4)
uniqueNums.remove(2)
uniqueNums.removeAll()
print(uniqueNums.isEmpty) // true
Dictionary: Dictionary
Dictionaries store key-value pairs, where keys are unique and used to look up corresponding values.
// Create a dictionary with string keys and string values
var userProfile: [String: String] = ["username": "alice123", "email": "alice@example.com"]
// Common operations
print(userProfile["username"]) // "alice123"
userProfile["username"] = "alice_dev" // Update existing value
userProfile["bio"] = "iOS developer" // Add new key-value pair
print(userProfile.count) // 3
print(userProfile.isEmpty) // false
userProfile.removeValue(forKey: "bio") // Remove entry
userProfile.removeAll() // Clear entire dictionary
Check Variable Type
To check the type of a value at runtime, use type(of:):
var username = "alice"
var tags = ["Swift", "iOS"]
print(type(of: username)) // String
print(type(of: tags)) // Array<String>
Operators
Swift supports most common operators found in other C-like languages:
Assignment Operator
= to assign a value to a variable/constant.
Arithmetic Operators
| Operator | Description |
|---|---|
+ |
Addition |
- |
Subtraction |
* |
Multiplication |
/ |
Division |
% |
Remainder (only works for integers) |
+=, -=, *=, /=, %= |
Compound assignment |
Note: Swift removed the
++and--increment/decrement operators starting from Swift 2.2.
Logical Operators
&& (AND), || (OR), ! (NOT). Unlike JavaScript, Swift logical operators only work with Boolean values.
Comparison Operators
==, !=, >, >=, <, <=, work the same as most other languages.
Ternary Conditional Operator
condition ? valueIfTrue : valueIfFalse
let isPassed = score >= 60 ? "Pass" : "Fail"
Range Operators
Swift has two special range operators:
a...b: Closed range, includes all values fromatob(inclusive of both)a..<b: Half-open range, includes all values fromaup to but not includingb
You can check if a value is inside a range with the ~= operator:
let validRange = 0..<10
print(validRange ~= 5) // true
print(validRange ~= 10) // false
Control Flow
If-Else
If statements in Swift do not require parentheses around the condition, but require curly braces for the body:
let score = 85
if score >= 90 {
print("Grade A")
} else if score >= 60 {
print("Pass")
} else {
print("Fail")
}
Switch
Switch statements in Swift do not require a break after each case (it is implicit), and a default case is required to handle all unmatched values. A single case can match multiple values:
let grade = "B"
switch grade {
case "A", "B", "C":
print("Pass")
case "D", "F":
print("Fail")
default:
print("Invalid grade")
}
Loops
For-In Loop
Use for-in to iterate over ranges, arrays, dictionaries, and other collections:
// Iterate over a closed range
for i in 1...5 {
print(i) // Output 1, 2, 3, 4, 5
}
// Iterate over an array
let fruits = ["Apple", "Banana", "Orange"]
for fruit in fruits {
print(fruit) // Output each fruit name
}
// Iterate over dictionary key-value pairs
let profile = ["username": "alice", "age": "28"]
for (key, value) in profile {
print("\(key): \(value)")
}
While Loop
While loops run as long as the condition is true:
var count = 0
let maxCount = 5
while count < maxCount {
print(count)
count += 1
}
Repeat-While
Repeat-While is equivalent to do-while in other languages: it runs the loop body at least once, then checks the condition:
var count = 0
repeat {
print(count)
count += 1
} while count < 5
Functions
Functions are reusable blocks of code that perform a specific task. Functions are declared with the func keyword.
Basic Function Definition
func checkPassed(score: Int) -> Bool {
return score >= 60
}
// Call the function
print(checkPassed(score: 85)) // true
You can omit parameters or the return value:
// No parameters, has return value
func getAppName() -> String {
return "Demo App"
}
// Has parameters, no return value
func printMessage(message: String) {
print(message)
}
Special Function Parameter Features
- Omit external parameter name: Use
_before the parameter name to omit the external name when calling:
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
// Call without parameter names
add(2, 3)
- Default parameter values: You can set a default value for parameters:
func greet(name: String, greeting: String = "Hello") {
print("\(greeting) \(name)")
}
greet(name: "Alice") // Uses default greeting: Hello Alice
greet(name: "Alice", greeting: "Hi") // Hi Alice
- Variadic parameters: Add
...after the type to accept any number of arguments of that type:
func sum(_ numbers: Int...) -> Int {
return numbers.reduce(0, +)
}
sum(1, 2, 3, 4) // Returns 10
- Inout parameters: By default, function parameters cannot be modified inside the function. Use
inoutto allow modifying the original external variable:
func increment(_ num: inout Int) {
num += 1
}
var value = 5
increment(&value)
print(value) // 6
Closures
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures can capture and store references to any constants and variables from the context in which they are defined.
// Counter example that demonstrates value capture
func makeCounter() -> () -> Int {
var count = 0
let increment = {
count += 1
return count
}
return increment
}
let counter = makeCounter()
print(counter()) // 1
print(counter()) // 2
print(counter()) // 3
Inline closure syntax:
let add: (Int, Int) -> Int = { a, b in
return a + b
}
add(2, 3) // 5
Custom Types
Enums
Enums define a group of related values with a common type:
enum AppTheme: String {
case light = "Light Mode"
case dark = "Dark Mode"
}
var currentTheme = AppTheme.dark
print(currentTheme.rawValue) // Dark Mode
Structs vs Classes
Swift has two main types for defining custom data structures: structs and classes. The key difference is that structs are value types and classes are reference types.
Struct Definition
struct User {
var name: String
var age: Int
mutating func increaseAge() {
age += 1
}
}
Class Definition
class User {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func increaseAge() {
age += 1
}
}
Value vs Reference Type
When you assign a value type (struct) to a new variable, it creates a full copy of the original value, so modifying the copy does not affect the original:
struct UserStruct {
var name: String
}
var user1 = UserStruct(name: "Alice")
var user2 = user1
user2.name = "Bob"
print(user1.name) // Alice (original unchanged)
print(user2.name) // Bob
When you assign a reference type (class) to a new variable, it copies a reference to the same underlying instance, so modifying the copy modifies the original:
class UserClass {
var name: String
init(name: String) {
self.name = name
}
}
var userA = UserClass(name: "Alice")
var userB = userA
userB.name = "Bob"
print(userA.name) // Bob (original modified)
print(userB.name) // Bob
iOS Project Setup
Create a New Project
- Open Xcode, select Create a new Xcode project
- Select App as the project template, click Next
- Fill in your project details, select Storyboard as the Interface option, click Next
- Select a location to save your project and create it.
Run the Project
The default project structure for a new iOS project:
AppDelegate: Handles application lifecycle eventsSceneDelegate: Manages multiple app windows/scenesViewController: The default root view controller for your appMain.storyboard: The visual interface fileAssets.xcassets: Stores image and other static assetsLaunchScreen.storyboard: The launch screen shown when the app startsInfo.plist: Project configuration settings
To run the project: select an iOS simulator from the dropdown in the top-left corner, then click the play button. The simulator will launch and run your app.
Basic iOS UI Components
UILabel (Text Label)
UILabel is used to display static text on the screen. Add the following code to your ViewController.swift's viewDidLoad method to create a label:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// Create a label with position and size
let titleLabel = UILabel(frame: CGRect(x: 20, y: 100, width: 200, height: 60))
titleLabel.text = "Hello iOS"
titleLabel.font = .systemFont(ofSize: 30)
titleLabel.textColor = .systemBlue
titleLabel.shadowColor = .systemGreen
titleLabel.shadowOffset = CGSize(width: 2, height: 2)
view.addSubview(titleLabel)
}
}
To enable multi-line text, set the numberOfLines property: set it to 0 for unlimited lines (requires enough height to fit all text):
let longTextLabel = UILabel(frame: CGRect(x: 20, y: 200, width: 300, height: 200))
longTextLabel.text = "This is a long piece of text that will wrap across multiple lines when it reaches the edge of the label"
longTextLabel.numberOfLines = 0
To apply custom styling to parts of the text, use NSMutableAttributedString:
let attributedLabel = UILabel(frame: CGRect(x: 20, y: 400, width: 300, height: 100))
let text = "Hello, this is colored text"
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttributes([
NSAttributedString.Key.foregroundColor: UIColor.red,
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 24)
], range: NSRange(location: 12, length: 12))
attributedLabel.attributedText = attributedText
attributedLabel.numberOfLines = 0
view.addSubview(attributedLabel)
UIButton (Button)
UIButton is the standard interactive control for handling taps.
// Create a system-style button
let actionButton = UIButton(type: .system)
actionButton.frame = CGRect(x: 20, y: 550, width: 100, height: 40)
actionButton.setTitle("Tap Me", for: .normal)
// Add tap event handler
actionButton.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
view.addSubview(actionButton)
Event handler method:
@objc func buttonTapped() {
print("Button was tapped")
}
For custom-styled buttons, use the custom type, which allows you to set custom background colors, highlight states, and images:
let customButton = UIButton(type: .custom)
customButton.frame = CGRect(x: 140, y: 550, width: 150, height: 40)
customButton.backgroundColor = .systemBlue
customButton.setTitle("Custom Button", for: .normal)
customButton.setTitleColor(.white, for: .normal)
customButton.setTitleColor(.lightGray, for: .highlighted)
customButton.layer.cornerRadius = 8
view.addSubview(customButton)
To create an image button, first add your image file to Assets.xcassets, then set it on the button:
let imageButton = UIButton(type: .custom)
imageButton.frame = CGRect(x: 20, y: 620, width: 100, height: 100)
imageButton.setImage(UIImage(named: "your-image-name"), for: .normal)
view.addSubview(imageButton)
UIImageView (Image View)
UIImageView is used to display static images on screen:
// Add your image to Assets.xcassets first
let productImage = UIImage(named: "product")
let imageView = UIImageView(image: productImage)
imageView.frame = CGRect(x: 20, y: 100, width: 200, height: 150)
// Get original image size if needed:
if let size = productImage?.size {
print("Original image size: \(size)")
}
view.addSubview(imageView)
UITextField (Text Input Field)
UITextField is used for single-line text input from the user.
let emailField = UITextField(frame: CGRect(x: 20, y: 750, width: 280, height: 40))
emailField.placeholder = "Enter your email"
emailField.borderStyle = .roundedRect
view.addSubview(emailField)
To get the input text when a button is tapped:
@objc func handleSubmit() {
guard let text = emailField.text else { return }
print("User input: \(text)")
}
You can use the delegate pattern to listen for editing events:
// Make ViewController conform to UITextFieldDelegate
class ViewController: UIViewController, UITextFieldDelegate {
// ... in viewDidLoad:
emailField.delegate = self
// Delegate methods:
func textFieldDidBeginEditing(_ textField: UITextField) {
print("User started editing")
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}