How to Pass Data to Unity in Salesforce Lightning

How to pass data to Unity:

  1. We define a Lightning Component that wraps Unity Canvas App.

    <aura:component implements="force:hasRecordId,force:hasSObjectName,force:lightningQuickActionWithoutHeader">    
        <!-- This is for styling the modal -->
        <aura:html tag="style">
        .slds-modal__container {
            width: 50% !important;
        	max-width:70% !important;
        }
    	</aura:html>
        <!-- This is for loading data if needed, 
        for basic use (entityName + id), we don't use this loader
        <aura:attribute name="getRecordId" type="Object"/>    
        <aura:attribute name="opportunityRecord" type="Object"/>
        <aura:attribute name="recordLoadError" type="String"/>
        <force:recordData aura:id="recordLoader"
            recordId="{!v.recordId}"
            fields="Id,AccountId"
            targetFields="{!v.opportunityRecord}"
            targetError="{!v.recordLoadError}"
        />
        --> 
        <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
        <force:canvasApp developerName="QuoteConfigurator_demo_master" parameters="{!v.getRecordId}"/>
    </aura:component>
    

    In the above code:

    • We use <force:hasRecordId> and <force:hasSObjectName> to tell Lightning to include recordId and sObjectName which are entityId and entityType we need.

    • We use <aura:handler name=”init”> to build v.getRecordId to pass it to the ‘parameters’ attribute of the Unity canvas app. This is done by c.doInit which is a function of the controller. The controller code is as follows:

      ({
      	doInit: function(cmp) {
              var recordId = cmp.get("v.recordId");
              var sObjectName = cmp.get("v.sObjectName");
              
              // NOTE: Lightning is very picky about quotes.
              // JSON keys and values MUST be wrapped with double quotes.
              // See: https://salesforce.stackexchange.com/questions/120473/canvas-app-in-lightning-component        
          	var output = '{"recordId": "' + recordId + '","type":"' + sObjectName + '"}';
          	cmp.set("v.getRecordId", output);
          }
      })
      

      Then, when we refresh signedrequest, the ‘getRecordId’ is passed to ‘parameters’ as in the below example (line 61 to 64):

      JSON
      {
         "algorithm":"xxxxxxxx",
         "issuedAt":1848532899,
         "userId":"xxxxxxxx",
         "client":{
            "refreshToken":null,
            "instanceId":"_:QuoteConfigurator_demo_master:41:1125;a:canvasapp",
            "targetOrigin":"https://pricefx--dev.cs101.my.salesforce.com",
            "instanceUrl":"https://pricefx--Dev.cs101.my.salesforce.com",
            "oauthToken":"xxxxxxxx"
         },
         "context":{
            "user":{
               ...
            },
            "links":{
               "loginUrl":"https://pricefx--Dev.cs101.my.salesforce.com",
               "enterpriseUrl":"/services/Soap/c/48.0/00D1X0000008g8J",
               "metadataUrl":"/services/Soap/m/48.0/00D1X0000008g8J",
               "partnerUrl":"/services/Soap/u/48.0/00D1X0000008g8J",
               "restUrl":"/services/data/v48.0/",
               "sobjectUrl":"/services/data/v48.0/sobjects/",
               "searchUrl":"/services/data/v48.0/search/",
               "queryUrl":"/services/data/v48.0/query/",
               "recentItemsUrl":"/services/data/v48.0/recent/",
               "chatterFeedsUrl":"/services/data/v31.0/chatter/feeds",
               "chatterGroupsUrl":"/services/data/v48.0/chatter/groups",
               "chatterUsersUrl":"/services/data/v48.0/chatter/users",
               "chatterFeedItemsUrl":"/services/data/v31.0/chatter/feed-items",
               "userUrl":"/0051v000004fp7IAAQ"
            },
            "application":{
               "name":"QuoteConfigurator_demo_master",
               "canvasUrl":"https://demo.pricefx.eu/",
               "applicationId":"06P1X0000008eNR",
               "version":"1.0",
               "authType":"SIGNED_REQUEST",
               "referenceId":"09H1X0000004SAX",
               "options":[
      
               ],
               "samlInitiationMethod":"IdpInitiated",
               "isInstalledPersonalApp":false,
               "developerName":"QuoteConfigurator_demo_master",
               "namespace":""
            },
            "environment":{
               "referer":null,
               "locationUrl":"https://pricefx--dev.lightning.force.com/lightning/r/Opportunity/0061X000005lcTbQAI/view",
               "displayLocation":"Aura",
               "sublocation":null,
               "uiTheme":"Theme3",
               "dimensions":{
                  "width":"800px",
                  "height":"900px",
                  "maxWidth":"1000px",
                  "maxHeight":"2000px",
                  "clientWidth":"832px",
                  "clientHeight":"555px"
               },
               "parameters":{
                  "recordId":"0061X000005lcTbQAI",
                  "type":"Opportunity"
               },
               "record":{
      
               },
               "version":{
                  "season":"SPRING",
                  "api":"48.0"
               }
            },
            "organization":{
               "organizationId":"00D1X0000008g8JUAQ",
               "name":"Price FX",
               "multicurrencyEnabled":true,
               "namespacePrefix":null,
               "currencyIsoCode":"EUR"
            }
         }
      }
      

      That is all for the Salesforce part.

  2. In the Unity canvas app, instead of retrieving data from ‘record.attributes’ of signRequest response:

    JavaScript
    const attributes = salesforce.context.environment.record.attributes;
    Log.debug(TAG, '_refreshSignedRequest()', { attributes });
    
    

    we have to get it from the ‘parameters’ + ‘links' mentioned above, using this 'buildAttributes’ function:

    JavaScript
    const buildAttributes = ({ record, parameters }, links) => {
      if (record && record.attributes) {
        return record.attributes;
      }
    
      // when AppCanvas wrapped by LightningComponent, record is not included
      // so we have to build attributes from paramters that passed to AppCanvas
      const type = _.get(parameters, 'type');
      const recordId = _.get(parameters, 'recordId');
      const hostname = _.get(links, 'loginUrl');
      const sObjectLink = _.get(links, 'sobjectUrl');
      return {
        type: parameters ? parameters.type : '',
        url: hostname + sObjectLink + type + '/' + recordId
      };
    };
    

    Then the Unity canvas app can communicate to Salesforce as usually.