Search code examples
iosswiftapple-push-notificationsonesignal

How to handle with Swift Onesignal push notification when iOS application is closed?


I am developing a webview mobile application using Swift on the iOS platform. I use Universal Link and OneSignal in the app. When the application is closed or in the background, etc. In any case, deeplink works perfectly. No problem so far.

The problem is OneSignal Natification. If the app is closed and I send a push notification with a URL, I cannot redirect to the relevant URL.

There is no problem if the application is open or running in the background. The URL opens in webview. However, as I mentioned, the problem occurs when the application is closed. Is there anyone who can help me with this?

I think this issue should be resolved in the SceneDelegate.swift file. However, although I tried many things, I could not find a solution. My work is briefly below:

// AbcViewController.swift file:

import UIKit
import WebKit
import OneSignalFramework
class AbcViewController: UIViewController {
    @IBOutlet weak var webView: WKWebView!
    var urlOneSignalNotification: URL?
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(handNotURL), name: Notification.Name(rawValue: "urlReceived"), object: nil)
        
        // Define default URL
        let defaultURL = URL(string: "example.com")!
        let url =  urlOneSignalNotification ?? defaultURL
        var request = URLRequest(url: url)
        webView.load(request)
    }
    
    // Get
    @objc func handNotURL(_ notification: Notification) {
        if let url = notification.object as? URL {
            urlOneSignalNotification = url
            // Load the URL in the web view
            let request = URLRequest(url: urlOneSignalNotification!)
            webView.load(request)
        } else {
            urlOneSignalNotification = nil 
        }
    }
}

// AppDelegate.swift

import UIKit
import OneSignalFramework
import UserNotifications
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
//...
}
extension AppDelegate: UNUserNotificationCenterDelegate {
  func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        guard let data = userInfo as? [String: Any] else {
            completionHandler(.noData)
            return
        }
       
        if let customData = data["custom"] as? [String: Any], let urlString = customData["u"] as? String {
            if let url = URL(string: urlString) {
                NotificationCenter.default.post(name: Notification.Name(rawValue: "urlReceived"), object: url)
            } else {
            }
        } 
       
        completionHandler(.newData)
    }
}

Solution

  • From cold startup, the apps often need a small amount of time to be ready to use, from main.swift to application:(_didFinishLauchingWithOptions:).

    So you can either define a class to store the payload from the notifications and then use it later when the app loads, or create a wait right before NotificationCenter.default.post.

    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
        NotificationCenter.default.post(name: Notification.Name(rawValue: "urlReceived"), object: url)
    }
    
    //Or
    Task { @MainActor in
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        NotificationCenter.default.post(name: Notification.Name(rawValue: "urlReceived"), object: url)
    }