Можете ли вы выполнить сценарий Applescript из приложения Swift

У меня простой AppleScript, который отправляет электронное письмо. Как я могу назвать это из приложения Swift?

(Я не смог найти ответ через Google.)

Ответ 1

Не проверено, но похоже, что можно сделать что-то вроде (добавлен произвольный путь к скрипту):

import Foundation

let task = NSTask()
task.launchPath = "/usr/bin/osascript"
task.arguments = ["~/Desktop/testscript.scpt"]

task.launch()

[ОБНОВЛЕНИЕ Сейчас 2019 год, и, возможно, за последние несколько лет возник ряд проблем. Здесь обновленный код, который я только что протестировал на моей машине, которая находится на OSX Mojave 10.14.5]

ObjC.import('Foundation');

let task = $.NSTask.alloc.init;
task.launchPath = "/usr/bin/osascript";
task.arguments = ["/testing_folder/test.scpt"];

task.launch

Ответ 2

Как предлагает Kamaros, вы можете напрямую вызвать NSApplescript без необходимости запуска отдельного процесса через NSTask (как предлагает CRGreen).

Быстрый код

let myAppleScript = "..."
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: myAppleScript) {
    if let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError(
                                                                       &error) {
        print(output.stringValue)
    } else if (error != nil) {
        print("error: \(error)")
    }
}

Ответ 3

Для тех, кто получает предупреждение ниже для Swift 4, для строки при создании NSAppleEventDescriptor из ответа Zekel

Необязательное выражение типа "NSAppleEventDescriptor", используемое при проверке опциональности

Вы можете избавиться от этого с помощью этой отредактированной короткой версии:

let myAppleScript = "..."
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: myAppleScript) {
    if let outputString = scriptObject.executeAndReturnError(&error).stringValue {
        print(outputString)
    } else if (error != nil) {
        print("error: ", error!)
    }
}

Тем не менее, вы, возможно, также поняли; с помощью этого метода система записывает это сообщение на консоль каждый раз, когда вы запускаете скрипт:

AppleEvents: получено сообщение MS Mach, которое не было сложного типа, как ожидалось в getMemoryReference.

Очевидно, это объявленная ошибка штатным разработчиком Apple, и, как говорят, это "просто" безвредный спам в журнале, который планируется удалить в будущих обновлениях ОС, как вы можете видеть в этом очень длинном сообщении на форуме разработчиков для Apple и ТАКом вопросе. ниже:

AppleEvents: получено сообщение MS Mach, которое не было сложного типа, как ожидалось в getMemoryReference

Спасибо Apple за эти миллиарды ненужных консольных журналов.

Ответ 4

Вы можете попробовать NSAppleScript, от Apple Technical Note TN2084 Использование сценариев AppleScript в приложениях Cocoa https://developer.apple.com/library/mac/technotes/tn2084/_index.html

NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
            @"\
            set app_path to path to me\n\
            tell application \"System Events\"\n\
            if \"AddLoginItem\" is not in (name of every login item) then\n\
            make login item at end with properties {hidden:false, path:app_path}\n\
            end if\n\
            end tell"];

returnDescriptor = [scriptObject executeAndReturnError: &errorDict];

Ответ 5

Я боролся несколько часов, но ничего не получилось. Наконец, мне удалось запустить AppleScript через оболочку:

let proc = Process()
proc.launchPath = "/usr/bin/env"
proc.arguments = ["/usr/bin/osascript", "scriptPath"]
proc.launch()

Dunno - это лучший способ сделать это, но, по крайней мере, это работает.

Ответ 6

По состоянию на март 2018 года, я думаю, что самый сильный ответ на эту тему по-прежнему является принятым ответом с 2011 года. Реализации, связанные с использованием NSAppleScript или OSAScript, понесли недостатки, имеющие некоторые незначительные, но очень неприятные утечки памяти, без каких-либо дополнительных преимуществ. Любой, кто пытается получить ответ на выполнение (в Swift 4), может попробовать:

let manager = FileManager()
// Note that this assumes your .scpt file is located somewhere in the Documents directory
let script: URL? = try? manager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
if let scriptPath = script?.appendingPathComponent("/path/to/scriptName").appendingPathExtension("scpt").path {
    let process = Process()
    if process.isRunning == false {
        let pipe = Pipe()
        process.launchPath = "/usr/bin/osascript"
        process.arguments = [scriptPath]
        process.standardError = pipe
        process.launch()
    }
}