iOS应用无需重启:通知授权后更新OneSignal PushID
Hey there! Let's solve this issue where your OneSignal player ID only updates after an app restart post-notification authorization. We'll cover two key goals: triggering the URL update immediately after user authorization and efficiently monitoring for when the player ID becomes available.
Step 1: Use OneSignal's Subscription Observer for Real-Time Updates
OneSignal provides a built-in observer that fires whenever the user's subscription status changes—this includes when the player ID becomes available after authorization. This is way more efficient than polling with a timer, since it only triggers when there's an actual change.
First, update your WebViewController.swift to set up the observer (preferably in viewDidLoad or viewWillAppear):
import OneSignal import UserNotifications class WebViewController: UIViewController { var pushIDOneSignal: String? var webviewurl: String = "your-base-url-here?" // Ensure your base URL ends with a valid separator like ? or & override func viewDidLoad() { super.viewDidLoad() // Set up OneSignal subscription observer OneSignal.addSubscriptionObserver(self) // Check initial player ID on load updateWebViewURLWithPlayerID() } } // Conform to OneSignal's SubscriptionObserver protocol extension WebViewController: OSSubscriptionObserver { func onOSSubscriptionChanged(_ stateChanges: OSSubscriptionStateChanges) { // Update the player ID when subscription state changes pushIDOneSignal = stateChanges.to.userId // Trigger URL update and reload the web view if needed updateWebViewURLWithPlayerID() } }
Step 2: Handle Notification Authorization Completion
To catch the exact moment the user grants notification permissions, listen for the authorization status change. Add this code to viewDidLoad:
// Listen for notification authorization status changes NotificationCenter.default.addObserver(self, selector: #selector(handleNotificationAuthorizationChange), name: UIApplication.userDidChangeNotificationSettingsNotification, object: nil)
Then add the corresponding selector method:
@objc private func handleNotificationAuthorizationChange() { // Force OneSignal to check for updated subscription status OneSignal.getDeviceState { deviceState in DispatchQueue.main.async { self.pushIDOneSignal = deviceState?.userId self.updateWebViewURLWithPlayerID() } } }
Step 3: Encapsulate URL Update Logic
Create a reusable method to handle URL concatenation and web view reload—this keeps your code clean and ensures consistency:
private func updateWebViewURLWithPlayerID() { guard let playerID = pushIDOneSignal, !playerID.isEmpty else { // Player ID is nil or empty—load the base URL (the observer will trigger updates when it becomes available) webView.load(URLRequest(url: URL(string: webviewurl)!)) return } // Avoid duplicate player ID entries in the URL if !webviewurl.contains("pushIDOneSignal=") { let separator = webviewurl.contains("?") ? "&" : "?" let updatedURL = "\(webviewurl)\(separator)pushIDOneSignal=\(playerID)" webviewurl = updatedURL } // Reload the web view with the updated URL webView.load(URLRequest(url: URL(string: webviewurl)!)) }
Step 4: "Continuous Detection" (Efficient Fallback)
The subscription observer and authorization listener already handle continuous detection efficiently—they only trigger when there's a relevant change. If you want a fallback (e.g., in case OneSignal's observer misses an update), you can add a gentle timer (use sparingly to avoid unnecessary resource usage):
var playerIDCheckTimer: Timer? override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Optional: Check every 5 seconds (adjust interval as needed) playerIDCheckTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in guard let self = self else { return } OneSignal.getDeviceState { deviceState in DispatchQueue.main.async { if let userId = deviceState?.userId, userId != self.pushIDOneSignal { self.pushIDOneSignal = userId self.updateWebViewURLWithPlayerID() } } } } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // Invalidate timer to prevent memory leaks playerIDCheckTimer?.invalidate() playerIDCheckTimer = nil }
Key Notes
- Always use
DispatchQueue.main.asyncwhen updating UI elements (like the web view) from callbacks—most OneSignal methods run on background threads. - Double-check that your OneSignal SDK is properly initialized in your app's
AppDelegateorSceneDelegatebefore using these methods.
内容的提问来源于stack exchange,提问作者user6810198

