Automate with Swift
CocoaConf DC, April 10, 2015
Tony Ingraldi
GitHub: tingraldi • Twitter: @TonyIngraldi
Itinerary
• Motivation: Why Automate?
• A bit of history
• Why Swift?
• Nuts and bolts
Show of Hands?
• Have you used…
• AppleScript?
• Automator?
• Other form of application scripting?
Why Automate?
• Improve productivity
• Avoid repetitive tasks
• Streamline workflows
• Opportunities for experimentation
• Add missing features to OS X apps!
Scriptable Applications
Demo
Sneak peak
Open Scripting Architecture
• Introduced way back in October 1993
• The state of the Mac looked like this
Beige!
Open Scripting Architecture
• Survived the transition to OS X
• Leveraged by OS X to launch applications, open
documents, etc.
• A standard mechanism for interapplication
communication
Language Options
• AppleScript (there from the beginning)
• JavaScript for Automation (as of Yosemite)
• Python (PyObjC, py-appscript)
• Ruby (RubyCocoa, RubyOSA)
• And now Swift!
Language Options
• AppleScript (there from the beginning)
• JavaScript for Automation (as of Yosemite)
• Python (PyObjC, py-appscript)
• Ruby (RubyCocoa, RubyOSA)
• And now Swift!
AppleScript
• A “natural language” programming model
• Primarily geared toward being the “glue”
between apps
• Limited inherent functionality
AppleScript
• Can be frustrating to people who are fluent in
“normal” programming languages
• Verbosity is a hallmark
set fileName to "name.jpg"
set sansExtension to text 1 thru ((offset of "." in fileName) - 1) of fileName
Swift
• A “primary” language
• Ready access to Cocoa
• Can run Swift scripts using #! convention
• A familiar coding style
let fileName = "name.jpg"
let sansExtension = fileName.stringByDeletingPathExtension
Scripting Bridge
• Introduced in Mac OS X Leopard (version 10.5)
• Provides high-level Objective-C access to
scriptable applications
• Can be leveraged in Swift, with a bit of extra
work
Scripting Bridge Recipe
• 1 part sdef
• 1 part sdp
• A pinch of manual intervention
• Automate to taste
Scripting Definition
• Scriptable applications include resources that
describe their scripting interface
• Can be in a variety of forms
• The sdef command-line utility extracts the
scripting definition
• Writes to standard output in XML format
Scripting Definition
• Using sdef
sdef /path/to/App.app > App.sdef
• For the most part, using sdef is “run and forget”
• Until you find out what’s wrong when you run sdp
• Some sdp warnings can be ignored
Scripting Definition
• Using sdp
sdp -fh --basename App App.sdef
Demo
sdef and sdp
Kicking the Tires
• Xcode
• Swift and ObjC command line targets
• Includes the header generated by sdp
• Experiment in main.swift and main.m
Demo
Kicking the tires in Xcode
Don’t Run with Scissors
• Using Objective-C header leads to pervasive
AnyObject typing
• Works, but can lead to ambiguity
• For properties, sometimes have to resort to
using valueForKey
• Sometimes leads to awkward method
invocation
An Alternative
• Create Swift protocols that cover the generated
Objective-C API
• 👍 Supports rich set of types
• 👍 API translation can be automated
• 👎 Requires optional declaration of all
properties and methods
Objective-C Excerpt
@interface AcornApplication : SBApplication
@property (copy, readonly) NSString *name;
- (SBElementArray *) windows;
- (NSString *) taunt;
@end
SBObject
@objc public protocol SBObjectProtocol:
NSObjectProtocol {
func get() -> AnyObject!
}
SBApplication
@objc public protocol SBApplicationProtocol:
SBObjectProtocol {
func activate()
var delegate:SBApplicationDelegate! { get set }
}
Application Protocol
@objc public protocol AcornApplication:
SBApplicationProtocol {
optional var name: String { get }
optional func windows() -> SBElementArray
optional func taunt() -> String
}
extension SBApplication : AcornApplication {}
Putting it Together
import ScriptingBridge
@objc public protocol AcornApplication : … {…}
extension SBApplication : AcornApplication {}
let acorn = SBApplication
.applicationWithBundleIdentifier(
“com.flyingmeat.Acorn4"
) as! AcornApplication
acorn.taunt!()
Acorn Taunt
Plan of Attack
• Create frameworks for target applications
• Install in /Library/Frameworks
• Write scripts instead of compiled code
By the Way
• Playgrounds don’t fit well
• The Swift REPL could spontaneously combust at
any moment
• What’s a scripter to do?
• Use a text editor
• Edit, run, repeat
Script Invocation
• Hash-bang on the first line
#!/usr/bin/xcrun swift -F /Library/Frameworks
• chmod +x SomeScript.swift
A Little Help
https://github.com/tingraldi/SwiftScripting
• Header conversion utilities
• Sample Application Scripting Frameworks
• Sample Scripts
• Scripting Utilities Framework
Demo
A Scripting Framework
A Little More Help
import ScriptingBridge
@objc public protocol AcornApplication {…}
extension SBApplication : AcornApplication {}
let acorn = SBApplication
.applicationWithBundleIdentifier(
"com.flyingmeat.Acorn4"
) as! AcornApplication
acorn.taunt!()
A Little More Help
import ScriptingBridge
import AcornScripting // Acorn Swift protocols
let acorn = SBApplication
.applicationWithBundleIdentifier(
"com.flyingmeat.Acorn4"
) as! AcornApplication
acorn.taunt!()
A Little More Help
import ScriptingUtilities // Helper framework
import AcornScripting // Acorn Swift protocols
let acorn = Application(name: "Acorn")
as! AcornApplication
acorn.taunt!()
Where to Put Scripts?
• Short answer: anywhere
• Some applications have special script folders
• Scripting Menu
• Invoke via an Automator Service
Demo
Scripting Menu, etc.
Summary
• Automate for productivity
• Add “missing features”
• Reinforce/leverage Swift expertise
Questions?

