Learn Swift - Observability & Production Support
Series/Learn Swift/Episode 21
Episode 21 of 23

Learn Swift - Observability & Production Support

This episode covers production support for Swift apps: crash reporting and analytics, structured logging with OSLog along with telemetry and performance monitoring, health checks and user feedback loops, plus incident response and release rollback.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

After an app is released, the work changes shape: from adding features to understanding how the app behaves in the hands of thousands of users. Episode 21 covers observability and production support — crash reporting, structured logging, telemetry, health checks, and the process of responding to incidents.

Observability answers three questions that determine production quality: what is happening, why it's happening, and how to prevent it from recurring. Without observability, every incident is an expensive puzzle.

Crash Reporting and Analytics

Capturing Crashes in Production

Crash reporters like Firebase Crashlytics collect crash stack traces from user devices and present them in a dashboard. Key information for every crash:

  • Symbolicated stack trace.
  • Device and OS version.
  • App version.
  • Steps leading to the crash (breadcrumbs).

Analytics for Behavior

Analytics records what users do — not just when they crash. A compact integration uses an SDK or custom telemetry:

Recording an analytics event
struct Analytics {
    static func catat(_ nama: String, data: [String: String] = [:]) {
        var event = data
        event["event"] = nama
        event["app_version"] = Bundle.main.object(forInfoDictionaryKey:
            "CFBundleShortVersionString") as? String ?? "?"
        kirimEvent(event)
    }
}
 
Analytics.catat("login_berhasil", data: ["metode": "apple"])

Analytics.catat("login_berhasil", ...) records an event with app version context. Consistent event schema — stable names, documented fields — keeps analytics data answerable years later.

Logging and Telemetry

OSLog for Structured Logging

OSLog is Apple's official logging system, replacing print, which shouldn't be used in production. Logs are grouped by subsystem and category, and have severity levels:

Logger with OSLog
import OSLog
 
let logger = Logger(subsystem: "com.example.Aplikasi",
                    category: "jaringan")
 
func ambilData() async throws -> Data {
    logger.info("Memulai request")
    do {
        let (data, response) = try await URLSession.shared.data(from: url)
        logger.debug("Respons: \(response)")
        return data
    } catch {
        logger.error("Request gagal: \(error.localizedDescription)")
        throw error
    }
}

let logger = Logger(subsystem: "com.example.Aplikasi", category: "jaringan") creates a logger per category. Levels like info, debug, and error help filter while debugging. OSLog logs can be accessed from the terminal:

Streaming logs from the terminal
log stream --predicate 'subsystem == "com.example.Aplikasi"'

log stream --predicate 'subsystem == "..."' shows app logs in real time — the primary tool when verifying production behavior on a simulator or device.

Telemetry and Performance Monitoring

Telemetry measures app health continuously: startup time, crash-free rate, request latency, and custom metrics. Tools like performance monitoring SDKs, or your own implementation recording the duration of critical operations:

Measuring operation duration
let mulai = Date()
let data = try await ambilData()
let durasi = Date().timeIntervalSince(mulai)
print("Durasi request: \(durasi)s")

let durasi = Date().timeIntervalSince(mulai) measures the operation time. Send this metric in aggregate — for example p95 — to detect subtle performance degradation before users complain.

Health Checks and Feedback Loops

Health Checks on the Server

For a Swift server (episode 19), a health check tells the orchestration platform whether an instance is healthy. Vapor provides it directly:

Health check in Vapor
import Vapor
 
let healthRoute = app.get("health") { req in
    return HTTPStatus.ok
}

app.get("health") { req in return HTTPStatus.ok } returns status 200 while the server is alive. Platforms like Kubernetes and load balancers call this endpoint periodically to detect unresponsive instances.

User Feedback Loops

User complaints are irreplaceable observability data. Build clear feedback channels: a report-a-problem button in the app, support email, and forums. When an incident happens, information from users often provides context that telemetry doesn't capture.

Incident Response and Rollback

Responding to Incidents

A recommended incident handling process:

  • Detection: alerts triggered by a spike in crash rate or error rate.
  • Triage: determine impact and priority.
  • Mitigation: stop the spread — usually via rollback or a kill switch.
  • Communication: keep the team and affected users informed.
  • Post-mortem: find the root cause without hunting for a scapegoat.

Rollback and Kill Switches

Rollback returns the release to the previous stable version. A kill switch disables a specific feature remotely without a new release — invaluable for risky features:

Simple kill switch
struct Konfigurasi {
    static let fiturRisikoAktif = ProcessInfo.processInfo
        .environment["FITUR_RISIKO"] != "off"
}

ProcessInfo.processInfo.environment["FITUR_RISIKO"] reads a flag from the environment — the server can disable a feature without redeploying. On client apps, a similar pattern uses remote config updated server-side.

Info

A well-handled incident isn't a failure — it's an investment. Each post-mortem produces runbooks, new dashboards, and alerts that make the next similar incident end far faster.

Closing

Key takeaways:

  • Crash reporting and analytics give a picture of failures and behavior in production.
  • OSLog provides structured logging with levels and categories.
  • Telemetry measures critical metrics like startup time and request latency.
  • Health check endpoints make servers easy for orchestrators to monitor.
  • User feedback loops complement telemetry data with real context.
  • Rollback and kill switches slow the impact of incidents.

In the next episode, episode 22, we'll cover stable modern features and future trends — the latest stable Swift features like concurrency, macros, and result builders, Swift Package Manager improvements and ABI stability, Swift's strength in the Apple ecosystem and cross-platform, plus strategies to keep your Swift skills relevant. Your long journey comes to a close!