SketchAPI icon indicating copy to clipboard operation
SketchAPI copied to clipboard

Access to shared components used in Layers

Open WCByrne opened this issue 6 years ago • 7 comments

Is it possible to retrieve share components that are used in a layers/containers? I've previously found and hooked into versions of the functions below which have worked well but I'm hoping to move over the JS API soon and avoid the endless searching through header files for functions that solve my needs.

layerStyles.sharedObjectsInLayer(...)
MSPasteboardLayersBase.usedSymbolsInContainer_document(...)

WCByrne avatar Jun 27 '18 01:06 WCByrne

you can do something like:

function recurseThroughLayers(root, fn) {
  fn(root)
  if (root.layers) {
    root.layers.forEach(l => recurseThroughLayers(l, fn))
  }
}

const sharedStyleUsed = []
recurseThroughLayers(layer, l => {
  if (l.style && l.style.sharedStyleId) {
    layerStyledUsed.push(l.style.sharedStyle)
  }
})

const symbolMasterUsed = []
recurseThroughLayers(layer, l => {
  if (l.type === 'SymbolInstance') {
    symbolMasterUsed.push(l.symbolMaster)
  }
})

The shared styles will only be available in the next sketch release (see https://github.com/BohemianCoding/SketchAPI/pull/236).

I don't really want to add overly specific APIs when it can be achieve with what we already have, especially when it's just about writing what you want: "retrieve the symbol masters used in a layer's children"? yeah, go through its children and check if they are a symbol instance.

mathieudutour avatar Aug 05 '18 16:08 mathieudutour

We discussed this a bit in the slack. There seem to be two additional issues

  • the algorithm given doesn't fully mimic the behavior of usedSymbolsInContainer_document. usedSymbolsInContainer_document takes into account recursive nested symbols - returning all symbols used at all levels e.g. if your artboard contains a card which uses a button symbol, usedSymbolsInContainer_document will return card AND button. The given algorithm would just return card, and an additional recursion would be required to find the button.
  • the algorithm performs quite significantly slower than usedSymbolsInContainer_document. This might be because iterating through the list of all layers is pretty expensive if there are many many layers, as they all need to be "loaded" into javascript? I also assume usedSymbolsInContainer_document is using something more like NSPredicate to sift quickly through the right layers, and therefore only touching what it needs

robintindale avatar Oct 08 '18 10:10 robintindale

Looks like the internal version might also returns masters for used overrides. I just found another bug related to some "used symbols" not being included using the JS approach updated to recurse masters for the nested instances. But jumping to the master for each instance fails to account for overrides.

WCByrne avatar Oct 10 '18 22:10 WCByrne

@mathieudutour Any thoughts on this? I've have a workaround for digging through nested symbols now but I still have people writing in about the performance of my plugin since I made the switch. Would be nice to know if the internals are in fact doing this in a better way. Any pro tips?

WCByrne avatar Oct 11 '18 22:10 WCByrne

The algorithm is pretty much the same:

fn1:
- enumerate the symbol instances in the container
- for each symbol instance
  - fn2
  - for each symbol override, fn2

fn2:
- if we haven't seen it yet:
    - add it to the seen set
    - get the symbol master and f1 with the symbol master as the container

But yes, it will always be slower on JS because the cocoascript bridge has an overhead so it's slower than just doing everything on the obj-c side and getting the result.

I still don't really want to add it to the main API because I don't think there is a lot of use cases for it. What I'd consider tho is creating a new module (maybe sketch/performance) which exposes those kinds of algorithms.

mathieudutour avatar Oct 12 '18 08:10 mathieudutour

Got it, good to know it's just the wrapper. That's the logic I ended up with. Since I needed both styles and symbols I also went with a merged sharedComponentsInLayout(layer, document) -> {symbols, styles} function to get everything in one traverse. That seems to have sped things back up a bit.


As for the future, a separate module could make sense as long as there is a clear distinction of where new functionality belongs. Not sure performance is the right name but maybe something more generic about helpers/extensions.

I imagine it would benefit the community in the long run if every developer didn't have to work through things like this, and possibly get it wrong. Agreed, this example in particular is probably not a common use case but after working through it I would say it was a little less trivial than originally thought. I'm sure there are other more common examples like this.

WCByrne avatar Oct 12 '18 13:10 WCByrne

Seems like style overrides now play a part in this 😄

WCByrne avatar Oct 19 '18 23:10 WCByrne