UINavigationController
Completion Handler
iOS Development
pushViewController
Swift Programming

Completion handler for UINavigationController pushViewControlleranimated?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

UINavigationController.pushViewController(_:animated:) does not provide a built-in completion handler. This is a frequent need when you must run code only after the navigation transition finishes.

This article shows safe patterns to simulate completion behavior using transition coordinator and CATransaction.

Core Sections

1) Transition coordinator approach

swift
1func push(_ vc: UIViewController,
2          from nav: UINavigationController,
3          animated: Bool,
4          completion: (() -> Void)? = nil) {
5    nav.pushViewController(vc, animated: animated)
6
7    guard animated, let coordinator = nav.transitionCoordinator else {
8        completion?()
9        return
10    }
11
12    coordinator.animate(alongsideTransition: nil) { _ in
13        completion?()
14    }
15}

This hooks into UIKit transition lifecycle.

2) CATransaction fallback pattern

swift
1import UIKit
2
3func pushWithCATransaction(_ vc: UIViewController,
4                           nav: UINavigationController,
5                           animated: Bool,
6                           completion: (() -> Void)? = nil) {
7    CATransaction.begin()
8    CATransaction.setCompletionBlock(completion)
9    nav.pushViewController(vc, animated: animated)
10    CATransaction.commit()
11}

Useful in many cases, but transition coordinator is often semantically cleaner.

3) Handle non-animated pushes

Always invoke completion immediately for non-animated transitions to keep call-site behavior consistent.

4) Avoid retain cycles

When closure captures self, use weak capture where appropriate.

swift
push(nextVC, from: nav, animated: true) { [weak self] in
    self?.refreshUI()
}

5) Prefer navigation delegation for global flow

For app-wide flow orchestration, consider navigation controller delegate methods instead of ad hoc completion wrappers.

6) Production checklist for iOS navigation completion flow

Code examples are necessary, but production readiness depends on how this pattern behaves under failure, load, and operational drift. Before rollout, define success criteria that are measurable. A useful baseline is three metrics: correctness (for example, expected output match rate), reliability (error rate and retry behavior), and latency (p95 or p99 execution time). Capture these metrics in a repeatable test environment rather than relying on ad hoc local runs. If external systems are involved, include at least one synthetic fault scenario such as timeout, malformed payload, or temporary dependency outage. This confirms the implementation fails predictably and recovers in a controlled way.

Document environment assumptions close to the code. Include runtime version constraints, required environment variables, and exact dependency versions used during validation. Many regressions come from mismatched environments rather than algorithmic changes. A short README snippet or inline comment that names these assumptions can prevent repeated troubleshooting later. Also define ownership for operational issues: who receives alerts, what threshold triggers action, and what rollback path is acceptable. Without explicit ownership and rollback criteria, otherwise small incidents can take longer to resolve.

A practical rollout sequence is:

  1. Run automated checks (lint, unit tests, static validation) in CI.
  2. Execute a smoke test against representative input sizes.
  3. Validate one failure mode and verify error visibility in logs.
  4. Deploy behind a feature flag or phased rollout if possible.
  5. Monitor key metrics for a defined stabilization window.
bash
1# Example operator workflow
2make lint
3make test
4./scripts/smoke_check.sh

Finally, keep a short limitations section. State what the current approach intentionally does not optimize or support. This prevents accidental misuse by future contributors and keeps design discussions grounded in explicit tradeoffs. For long-lived systems, schedule periodic review of this implementation, especially after runtime upgrades or library changes. A lightweight maintenance cadence often catches compatibility issues before they become production incidents.

Common Pitfalls

  • Assuming pushViewController has built-in completion support.
  • Forgetting to call completion when animated is false.
  • Capturing strong references in completion blocks and leaking controllers.
  • Triggering follow-up UI actions before transition fully completes.
  • Using multiple competing completion mechanisms in one navigation flow.

Summary

To run code after a push transition, wrap navigation in a helper using transition coordinator (or CATransaction fallback) and enforce consistent completion semantics for animated and non-animated paths. This prevents timing bugs in navigation-heavy iOS screens.


Course illustration
Course illustration

All Rights Reserved.