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") ]>
    }
}
4
  • Just iterate thru the NSAppleEventDescriptor and pull out the strings one by one and append them to an array? Commented Apr 6, 2019 at 17:57
  • @matt That is what I did (I updated the question). I didn't initially know you could iterate through the descriptor. Commented Apr 6, 2019 at 18:01
  • developer.apple.com/documentation/foundation/… Just a matter of consulting the docs really :) Commented Apr 6, 2019 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 via the AppleScript-ObjC bridge. Commented Apr 7, 2019 at 7:32

2 Answers 2

3

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()
Sign up to request clarification or add additional context in comments.

3 Comments

I would combine the 2 if statements inside the loop to just if let s = listDescriptor.atIndex(i)?.stringValue {.
Note that 1...listDescriptor.numberOfItems crashes for an empty array. Suggestion: (0..<listDescriptor.numberOfItems).compactMap { listDescriptor.atIndex($0+1)?.stringValue }
@MartinR Good catch
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.