ディープリンク対応
ディープリンクは、URL やプッシュ通知からアプリ内の特定の画面に直接遷移する機能です。NavigationStack の path を活用することで、SwiftUI でも簡単に実装できます。
ディープリンクの基本
URL スキームを設定し、onOpenURL でリンクを処理します。
struct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: String.self) { value in
DetailView(id: value)
}
}
.onOpenURL { url in
handleDeepLink(url)
}
}
func handleDeepLink(_ url: URL) {
// myapp://detail/123 のような URL を処理
guard let host = url.host else { return }
if host == "detail" {
let id = url.lastPathComponent
path.append(id)
}
}
}URL パーサーの実装
複雑な URL 構造に対応するパーサーを作成します。
enum DeepLink: Hashable {
case home
case profile(userId: String)
case article(articleId: String)
case settings
static func parse(_ url: URL) -> [DeepLink]? {
guard url.scheme == "myapp" else { return nil }
let pathComponents = url.pathComponents.filter { $0 != "/" }
switch url.host {
case "profile":
guard let userId = pathComponents.first else { return nil }
return [.profile(userId: userId)]
case "article":
guard let articleId = pathComponents.first else { return nil }
return [.article(articleId: articleId)]
case "settings":
return [.settings]
default:
return [.home]
}
}
}path を使った遷移
struct ContentView: View {
@State private var path: [DeepLink] = []
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: DeepLink.self) { link in
switch link {
case .home:
HomeView()
case .profile(let userId):
ProfileView(userId: userId)
case .article(let articleId):
ArticleView(articleId: articleId)
case .settings:
SettingsView()
}
}
}
.onOpenURL { url in
if let links = DeepLink.parse(url) {
path = links
}
}
}
}onOpenURL
アプリが URL で起動されたときに呼ばれる。バックグラウンドからの復帰時も発火する。
path の直接設定
path に配列を代入することで、複数階層を一度に構築できる。戻るボタンも正しく動作する。
プッシュ通知からの遷移
プッシュ通知のペイロードに遷移先情報を含め、タップ時に画面遷移を実行します。
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var deepLinkHandler: ((DeepLink) -> Void)?
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse
) async {
let userInfo = response.notification.request.content.userInfo
if let type = userInfo["type"] as? String,
let id = userInfo["id"] as? String {
switch type {
case "article":
deepLinkHandler?(.article(articleId: id))
case "profile":
deepLinkHandler?(.profile(userId: id))
default:
break
}
}
}
}ユニバーサルリンク対応
ユニバーサルリンク(https://example.com/article/123)も同様に onOpenURL で処理できます。
.onOpenURL { url in
// URL スキームとユニバーサルリンク両方を処理
if url.scheme == "myapp" {
// カスタム URL スキーム
if let links = DeepLink.parse(url) {
path = links
}
} else if url.host == "example.com" {
// ユニバーサルリンク
if let links = DeepLink.parseWebURL(url) {
path = links
}
}
}URL スキーム(myapp://)
アプリ固有のスキーム。設定が簡単だが、他のアプリが同じスキームを使う可能性がある。
ユニバーサルリンク(https://)
Web ドメインに紐付いたリンク。設定が複雑だが、より安全で SEO にも有利。
状態の復元
アプリ終了後も遷移状態を復元するには、path を永続化します。
struct ContentView: View {
@SceneStorage("navigationPath") private var pathData: Data?
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: DeepLink.self) { /* ... */ }
}
.onAppear {
if let data = pathData {
path = try? JSONDecoder().decode(
NavigationPath.CodableRepresentation.self,
from: data
).map { NavigationPath($0) } ?? NavigationPath()
}
}
.onChange(of: path) { _, newPath in
pathData = try? JSONEncoder().encode(newPath.codable)
}
}
}ディープリンクは UX を向上させる重要な機能です。NavigationStack の path を活用することで、従来よりもシンプルに実装できるようになりました。