delcrative vs imperative

iOS UIKit vs SwiftUI

UIKit (Imperative Approach):

First, you would create a view controller, which is usually done using Interface Builder and a storyboard, or programmatically in a Swift file.
Here's how you might create a button programmatically in a UIViewController subclass:


import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton(type: .system) // Create a UIButton instance
        button.setTitle("Tap me!", for: .normal) // Set button title
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) // Add target for button tap event
        
        button.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(button) // Add button to view
        
        NSLayoutConstraint.activate([
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ]) // Center button in the view
    }
    
    @objc func buttonTapped() {
        print("Button tapped!")
    }
}

In the SwiftUI example, you declare the UI and its behavior directly in a single block of code. The Button view automatically handles the tap event and triggers the action closure when the user taps the button.

In summary, UIKit uses an imperative approach, where you create UI elements, set their properties, and add event handlers manually. In contrast, SwiftUI uses a declarative approach, where you define the UI and its behavior in a more concise and readable way, and the framework automatically handles updates and events.

import SwiftUI

struct ContentView: View {
    var body: some View {
        Button(action: {
            print("Button tapped!")
        }) {
            Text("Tap me!")
        }
    }
}

Example 1: Creating a label and a text field

UIKit (Imperative Approach):

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let label = UILabel()
        label.text = "Name:"
        
        let textField = UITextField()
        textField.borderStyle = .roundedRect
        textField.placeholder = "Enter your name"
        
        label.translatesAutoresizingMaskIntoConstraints = false
        textField.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(label)
        view.addSubview(textField)
        
        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            label.topAnchor.constraint(equalTo: view.topAnchor, constant: 100),
            
            textField.leadingAnchor.constraint(equalTo: label.trailingAnchor, constant: 10),
            textField.centerYAnchor.constraint(equalTo: label.centerYAnchor),
            textField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
        ])
    }
}

SwiftUI (Declarative Approach):

import SwiftUI

struct ContentView: View {
    @State private var name = ""
    
    var body: some View {
        HStack {
            Text("Name:")
            TextField("Enter your name", text: $name)
                .textFieldStyle(RoundedBorderTextFieldStyle())
        }
        .padding()
    }
}

Example 2: Creating a simple table view (list)

UIKit (Imperative Approach):


import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    let items = ["Item 1", "Item 2", "Item 3"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tableView = UITableView()
        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)
        
        NSLayoutConstraint.activate([
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel?.text = items[indexPath.row]
        return cell
    }
}
import SwiftUI

struct ContentView: View {
    let items = ["Item 1", "Item 2", "Item 3"]
    
    var body: some View {
        List(items, id: \.self) { item in
            Text(item)
        }
    }
}

These examples further demonstrate the difference between UIKit's imperative approach and SwiftUI's declarative approach. In UIKit, you create UI elements, set their properties, manage layout constraints, and implement delegate methods. With SwiftUI, you define the UI structure and behavior in a more concise way, and the framework handles the rest.

Android UI vs Jetpack Compose

Android UI development is primarily imperative, similar to iOS's UIKit. In Android development, you create user interfaces by instantiating UI components called "views," setting their properties, and adding them to a view hierarchy. You can create views using XML layouts, which are a more declarative way of defining UI structure, or you can build them programmatically in Java or Kotlin code.

For example, here's how you might create a simple button with a label in Android using an XML layout (declarative approach) and an onClick listener in Kotlin (imperative approach):

Create an XML layout file called activity_main.xml:



<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tap me!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

In the MainActivity Kotlin class, set the content view to the XML layout and add an onClick listener:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.Toast

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val button: Button = findViewById(R.id.button)
        button.setOnClickListener {
            Toast.makeText(this, "Button tapped!", Toast.LENGTH_SHORT).show()
        }
    }
}
Select a repo