Unit 5: iPhone Project Templates

INT372 — Iphone Application Programming 9 min read

I. Orientation — UIKit Project Structure

An iPhone project template provides the initial files, configuration, application lifecycle, and interface structure needed to build an iOS application. In UIKit projects, the interface consists of views managed by view controllers, while container controllers such as UINavigationController and UITabBarController coordinate movement between screens.

  • Project foundation: An Xcode iOS application normally contains Swift source files, asset catalogs, configuration settings, and an application entry point.
  • UIKit architecture: UIViewController manages a screen, UIView displays content, and UIKit controls such as UIButton, UICollectionView, and UIToolbar handle interaction.
  • Application lifecycle: UIApplicationDelegate handles application-level events, while UISceneDelegate manages window scenes in scene-based UIKit applications.
  • Interface construction:
    • Storyboards describe scenes and connections visually.
    • Programmatic UIKit constructs views and constraints in Swift.
  • Delegation convention: UIKit objects frequently report events through delegate protocols, such as UICollectionViewDelegate.
  • Data-source convention: Views that display collections request their content from a data source, such as UICollectionViewDataSource.
  • Model-view-controller principle: Models hold application data, views present it, and controllers coordinate data, presentation, and user actions.
  • Main-thread rule: Interface updates must occur on the main thread because UIKit is not thread-safe.
  • Persistence principle: Temporary state remains in memory, while durable data can be stored in files, preferences, Core Data, or an SQLite database.

II. Navigation Controllers — Hierarchical Screen Movement

A. Using navigation controllers

A navigation controller manages a stack of view controllers and supports forward and backward movement through hierarchical content.

  • Core class: UINavigationController is a container controller whose viewControllers property represents the navigation stack.
  • Root controller: The first controller forms the base of the hierarchy and cannot be removed by an ordinary back operation.
  • Push operation: pushViewController(_:animated:) places a controller on top of the stack.
  • Pop operation: popViewController(animated:) removes the top controller and reveals the preceding screen.
  • Navigation bar: UINavigationBar commonly displays a title, a back button, and UIBarButtonItem commands.
  • Controller configuration: Each screen configures its bar through its navigationItem, rather than modifying the navigation bar directly.
SWIFT
let detail = DetailViewController()
detail.title = "Details"
navigationController?.pushViewController(detail, animated: true)

// Inside DetailViewController:
navigationController?.popViewController(animated: true)
  • Storyboard navigation: A navigation controller can be made the initial controller, with its root relationship connected to the first content controller.
  • Programmatic setup: The window’s root controller can be assigned explicitly:
SWIFT
let home = HomeViewController()
window?.rootViewController =
    UINavigationController(rootViewController: home)
  • Limitation: Stack navigation suits drill-down workflows such as “Albums → Songs”; unrelated destinations are better represented by tabs.

III. Auto Layout — Constraint-Based Interface Design

A. Concept of auto-layout

Auto Layout calculates view positions and sizes from constraints, allowing one interface to adapt to different iPhone dimensions, orientations, safe areas, and content sizes.

  • Constraint relationship: A constraint expresses a relation between layout attributes:
TEXT
item1.attribute1 = multiplier × item2.attribute2 + constant
  • Symbols:
    • item1 and item2 are views or layout guides.
    • attribute is a property such as leading, width, or centerY.
    • multiplier scales the second attribute.
    • constant adds a fixed distance measured in points.
  • Common anchors: leadingAnchor, trailingAnchor, topAnchor, bottomAnchor, widthAnchor, and centerXAnchor create type-safe constraints.
  • Safe area: view.safeAreaLayoutGuide avoids system-covered regions such as the status bar and Home indicator.
  • Programmatic requirement: translatesAutoresizingMaskIntoConstraints must normally be set to false for manually constrained views.