CocoaConf DC - Automate with Swift - Tony Ingraldi

  • 1.
    Automate with Swift CocoaConfDC, April 10, 2015 Tony Ingraldi GitHub: tingraldi • Twitter: @TonyIngraldi
  • 2.
    Itinerary • Motivation: WhyAutomate? • A bit of history • Why Swift? • Nuts and bolts
  • 3.
    Show of Hands? •Have you used… • AppleScript? • Automator? • Other form of application scripting?
  • 4.
    Why Automate? • Improveproductivity • Avoid repetitive tasks • Streamline workflows • Opportunities for experimentation • Add missing features to OS X apps!
  • 5.
  • 6.
  • 7.
    Open Scripting Architecture •Introduced way back in October 1993 • The state of the Mac looked like this Beige!
  • 8.
    Open Scripting Architecture •Survived the transition to OS X • Leveraged by OS X to launch applications, open documents, etc. • A standard mechanism for interapplication communication
  • 9.
    Language Options • AppleScript(there from the beginning) • JavaScript for Automation (as of Yosemite) • Python (PyObjC, py-appscript) • Ruby (RubyCocoa, RubyOSA) • And now Swift!
  • 10.
    Language Options • AppleScript(there from the beginning) • JavaScript for Automation (as of Yosemite) • Python (PyObjC, py-appscript) • Ruby (RubyCocoa, RubyOSA) • And now Swift!
  • 11.
    AppleScript • A “naturallanguage” programming model • Primarily geared toward being the “glue” between apps • Limited inherent functionality
  • 12.
    AppleScript • Can befrustrating to people who are fluent in “normal” programming languages • Verbosity is a hallmark set fileName to "name.jpg" set sansExtension to text 1 thru ((offset of "." in fileName) - 1) of fileName
  • 13.
    Swift • A “primary”language • Ready access to Cocoa • Can run Swift scripts using #! convention • A familiar coding style let fileName = "name.jpg" let sansExtension = fileName.stringByDeletingPathExtension
  • 14.
    Scripting Bridge • Introducedin Mac OS X Leopard (version 10.5) • Provides high-level Objective-C access to scriptable applications • Can be leveraged in Swift, with a bit of extra work
  • 15.
    Scripting Bridge Recipe •1 part sdef • 1 part sdp • A pinch of manual intervention • Automate to taste
  • 16.
    Scripting Definition • Scriptableapplications include resources that describe their scripting interface • Can be in a variety of forms • The sdef command-line utility extracts the scripting definition • Writes to standard output in XML format
  • 17.
    Scripting Definition • Usingsdef sdef /path/to/App.app > App.sdef • For the most part, using sdef is “run and forget” • Until you find out what’s wrong when you run sdp • Some sdp warnings can be ignored
  • 18.
    Scripting Definition • Usingsdp sdp -fh --basename App App.sdef
  • 19.
  • 20.
    Kicking the Tires •Xcode • Swift and ObjC command line targets • Includes the header generated by sdp • Experiment in main.swift and main.m
  • 21.
  • 22.
    Don’t Run withScissors • Using Objective-C header leads to pervasive AnyObject typing • Works, but can lead to ambiguity • For properties, sometimes have to resort to using valueForKey • Sometimes leads to awkward method invocation
  • 23.
    An Alternative • CreateSwift protocols that cover the generated Objective-C API • 👍 Supports rich set of types • 👍 API translation can be automated • 👎 Requires optional declaration of all properties and methods
  • 24.
    Objective-C Excerpt @interface AcornApplication: SBApplication @property (copy, readonly) NSString *name; - (SBElementArray *) windows; - (NSString *) taunt; @end
  • 25.
    SBObject @objc public protocolSBObjectProtocol: NSObjectProtocol { func get() -> AnyObject! }
  • 26.
    SBApplication @objc public protocolSBApplicationProtocol: SBObjectProtocol { func activate() var delegate:SBApplicationDelegate! { get set } }
  • 27.
    Application Protocol @objc publicprotocol AcornApplication: SBApplicationProtocol { optional var name: String { get } optional func windows() -> SBElementArray optional func taunt() -> String } extension SBApplication : AcornApplication {}
  • 28.
    Putting it Together importScriptingBridge @objc public protocol AcornApplication : … {…} extension SBApplication : AcornApplication {} let acorn = SBApplication .applicationWithBundleIdentifier( “com.flyingmeat.Acorn4" ) as! AcornApplication acorn.taunt!()
  • 29.
  • 30.
    Plan of Attack •Create frameworks for target applications • Install in /Library/Frameworks • Write scripts instead of compiled code
  • 31.
    By the Way •Playgrounds don’t fit well • The Swift REPL could spontaneously combust at any moment • What’s a scripter to do? • Use a text editor • Edit, run, repeat
  • 32.
    Script Invocation • Hash-bangon the first line #!/usr/bin/xcrun swift -F /Library/Frameworks • chmod +x SomeScript.swift
  • 33.
    A Little Help https://github.com/tingraldi/SwiftScripting •Header conversion utilities • Sample Application Scripting Frameworks • Sample Scripts • Scripting Utilities Framework
  • 34.
  • 35.
    A Little MoreHelp import ScriptingBridge @objc public protocol AcornApplication {…} extension SBApplication : AcornApplication {} let acorn = SBApplication .applicationWithBundleIdentifier( "com.flyingmeat.Acorn4" ) as! AcornApplication acorn.taunt!()
  • 36.
    A Little MoreHelp import ScriptingBridge import AcornScripting // Acorn Swift protocols let acorn = SBApplication .applicationWithBundleIdentifier( "com.flyingmeat.Acorn4" ) as! AcornApplication acorn.taunt!()
  • 37.
    A Little MoreHelp import ScriptingUtilities // Helper framework import AcornScripting // Acorn Swift protocols let acorn = Application(name: "Acorn") as! AcornApplication acorn.taunt!()
  • 38.
    Where to PutScripts? • Short answer: anywhere • Some applications have special script folders • Scripting Menu • Invoke via an Automator Service
  • 39.
  • 40.
    Summary • Automate forproductivity • Add “missing features” • Reinforce/leverage Swift expertise
  • 41.