• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
[Swift] best way to support 'keyword' args, symbolic values, show values as literals?
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Swift] best way to support 'keyword' args, symbolic values, show values as literals?


  • Subject: [Swift] best way to support 'keyword' args, symbolic values, show values as literals?
  • From: has <email@hidden>
  • Date: Mon, 15 Jun 2015 18:17:15 +0100

Hi folks,

Some of the old uns here might remember that many years ago I wrote a nice little library named appscript which allowed you to control "AppleScriptable" applications from Python/Ruby/ObjC (and, unlike the alternatives, actually didn't suck), allowing you to write code like this (Python):

    app('TextEdit').documents['Read Me'].paragraphs[1].get()

and this (ObjC):

TEApplication *textedit = [TEApplication applicationWithName: @"TextEdit"]; TEReference *ref = [[textedit.documents byName: @"ReadMe"].paragraphs at: 1];
    NSString *result = [[ref get] send];

instead of this (AppleScript):

    tell application "TextEdit"
        get paragraph 1 of document "ReadMe"
    end tell

Thanks to Apple actioning Radar tickets 19169736 <http://openradar.appspot.com/19169736>, 19169791 <http://openradar.appspot.com/19169791>, and 18944184 <http://openradar.appspot.com/18944184>, there are now modern, fully supported NSAppleEventDescriptor APIs arriving in 10.11 that will allow third-party code to do all this stuff without having to go through ancient legacy or deprecated Carbon APIs any more.

So I'm starting to work on an Apple event bridge for Swift 2.0, built on top of a cleaned-up fork[1] of my old Appscript.framework. So far it's able to construct basic object specifiers, e.g.:

    TEApplication(name: "TextEdit").documents[1].name

and symbolic values (AEDescs of typeType and typeEnumerated, aka 'class' and 'constant' names in AS):

    TESymbol.name

Complex specifiers and commands are not yet up and running (once they are, I'll push the code) and this being my first substantial foray into Swift, I could do with advice and suggestions on how best to present the API. Here's 3 questions to start:

...

1. AppleScript, Apple events, etc. use optional keyword-based parameters, not ordered parameters as in ObjC and Swift. For ObjC I just used chained method calls to build up and send complex events, e.g:

   tell application "TextEdit" to make new document ¬
          with properties {text:"Hello World\n"}

translates to objc-appscript as:

    [[[[textedit make] new_: [TEConstant document]]
             withProperties: @{TEConstant.text: @"Hello World"}] send]

which works fine but is pretty ugly compared to the Python equivalent:

textedit.make(new=k.document, with_properties={k.text: "Hello World"}, timeout=30)

Translating the ObjC API directly to Swift would result in code like this:

    textedit.make.new(TEConstant.document).withProperties(
                      [TEConstant.text: "Hello World"]).send(timeout: 30)

which isn't particularly intuitive or readable, so I'm looking for other ways to do it, preferably that allow the entire Apple event to be built and sent in a single method call.

If I understand Swift, there are a couple possible approaches that would let me get much closer to the Python/Ruby syntax: optional arguments and dicts. Since optional args are only omittable from the end of the args list, that'd mean a syntax something like this:

    textedit.make(new: TESymbol.document, at: nil, withData: nil,
       withProperties: [TESymbol.text: "Hello World"],
      eventAttributes: [keyTimeoutAttr: 30])

which isn't the most beautiful (and starts to look unpleasant when constructing application commands that have really long verbose argument lists), but is possibly the most "Swiftian" way to do it. Another option would be to do what I did in Ruby, which was for each command to take 0, 1, or 2 of the following arguments: a direct parameter, and a dictionary containing all keyword parameters and/or event attributes:

    textedit.make(args: [
            TESymbol.new: TESymbol.document,
            TESymbol.withProperties: [TESymbol.text: "Hello World"],
            TESymbol.timeout: 30])

or:

    textedit.make(args: [
            "new": TESymbol.document,
            "withProperties": [TESymbol.text: "Hello World"],
            "timeout": 30])

depending on whether one prefers parameter names to be given as symbols or strings.

If anyone has any thoughts or other ideas, I'd very much like to hear them.

...

2. Does anyone have any particular thoughts on how best to present symbolic values? In Python I just had an dynamic object (`k`) that pretended to be an infinite namespace so one could write `k.document`, `k.text`, etc. and the application would figure out what AEDesc code ('docu', 'ctxt') that actually represented when packing it into the AE. For now I'm emulating the objc-appscript approach, which is to have a statically generated class (e.g. `TESymbol`) with a whole bunch of class methods on it that return the corresponding instances (e.g. `TESymbol.document`).

Right now I'm defining these on the generated Swift class as static vars, but I dunno if there'd be a better way of presenting these, e.g. as lazy vars on the module itself (`TEDocument`, `TEText`, etc). (Bear in mind that these symbols are defined all over the place - some in the app's terminology, some in AppleScript's - so some looseness/flexibility is required vis-a-vis types.)

I'm also wondering if it'd be better to drop the ObjC-style name prefixes and just use the module's own namespace name to disambiguate as needed, e.g. `TEGlue.document`, `TEGlue.text`, or even (e.g.) `kTEDocument`, `kTEText`?

Again, any thoughts/preferences? (Bear in mind that a Swift program might use several different apps, so each app's terms really should stay in their own namespace to avoid conflicts/confusion.)

...

3. Is there any easy way to get the _literal_ representation of a standard Swift type? As with py-appscript, objc-appscript, etc. the goal is to enable a user to print an object specifier and be able to copy-and-paste that straight into another script - i.e. `-description` should always return a string that represents valid Swift code.

Python provides a _very_ convenient `repr()` function and "{!r}" interpolation that does this automatically; however, for ObjC I had to write my own code to describe NSNumbers, NSStrings, NSArrays, etc. using literal ObjC syntax - @1, @"...", @[...]", etc. - since those classes' own -description methods would return "human-readable" rather than literal representations. Is there a Python-like way to do it in Swift, or do I have to write another formatter from scratch?

...

Feel free to rummage around the Python/Ruby/ObjC documentation on the appscript site if it helps to inspire, and all advice, suggestions, criticisms, etc. will be much appreciated.

Many thanks,

has

--

[1] https://bitbucket.org/hhas/appleeventbridge

[2] http://appscript.sourceforge.net

_______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden


  • Follow-Ups:
    • Re: [Swift] best way to support 'keyword' args, symbolic values, show values as literals?
      • From: Dave <email@hidden>
    • Re: [Swift] best way to support 'keyword' args, symbolic values, show values as literals?
      • From: Quincey Morris <email@hidden>
  • Prev by Date: Re: Language options: Objective-C, Swift, C or C++?
  • Next by Date: Re: [Swift] best way to support 'keyword' args, symbolic values, show values as literals?
  • Previous by thread: Re: description method question
  • Next by thread: Re: [Swift] best way to support 'keyword' args, symbolic values, show values as literals?
  • Index(es):
    • Date
    • Thread