I am trying to set up deep linking in my app. I have set up the app as well as the push notifications. However, I am unable to complete the final link from where the AppDelegate receives the user click on a notification to the screen that I want to deep link to. I basically want to call viewRouter.goToFred() from the AppDelegate. I have set up a git repo here (https://github.com/cameronhenige/SwiftUITestDeepLink) with an app that has everything set up except for this final piece. Could somebody help me figure this out? Here are the relevant pieces of code. Thanks!
import SwiftUI
struct MainView: View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
NavigationView {
List {
ForEach(viewRouter.pets) { pet in
NavigationLink(
destination: PetView(),
tag: pet,
selection: $viewRouter.selectedPet,
label: {
Text(pet.name)
}
)
}
Button("Send Notification to Fred") {
viewRouter.sendNotification()
}
Button("Manually go to Fred") {
viewRouter.goToFred()
}
}
}
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView()
}
}
import SwiftUI
@main
struct ContentView: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
MainView().environmentObject(ViewRouter())
}
}
}
import Foundation
import UserNotifications
class ViewRouter: ObservableObject {
@Published var selectedPet: Pet? = nil
@Published var pets: [Pet] = [Pet(name: "Louie"), Pet(name: "Fred"), Pet(name: "Stanley")]
func goToFred() {
self.selectedPet = pets[1]
}
func sendNotification() {
let content = UNMutableNotificationContent()
let categoryIdentifire = "Notification Type"
content.title = "Go To Fred"
content.body = "Click me to go to Fred."
content.sound = UNNotificationSound.default
content.badge = 1
content.categoryIdentifier = categoryIdentifire
let request = UNNotificationRequest(identifier: "identifier", content: content, trigger: nil)
UNUserNotificationCenter.current().add(request) { (error) in
if let error = error {
print("Error \(error.localizedDescription)")
}
}
}
}
import SwiftUI
struct PetView: View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
if let pet = viewRouter.selectedPet {
Text(pet.name)
} else {
EmptyView()
}
}
}
struct PetView_Previews: PreviewProvider {
static var previews: some View {
PetView()
}
}
import Foundation
struct Pet: Identifiable, Hashable {
var name: String
var id: String { name }
}
import Foundation
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
let notificationCenter = UNUserNotificationCenter.current()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
return true
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
print("Notification created")
// Change this to your preferred presentation option
completionHandler([[.alert, .sound]])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print("user clicked on notification. Now take them to Fred.")
//todo Somehow I want to call viewRouter.goToFred(), but I don't have access to that object in AppDelegate
completionHandler()
}
}
I try this approach and it works. Admittedly I dont know if this is the correct or suggested way.
The appDelegate
has to get reference to the same ViewRouter
instance that MainView is using, and where ContentView
has to hook the appDelegate
with the ViewRouter
instance.
ContentView.swift:
@main
struct ContentView: App {
let router = ViewRouter()
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
MainView()
.environmentObject(router)
.onAppear(perform:setUpAppDelegate)
}
}
func setUpAppDelegate(){
appDelegate.router = router
}
}
AppDelegate.swift:
weak var router: ViewRouter?
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print("user clicked on notification. Now take them to Fred.")
//todo Update the ViewRouter to
router?.goToFred()
completionHandler()
}