SWIFT
label.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
    label.leadingAnchor.constraint(
        equalTo: view.safeAreaLayoutGuide.leadingAnchor,
        constant: 16
    ),
    label.trailingAnchor.constraint(
        equalTo: view.safeAreaLayoutGuide.trailingAnchor,
        constant: -16
    ),
    label.topAnchor.constraint(
        equalTo: view.safeAreaLayoutGuide.topAnchor,
        constant: 20
    )
])
  • Intrinsic content size: Controls such as labels and buttons provide a natural size based on their content.
  • Priority system: Priorities range from 1 to 1000; 1000 means required. Content-hugging resists expansion, while compression-resistance resists shrinking.
  • Layout errors:
    • Ambiguous layouts lack enough constraints to determine a unique frame.
    • Unsatisfiable layouts contain mutually incompatible required constraints.
  • Adaptation: Size classes and trait collections supplement constraints when compact and regular environments require structurally different layouts.

IV. Media Playback — Audio and Video Services

A. Playing audio and video files

Apple’s AVFoundation framework provides the principal APIs for playing local or remote audio and video media.

  • Framework import: Media classes become available through import AVFoundation; video interfaces may additionally use AVKit.
  • Audio playback: AVAudioPlayer is suitable for a local sound file when precise playback control is required.
  • Preparation: The player must remain strongly referenced; a local variable may be deallocated and stop playback.
SWIFT
import AVFoundation

var audioPlayer: AVAudioPlayer?

func playAudio() {
    guard let url = Bundle.main.url(
        forResource: "music",
        withExtension: "mp3"
    ) else { return }

    audioPlayer = try? AVAudioPlayer(contentsOf: url)
    audioPlayer?.prepareToPlay()
    audioPlayer?.play()
}
  • Audio controls: play(), pause(), stop(), volume, and currentTime control reproduction.
  • Audio session: AVAudioSession defines behavior relative to silent mode, background audio, recording, and other applications’ sound.
  • Video playback: AVPlayerViewController supplies standard playback controls around an AVPlayer.
SWIFT
import AVKit

let player = AVPlayer(url: videoURL)
let controller = AVPlayerViewController()
controller.player = player
present(controller, animated: true) {
    player.play()
}
  • Local and remote media: A bundle URL addresses packaged media, whereas an HTTPS URL can identify streamed content.
  • Lifecycle handling: Playback should pause or release resources when the interface disappears if continued playback is not intended.
  • Limitations: Network playback requires buffering and failure handling; protected or unsupported formats cannot be treated as ordinary media files.

V. Collection Views — Reusable Grid and List Interfaces

A. Implementing UICollectionView

A collection view presents reusable cells in layouts ranging from simple lists and grids to compositional, section-based arrangements.

  • Core object: UICollectionView separates data, cell presentation, layout, and interaction.
  • Data source: UICollectionViewDataSource supplies section counts, item counts, and configured cells.
  • Delegate: UICollectionViewDelegate reports interactions such as item selection.
  • Cell registration: A cell class or nib must be registered with a reuse identifier before dequeueing.
  • Reuse mechanism: dequeueReusableCell(withReuseIdentifier:for:) recycles off-screen cells, reducing object creation and memory use.
SWIFT
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(
    UICollectionViewCell.self,
    forCellWithReuseIdentifier: "Cell"
)

func collectionView(
    _ collectionView: UICollectionView,
    numberOfItemsInSection section: Int
) -> Int {
    items.count
}

func collectionView(
    _ collectionView: UICollectionView,
    cellForItemAt indexPath: IndexPath
) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(
        withReuseIdentifier: "Cell",
        for: indexPath
    )
    cell.contentView.backgroundColor = .systemBlue
    return cell
}
  • Index paths: IndexPath.section identifies a section and IndexPath.item identifies an item within it.
  • Flow layout: UICollectionViewFlowLayout supports item size, spacing, scrolling direction, headers, and footers.
  • Modern layout: UICollectionViewCompositionalLayout describes sections using items, groups, and sections for more complex arrangements.
  • Data updates: reloadData() refreshes everything; batch updates or diffable data sources animate precise insertions, removals, and moves.
  • Performance rule: Expensive image loading should occur asynchronously, and completion code must confirm that the reused cell still represents the intended item.

VI. Toolbars — Contextual Command Presentation

A. Implementing UIToolbar

A toolbar presents commands that apply to the current screen or selected content, commonly along the bottom edge of an interface.

  • Core class: UIToolbar displays an ordered array of UIBarButtonItem objects.
  • Command items: Bar button items may contain system icons, titles, custom views, or menu actions.
  • Flexible spacing: .flexibleSpace() distributes commands across the available toolbar width.
  • Target-action pattern: A button invokes a selector on its target when tapped.
