If you want to distribute an unmanaged package that needs sample data, or default data, you will hit a few roadblocks when you try to do so. First, Salesforce does not allow the ideal solution; it will not call the installHandler interface, even though it allows you to implement it. Apex will compile your installHandler class, but when somebody installs your package, it will just be ignored. Your app will install okay otherwise, but it will not have its data.
Next, you might try to put your install code in the constructor of your Apex controller. However, DML code is not allowed in a constructor, so that will not work either.
Your third idea might be to put your install code in a future method and call it from your constructor. But that is also forbidden.
Your fourth idea might be to use the “action” attribute of the Apex “page” tag like this:
<apex:page controller="YourController" action="{!makeData}">
Your makeData() method will be fired before the page loads, however the manual warns: “This action should not be used for initialization or DML.”
We might guess that there is something in the Salesforce architecture at a deeper level that simply makes this sort of thing impossible, no matter what loophole you might dream up. Consequently, we have to install our data after the app is installed and loaded into a page. And I can think of two options:
First, you might put a button on the page called “Make Data” and inform your users to click it the first time they run your app. Your constructor would check the database to see if any data was present, and if so, hide the button so that the user never sees it again.
Second, the only way that I can see to install your data automatically is to use an Apex actionPoller component on your Visualforce page:
<apex:actionPoller action="{!checkData}" interval="5" enabled="{!pollerOn}"/>
So, add a property called pollerOn to your controller, and set it to true in the constructor. That will turn on the actionPoller, and 5 seconds after the page loads, your checkData() method will be called. The method should immediately set pollerOn to false so that the actionPoller does not fire again. Your checkData() method would then check the database, and if there were no data, it would insert it.
This approach works fine, however you cannot reduce the 5-second delay since that is the minimum interval for the actionPoller component. Consequently, if your app needs to immediately display the data, there is a chance that the user will conclude that the app is broken, and close the page before your code even gets called. So, you just need to put a “Please wait a moment” message somewhere on the page.
As you will see in the code below, I put my data into JSON format, and uploaded it into my package as a static resource.
So, in addition to the apex markup discussed above, we have to add the pollerOn property to the controller:
public string pollerOn {get;set;}
And in the constructor, we initialize it to true:
pollerOn='true';
That will cause checkData() to run 5 seconds after the app opens:
public void checkData() { pollerOn='false'; //Turn the actionPoller off immediately. integer n = [Select count() From Sample__c]; if (n==0) { //There is no data in the database, so load it from our static resource: StaticResource sr = [select Body from StaticResource where Name = 'TheData']; ListtheData = (List ) JSON.deserialize(sr.Body.toString(), List .class); insert theData; } presentData(); //Do whatever you normally do after the page loads. }
The checkData() method will run every time your app launches, however it will be very quick, and not likely noticeable by the user.
To see how well this approach works, install my app into your SFDC org.