[SalesForce] Lightning: Get sobject tab icon

Is there a way to get an SObject (Tab) icon (name) to be referenced from the design system resource?

I'm building a custom visualforce page that will be displayed using Lightning, so I need to style it accordingly. My problem is that I really don't know what is the best practice to get the icon or at least it's name so I can reference the design system resource, here is an example:

I have a visualforce page with an icon as:

<span class="slds-icon_container slds-icon_container--circle slds-icon-action-description">
    <svg aria-hidden="true" class="slds-icon slds-icon--small">
        <use xlink:href="{!URLFOR($Resource.SLDS100,'assets/icons/custom-sprite/svg/symbols.svg#custom44')}"></use>
   </svg>
</span>

And I need to do something like this:

<span class="slds-icon_container slds-icon_container--circle slds-icon-action-description">
    <svg aria-hidden="true" class="slds-icon slds-icon--small">
        <use xlink:href="{!URLFOR($Resource.SLDS100,iconName)}"></use>
   </svg>
</span>

Where iconName is the URL of the svg icon of the current standardController object, right now I'm getting it from the extension like (not exactly):

public String getIconName(){
    List<Schema.DescribeTabSetResult> tabSetDesc = Schema.describeTabs();
    List<Schema.DescribeTabResult> tabDesc = new List<Schema.DescribeTabResult>();
    List<Schema.DescribeIconResult> iconDesc = new List<Schema.DescribeIconResult>();

    for(Schema.DescribeTabSetResult tsr : tabSetDesc) {
        tabDesc.addAll(tsr.getTabs());
    }

    for(Schema.DescribeTabResult tr : tabDesc) {
        if (tr.getSobjectName() == 'Custom__c') {
            iconDesc.addAll(tr.getIcons());
        }
    }

    String u;
    for (Schema.DescribeIconResult ir : iconDesc) {
        if (ir.getContentType() == 'image/svg+xml'){
            u = ir.getUrl();
            break;
        }
    }
    Integer s = u.indexOf('/custom/') + '/custom/'.length(),
            e = u.length() - 4;
    return 'assets/icons/custom-sprite/svg/symbols.svg#' + u.substring(s, e);
}

But with lightning in mind, it really looks like there should be a better way to achieve this (without all those loops), and this workaround doesn't work for a custom icon.

Notes:

  1. Similar question
  2. This needs to work in a managed package, where the icon changes from org to org.

Best Answer

For any of those who may be looking for an even more generic solution, or possibly a use case where you are simply using the SLDS styles in a VF page, I have a method which returns the lightning style class for the wrapper of the tab icon, in addition to its Lightning png icon to fill it. This is supported in a managed package, and will also pull custom icons that are defined by customers/packages installed. If an object is passed in that does not have a tab definition, it simply returns the SLDS Custom icon as a default. My use case required dynamic loading of the icon depending on a configuration for the object, so a dynamic IMG binding was much more realistic to switch up on the fly than repainting an SVG.

Method that takes in sObject Name and returns a map of properties (iconStyle, iconURL)

/***
 * Explores the schema of the soject passed in and finds lightning icon style and image url for sObject
 * If nothing is found, defaults to the custom icon
 *
 * @param   sObjectTypeName       the sObject to find the icon for
 * @return  Map<String,String>    Map of properties about the icon to be consumed by the front end.
 *                                Right now, returns values for keys: iconStyle,iconURL
 */
global static Map<String,String> retrieveIconForObject(String sObjectName) {    
    String iconStyle;
    String iconURL;
    String iconTag;
    Map<String,String>                iconPropertyMap = new Map<String,String>();
    List<Schema.DescribeTabSetResult> tabSetDesc      = Schema.describeTabs();
    List<Schema.DescribeTabResult>    tabDesc         = new List<Schema.DescribeTabResult>();
    List<Schema.DescribeIconResult>   iconDesc        = new List<Schema.DescribeIconResult>();

    for(Schema.DescribeTabSetResult tsr : tabSetDesc){
       tabDesc.addAll(tsr.getTabs()); 
    }
    for(Schema.DescribeTabResult tr : tabDesc) {
        if( sObjectName == tr.getSobjectName() ) {
            system.debug(tr.getIcons());
            if(!tr.getIcons().isEmpty()){
                iconDesc.addAll(tr.getIcons());
            }else{
                if( tr.isCustom() == true ) {
                    iconStyle = 'slds-icon-standard-custom';
                    iconURL   = '/img/icon/t4v35/standard/custom_120.png';
                } else {
                    iconStyle = 'slds-icon-standard-' + sObjectName.toLowerCase();
                    iconURL   = '/img/icon/t4v35/standard/' + sObjectName.toLowerCase() + '_120.png';
                }
            }
        }
    }
    for (Schema.DescribeIconResult ir : iconDesc) {
        if(ir.getTheme() =='custom' && !ir.getContentType().startsWith('image/svg')){//custom icon thats not an svg
            iconTag   = 'custom';
            iconURL   = ir.getURL();
            iconStyle = 'slds-icon-standard-custom';
            break;
        }else if(ir.getWidth() == 120 && ir.getTheme() =='theme4' && ir.getContentType() == 'image/png'){//large lightning icon image
            if(ir.getURL().contains('/custom/')){ //Icon is from the Custom Sprite
                iconTag    = ir.getURL().substringBetween('custom/','.png').substringBefore('_');
                iconURL    = '/img/icon/t4v35/custom/' + iconTag + '_120.png';
                iconStyle  = 'slds-icon-custom-'+ iconTag;
                break;
            }else if(ir.getURL().contains('/standard/')){//Icon is from the Standard Sprite
                iconTag    = ir.getURL().substringBetween('standard/','.png').substringBefore('_');
                iconURL    = '/img/icon/t4v35/standard/' + iconTag + '_120.png';
                iconStyle  = 'slds-icon-standard-'+ iconTag;
                break;
            }
        }
    }
    //if no icons are found, just default to the Custom lightning icon
    if(iconStyle == null){
        iconStyle = 'slds-icon-standard-custom';
        iconURL   = '/img/icon/t4v35/standard/custom_120.png';
        iconTag   = 'No Lightning Icon Found';
    }
    //return a map with our properties for the front end
    iconPropertyMap.put('iconStyle',iconStyle);
    iconPropertyMap.put('iconURL',  iconURL);

    return iconPropertyMap;
}

Example usage for binding:

<span data-bind="css: selectedMapObjectIconClass" class="slds-icon_container slds-avatar">
  <img data-bind="attr:{src: selectedMapObjectIconUrl}"></img>
</span>