LetsMoveIt

Es gibt zwar bereits schon ein ähnliches Projekt aber ich wollte eine Lösung die mit reinen Bordmitteln realisiert werden kann und keine Installation von einem extra Framework bedingt. Das Ergebnis ist „LetsMoveIt“. Man braucht lediglich nur eine eine Swift-Datei (in meinem Fall „LetsMoveIt.swift“) mit nachfolgendem Inhalt in seinem Projekt anlegen. Natürlich kann man die Funktion „ToApps()“ auch in jedem anderen Dokument platzieren, aber ich finde eine gesonderte Klasse irgendwie eleganter. Aber das ist ja wie so oft geschmackssache und bleibt natürlich jedem anheim gestellt es anders zu tun. (-:

//
//  LetsMoveIt.swift
//
//  Created by Sascha Lamprecht 04.06.2021
//

import Cocoa

class LetsMoveIt: NSViewController {
    
    func ToApps() {

        let url = URL(fileURLWithPath: Bundle.main.resourcePath!)
        var LaunchPath = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString.replacingOccurrences(of: "file://", with: "").replacingOccurrences(of: "%20", with: " ")
        LaunchPath.removeLast()
        let RealAppName = URL(fileURLWithPath: LaunchPath).lastPathComponent
        
        if LaunchPath.contains("/Applications/") {
            return
        }
        if UserDefaults.standard.bool(forKey: "Supress") {
            return
        }
        
        let alert = NSAlert()
        alert.messageText = NSLocalizedString("Move to Applications folder?", comment: "")
        alert.informativeText = NSLocalizedString("I can move myself to the Applications folder if you'd like. This will keep your Downloads folder uncluttered.", comment: "")
        alert.alertStyle = .informational
        alert.showsSuppressionButton = true
        let Button = NSLocalizedString("Do Not Move", comment: "")
        alert.addButton(withTitle: Button)
        let CancelButtonText = NSLocalizedString("Move to Applications Folder", comment: "")
        alert.addButton(withTitle: CancelButtonText)

        if alert.runModal() == .alertFirstButtonReturn {
            if let supress = alert.suppressionButton {
                let state = supress.state
                switch state {
                case NSControl.StateValue.on:
                UserDefaults.standard.set(true, forKey: "Supress")
                default: break
                }
            }
            return
        }

        let admin_check = "user=$( id -un ); admin_check=$( groups \"$user\" | grep -w -q admin ); echo \"$admin_check\""
        let process            = Process()
        process.launchPath     = "/bin/bash"
        process.arguments      = ["-c", admin_check]
        process.launch()
        process.waitUntilExit()
        
        let fileManager = FileManager.default
        let path = "/Applications/" + RealAppName
            if admin_check.contains(" admin ") {
                do {
                    if fileManager.fileExists(atPath: path) {
                        try fileManager.removeItem(atPath: path)
                    }
                    try fileManager.copyItem(atPath: LaunchPath, toPath: path)
                    try fileManager.removeItem(atPath: LaunchPath)
                } catch {
                    return
                }
            } else {
                let move_to_apps = "osascript -e 'do shell script \"rm -rf /Applications/" + RealAppName + "; cp -r \\\"" + LaunchPath + "\\\" /Applications/; chown -R " + NSUserName() + ":staff \\\"/Applications/" + RealAppName + "\\\"; rm -r \\\"" + LaunchPath + "\\\"\" with administrator privileges'"
                let process            = Process()
                process.launchPath     = "/bin/bash"
                process.arguments      = ["-c", move_to_apps]
                process.launch()
                process.waitUntilExit()
            }
        let task = Process()
        task.launchPath = "/usr/bin/open"
        task.arguments = [path]
        task.launch()
        exit(0)
    }
}

Die Funktion kann jetzt von jedem beliebigem Ort im Projekt mit folgender Syntax aufgerufen werden. Zum Beispiel aus AppDelegate.swift:

func applicationDidFinishLaunching(_ aNotification: Notification) {
    LetsMoveIt().ToApps()
}

Es öffnet sich dann folgender Dialog, sollte die Applikation sich noch nicht in „/Applications“ befinden:

Man hat nun die Möglichkeit die Applikation nach dorthin verschieben zu lassen.
Falls LetsMoveIt auch deutsch enthalten soll hier noch die entsprechenden Strings:

/* No comment provided by engineer. */
"Do Not Move" = "Nicht verschieben";

/* No comment provided by engineer. */
"I can move myself to the Applications folder if you'd like. This will keep your Downloads folder uncluttered." = "Ich kann mich selbst in den Programme-Ordner verschieben wenn Du möchtest. Somit bleibt Dein Download-Ordner aufgeräumt.";

/* No comment provided by engineer. */
"Move to Applications Folder" = "In den Programme-Ordner verschieben";

/* No comment provided by engineer. */
"Move to Applications folder?" = "In den Programme-Ordner verschieben?";