2

I have a complicated AppleScript that returns a list of strings that I need to access from Swift. I've boiled it down to a simple example and I just can't figure out how to map the AppleScript strings to an array of Swift strings.

let listOfStringsScript = """
                          set listOfStrings to { "one", "two", "three" }
                          """

 if let scriptObject = NSAppleScript(source: listOfStringsScript) {
    var errorDict: NSDictionary? = nil
    let resultDescriptor = scriptObject.executeAndReturnError(&errorDict)

    if errorDict == nil {
      // TODO: convert the resultDescriptor (NSAppleEventDescriptor) into an array of strings
      print(resultDescriptor)
      // OUTPUT: <NSAppleEventDescriptor: [ 'utxt'("one"), 'utxt'("two"), 'utxt'("three") ]>
    }
}
RobertJoseph
  • 7,968
  • 12
  • 68
  • 113
  • Just iterate thru the NSAppleEventDescriptor and pull out the strings one by one and append them to an array? – matt Apr 06 '19 at 17:57
  • @matt That is what I did (I updated the question). I didn't initially know you could iterate through the descriptor. – RobertJoseph Apr 06 '19 at 18:01
  • https://developer.apple.com/documentation/foundation/nsappleeventdescriptor/1408027-atindex Just a matter of consulting the docs really :) – matt Apr 06 '19 at 18:42
  • Is the script part of your app? (As opposed to user-supplied.) If so, you’ll find it a lot simpler to [call AppleScript directly from Swift](http://appscript.sourceforge.net/asoc.html) via the AppleScript-ObjC bridge. – foo Apr 07 '19 at 07:32

2 Answers2

2

Answer with help from @Alexander and @MartinR:

extension NSAppleEventDescriptor {

  func toStringArray() -> [String] {
    guard let listDescriptor = self.coerce(toDescriptorType: typeAEList) else {
      return []
    }

    return (0..<listDescriptor.numberOfItems)
      .compactMap { listDescriptor.atIndex($0 + 1)?.stringValue }
  }

}

...

let resultDescriptor = scriptObject.executeAndReturnError(&errorDict)
let subjectLines = resultDescriptor.toStringArray()
RobertJoseph
  • 7,968
  • 12
  • 68
  • 113
1

An alternative is to gather the Apple Script result as lines of text separated by line breaks and then parse the string in Swift.

So break up the Apple Script result using

set AppleScript's text item delimiters to linefeed

Then simply parse

let selectedItems = scriptExecuted.stringValue!
let selectedItemsFiltered = selectedItems.components(separatedBy: .newlines)

.components returns a string array

tyirvine
  • 1,861
  • 1
  • 19
  • 29