Using Classes and Initializers in Swift
Learn how to create custom classes with initializers in Swift to model your data, leading to cleaner, more efficient, and more organized code.
Note:
This tutorial explains how to use Swift classes and init methods to structure your data, making your code more readable and maintainable.
Overview
Creating a more efficient and clean coding structure is crucial for project clarity and scalability. Instead of constantly creating and passing around individual variables, you can use a Class with an initializer (init) to bundle related data together. This approach is fundamental to object-oriented programming and greatly benefits developers.
In this project, we'll refactor our previous UITableView example to use a custom City class, making it easier to manage city data (image, name, and region) and pass it between ViewControllers.
Reference:
Step 1: Project Setup
The initial project setup is similar to the previous tutorial. You will need:
- A
UITableViewControlleras the main view. - A
DetailsViewControllerto show the details. - A segue with the identifier
toDetailsVCconnecting a table view cell to theDetailsViewController.
Step 2: Creating the City Class
Create a new Swift file named City.swift. This file will contain our custom class for modeling city data.
Using a class allows us to define a blueprint for an object. The init method is the constructor that initializes the properties of the object when it's created.
// In City.swift
import Foundation
import UIKit // Needed for UIImage
class City {
var name: String
var region: String
var image: UIImage
init(name: String, region: String, image: UIImage) {
self.name = name
self.region = region
self.image = image
}
}
Step 3: Populating Data Using the City Class
In your main ViewController, instead of creating separate arrays for names and images, we'll create a single array of City objects.
Create the Array
First, declare an empty array that will hold City objects.
var cityArray = [City]()
Populate the Array
In viewDidLoad, create instances of your City class and append them to the array. This is much cleaner than managing multiple parallel arrays.
// In ViewController.swift's viewDidLoad
let istanbul = City(name: "Istanbul", region: "Marmara", image: UIImage(named: "istanbul")!)
let ankara = City(name: "Ankara", region: "İç Anadolu", image: UIImage(named: "ankara")!)
let izmir = City(name: "İzmir", region: "Ege", image: UIImage(named: "izmir")!)
cityArray.append(istanbul)
cityArray.append(ankara)
cityArray.append(izmir)
Step 4: Updating the TableView
Now, update the UITableViewDataSource methods to work with the cityArray.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cityArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
// Access the name property of the City object at the current row
cell.textLabel?.text = cityArray[indexPath.row].name
return cell
}
Step 5: Passing the City Object via Segue
This is where using a class really shines. We only need to pass one City object instead of multiple variables.
Handle Row Selection
In didSelectRowAt, get the selected City object and trigger the segue. We'll use a variable userChoice to hold the selected object.
// In ViewController.swift, declared at the class level
var userChoice: City?
// ...
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
userChoice = cityArray[indexPath.row]
performSegue(withIdentifier: "toDetailsVC", sender: nil)
}
Prepare for Segue
In prepare(for:sender:), pass the entire userChoice object to the DetailsViewController.
// In ViewController.swift
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toDetailsVC" {
if let destinationVC = segue.destination as? DetailsViewController {
destinationVC.selectedCity = userChoice
}
}
}
Step 6: Updating the DetailsViewController
Finally, modify the DetailsViewController to receive a single City object and display its properties.
// In DetailsViewController.swift
import UIKit
class DetailsViewController: UIViewController {
@IBOutlet weak var cityImageView: UIImageView!
@IBOutlet weak var cityNameLabel: UILabel!
@IBOutlet weak var cityRegionLabel: UILabel!
// A single variable to hold the passed object
var selectedCity: City?
override func viewDidLoad() {
super.viewDidLoad()
// Use the properties of the selectedCity object
cityNameLabel.text = selectedCity?.name
cityRegionLabel.text = selectedCity?.region
cityImageView.image = selectedCity?.image
}
}
Note:
By modeling your data with classes, you create a more robust, organized, and scalable codebase. Passing entire objects between view controllers is much cleaner than handling multiple individual variables.