Why is userInfo["logFile"] not accessible when user clicks notification?

I am new to Swift so clearly I'm missing a piece of the puzzle.

In the following code, I set userInfo["logFile"] using showNotification("Backup initiated...", self.config).

I confirmed userInfo["logFile"] is set using print. Why is that property lost when the app is not running and a user clicks a notification in the notification center?

Thanks!

//
//  AppDelegate.swift
//  Borg Backup
//
//  Created by Sun Knudsen on 2020-11-09.
//

import Cocoa
import SwiftUI
import UserNotifications

struct Config : Codable {
  let script: String
  let logFile: String
  let initiatedNotifications: Bool
  let completedNotifications: Bool
  let failedNotifications: Bool
}

func loadConfig(_ fileName: String) -> Config? {
  let url = URL(fileURLWithPath: fileName)
  let decoder = JSONDecoder()
  guard
    let data = try? Data(contentsOf: url),
    let person = try? decoder.decode(Config.self, from: data)
  else {
    return nil
  }
  return person
}


func showNotification(_ body: String, _ config: Config){
  let notificationCenter = UNUserNotificationCenter.current()
  let content = UNMutableNotificationContent()
  content.body = body
  content.userInfo = ["logFile" : config.logFile]
  let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
  notificationCenter.add(request) { (error:Error?) in
    if error != nil {
      print(error?.localizedDescription ?? "Could not add notification")
    }
  }
}

func shell(_ command: String, completion: ((_ status: Int32, _ output: String?) -> Void)? = nil) {
  let task = Process()
  let pipe = Pipe()
  
  task.launchPath = "/bin/zsh"
  task.arguments = ["-c", command]
  task.standardOutput = pipe
  task.waitUntilExit()
  task.launch()
  
  let data = pipe.fileHandleForReading.readDataToEndOfFile()
  let output = String(data: data, encoding: .utf8)
  
  completion?(task.terminationStatus, output)
}

func terminate() -> Void {
  DispatchQueue.main.async {
    NSApplication.shared.terminate(nil)
  }
}

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {
  var config = Config(script: "/usr/local/bin/borg-backup.sh", logFile: "/usr/local/var/log/borg-backup.log", initiatedNotifications: true, completedNotifications: true, failedNotifications: true)
  func applicationDidFinishLaunching(_ notification: Notification) {
    // Check if notifications are allowed
    UNUserNotificationCenter.current().delegate = self
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (allowed, error) in
      if (!allowed) {
        DispatchQueue.main.async {
          let alert = NSAlert.init()
          alert.messageText = "Please allow notifications in System Preferences / Security & Privacy"
          alert.runModal()
        }
        terminate()
      } else if notification.userInfo?[NSApplication.launchUserNotificationUserInfoKey] as? UNNotificationResponse != nil {
        if let logFile = notification.userInfo?["logFile"] as? String {
          shell("open \(logFile)")
        }
        terminate()
      } else {
        // Read app arguments and set config if valid configuration file is provided
//        if (CommandLine.arguments.indices.contains(1) && CommandLine.arguments[1].range(of: ".json$", options: .regularExpression, range: nil, locale: nil) != nil) {
        if (CommandLine.arguments[1].range(of: ".json$", options: .regularExpression, range: nil, locale: nil) != nil) {
          if let _config = loadConfig(CommandLine.arguments[1]) {
            self.config = _config
          } else {
            DispatchQueue.main.async {
              let alert = NSAlert.init()
              alert.messageText = "Invalid config file"
              alert.runModal()
            }
            terminate()
          }
        }
        
        if (self.config.initiatedNotifications) {
          showNotification("Backup initiated...", self.config)
        }
        
        // Run script and log output
//        let command = "set -o pipefail; \(self.config.script) 2>&1 | tee -a \(self.config.logFile)"
        let command = "set -o pipefail; cat \(self.config.script) 2>&1 | tee -a \(self.config.logFile)"
        
        shell(command) {(status: Int32, output: String?) in
          if (status == 0) {
            if (self.config.completedNotifications) {
              showNotification("Backup completed", self.config)
            }
            // Truncate log file to last 1000 lines
            shell("echo \"$(tail -n 1000 \(self.config.logFile))\" > \(self.config.logFile)") {(status: Int32, output: String?) in
              terminate()
            }
          } else {
            if (self.config.failedNotifications) {
              showNotification("Backup failed", self.config)
            }
            terminate()
          }
        }
      }
    }
  }
  
  func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    shell("open \(self.config.logFile)")
  }
}


Read more here: https://stackoverflow.com/questions/64953809/why-is-userinfologfile-not-accessible-when-user-clicks-notification

Content Attribution

This content was originally published by sunknudsen at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: