Scripting equivalent of Zoom to Selection

Hello helpful people.

Is there a scripting equivalent to View → Zoom To → Selection (or ⌘2 on US keyboards)?

My old method of doing this recently broke and I’m looking for a replacement. That was the following, if anyone is curious:

doc.actionsController().actionForID("MSZoomToSelectionAction").zoomToSelection(nil);

Thanks!

Hello there!

This method still works (as of the latest 101 Beta) but you need to update the action identifier bit:

context.document
    .actionsController()
    .actionForID("Sketch.MSZoomToSelectionAction") // <-- note the "Sketch." prefix
    .zoomToSelection(nil);
2 Likes

Thank you! Works like a charm. Can I ask, how did you know this? Or, better question, how could I have figured this out on my own?

Happy to help!

Well, I’m afraid there’s no simple answer to the “how” question, but I guess I can demonstrate my thought process at least:

Step 1

As you may know, Sketch is historically written in Objective-C with some newer parts (and some modernized old ones) in Swift. And unlike Objective-C classes, Swift classes may include a namespace as part of their name.

In some cases, when a legacy internal Sketch class name is involved (which is usually “MS”+something, e.g. MSZoomToSelectionAction), it makes sense to check if it’s been simply rewritten from Objective-C to Swift in a newer version of Sketch and thus now contains a namespace prefix (the Sketch. bit in our case).

The namespace itself is usually just a name of the executable/library file that contains the given Sketch class – and figuring out the exact one is a game of trial and error. Here’s a few heuristics that I find useful more often than not:

  • if it’s something from the Inspector panel, it probably has the SketchControllers. prefix
  • if it’s a class that looks like it could be a part of the rendering pipeline, then it probably has the SketchRendering. prefix
  • if it’s something like a layer or any other part of the document hierarchy, the prefix is likely to be SketchModel.
  • if it’s something that only makes sense as part of Sketch.app and not in, say, sketchtool CLI (like the “zoom to selection” menu action), chances are the prefix is simply Sketch.

This is obviously not an exhaustive list of available prefixes nor possible heuristics. The more robust way to figure out a prefix for sure would be to scan a Sketch.app bundle and find where a particular class name is mentioned. Like this:

$ cd /Applications/Sketch.app
$ grep -R "MSZoomToSelectionAction" --exclude="*.js" *

Binary file Contents/MacOS/Sketch matches

The final Sketch bit in the “Binary file Contents/MacOS/Sketch matches” message is our possible prefix that is at least worth trying out. Although you’d likely to receive not one but multiple filenames to go through instead:

$ grep -R "MSImmutableLayerAncestry" --exclude="*.js" *

Binary file Contents/MacOS/sketchtool matches
Binary file Contents/MacOS/Sketch matches
Binary file Contents/Frameworks/SketchModel.framework/Versions/A/SketchModel matches
Binary file Contents/Frameworks/SketchAnnotations.framework/Versions/A/SketchAnnotations matches
Binary file Contents/Frameworks/SketchControllers.framework/Versions/A/SketchControllers matches
Binary file Contents/Frameworks/SketchRendering.framework/Versions/A/SketchRendering matches
Binary file Contents/Frameworks/SketchPrototyping.framework/Versions/A/SketchPrototyping matches

(in this particular example, since we’re talking about something with a “layer” in its name, it’s worth examining SketchModel.framework library and SketchModel. prefix first).

Step #2

Sometimes it’s not just a class name that’s changed, but its behavior/API as well, so there’re renamed functions, removed properties, etc. The only way to figure out how to use a newer version of a class is to load its containing executable/library file (see Step 1) into a disassembler and try to make it make sense. Reverse-engineering is a complex topic of its own and I’m sure there’re quite a few blog posts and articles covering the basics like this one for instance (it’s for iOS’s UIKit, but the general approach is virtually the same).

I’ll also be glad to offer my help with everything that involves reverse-engineering Sketch and figuring out how different internal components work there, so don’t hesitate to post questions if you ever have one!