iOS development
Swift programming
open URL in Safari
iOS app tutorial
mobile app development

How to launch Safari and open URL from iOS app

Master System Design with Codemia

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

Introduction

Opening a URL from an iOS app is simple at API level, but the right implementation depends on user experience and security requirements. You can open external Safari, or keep users in-app with a Safari view controller. This guide covers both approaches, plus validation and fallback behavior for production-quality apps.

Core Topic Sections

Choose external Safari versus in-app browser

You have two standard options:

  1. UIApplication.shared.open launches Safari app.
  2. SFSafariViewController keeps browsing inside your app.

Use external Safari when users should fully leave app context. Use in-app Safari for smoother flow and shared cookie behavior without embedding a custom web renderer.

Open URL in external Safari

swift
1import UIKit
2
3func openInSafari(_ urlString: String) {
4    guard let url = URL(string: urlString) else {
5        print("Invalid URL")
6        return
7    }
8
9    UIApplication.shared.open(url, options: [:]) { success in
10        print("Open success: \(success)")
11    }
12}

Always validate URL construction before calling open.

Open URL with SFSafariViewController

swift
1import SafariServices
2import UIKit
3
4func openInAppSafari(from vc: UIViewController, urlString: String) {
5    guard let url = URL(string: urlString) else { return }
6    let safariVC = SFSafariViewController(url: url)
7    safariVC.preferredControlTintColor = .systemBlue
8    vc.present(safariVC, animated: true)
9}

This preserves app context and gives a trusted browsing experience without building your own browser logic.

Validate URL scheme and user intent

Avoid opening unknown or malformed URLs automatically. For user safety:

  1. Whitelist expected schemes, typically https.
  2. Confirm action from user interaction.
  3. Log rejected URLs for diagnostics.
swift
func isSafeWebURL(_ url: URL) -> Bool {
    return ["https", "http"].contains(url.scheme?.lowercased() ?? "")
}

Use stricter policy in finance or healthcare apps.

Support custom schemes with caution

If you need to open other apps using custom URL schemes, check capability first.

swift
if let url = URL(string: "maps://?q=Toronto"), UIApplication.shared.canOpenURL(url) {
    UIApplication.shared.open(url)
}

For privacy, ensure required query schemes are declared where platform rules require it.

Universal links can route users to installed apps or web fallback. When opening external content, test both installed-app and browser-only scenarios.

A robust flow:

  1. Attempt universal link.
  2. If unavailable, open web URL.
  3. Show user-friendly error if neither path succeeds.

This avoids dead-end navigation in edge cases.

UI and lifecycle considerations

If opening URLs from background tasks or async callbacks, make sure presentation occurs on active foreground view controller.

Threading guideline:

swift
DispatchQueue.main.async {
    // present safari or call open
}

Calling presentation APIs off main thread can cause warnings or runtime issues.

Testing strategy

Add tests for:

  1. Valid and invalid URL strings.
  2. https allow list behavior.
  3. External Safari open callback handling.
  4. In-app Safari presentation from active controller.

UI tests can verify that tapping a link button triggers expected navigation path.

Add a reusable URL opener service

As projects grow, opening URLs from many screens can create duplicated logic. Wrap validation and opening behavior in one small service so policy changes happen in one place.

swift
1import UIKit
2import SafariServices
3
4final class URLOpener {
5    func openExternal(_ raw: String) {
6        guard let url = URL(string: raw), ["https", "http"].contains(url.scheme?.lowercased() ?? "") else { return }
7        UIApplication.shared.open(url)
8    }
9
10    func makeInAppController(_ raw: String) -> SFSafariViewController? {
11        guard let url = URL(string: raw), url.scheme?.lowercased() == "https" else { return nil }
12        return SFSafariViewController(url: url)
13    }
14}

This pattern also simplifies unit testing because callers depend on one API.

Common Pitfalls

  • Opening unvalidated strings and crashing on invalid URL creation.
  • Choosing external Safari when in-app flow is required for user retention.
  • Presenting SFSafariViewController from inactive or wrong controller.
  • Auto-launching URLs without clear user action or policy checks.
  • Ignoring fallback behavior for universal links and app-not-installed scenarios.

Summary

  • Use UIApplication.open for external Safari and SFSafariViewController for in-app browsing.
  • Validate URLs and schemes before opening.
  • Apply user-intent and security checks in navigation flows.
  • Handle presentation on main thread and active view controllers.
  • Test fallback paths so URL navigation remains reliable across devices.

Course illustration
Course illustration

All Rights Reserved.