Я пытаюсь создавать и добавлять наблюдателя каждый раз, когда приложение запускается на Mac в Swift 5. Я успешно сделал это с pyobjc, и он работает нормально, однако я сдался, потому что слишком сложно конвертировать NSFileManagerметоды получения текущего пользователя, вошедшего в систему, так как инструмент запускается с правами root через LaunchDaemon.
Я уже пытался преобразовать код Python Objective C в Swift, но мне не хватает опыта.Я пробовал следующие учебные пособия: http://cocoaapi.hatenablog.com/entry/AppKit/NSWorkspace/NSWorkspaceWillLaunchApplicationNotification и NSNotificationCenter addObserver в Swift .
//
// main.swift
// AppBlocker
//
// Created by jayke on 6/16/19.
// Copyright © 2019 Jayke Peters. All rights reserved.
//
import Foundation
import AppKit
// Custom Notification Center Object
let nc = NSWorkspace.shared.notificationCenter
func notifyNSWorkspaceWillLaunchApplicationNotification(notify:NSNotification) {
let bundleID : String = (notify.userInfo! as Dictionary)["NSApplicationBundleIdentifier"]! as! String
NSLog("Bundle ID = %@",bundleID)
let appName : String = (notify.userInfo! as Dictionary)["NSApplicationName"]! as! String
NSLog("application name = %@",appName)
let appPath : String = (notify.userInfo! as Dictionary)["NSApplicationPath"]! as! String
NSLog("application path = %@",appPath)
let appProcessID : NSNumber = (notify.userInfo! as Dictionary)["NSApplicationProcessIdentifier"]! as! NSNumber
NSLog("process id = %d",appProcessID.intValue)
let runningApp : NSRunningApplication = (notify.userInfo! as Dictionary)["NSWorkspaceApplicationKey"]! as! NSRunningApplication
NSLog("runningApp = %@",runningApp.description)
NSLog("runningApp = %@",runningApp.launchDate!.description)
}
nc.addObserver(forName: application, object: <#T##Any?#>, queue: <#T##OperationQueue?#>, using: <#T##(Notification) -> Void#>)
// Keep the application running
//CFRunLoopRun()
Код Python (существует и работает)
#!/usr/bin/python
print("Status: Starting AppBlocker")
import Foundation
from AppKit import *
from PyObjCTools import AppHelper
import signal
import re
import os
import sys
import shutil
import json
import threading
print("Status: AppBlocker Ready")
### CONFIG FILE LOCATION ###
config = "/.AppBlocker.json"
############################
def combineBundleIdentifiers(BundleIdentifiers):
return "(" + ")|(".join(BundleIdentifiers.keys()) + ")"
def loadConfig():
global blockedBundleIdentifiers, blockedBundleIdentifiersCombined
try:
blockedBundleIdentifiers = json.load(open(config))
except IOError:
print("Error: Could not open config file {}".format(config))
exit(1)
except ValueError:
print("Error: Invalid JSON")
exit(1)
blockedBundleIdentifiersCombined = combineBundleIdentifiers(blockedBundleIdentifiers)
def refreshConfig():
global blockedBundleIdentifiers, blockedBundleIdentifiersCombined
_blockedBundleIdentifiers = blockedBundleIdentifiers.copy()
threading.Timer(15, refreshConfig).start()
try:
blockedBundleIdentifiers = json.load(open(config))
except IOError:
print("Error: Code 5")
except ValueError:
print("Error: Invalid JSON Config, using previous config instead")
if _blockedBundleIdentifiers != blockedBundleIdentifiers:
print("Status: Config Changed")
blockedBundleIdentifiersCombined = combineBundleIdentifiers(blockedBundleIdentifiers)
## Match Regex Identifier Options
def matchIdentifier(dictionary, substr):
result = {}
for key in dictionary.keys():
if re.match(key, substr):
result = dictionary[key]
break
return result
## Defaults
deleteBlockedApplication = False
# Whether the user should be alerted that the launched applicaion was blocked
alertUser = False
# Message displayed to the user when application is blocked
alertMessage = "The application \"{appname}\" has been blocked by IT"
alertInformativeText = "Contact your administrator for more information"
# Use a custom Icon for the alert. If none is defined here, the Python rocketship will be shown.
alertIconPath = "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/Actions.icns"
# Define callback for notification
class AppLaunch(NSObject):
def appLaunched_(self, notification):
# Store the userInfo dict from the notification
userInfo = notification.userInfo
# Get the laucnhed applications bundle identifier
bundleIdentifier = userInfo()['NSApplicationBundleIdentifier']
# Check if launched app's bundle identifier matches any 'blockedBundleIdentifiers'
matched = re.match(blockedBundleIdentifiersCombined, bundleIdentifier)
if matched:
# Get PID of launchd app
pid = userInfo()['NSApplicationProcessIdentifier']
# Quit launched app
try:
os.kill(pid, signal.SIGKILL)
except OSError:
print("Error: Could not kill process " + str(pid))
## OPTIONS
try:
# Verbatim Identifier
options = blockedBundleIdentifiers[bundleIdentifier]
except:
# Regex Identifier
options = matchIdentifier(blockedBundleIdentifiers, matched.group())
# Get path of launched app
path = userInfo()['NSApplicationPath']
if 'deleteBlockedApplication' in options:
if options['deleteBlockedApplication']:
try:
shutil.rmtree(path)
except OSError, e:
print("Error: %s - %s." % (e.filename,e.strerror))
# Alert user
if 'alertUser' in options:
if options['alertUser']:
if 'alertMessage' in options:
alert(options['alertMessage'], alertInformativeText, ["OK"])
else:
alert(alertMessage.format(appname=userInfo()['NSApplicationName']), alertInformativeText, ["OK"])
# Define alert class
class Alert(object):
def __init__(self, messageText):
super(Alert, self).__init__()
self.messageText = messageText
self.informativeText = ""
self.buttons = []
def displayAlert(self):
alert = NSAlert.alloc().init()
alert.setMessageText_(self.messageText)
alert.setInformativeText_(self.informativeText)
alert.setAlertStyle_(NSInformationalAlertStyle)
for button in self.buttons:
alert.addButtonWithTitle_(button)
if os.path.exists(alertIconPath):
icon = NSImage.alloc().initWithContentsOfFile_(alertIconPath)
alert.setIcon_(icon)
# Don't show the Python rocketship in the dock
NSApp.setActivationPolicy_(1)
NSApp.activateIgnoringOtherApps_(True)
alert.runModal()
# Define an alert
def alert(message="Default Message", info_text="", buttons=["OK"]):
ap = Alert(message)
ap.informativeText = info_text
ap.buttons = buttons
ap.displayAlert()
# Load the config
loadConfig()
# Start the refresher
refreshConfig()
# Register for 'NSWorkspaceDidLaunchApplicationNotification' notifications
nc = Foundation.NSWorkspace.sharedWorkspace().notificationCenter()
AppLaunch = AppLaunch.new()
nc.addObserver_selector_name_object_(AppLaunch, 'appLaunched:', 'NSWorkspaceWillLaunchApplicationNotification',None)
# Launch "app"
AppHelper.runConsoleEventLoop()
//
// main.swift
// AppBlocker
//
// Created by jayke on 6/16/19.
// Copyright © 2019 Jayke Peters. All rights reserved.
//
import Foundation
import AppKit
// New Shared Notification Center Object
let nc = NSWorkspace.shared.notificationCenter
// Observer class
class Observer {
init() {
nc.addObserver(self, selector: #selector(notificationDidTrigger), name: NSWorkspace.willLaunchApplicationNotification, object: nil)
}
// Blacklisted Process Names
let blackListedProcessNames = ["com.apple.AppStore"]
// Kill Process Method
private func killProcess(_ processId: Int) {
if let process = NSRunningApplication.init(processIdentifier: pid_t(processId)) {
print("Killing \(processId): \(String(describing: process.localizedName!))")
process.forceTerminate()
}
}
// Application Launch Method
@objc func notificationDidTrigger(notification: Notification) {
// NOTIFICATION USER INFORMATION
let userInfo = notification.userInfo
if let processBundleIdentifier: String = notification.userInfo?["NSApplicationBundleIdentifier"] as? String {
if let processId = userInfo?["NSApplicationProcessIdentifier"] as? Int {
if (blackListedProcessNames.contains(processBundleIdentifier)) {
killProcess(processId)
}
}
}
}
}
// Create a new observer
let obj = Observer()
// Keep the tool running
CFRunLoopRun()
->