LetsMoveIt
There is already a similar Project but I wanted a solution that can be implemented with pure on-board resources and does not require the installation of an extra framework. The result is “LetsMoveIt”. You only need to create a Swift file (in my case “LetsMoveIt.swift”) with the following content in your project. Of course you can place the “ToApps ()” function in any other document, but I find a separate class somehow more elegant. But as is so often the case, it's a matter of taste and of course it's up to everyone to do it differently. (-:
//
// 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)
}
}
The function can now be called from anywhere in the project using the following syntax. For example from AppDelegate.swift:
func applicationDidFinishLaunching(_ aNotification: Notification) {
LetsMoveIt().ToApps()
}
The following dialog then opens if the application is not yet in “/ Applications”:
You now have the option to move the application there.
If LetsMoveIt should also contain German, here are the corresponding 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?";