SWIFT
let add = UIBarButtonItem(
    systemItem: .add,
    primaryAction: UIAction { _ in
        self.addRecord()
    }
)
let space = UIBarButtonItem(systemItem: .flexibleSpace)
let trash = UIBarButtonItem(
    systemItem: .trash,
    primaryAction: UIAction { _ in
        self.deleteRecord()
    }
)

toolbar.setItems([add, space, trash], animated: false)
  • Navigation integration: A navigation controller provides a managed toolbar through setToolbarHidden(_:animated:) and each controller’s toolbarItems.
  • Layout: A standalone toolbar should be constrained to the view’s leading, trailing, and safe-area bottom anchors.
  • State feedback: Setting isEnabled to false communicates that a command is unavailable.
  • Distinction: A toolbar performs actions such as add or delete; it should not be used as primary navigation between independent application sections.

VII. Tab Bars — Parallel Application Sections

A. UITabBar in applications

A tab bar provides persistent switching among peer-level sections, with each tab maintaining a distinct application context.

  • Container controller: UITabBarController manages child view controllers and coordinates the UITabBar.
  • Tab representation: Each child defines a UITabBarItem containing a title, image, selected image, or badge.
  • Parallel hierarchy: A tab commonly contains its own navigation controller, preserving a separate navigation stack for that section.
SWIFT
let home = UINavigationController(
    rootViewController: HomeViewController()
)
home.tabBarItem = UITabBarItem(
    title: "Home",
    image: UIImage(systemName: "house"),
    tag: 0
)

let settings = SettingsViewController()
settings.tabBarItem = UITabBarItem(
    title: "Settings",
    image: UIImage(systemName: "gearshape"),
    tag: 1
)

let tabs = UITabBarController()
tabs.viewControllers = [home, settings]
  • Selection: selectedIndex changes the active controller programmatically; the default initial index is 0.
  • Delegate events: UITabBarControllerDelegate can approve selection or react when a tab becomes active.
  • Badges: tabBarItem.badgeValue displays concise status such as an unread count.
  • Design constraint: Tabs should represent stable top-level destinations, not temporary actions or sequential workflow steps.
  • Scalability: Too many controllers reduce clarity and may cause UIKit to expose additional destinations through a “More” interface.

VIII. SQLite — Relational Data Persistence

A. Database using SQLite

SQLite is an embedded relational database engine that stores structured application data in a local file without requiring a separate database server.

  • Database model: Data is organized into tables containing rows and columns; SQL statements create, read, update, and delete records.
  • Primary key: INTEGER PRIMARY KEY supplies a unique row identifier and corresponds to SQLite’s row identifier behavior.
  • Schema example:
SQL
CREATE TABLE IF NOT EXISTS notes (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL,
    body TEXT,
    created_at REAL NOT NULL
);
  • Column meanings:
    • id uniquely identifies a note.
    • title requires text because of NOT NULL.
    • body stores optional text.
    • created_at can store a timestamp as a numeric value.
  • C API workflow: Import SQLite3, open a database with sqlite3_open, prepare SQL using sqlite3_prepare_v2, bind values, execute with sqlite3_step, and release statements using sqlite3_finalize.
  • Parameter binding: Placeholders prevent quotation errors and SQL injection.
SQL
INSERT INTO notes (title, body, created_at)
VALUES (?, ?, ?);
  • Result reading: A successful query returns rows through repeated sqlite3_step calls while the result equals SQLITE_ROW.
  • Transactions: BEGIN, COMMIT, and ROLLBACK make related changes atomic; either the complete group succeeds or it is undone.
  • Concurrency: Database operations should run away from the main thread to prevent interface stalls, while resulting UIKit updates return to the main thread.
  • Storage location: A writable database belongs in the application’s sandbox, commonly under Application Support or Documents, not inside the read-only application bundle.
  • Resource safety: Every prepared statement must be finalized, and the database connection must eventually be closed with sqlite3_close.
  • Alternatives and limitations: Direct SQLite offers control but requires manual SQL, type conversion, migrations, and error handling; Core Data or a tested SQLite wrapper can reduce repetitive persistence code.