diff --git a/cbits/Main.m b/cbits/Main.m
new file mode 100644
--- /dev/null
+++ b/cbits/Main.m
@@ -0,0 +1,62 @@
+#include "HsFFI.h"
+#import <Cocoa/Cocoa.h>
+#import <WebKit/WebKit.h>
+
+extern void withWebView(WKWebView *, HsStablePtr);
+
+@interface AppDelegate : NSObject <NSApplicationDelegate>
+@property (nonatomic, assign) IBOutlet NSWindow *window;
+@property (nonatomic, assign) HsStablePtr handler;
+
+- (instancetype)initApp:(HsStablePtr)handler progName:(NSString *)progName;
+@end
+
+@implementation AppDelegate
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+    // Insert code here to tear down your application
+}
+
+-(id)initApp:(HsStablePtr)handler progName:(NSString *)progName {
+    self = [super init];
+    if (self) {
+        _handler = handler;
+        NSRect contentSize = NSMakeRect(0.0, 500.0, 1000.0, 700.0);
+        NSUInteger windowStyleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
+        _window = [[NSWindow alloc] initWithContentRect:contentSize styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:YES];
+        _window.backgroundColor = [NSColor whiteColor];
+        _window.title = progName;
+    }
+    return self;
+}
+
+-(void)applicationWillFinishLaunching:(NSNotification *)notification {
+    WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
+    [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
+    WKWebView *webView = [[WKWebView alloc] initWithFrame:_window.contentView.frame configuration:theConfiguration];
+    [_window setContentView:webView];
+    withWebView(webView, _handler);
+}
+
+-(void)applicationDidFinishLaunching:(NSNotification *)notification {
+    [_window orderFrontRegardless];
+    [_window center];
+    [NSApp activateIgnoringOtherApps:YES];
+}
+
+-(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
+    return YES;
+}
+
+@end
+
+void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName) {
+    @autoreleasepool {
+        NSApplication *application = [NSApplication sharedApplication];
+        AppDelegate *appDelegate = [[AppDelegate alloc] initApp:handler progName:[NSString stringWithCString:progName encoding:NSUTF8StringEncoding]];
+        [application setDelegate:appDelegate];
+        char * argv [] =  {"", 0};
+        NSApplicationMain(0, argv);
+    }
+}
+
diff --git a/cbits/WKWebView.m b/cbits/WKWebView.m
--- a/cbits/WKWebView.m
+++ b/cbits/WKWebView.m
@@ -39,7 +39,7 @@
 
 @end
 
-void addJSaddleHandler(WKWebView *webView, HsStablePtr startHandler,  HsStablePtr resultHandler) {
+void addJSaddleHandler(WKWebView *webView, HsStablePtr startHandler, HsStablePtr resultHandler) {
     JSaddleHandler * handler = [[JSaddleHandler alloc] initHandler:startHandler resultHandler:resultHandler];
     [[[webView configuration] userContentController] addScriptMessageHandler:handler name:@"jsaddle"];
     webView.navigationDelegate = handler;
diff --git a/jsaddle-wkwebview.cabal b/jsaddle-wkwebview.cabal
--- a/jsaddle-wkwebview.cabal
+++ b/jsaddle-wkwebview.cabal
@@ -1,5 +1,5 @@
 name: jsaddle-wkwebview
-version: 0.8.0.0
+version: 0.8.1.0
 cabal-version: >=1.10
 build-type: Simple
 license: MIT
@@ -28,6 +28,9 @@
         jsaddle >= 0.8.0.0 && <0.9
     default-language: Haskell2010
     hs-source-dirs: src
+    frameworks: Cocoa, WebKit
     ghc-options: -ferror-spans -Wall
-    c-sources: cbits/WKWebView.m
+    c-sources:
+        cbits/Main.m
+        cbits/WKWebView.m
 
diff --git a/src/Language/Javascript/JSaddle/WKWebView.hs b/src/Language/Javascript/JSaddle/WKWebView.hs
--- a/src/Language/Javascript/JSaddle/WKWebView.hs
+++ b/src/Language/Javascript/JSaddle/WKWebView.hs
@@ -2,8 +2,10 @@
 module Language.Javascript.JSaddle.WKWebView
     ( jsaddleMain
     , WKWebView(..)
+    , run
     ) where
 
+import System.Environment (getProgName)
 import Control.Monad (void, join)
 import Control.Concurrent (forkIO)
 
@@ -12,23 +14,30 @@
 import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
 import Data.Aeson (encode, decode)
 
-import Foreign.C.String (CString)
+import Foreign.C.String (CString, withCString)
 import Foreign.Ptr (Ptr)
 import Foreign.StablePtr (StablePtr, newStablePtr, deRefStablePtr)
 
-import Language.Javascript.JSaddle (Result, JSM)
-import Language.Javascript.JSaddle.Types (Batch(..))
+import Language.Javascript.JSaddle (Results, JSM)
 import Language.Javascript.JSaddle.Run (runJavaScript)
 import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
 
 newtype WKWebView = WKWebView (Ptr WKWebView)
 
 foreign export ccall jsaddleStart :: StablePtr (IO ()) -> IO ()
-foreign export ccall jsaddleResult :: StablePtr (Result -> IO ()) -> CString -> IO ()
-foreign import ccall addJSaddleHandler :: WKWebView -> StablePtr (IO ()) -> StablePtr (Result -> IO ()) -> IO ()
+foreign export ccall jsaddleResult :: StablePtr (Results -> IO ()) -> CString -> IO ()
+foreign export ccall withWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
+foreign import ccall addJSaddleHandler :: WKWebView -> StablePtr (IO ()) -> StablePtr (Results -> IO ()) -> IO ()
 foreign import ccall loadHTMLString :: WKWebView -> CString -> IO ()
 foreign import ccall evaluateJavaScript :: WKWebView -> CString -> IO ()
+foreign import ccall runInWKWebView :: StablePtr (WKWebView -> IO ()) -> CString -> IO ()
 
+run :: JSM () -> IO ()
+run f = do
+    handler <- newStablePtr (jsaddleMain f)
+    progName <- getProgName
+    withCString progName $ runInWKWebView handler
+
 jsaddleMain :: JSM () -> WKWebView -> IO ()
 jsaddleMain f webView = do
     (processResult, start) <- runJavaScript (\batch ->
@@ -46,13 +55,18 @@
 jsaddleStart :: StablePtr (IO ()) -> IO ()
 jsaddleStart ptrHandler = join (deRefStablePtr ptrHandler)
 
-jsaddleResult :: StablePtr (Result -> IO ()) -> CString -> IO ()
+jsaddleResult :: StablePtr (Results -> IO ()) -> CString -> IO ()
 jsaddleResult ptrHandler s = do
     processResult <- deRefStablePtr ptrHandler
     result <- packCString s
     case decode (fromStrict result) of
         Nothing -> error $ "jsaddle WebSocket decode failed : " <> show result
         Just r  -> processResult r
+
+withWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
+withWebView webView ptrF = do
+    f <- deRefStablePtr ptrF
+    f webView
 
 jsaddleJs :: ByteString
 jsaddleJs = ghcjsHelpers <> "\
