SWT on OSX (Cocoa) must be run with the main SWT UI thread on the main Java Thread.
Here is a code idiom that starts the UI dispatch loop on the main thread and allows you to pass in your application as a Runnable.
On OSX you must start the JVM using the VM argument
-XstartOnFirstThread
Here is the code for the dispatch loop:
package com.chrisnewland.swt.launcher;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

public class SWTApplicationLauncher
{
   private static Display display = new Display();

   public static void launchApplication(Runnable app)
   {
       Thread appThread = new Thread(app);

       appThread.start();

       // =======================================
       // EVENT DISPATCH LOOP
       // =======================================
       while (!display.isDisposed())
       {
           try
           {
               if (!display.readAndDispatch())
               {
                   display.sleep();
               }
           }
           catch (Exception e)
           {
               // log errors
           }
       }
   }
   
   /*
    * provide access to the Display object for scheduling UI updates via Display.asyncExec() or Display.syncExec()
    */

   public static Display getDisplay()
   {
       return display;
   }
}
And example usage:
package com.chrisnewland.swt.launcher;

public class TestSWTLauncher implements Runnable
{
   @Override
   public void run()
   {
       // main application        
   }

   public static void main(String[] args)
   {
       SWTApplicationLauncher.launchApplication(new TestSWTLauncher());
   }
}
Launch using:
java -XstartOnFirstThread com.chrisnewland.swt.launcher.TestSWTLauncher