Articles Projects About Subscribe
← All issues Newsletter

iOS Dev Weekly — Issue #69

Opening

Hardware week again, and with it the usual flood of press. But the quieter reality for teams right now is stabilizing SwiftUI apps that stream live state without blowing up the main actor or starving rendering. If your feed-driven screens stutter under load, this issue is for you.


This Week’s Big Story

AsyncSequence Patterns for Real-Time SwiftUI Views

SwiftUI is fine with “fast enough,” but it falls apart with bursty, unordered, or long-lived inputs unless you own buffering, cancellation, and isolation. The core move is to treat each live source as an AsyncSequence boundary you can reason about and test, not as a delegate call spraying mutations across the app. If you’ve ever shipped a screen that only recovers after a full relaunch, this is the mental model that prevents it.


Trend Signals

• iPhone Duo — A new form factor always reopens layout and gesture assumptions; expect edge cases in scene phases and window sizes to matter again. [Source: HackerNews]

• iPhone 18 Pro and iPhone 18 Pro Max — New silicon typically alters energy/perf trade-offs for background tasks; revalidate your refresh cadence and metrics on device. [Source: HackerNews]

• Apple Watch Series 12 — The health-sensing story advances, which usually means more cadence-sensitive streams; design HealthKit bridges as AsyncSequences, not timers. [Source: HackerNews]

• Apple Watch Ultra 4 — Endurance workloads highlight cancellation discipline; long-running Tasks must checkpoint and yield responsibly to avoid UI back-pressure. [Source: HackerNews]


Swift Snippet of the Week

import SwiftUI
import Observation

struct TickerEvent: Identifiable, Sendable { let id = UUID(); let value: Int; let time: Date }

actor TickerSource {
    private var continuation: AsyncStream<TickerEvent>.Continuation?
    private var pump: Task<Void, Never>?
    let id = UUID()
    func stream(buffer: Int = 64) -> AsyncStream<TickerEvent> {
        AsyncStream(bufferingPolicy: .bufferingOldest(buffer)) { cont in
            continuation = cont
            cont.onTermination = { [weak self] _ in Task { await self?.stop() } }
        }
    }
    func startDemo() {
        pump?.cancel()
        pump = Task {
            var n = 0
            while !Task.isCancelled {
                for _ in 0..<Int.random(in: 1...6) {
                    n += 1; continuation?.yield(.init(value: n, time: .now))
                }
                try? await Task.sleep(for: .milliseconds(Int.random(in: 60...220)))
            }
// … (truncated for newsletter)

This pattern encodes explicit back-pressure, lifecycle-aware cancellation, and actor isolation at the source—practical guardrails that keep real-time SwiftUI views responsive under bursty load.


Community Picks

iPhone Duo — If you target novel screen states, audit orientation, split-view, and scrolling assumptions now.

iPhone 18 Pro and iPhone 18 Pro Max — Treat the first on-device perf pass as a requirement; simulator won’t tell you thermal headroom stories.

Apple Watch Series 12 — Health streams amplify coalescing strategy: debounce off-main, render succinctly.


Until Next Time

If your real-time screens feel fragile, the full piece walks through AsyncSequence boundaries you can test, instrument, and trust. Give it a read, then hit reply with what you’re seeing in production or forward it to the teammate wrestling with Task cancellation right now. I’m also unpacking trade-offs in a LinkedIn thread—jump in and tell me how you’re shaping buffers and back-pressure this cycle.